Skip to content

Commit 308d1f3

Browse files
committed
Adds registerMcpServerDefinitionProvider (wip)
1 parent 0d482fc commit 308d1f3

File tree

5 files changed

+248
-0
lines changed

5 files changed

+248
-0
lines changed

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@
6767
}
6868
},
6969
"contributes": {
70+
"mcpServerDefinitionProviders": [
71+
{
72+
"id": "gitlens.mcpProvider",
73+
"label": "GitLens MCP Provider"
74+
}
75+
],
7076
"configuration": [
7177
{
7278
"id": "current-line-blame",

src/@types/vscode.lm.mcp.d.ts

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License.
4+
* See License.txt in the project root for license information.
5+
*--------------------------------------------------------------------------------------------*/
6+
7+
/**
8+
* Type Definition for Visual Studio Code 1.101 Extension API
9+
* See https://code.visualstudio.com/api for more information
10+
*/
11+
12+
declare module 'vscode' {
13+
/**
14+
* McpStdioServerDefinition represents an MCP server available by running
15+
* a local process and operating on its stdin and stdout streams. The process
16+
* will be spawned as a child process of the extension host and by default
17+
* will not run in a shell environment.
18+
*/
19+
export class McpStdioServerDefinition {
20+
/**
21+
* The human-readable name of the server.
22+
*/
23+
readonly label: string;
24+
25+
/**
26+
* The working directory used to start the server.
27+
*/
28+
cwd?: Uri;
29+
30+
/**
31+
* The command used to start the server. Node.js-based servers may use
32+
* `process.execPath` to use the editor's version of Node.js to run the script.
33+
*/
34+
command: string;
35+
36+
/**
37+
* Additional command-line arguments passed to the server.
38+
*/
39+
args: string[];
40+
41+
/**
42+
* Optional additional environment information for the server. Variables
43+
* in this environment will overwrite or remove (if null) the default
44+
* environment variables of the editor's extension host.
45+
*/
46+
env: Record<string, string | number | null>;
47+
48+
/**
49+
* Optional version identification for the server. If this changes, the
50+
* editor will indicate that tools have changed and prompt to refresh them.
51+
*/
52+
version?: string;
53+
54+
/**
55+
* @param label The human-readable name of the server.
56+
* @param command The command used to start the server.
57+
* @param args Additional command-line arguments passed to the server.
58+
* @param env Optional additional environment information for the server.
59+
* @param version Optional version identification for the server.
60+
*/
61+
constructor(
62+
label: string,
63+
command: string,
64+
args?: string[],
65+
env?: Record<string, string | number | null>,
66+
version?: string,
67+
);
68+
}
69+
70+
/**
71+
* McpHttpServerDefinition represents an MCP server available using the
72+
* Streamable HTTP transport.
73+
*/
74+
export class McpHttpServerDefinition {
75+
/**
76+
* The human-readable name of the server.
77+
*/
78+
readonly label: string;
79+
80+
/**
81+
* The URI of the server. The editor will make a POST request to this URI
82+
* to begin each session.
83+
*/
84+
uri: Uri;
85+
86+
/**
87+
* Optional additional heads included with each request to the server.
88+
*/
89+
headers: Record<string, string>;
90+
91+
/**
92+
* Optional version identification for the server. If this changes, the
93+
* editor will indicate that tools have changed and prompt to refresh them.
94+
*/
95+
version?: string;
96+
97+
/**
98+
* @param label The human-readable name of the server.
99+
* @param uri The URI of the server.
100+
* @param headers Optional additional heads included with each request to the server.
101+
*/
102+
constructor(label: string, uri: Uri, headers?: Record<string, string>, version?: string);
103+
}
104+
105+
/**
106+
* Definitions that describe different types of Model Context Protocol servers,
107+
* which can be returned from the {@link McpServerDefinitionProvider}.
108+
*/
109+
export type McpServerDefinition = McpStdioServerDefinition | McpHttpServerDefinition;
110+
111+
/**
112+
* A type that can provide Model Context Protocol server definitions. This
113+
* should be registered using {@link lm.registerMcpServerDefinitionProvider}
114+
* during extension activation.
115+
*/
116+
export interface McpServerDefinitionProvider<T extends McpServerDefinition = McpServerDefinition> {
117+
/**
118+
* Optional event fired to signal that the set of available servers has changed.
119+
*/
120+
readonly onDidChangeMcpServerDefinitions?: Event<void>;
121+
122+
/**
123+
* Provides available MCP servers. The editor will call this method eagerly
124+
* to ensure the availability of servers for the language model, and so
125+
* extensions should not take actions which would require user
126+
* interaction, such as authentication.
127+
*
128+
* @param token A cancellation token.
129+
* @returns An array of MCP available MCP servers
130+
*/
131+
provideMcpServerDefinitions(token: CancellationToken): ProviderResult<T[]>;
132+
133+
/**
134+
* This function will be called when the editor needs to start a MCP server.
135+
* At this point, the extension may take any actions which may require user
136+
* interaction, such as authentication. Any non-`readonly` property of the
137+
* server may be modified, and the extension should return the resolved server.
138+
*
139+
* The extension may return undefined to indicate that the server
140+
* should not be started, or throw an error. If there is a pending tool
141+
* call, the editor will cancel it and return an error message to the
142+
* language model.
143+
*
144+
* @param server The MCP server to resolve
145+
* @param token A cancellation token.
146+
* @returns The resolved server or thenable that resolves to such. This may
147+
* be the given `server` definition with non-readonly properties filled in.
148+
*/
149+
resolveMcpServerDefinition?(server: T, token: CancellationToken): ProviderResult<T>;
150+
}
151+
152+
export namespace lm {
153+
/**
154+
* Registers a provider that publishes Model Context Protocol servers for the editor to
155+
* consume. This allows MCP servers to be dynamically provided to the editor in
156+
* addition to those the user creates in their configuration files.
157+
*
158+
* Before calling this method, extensions must register the `contributes.mcpServerDefinitionProviders`
159+
* extension point with the corresponding {@link id}, for example:
160+
*
161+
* ```js
162+
* "contributes": {
163+
* "mcpServerDefinitionProviders": [
164+
* {
165+
* "id": "cool-cloud-registry.mcp-servers",
166+
* "label": "Cool Cloud Registry",
167+
* }
168+
* ]
169+
* }
170+
* ```
171+
*
172+
* When a new McpServerDefinitionProvider is available, the editor will present a 'refresh'
173+
* action to the user to discover new servers. To enable this flow, extensions should
174+
* call `registerMcpServerDefinitionProvider` during activation.
175+
* @param id The ID of the provider, which is unique to the extension.
176+
* @param provider The provider to register
177+
* @returns A disposable that unregisters the provider when disposed.
178+
*/
179+
export function registerMcpServerDefinitionProvider(
180+
id: string,
181+
provider: McpServerDefinitionProvider,
182+
): Disposable;
183+
}
184+
}

src/container.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ConfigurationChangeEvent, Disposable, Event, ExtensionContext } fr
22
import { EventEmitter, ExtensionMode } from 'vscode';
33
import {
44
getGkCliIntegrationProvider,
5+
getMcpProvider,
56
getSharedGKStorageLocationProvider,
67
getSupportedGitProviders,
78
getSupportedRepositoryLocationProvider,
@@ -232,6 +233,11 @@ export class Container {
232233
this._disposables.push((this._statusBarController = new StatusBarController(this)));
233234
this._disposables.push((this._codeLensController = new GitCodeLensController(this)));
234235

236+
const mcpProvider = getMcpProvider(this);
237+
if (mcpProvider != null) {
238+
this._disposables.push(mcpProvider);
239+
}
240+
235241
const webviews = new WebviewsController(this);
236242
this._disposables.push(webviews);
237243
this._disposables.push((this._views = new Views(this, webviews)));

src/env/node/gk/mcp.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import type { Event, McpServerDefinition, McpStdioServerDefinition } from 'vscode';
2+
import { version as codeVersion, Disposable, EventEmitter, lm } from 'vscode';
3+
import type { Container } from '../../../container';
4+
import { satisfies } from '../../../system/version';
5+
6+
export class McpProvider implements Disposable {
7+
static #instance: McpProvider | undefined;
8+
9+
static create(container: Container): McpProvider | undefined {
10+
if (!satisfies(codeVersion, '>= 1.101.0') || !lm.registerMcpServerDefinitionProvider) return undefined;
11+
12+
if (this.#instance == null) {
13+
this.#instance = new McpProvider(container);
14+
}
15+
16+
return this.#instance;
17+
}
18+
19+
private readonly _disposable: Disposable;
20+
private readonly _onDidChangeMcpServerDefinitions = new EventEmitter<void>();
21+
get onDidChangeMcpServerDefinitions(): Event<void> {
22+
return this._onDidChangeMcpServerDefinitions.event;
23+
}
24+
25+
private serverDefinitions: McpServerDefinition[] = [];
26+
27+
private constructor(private readonly container: Container) {
28+
this._disposable = Disposable.from(
29+
lm.registerMcpServerDefinitionProvider('gitlens.mcpProvider', {
30+
onDidChangeMcpServerDefinitions: this._onDidChangeMcpServerDefinitions.event,
31+
provideMcpServerDefinitions: () => this.provideMcpServerDefinitions(),
32+
}),
33+
);
34+
}
35+
36+
private provideMcpServerDefinitions(): Promise<McpServerDefinition[]> {
37+
return Promise.resolve([]);
38+
}
39+
40+
registerMcpServer(): Promise<void> {
41+
return Promise.resolve();
42+
}
43+
44+
dispose(): void {
45+
this._disposable.dispose();
46+
}
47+
}

src/env/node/providers.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { GkCliIntegrationProvider } from './gk/cli/integration';
1414
import { LocalRepositoryLocationProvider } from './gk/localRepositoryLocationProvider';
1515
import { LocalSharedGkStorageLocationProvider } from './gk/localSharedGkStorageLocationProvider';
1616
import { LocalGkWorkspacesSharedStorageProvider } from './gk/localWorkspacesSharedStorageProvider';
17+
import { McpProvider } from './gk/mcp';
1718

1819
let gitInstance: Git | undefined;
1920
function ensureGit(container: Container) {
@@ -71,3 +72,7 @@ export function getSupportedWorkspacesStorageProvider(
7172
export function getGkCliIntegrationProvider(container: Container): GkCliIntegrationProvider {
7273
return new GkCliIntegrationProvider(container);
7374
}
75+
76+
export function getMcpProvider(container: Container): McpProvider | undefined {
77+
return McpProvider.create(container);
78+
}

0 commit comments

Comments
 (0)