Add VSIX support for Fabric MCP server (#1012)

* Initial work on VSIX for Fabric MCP sever

* Enhanced Fabric MCP Server README for VSIX extension

- Added package-specific annotations for VSIX, NuGet, and NPM packages
- Added installation badges with Fabric branding (hidden from VSIX)
- Added GitHub Release badge for version tracking
- Simplified description for VSIX users vs GitHub viewers
- Enhanced example prompts (added 3 new practical examples)
- Hidden CLI tools table from VSIX (not relevant for extension users)
- Added VSIX-specific installation instructions
- Customized support section for different package types
- Updated table of contents to reflect conditional sections
- Followed Azure MCP Server patterns for consistency

* Update Fabric MCP extension display name to 'Microsoft Fabric MCP Server'

* Remove .DS_Store files and add to .gitignore

* Clean up .DS_Store file deletions

* addressing PR comments

* Comment out broken link from the README file

* Fix README

* Fix icon and enabled vsix in build.yml

* Remove Extension Packs

---------

Co-authored-by: Hasan Abo-Shally <HasanAboShally@gmail.com>
This commit is contained in:
Amos HerschandHasan Abo-Shally authored and GitHub committed 2025-11-13 21:10:27 +02:00
1 parent 256f677bc6
commit 4e3c42e4e7
30 files changed
+9165 -13

No files matched your search

+1
View File
@@ -21,3 +21,4 @@ node_modules/
generated/
/docs/commandline
.DS_Store
+64 -5
View File
@@ -1,21 +1,33 @@
# <img height="36" width="36" src="https://learn.microsoft.com/fabric/media/fabric-icon.png" alt="Microsoft Fabric Logo" /> Microsoft Fabric MCP Server
<!--
See eng\scripts\Process-PackageReadMe.ps1 for instruction on how to annotate this README.md for package specific output
-->
# <!-- remove-section: start nuget;vsix remove_fabric_logo --><img height="36" width="36" src="https://learn.microsoft.com/fabric/media/fabric-icon.png" alt="Microsoft Fabric Logo" /> <!-- remove-section: end remove_fabric_logo -->Microsoft Fabric MCP Server <!-- insert-section: nuget;vsix;npm {{ToolTitle}} -->
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](https://github.com/microsoft/mcp/blob/main/LICENSE)
[![Repo](https://img.shields.io/badge/repo-microsoft/mcp-blue)](https://github.com/microsoft/mcp)
<!-- remove-section: start nuget;vsix;npm remove_install_links -->
[![Install Fabric MCP in VS Code](https://img.shields.io/badge/VS_Code-Install_Fabric_MCP_Server-0098FF?style=flat-square&logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=fabric.vscode-fabric)
[![GitHub](https://img.shields.io/badge/github-microsoft/mcp-blue.svg?style=flat-square&logo=github&color=6e3fa3)](https://github.com/microsoft/mcp)
[![GitHub Release](https://img.shields.io/github/v/release/microsoft/mcp?include_prereleases&filter=Fabric.Mcp.*&style=flat-square&color=6e3fa3)](https://github.com/microsoft/mcp/releases?q=Fabric.Mcp.Server-)
[![License](https://img.shields.io/badge/license-MIT-green?style=flat-square&color=6e3fa3)](https://github.com/microsoft/mcp/blob/main/LICENSE)
<!-- remove-section: end remove_install_links -->
<!-- insert-section: vsix {{A Visual Studio Code extension that provides AI agents with access to Microsoft Fabric's API specifications, schemas, examples, and best practices through the Model Context Protocol (MCP).}} -->
<!-- remove-section: start vsix -->
A local, AI-friendly Model Context Protocol (MCP) server that packages Microsoft Fabric's OpenAPI specifications, schema definitions, examples, and curated guidance into a single context layer for AI agents and developer tools.
Why this project?
- Provide a reliable, local-first source of Fabric API context for AI assistants and code generation tools.
- Reduce the risk of leaking production credentials while enabling rich, example-driven development.
- Make Fabric API discovery, schema lookup, and best-practice retrieval reproducible and scriptable.
<!-- remove-section: end -->
---
## Table of Contents
- [What Can You Do?](#what-can-you-do)
- [Getting Started](#getting-started)
- [Available Tools](#available-tools)
- [Getting Started](#getting-started)<!-- remove-section: start vsix -->
- [Available Tools](#available-tools)<!-- remove-section: end -->
- [Development and Contributing](#development-and-contributing)
- [Support](#support)
- [License](#license)
@@ -23,7 +35,11 @@ Why this project?
---
# What Can You Do?
<!-- insert-section: vsix {{Use the Fabric MCP Server to accelerate your Microsoft Fabric development with AI-powered assistance:}} -->
<!-- remove-section: start vsix -->
The Fabric MCP Server unlocks practical developer workflows by providing local access to Fabric API context:
<!-- remove-section: end -->
- Generate or scaffold Fabric resource definitions (Lakehouse, data pipelines, notebooks, reports).
- Retrieve official OpenAPI specs and JSON schema for validation and code generation.
@@ -36,6 +52,9 @@ The Fabric MCP Server unlocks practical developer workflows by providing local a
- "Create a Lakehouse resource definition with a schema that enforces a string column and a datetime column."
- "Show me the OpenAPI operations for 'notebook' and give a sample creation body."
- "List recommended retry/backoff behavior for Fabric APIs when rate-limited."
- "What are the available Fabric workload types I can work with?"
- "Generate a data pipeline configuration with sample data sources."
- "Show me best practices for authenticating with Fabric APIs."
</details>
@@ -43,6 +62,7 @@ The Fabric MCP Server unlocks practical developer workflows by providing local a
# Getting Started
<!-- remove-section: start vsix -->
## Prerequisites
- .NET 9.x SDK is recommended. Check `global.json` at the repository root for any pinned SDK version.
- If `global.json` pins a preview SDK not installed locally, either install the requested preview SDK or update `global.json` for local development.
@@ -94,9 +114,33 @@ The Fabric MCP Server unlocks practical developer workflows by providing local a
## Common Issues
- **SDK mismatch:** If `dotnet` outputs an SDK resolution error, inspect `global.json` and align local SDKs or update the file.
- **Path issues:** Always use absolute paths in MCP configuration to avoid path resolution problems.
<!-- remove-section: end -->
<!-- insert-section: vsix {{## Installation
The Fabric MCP Server extension is already installed! Simply start the server to begin using it.
### Starting the Server
1. Open the Command Palette (`Ctrl+Shift+P` / `Cmd+Shift+P`)
2. Run **MCP: List Servers**
3. Find **Fabric MCP Server** in the list and click the **Start** button
Once started, the Fabric MCP tools will be available in GitHub Copilot Chat.
### Configuration (Optional)
You can customize the server behavior in VS Code settings (search for "Fabric MCP"):
- **Server Mode**: Control how tools are exposed (`all` by default)
- **Server Arguments**: Add custom arguments to the server startup
Changes require restarting the MCP server from the **MCP: List Servers** view.
}} -->
---
<!-- remove-section: start vsix -->
# Available Tools
Use the server's CLI to query embedded data and examples. Commands are organized under a `publicapis` command group in code.
@@ -112,8 +156,11 @@ Use the server's CLI to query embedded data and examples. Commands are organized
> Always verify the available commands in your build via `--help` before scripting against them; command names and availability are code-driven and may change between releases.
---
<!-- remove-section: end -->
# Development and Contributing
<!-- remove-section: start vsix -->
We welcome contributions. Please follow the repository's contribution guidelines and the checklist below when preparing a PR.
**Contributor checklist**
@@ -124,6 +171,12 @@ We welcome contributions. Please follow the repository's contribution guidelines
- Provide a clear PR description and link relevant issues.
See [CONTRIBUTING](https://github.com/microsoft/mcp/blob/main/CONTRIBUTING.md) for full guidance.
<!-- remove-section: end -->
<!-- insert-section: vsix {{Interested in contributing to the Fabric MCP Server? Visit the [GitHub repository](https://github.com/microsoft/mcp) to get started.
See [CONTRIBUTING](https://github.com/microsoft/mcp/blob/main/CONTRIBUTING.md) for full contribution guidelines.
}} -->
---
@@ -131,10 +184,16 @@ See [CONTRIBUTING](https://github.com/microsoft/mcp/blob/main/CONTRIBUTING.md) f
If you encounter issues:
1. Search existing issues.
2. If none match, file a new issue with:
<!-- remove-section: start vsix -->
- OS and `.NET` SDK version (`dotnet --info`).
- The command used to start the server.
- Server logs and MCP client config (redact secrets).
- Steps to reproduce.
<!-- remove-section: end -->
<!-- insert-section: vsix {{ - Your OS and VS Code version
- Server logs from the **MCP: List Servers** view
- Steps to reproduce the issue
}} -->
For troubleshooting steps, see [TROUBLESHOOTING](https://github.com/microsoft/mcp/blob/main/servers/Fabric.Mcp.Server/TROUBLESHOOTING.md).
+2 -2
View File
@@ -8,10 +8,10 @@ parameters:
default: false
- name: PackageVSIX
type: boolean
default: false
default: true
- name: IncludeNative
type: boolean
default: false
default: true
trigger: none
pr: none
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@@ -6,11 +6,7 @@
<Description>Microsoft Fabric MCP Server - Model Context Protocol implementation for Fabric</Description>
<ReadmeUrl>https://github.com/Microsoft/mcp/blob/main/servers/Fabric.Mcp.Server#readme</ReadmeUrl>
<ReadmePath>$(RepoRoot)/servers/Fabric.Mcp.Server/README.md</ReadmePath>
<!--
Defaults to the microsoft logo if not specified.
<PackageIcon>$(RepoRoot)/servers/Fabric.Mcp.Server/fabric_icon.png</PackageIcon>
-->
<PackageIcon>$(RepoRoot)/servers/Fabric.Mcp.Server/fabric-icon.png</PackageIcon>
<!-- npm settings -->
<NpmPackageName>@microsoft/fabric-mcp</NpmPackageName>
@@ -27,7 +23,7 @@
<!-- vsix settings -->
<VsixPackagePrefix>vscode-fabric-mcp</VsixPackagePrefix>
<VsixDescription></VsixDescription>
<VsixPublisher>ms-azuretools</VsixPublisher>
<VsixPublisher>fabric</VsixPublisher>
</PropertyGroup>
<PropertyGroup>
@@ -0,0 +1,74 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module",
"project": "tsconfig.json"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": [ // Naming is enforced with some exceptions below
"warn",
{ // Names should be either camelCase or PascalCase, both are extensively used throughout this project
"selector": "default",
"format": [
"camelCase",
"PascalCase"
]
},
{ // const variables can also have UPPER_CASE
"selector": "variable",
"modifiers": [
"const"
],
"format": [
"camelCase",
"PascalCase",
"UPPER_CASE"
]
},
{ // private class properties can also have trailing underscores (privateMember_)
"selector": "classProperty",
"modifiers": [
"private"
],
"format": [
"camelCase",
"PascalCase"
],
"trailingUnderscore": "allow"
}
],
"@typescript-eslint/no-floating-promises": "warn", // Floating promises are bad, should do `void thePromise()`
"@typescript-eslint/no-inferrable-types": "off", // This gets upset about e.g. `const foo: string = 'bar'` because it's obvious that it's a string; it doesn't matter enough to enforce
"@typescript-eslint/no-unused-vars": [ // Unused variables aren't allowed, with an exception (below)
"warn",
{ // As a function parameter, unused parameters are allowed
"args": "none"
}
],
"@typescript-eslint/semi": "warn", // Elevate this to warning, we like semicolons
"curly": "warn", // May have been a mistake to include a `{curly}` inside a template string, you might mean `${curly}`
"eqeqeq": "warn", // Should use `===`, not `==`, nearly 100% of the time
"no-extra-boolean-cast": "off", // We !!flatten a lot of things into booleans this way
"no-throw-literal": "warn", // Elevate this from suggestion to warning
"semi": "off", // Covered by @typescript-eslint/semi
"no-empty": [
"error",
{ "allowEmptyCatch": true }
]
},
"ignorePatterns": [
"out",
"dist",
"**/*.d.ts"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
]
}
+104
View File
@@ -0,0 +1,104 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# MCP server binaries (do not commit)
server/
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# Next.js build output
.next
# Nuxt.js build / generate output
.nuxt
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and *not* Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# TernJS port file
.tern-port
# Mac desktop service store files
.DS_Store
dist
out
.vscode-test/
package.nls.*.json
*.vsix
*.zip
@@ -0,0 +1,9 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint",
"amodio.tsl-problem-matcher",
"streetsidesoftware.code-spell-checker"
]
}
+45
View File
@@ -0,0 +1,45 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Fabric MCP Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"preLaunchTask": "${defaultBuildTask}",
"env": {
// Disable telemetry when running the extension under the debugger.
// Set this to "verbose" to show telemetry in the console during debugging.
"DEBUGTELEMETRY": "true"
},
"outFiles": [
"${workspaceFolder}/dist/**/*.js",
"${workspaceFolder}/*.js"
]
},
{
"name": "Debug Unit Tests",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test/suite/unitTests"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}",
"env": {
// Disable test timeouts and telemetry when debugging tests.
"TEST_TIMEOUT": "0",
"DEBUGTELEMETRY": "true"
}
}
]
}
+18
View File
@@ -0,0 +1,18 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
// Help the cSpell extension find our configuration files (it doesn't import .yaml by default)
"cSpell.import": ["./.vscode/cspell.yaml"],
// Block committing directly to main branch
"git.branchProtection": ["main"],
// Keep files nice
"files.trimTrailingWhitespace": true,
"files.insertFinalNewline": true
}
+45
View File
@@ -0,0 +1,45 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "npm watch",
"type": "npm",
"script": "watch",
"problemMatcher": [
"$ts-webpack-watch",
"$tslint-webpack-watch"
],
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "npm dev-build",
"type": "npm",
"script": "dev-build",
"problemMatcher": [
"$ts-webpack-watch",
"$tslint-webpack-watch"
],
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build"
}
},
{
"type": "npm",
"script": "ci-build",
"group": "build",
"problemMatcher": [],
"label": "npm: ci-build",
"detail": "npm run lint && npm run build"
}
]
}
@@ -0,0 +1,16 @@
.azure-pipelines
.eslintignore
.vscode/**
.vscode-test/**
node_modules/**
!node_modules/vscode-nls
out/**
samples/**
src/**
.gitignore
webpack.config.js
**/tsconfig.json
**/tslint.json
**/*.map
**/*.ts
**/*.ps1
@@ -0,0 +1,4 @@
# Release History
### Added
- Initial Release
@@ -0,0 +1,31 @@
# Contributing to Fabric Model Context Protocol (MCP) VS Code Extension
Thank you for your interest in contributing to the Fabric MCP extension for Visual Studio Code!
## Building and Installation
You need to have `Node.js`, `npm` and `powershell` installed. Node 20 (LTS) or later is recommended for development.
1. **Build the VSIX file containing the extension**
```powershell
./eng/scripts/New-BuildInfo.ps1
./eng/scripts/Build-Code.ps1 -Server Fabric.Mcp.Server
./eng/scripts/Pack-Vsix.ps1
```
2. **Install the extension into VS Code**
Open VS Code Extensions view, click on the "..." menu and choose "Install from VSIX...". Point to the VSIX file created in step 1.
## Debugging
To debug extension code, use the "Run Extension" debug configuration provided in the `launch.json` file in the repo. This configuration will (re)build the extension as necessary—no need to build anything manually.
## Submitting a Change
Before submitting a PR, make sure that the unit tests pass (`npm run unit-test`) and that the code linter does not produce any errors or warnings (`npm run lint`).
You might find [ESLint for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) handy for keeping the code error-free as you are making changes.
## Legal
Before we can accept your pull request you will need to sign a **Contribution License Agreement**. All you need to do is to submit a pull request, then the PR will get appropriately labelled (e.g. `cla-required`, `cla-norequired`, `cla-signed`, `cla-already-signed`). If you already signed the agreement we will continue with reviewing the PR, otherwise system will tell you how you can sign the CLA. Once you sign the CLA all future PR's will be labeled as `cla-signed`.
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
"use strict";
/* eslint-disable no-undef */ // Ignore the fact that the engine (which is VSCode) is unknown by the linter
// This is the extension entrypoint, which imports extension.bundle.js, the actual extension code.
// This is in a separate file so we can properly measure extension.bundle.js load time.
const loadStats = {
loadStartTime: Date.now(),
loadEndTime: undefined
};
Object.defineProperty(exports, "__esModule", { value: true });
// eslint-disable-next-line @typescript-eslint/no-var-requires
const extension = require("./dist/extension");
async function activate(ctx) {
return await extension.activate(ctx);
}
async function deactivate(ctx) {
return await extension.deactivate(ctx);
}
exports.activate = activate;
exports.deactivate = deactivate;
/* eslint-enable no-undef */
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,126 @@
{
"name": "vscode-fabric-mcp-server",
"displayName": "Microsoft Fabric MCP Server",
"description": "Provides Model Context Protocol (MCP) integration and tooling for Fabric in Visual Studio Code.",
"version": "0.0.0",
"license": "MIT",
"icon": "resources/package-icon.png",
"preview": false,
"publisher": "fabric",
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"engines": {
"vscode": "^1.101.0"
},
"categories": [
"AI",
"Fabric",
"Chat"
],
"keywords": [
"fabric",
"agent",
"chat",
"copilot",
"language-model-tools",
"mcp",
"model context protocol",
"ai"
],
"bugs": {
"url": "https://github.com/microsoft/mcp/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/mcp"
},
"activationEvents": [
"onStartupFinished"
],
"main": "main.js",
"contributes": {
"mcpServerDefinitionProviders": [
{
"id": "fabricMcpProvider",
"label": "Fabric MCP Server Provider"
}
],
"configuration": {
"type": "object",
"title": "Fabric MCP",
"properties": {
"fabricMcp.enabledServices": {
"type": "array",
"items": {
"type": "string",
"enum": [
"publicapis"
],
"markdownEnumDescriptions": [
"Fabric public APIs — Container registry management."
]
},
"uniqueItems": true,
"markdownDescription": "Service namespaces to enable. Leave empty to enable all. Choose from the list to avoid typing errors. **Note:** Changes require restarting the MCP server if MCP Autostart is not configured (Command Palette → MCP: List Servers → Fabric MCP → Start/Restart)."
},
"fabricMcp.serverMode": {
"type": "string",
"enum": ["single", "namespace", "all"],
"default": "all",
"markdownDescription": "Server Mode determines how tools are exposed: `single` collapses every tool (100+) into one (1) single tool that routes internally, `namespace` collapses all tools down into logical Fabric service namespace grouping, while `all` (default) exposes every MCP tool directly to the MCP client. We recommend all as the right balance between MCP tool count and tool selection accuracy. **Note:** Changes require restarting the MCP server if MCP Autostart is not configured (Command Palette → MCP: List Servers → Fabric MCP → Start/Restart)."
},
"fabricMcp.readOnly": {
"type": "boolean",
"default": false,
"markdownDescription": "Start the Fabric MCP server in read-only mode (operations that modify resources will be disabled). **Note:** Changes require restarting the MCP server if MCP Autostart is not configured (Command Palette → MCP: List Servers → Fabric MCP → Start/Restart)."
}
}
}
},
"scripts": {
"vscode:prepublish": "npm run build",
"compile": "tsc -p ./",
"build": "webpack --mode production --devtool hidden-source-map",
"watch": "webpack --mode development --watch",
"dev-build": "webpack --mode development",
"lint": "eslint src --ext ts",
"test": "npm run compile && node ./out/test/runAllTests.js",
"unit-test": "npm run compile && node ./out/test/runUnitTests.js"
},
"devDependencies": {
"@azure/core-auth": "^1.4",
"@types/chai": "~4",
"@types/glob": "~7",
"@types/mocha": "~10",
"@types/node": "~16",
"@types/semver": "~7",
"@types/sinon": "^17.0.4",
"@types/vscode": "^1.101.0",
"@typescript-eslint/eslint-plugin": "~6",
"@typescript-eslint/parser": "~6",
"@vscode/test-electron": "~2",
"@vscode/vsce": "~3",
"chai": "~4",
"eslint": "~8",
"glob": "~8",
"mocha": "~10",
"node-loader": "~2",
"sinon": "^21.0.0",
"terser-webpack-plugin": "^5.3.14",
"ts-loader": "~9",
"typescript": "~5.1",
"webpack": "^5.99.9",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"@microsoft/vscode-azext-utils": "~2",
"@microsoft/vscode-azureresources-api": "~2",
"dayjs": "~1",
"dotenv": "~16",
"rxjs": "~7",
"semver": "~7",
"yaml": "~2"
}
}
@@ -0,0 +1,3 @@
{
"fabricMcpProvider": "Fabric MCP Server Provider"
}
@@ -0,0 +1,148 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
function isMcpAutoStartEnabled(): boolean {
const config = vscode.workspace.getConfiguration('chat.mcp');
const autostart = config.get<string>('autostart');
return autostart === 'newAndOutdated' || autostart === 'all';
}
function showMcpManualStartNotification() {
const message = 'Fabric MCP Server settings changed. Please restart the server manually: Command Palette → MCP: List Servers → Fabric MCP → Start';
const openCommandPalette = 'Open Command Palette';
void vscode.window.showInformationMessage(message, openCommandPalette).then(selection => {
if (selection === openCommandPalette) {
void vscode.commands.executeCommand('workbench.action.quickOpen', '>MCP: List Servers');
}
});
}
export function activate(context: vscode.ExtensionContext) {
const didChangeEmitter = new vscode.EventEmitter<void>();
// Determine platform and binary path once at activation
let binary = '';
const arch = process.arch;
if (process.platform === 'win32') {
if (arch === 'x64' || arch === 'arm64') {
binary = 'fabmcp.exe';
} else {
throw new Error('Unsupported Windows architecture: ' + arch);
}
} else if (process.platform === 'darwin' || process.platform === 'linux') {
if (arch === 'x64' || arch === 'arm64') {
binary = 'fabmcp';
} else {
throw new Error(`Unsupported ${process.platform} architecture: ${arch}`);
}
} else {
throw new Error('Unsupported platform: ' + process.platform);
}
// Use the binary from the extension's server/os folder
const binPath = path.join(context.extensionPath, 'server', binary);
if (!fs.existsSync(binPath)) {
throw new Error(`Fabric MCP binary not found at ${binPath}. Please ensure the server binary is present.`);
}
// Ensure executable permission on macOS and Linux (once at activation)
if ((process.platform === 'linux' || process.platform === 'darwin')) {
try {
fs.chmodSync(binPath, 0o755);
} catch (e) {
console.warn(`Failed to set executable permission on ${binPath}: ${e}`);
}
}
context.subscriptions.push(
vscode.lm.registerMcpServerDefinitionProvider('fabricMcpProvider', {
onDidChangeMcpServerDefinitions: didChangeEmitter.event,
provideMcpServerDefinitions: async () => {
// Read enabled MCP services from user/workspace settings
const config = vscode.workspace.getConfiguration('fabricMcp');
// Example: ["storage", "keyvault", ...]
const enabledServices: string[] | undefined = config.get('enabledServices');
const args = ['server', 'start'];
// Server Mode (single | namespace | all). Default 'all'.
const mode = config.get<string>('serverMode') || 'all';
if (mode) {
args.push('--mode', mode);
}
// Namespaces filter
if (enabledServices && Array.isArray(enabledServices) && enabledServices.length > 0) {
for (const svc of enabledServices) {
args.push('--namespace', svc);
}
}
// Read-only flag
const readOnly = config.get<boolean>('readOnly') === true;
if (readOnly) {
args.push('--read-only');
}
// Honor VS Code telemetry settings
// Only set FABRIC_MCP_COLLECT_TELEMETRY if telemetry is disabled
const env: Record<string, string | number | null> = {};
if (!vscode.env.isTelemetryEnabled) {
env.FABRIC_MCP_COLLECT_TELEMETRY = 'false';
}
return [
new vscode.McpStdioServerDefinition(
'Fabric MCP',
binPath,
args,
env
)
];
},
resolveMcpServerDefinition: async (server: vscode.McpServerDefinition) => {
// Optionally prompt for secrets or do other setup here
return server;
}
})
);
// Listen for changes to fabricMcp.enabledServices and re-register MCP server
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration((event) => {
if (
event.affectsConfiguration('fabricMcp.enabledServices') ||
event.affectsConfiguration('fabricMcp.serverMode') ||
event.affectsConfiguration('fabricMcp.readOnly')
) {
// Check if MCP autostart is enabled
if (!isMcpAutoStartEnabled()) {
// Show toast notification to guide user to manually start the server
showMcpManualStartNotification();
}
didChangeEmitter.fire();
}
})
);
// Listen for changes to VS Code telemetry settings and re-register MCP server
context.subscriptions.push(
vscode.env.onDidChangeTelemetryEnabled(() => {
void vscode.window.showInformationMessage(
'VS Code telemetry setting changed. Re-registering the MCP server.'
);
didChangeEmitter.fire();
})
);
}
export function deactivate() {
// No process management needed; VS Code will handle server lifecycle
}
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { runExtensionTests } from "./runExtensionTests";
async function main() {
try {
await runExtensionTests('suite', 'allTests');
} catch (err) {
console.error('Failed to run tests: ', err);
process.exit(1);
}
}
void main();
@@ -0,0 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as path from 'path';
import { runTests, downloadAndUnzipVSCode } from '@vscode/test-electron';
export async function runExtensionTests(...testPath: string[] ) {
try {
const vsCodePath = await downloadAndUnzipVSCode('stable');
// The folder containing the Extension Manifest package.json
const extensionDevelopmentPath = path.resolve(__dirname, '..', '..');
// The path to test runner
const extensionTestsPath = path.resolve(__dirname, ...testPath);
await runTests({
vscodeExecutablePath: vsCodePath,
extensionDevelopmentPath: extensionDevelopmentPath,
extensionTestsPath: extensionTestsPath,
extensionTestsEnv: {
DEBUGTELEMETRY: 'true',
}
});
} catch (err) {
console.error('Failed to run tests: ', err);
process.exit(1);
}
}
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { runExtensionTests } from "./runExtensionTests";
async function main() {
try {
await runExtensionTests('suite', 'unitTests');
} catch (err) {
console.error('Failed to run tests: ', err);
process.exit(1);
}
}
void main();
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as path from 'path';
import * as Mocha from 'mocha';
import * as glob from 'glob';
export function run(): Promise<void> {
const opts: Mocha.MochaOptions = {
ui: 'tdd',
color: true,
timeout: process.env.TEST_TIMEOUT ?? "10s"
};
const mocha = new Mocha(opts);
const testsRoot = path.resolve(__dirname, '..');
return new Promise((c, e) => {
glob('suite/**/**.test.js', { cwd: testsRoot }, (err, files) => {
if (err) {
return e(err);
}
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
try {
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
});
}
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import 'mocha';
import * as assert from 'assert';
import * as sinon from 'sinon';
import * as vscode from 'vscode';
import * as path from 'path';
import { activate } from '../../../extension';
suite('Fabric MCP Extension - activate', () => {
let context: vscode.ExtensionContext;
let registerStub: sinon.SinonStub;
let originalPlatform: string;
setup(() => {
// Mock ExtensionContext
context = {
subscriptions: [],
extensionPath: '/ext/path',
// ...other properties can be added as needed
} as unknown as vscode.ExtensionContext;
// Mock the MCP API class to expose executablePath for assertions
class MockMcpStdioServerDefinition {
executablePath: string;
constructor(id: string, executablePath: string, args: string[]) {
this.executablePath = executablePath;
}
}
// Assign the mock class to vscode after it is defined
(vscode as unknown as { McpStdioServerDefinition: typeof MockMcpStdioServerDefinition }).McpStdioServerDefinition = MockMcpStdioServerDefinition;
// Stub the MCP API on vscode.lm
const fakeDisposable = { dispose: sinon.stub() };
(vscode as unknown as { lm: { registerMcpServerDefinitionProvider: sinon.SinonStub } }).lm = {
registerMcpServerDefinitionProvider: sinon.stub().returns(fakeDisposable)
};
registerStub = (vscode as unknown as { lm: { registerMcpServerDefinitionProvider: sinon.SinonStub } }).lm.registerMcpServerDefinitionProvider;
// Stub EventEmitter
sinon.stub(vscode, 'EventEmitter').callsFake(() => {
return {
event: ((listener: (e: unknown) => unknown, thisArgs?: unknown, disposables?: vscode.Disposable[]) => {
return { dispose: () => { } };
}) as vscode.Event<unknown>,
dispose: sinon.stub()
} as Pick<vscode.EventEmitter<unknown>, 'event' | 'dispose'>;
});
// Save and override process.platform
originalPlatform = process.platform;
});
teardown(() => {
sinon.restore();
delete ((vscode as unknown) as { lm?: unknown }).lm;
Object.defineProperty(process, 'platform', { value: originalPlatform });
});
test('registers the MCP server provider and disposables', async () => {
await activate(context);
assert.ok(registerStub.calledOnce, 'registerMcpServerDefinitionProvider should be called');
// Check that a disposable object was added to subscriptions
assert.ok(context.subscriptions.some(s => typeof s === 'object' && typeof s.dispose === 'function'), 'Disposable should be added to subscriptions');
});
test('constructs correct server path for win32', async () => {
Object.defineProperty(process, 'platform', { value: 'win32' });
await activate(context);
const call = registerStub.getCall(0);
const provider = call.args[1];
const defs = await provider.provideMcpServerDefinitions();
assert.strictEqual(defs[0].executablePath, path.join('/ext/path', 'server', 'win-x64', 'fabmcp.exe'));
});
test('constructs correct server path for darwin', async () => {
Object.defineProperty(process, 'platform', { value: 'darwin' });
await activate(context);
const call = registerStub.getCall(0);
const provider = call.args[1];
const defs = await provider.provideMcpServerDefinitions();
assert.strictEqual(defs[0].executablePath, path.join('/ext/path', 'server', 'osx-x64', 'fabmcp'));
});
test('constructs correct server path for linux', async () => {
Object.defineProperty(process, 'platform', { value: 'linux' });
await activate(context);
const call = registerStub.getCall(0);
const provider = call.args[1];
const defs = await provider.provideMcpServerDefinitions();
assert.strictEqual(defs[0].executablePath, path.join('/ext/path', 'server', 'linux-x64', 'fabmcp'));
});
});
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as path from 'path';
import * as Mocha from 'mocha';
import * as glob from 'glob';
export function run(): Promise<void> {
const opts: Mocha.MochaOptions = {
ui: 'tdd',
color: true,
timeout: process.env.TEST_TIMEOUT ?? "10s",
slow: 200
};
const mocha = new Mocha(opts);
const testsRoot = path.resolve(__dirname, '..');
return new Promise((c, e) => {
// Find both legacy and new-style unit tests
glob('suite/unit/**/**.test.js', { cwd: testsRoot }, (err, suiteFiles) => {
if (err) {
return e(err);
}
glob('*.unit.test.js', { cwd: testsRoot }, (err2, unitFiles) => {
if (err2) {
return e(err2);
}
const files = [...suiteFiles, ...unitFiles];
files.forEach(f => mocha.addFile(path.resolve(testsRoot, f)));
try {
mocha.run(failures => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`));
} else {
c();
}
});
} catch (err) {
console.error(err);
e(err);
}
});
});
});
}
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as vscode from 'vscode';
export class TestMemento implements vscode.Memento {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private readonly values: Map<string, any> = new Map<string, any>();
keys(): readonly string[] {
return Array.from(this.values.keys());
}
get<T>(key: string, defaultValue?: T): T | undefined {
return this.values.get(key) ?? defaultValue;
}
update(key: string, value: unknown): Thenable<void> {
this.values.set(key, value);
return Promise.resolve();
}
}
@@ -0,0 +1,23 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import * as vscode from 'vscode';
export async function delay(ms: number, token?: vscode.CancellationToken): Promise<void> {
return new Promise<void>((resolve, reject) => {
let cancellationListener: vscode.Disposable;
const timeout = setTimeout(() => {
cancellationListener?.dispose();
resolve();
}, ms);
if (token) {
cancellationListener = token.onCancellationRequested(() => {
cancellationListener.dispose();
clearTimeout(timeout);
reject();
});
}
});
}
@@ -0,0 +1,5 @@
if ($IsLinux) {
xvfb-run -a npm run unit-test
} else {
npm run unit-test
}
@@ -0,0 +1,26 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2020",
"outDir": "out",
"lib": [
"es2020"
],
"sourceMap": true,
"rootDir": "src",
"strict": true, /* enable all strict type-checking options */
"alwaysStrict": true,
"noFallthroughCasesInSwitch": true,
"noImplicitThis": true,
"noUnusedLocals": false, // ESLint takes care of this for us
"noUnusedParameters": false, // ESLint takes care of this for us
"noImplicitAny": true,
"noImplicitReturns": true,
"strictNullChecks": true,
"skipLibCheck": true // Ignore typing issues in libraries
},
"exclude": [
"node_modules",
".vscode-test"
]
}
@@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// @ts-check // Check this file for typing issues (helps prevent mistakes in options passed)
/* eslint-disable no-undef */ // Ignore the fact that the engine (which is webpack) is unknown
// Good reference point:
// https://github.com/Microsoft/vscode/blob/main/extensions/shared.webpack.config.js
'use strict';
/* eslint-disable @typescript-eslint/no-var-requires */
const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
/* eslint-enable @typescript-eslint/no-var-requires */
const debugWebpack = !!process.env.DEBUG_WEBPACK;
/** @type {import('webpack').Configuration} */ // Here's where we can get typing help even though it's JS
const config = {
target: 'node', // vscode extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
cache: true, // Makes 'watch' builds way faster after the first full build
entry: './src/extension.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/
output: {
// the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/
path: path.resolve(__dirname, 'dist'),
filename: 'extension.js',
libraryTarget: 'commonjs2',
devtoolModuleFilenameTemplate: '../[resource-path]'
},
devtool: 'inline-source-map',
externals: {
vscode: 'commonjs vscode' // the vscode-module is created on-the-fly and must be excluded. Add other modules that cannot be webpack'ed, 📖 -> https://webpack.js.org/configuration/externals/
},
resolve: {
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
extensions: ['.ts', '.js']
},
module: {
rules: [
{
// Default TypeScript loader for .ts files
test: /\.ts$/,
exclude: /node_modules/,
use: [
{
loader: 'ts-loader'
}
]
},
{
test: /\.node$/,
loader: 'node-loader',
},
]
},
plugins: [
],
optimization: {
minimizer: [
new TerserPlugin({
terserOptions: {
/* eslint-disable @typescript-eslint/naming-convention */
// Keep class and function names so that stacks aren't useless and things like UserCancelledError work
keep_classnames: true,
keep_fnames: true,
/* eslint-enable @typescript-eslint/naming-convention */
}
}),
]
},
ignoreWarnings: [
// Suppress some webpack warnings caused by dependencies
{
// Ignore a warning from `@vscode/extension-telemetry`
module: /node_modules\/@vscode\/extension-telemetry/,
message: /Can't resolve 'applicationinsights-native-metrics'/
},
(warning) => false, // No other warnings should be ignored
],
};
if (debugWebpack) {
config.plugins = config.plugins || [];
config.plugins.push(new BundleAnalyzerPlugin({ analyzerMode: 'static' }));
console.log('Config:', config);
}
module.exports = config;
/* eslint-enable no-undef */