src/vs/platform/mcp/node/mcpGatewaySession.ts

287 LOC · 255 covered · 32 uncovered · 66 ranges · 18 concepts · 18 introducers · 16 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filemcpGatewaySession.ts ×2 · 4 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×2 · 4 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×8 · 22 introduced LOCmcpGatewaySession.ts ×8mcpGatewaySession.ts ×4 · 11 introduced LOCmcpGatewaySession.ts ×4mcpGatewaySession.ts ×2 · 5 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×2 · 5 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×2 · 18 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×2 · 6 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×4 · 17 introduced LOCmcpGatewaySession.ts ×4mcpGatewaySession.ts ×1 · 3 introduced LOCmcpGatewaySession.ts ×1mcpGatewaySession.ts ×1 · 2 introduced LOCmcpGatewaySession.ts ×1mcpGatewaySession.ts ×1 · 1 introduced LOCmcpGatewaySession.ts ×1mcpGatewaySession.ts ×1 · 1 introduced LOCmcpGatewaySession.ts ×1mcpGatewaySession.ts ×4 · 26 introduced LOCmcpGatewaySession.ts ×4mcpGatewaySession.ts ×1 · 2 introduced LOCmcpGatewaySession.ts ×1mcpGatewaySession.ts ×11 · 18 introduced LOCmcpGatewaySession.ts ×11mcpGatewaySession.ts ×2 · 19 introduced LOCmcpGatewaySession.ts ×2mcpGatewaySession.ts ×16 · 91 introduced LOCmcpGatewaySession.ts ×16mcpGatewaySession.test|title=McpGatewaySession broadcasts notifications to attached SSE clients|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession broadcasts notifications to attached SSE clients|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession disposes attached SSE clients and callback|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession disposes attached SSE clients and callback|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession emits list changed on tool invoker changes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession emits list changed on tool invoker changes|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession emits resources list changed on resource invoker changes|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession emits resources list changed on resource invoker changes|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession falls back to latest version for unsupported client version|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession falls back to latest version for unsupported client version|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession falls back to latest version when no params provided|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession falls back to latest version when no params provided|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession falls back to latest version when protocolVersion is not a string|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession falls back to latest version when protocolVersion is not a string|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession initialize response includes server info and capabilities|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession initialize response includes server info and capabilities|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession negotiates to each supported protocol version|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession negotiates to each supported protocol version|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession negotiates to older protocol version when client requests it|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession negotiates to older protocol version when client requests it|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession rejects non-initialize requests before initialized notification|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession rejects non-initialize requests before initialized notification|occurrence=1mcpGatewaySession.test|t…mcpGatewaySession.test|title=McpGatewaySession returns initialize result|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession returns initialize result|occurrence=1mcpGatewaySession.test|t…list with raw URIs|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession serves resources/list with raw URIs|occurrence=1list with raw URIs|occur…read with raw URIs|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession serves resources/read with raw URIs|occurrence=1read with raw URIs|occur…list with raw URI templates|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession serves resources/templates/list with raw URI templates|occurrence=1list with raw URI templa…call after initialized notification|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/platform/mcp/test/node/mcpGatewaySession.test|title=McpGatewaySession serves tools/list and tools/call after initialized notification|occurrence=1call after initialized n…Focused file · src/vs/platform/mcp/node/mcpGatewaySession.ts · 287 LOCnode/mcpGatewaySession.t…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- mcpGatewaySession.ts ×16
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import type * as http from 'http';
7 > import {
8 > IJsonRpcNotification, IJsonRpcRequest,
9 > isJsonRpcNotification, isJsonRpcResponse, JsonRpcError, JsonRpcMessage, JsonRpcProtocol, JsonRpcResponse
10 > } from '../../../base/common/jsonRpcProtocol.js';
11 > import { Disposable } from '../../../base/common/lifecycle.js';
12 > import { hasKey } from '../../../base/common/types.js';
13 > import { ILogger } from '../../log/common/log.js';
14 > import { IMcpGatewaySingleServerInvoker } from '../common/mcpGateway.js';
15 > import { MCP } from '../common/modelContextProtocol.js';
16 >
17 > const MCP_LATEST_PROTOCOL_VERSION = '2025-11-25';
18 > const MCP_SUPPORTED_PROTOCOL_VERSIONS = [
19 > '2025-11-25',
20 > '2025-06-18',
21 > '2025-03-26',
22 > '2024-11-05',
23 > '2024-10-07',
24 > ];
25 > const MCP_INVALID_REQUEST = -32600;
26 > const MCP_METHOD_NOT_FOUND = -32601;
27 > const MCP_INVALID_PARAMS = -32602;
28 >
29 > export class McpGatewaySession extends Disposable {
30 > private readonly _rpc: JsonRpcProtocol;
31 > private readonly _sseClients = new Set<http.ServerResponse>();
32 > private _lastEventId = 0;
33 > private _isInitialized = false;
34 >
35 > constructor(
36 > public readonly id: string,
37 > private readonly _logService: ILogger,
38 > private readonly _onDidDispose: () => void,
39 > private readonly _serverInvoker: IMcpGatewaySingleServerInvoker,
40 > ) {
41 > super();
42 >
43 > this._rpc = this._register(new JsonRpcProtocol(
44 > message => this._handleOutgoingMessage(message),
45 > {
46 > handleRequest: request => this._handleRequest(request),
47 > handleNotification: notification => this._handleNotification(notification),
48 > }
49 > ));
50 >
51 > this._register(this._serverInvoker.onDidChangeTools(() => {
52 > if (!this._isInitialized) { mcpGatewaySession.ts ×2
53 return;
54 }
56 > this._logService.info(`[McpGateway][session ${this.id}] Tools changed, notifying client`);
57 > this._rpc.sendNotification({ method: 'notifications/tools/list_changed' });
59 >
60 > this._register(this._serverInvoker.onDidChangeResources(() => {
61 > if (!this._isInitialized) { mcpGatewaySession.ts ×2
62 return;
63 }
65 > this._logService.info(`[McpGateway][session ${this.id}] Resources changed, notifying client`);
66 > this._rpc.sendNotification({ method: 'notifications/resources/list_changed' });
68 > }
69 >
70 > public attachSseClient(_req: http.IncomingMessage, res: http.ServerResponse): void {
71 > res.writeHead(200, { mcpGatewaySession.ts ×2
72 > 'Content-Type': 'text/event-stream',
73 > 'Cache-Control': 'no-cache, no-transform',
74 > 'Connection': 'keep-alive',
75 > });
76 >
77 > res.write(': connected\n\n');
78 > this._sseClients.add(res);
79 > this._logService.info(`[McpGateway][session ${this.id}] SSE client attached (total: ${this._sseClients.size})`);
80 >
81 > res.on('close', () => {
82 > this._sseClients.delete(res);
83 > this._logService.info(`[McpGateway][session ${this.id}] SSE client detached (total: ${this._sseClients.size})`);
84 > });
85 > }
87 > public async handleIncoming(message: JsonRpcMessage | JsonRpcMessage[]): Promise<JsonRpcResponse[]> {
88 > return this._rpc.handleMessage(message); mcpGatewaySession.ts ×11
89 > }
91 > public override dispose(): void {
92 > this._logService.info(`[McpGateway][session ${this.id}] Disposing session (SSE clients: ${this._sseClients.size})`);
93 > for (const client of this._sseClients) {
94 > if (!client.destroyed) { mcpGatewaySession.ts ×2
95 > client.end();
96 > }
97 > }
98 > this._sseClients.clear(); mcpGatewaySession.ts ×16
99 > this._onDidDispose();
100 > super.dispose();
101 > }
102 >
103 > private _handleOutgoingMessage(message: JsonRpcMessage): void {
104 > if (isJsonRpcResponse(message)) { mcpGatewaySession.ts ×11
105 > this._logService.debug(`[McpGateway][session ${this.id}] --> response: ${JSON.stringify(message)}`);
106 > return;
107 > }
109 > if (isJsonRpcNotification(message)) {
110 > this._logService.debug(`[McpGateway][session ${this.id}] --> notification: ${(message as IJsonRpcNotification).method}`);
111 > this._broadcastSse(message);
112 > return;
113 > }
114
115 this._logService.warn('[McpGatewayService] Ignored unsupported outgoing gateway message');
118 > private _broadcastSse(message: JsonRpcMessage): void {
119 > if (this._sseClients.size === 0) { mcpGatewaySession.ts ×4
120 > this._logService.debug(`[McpGateway][session ${this.id}] No SSE clients to broadcast to, dropping message`); mcpGatewaySession.ts ×2
121 > return;
122 > }
124 > const payload = JSON.stringify(message);
125 > const eventId = String(++this._lastEventId);
126 > this._logService.debug(`[McpGateway][session ${this.id}] Broadcasting SSE event id=${eventId} to ${this._sseClients.size}`);
127 > const lines = payload.split(/\r?\n/g);
128 > const data = [
129 > `id: ${eventId}`,
130 > 'event: message',
131 > ...lines.map(line => `data: ${line}`),
132 > '',
133 > ''
134 > ].join('\n');
135 >
136 > for (const client of [...this._sseClients]) {
137 > if (client.destroyed || client.writableEnded) {
138 this._sseClients.delete(client);
139 continue;
140 }
142 > client.write(data);
143 > }
146 > private async _handleRequest(request: IJsonRpcRequest): Promise<unknown> {
147 > this._logService.debug(`[McpGateway][session ${this.id}] <-- request: ${request.method} (id=${String(request.id)})`); mcpGatewaySession.ts ×11
148 >
149 > if (request.method === 'initialize') {
150 > return this._handleInitialize(request); mcpGatewaySession.ts ×4
151 > }
153 > if (!this._isInitialized) {
154 > this._logService.warn(`[McpGateway][session ${this.id}] Rejected request '${request.method}': session not initialized`); mcpGatewaySession.ts ×1
155 > throw new JsonRpcError(MCP_INVALID_REQUEST, 'Session is not initialized');
156 > }
158 > switch (request.method) {
159 > case 'ping':
160 return {};
161 > case 'tools/list': mcpGatewaySession.ts ×11
162 > return this._handleListTools(); mcpGatewaySession.ts ×8
163 > case 'tools/call': mcpGatewaySession.ts ×11
164 > return this._handleCallTool(request); mcpGatewaySession.ts ×8
165 > case 'resources/list': mcpGatewaySession.ts ×11
166 > return this._handleListResources(); mcpGatewaySession.ts ×2
167 > case 'resources/read': mcpGatewaySession.ts ×11
168 > return this._handleReadResource(request); mcpGatewaySession.ts ×4
169 > case 'resources/templates/list': mcpGatewaySession.ts ×11
170 > return this._handleListResourceTemplates(); mcpGatewaySession.ts ×2
171 > default: mcpGatewaySession.ts ×11
172 this._logService.warn(`[McpGateway][session ${this.id}] Unknown method: ${request.method}`);
173 throw new JsonRpcError(MCP_METHOD_NOT_FOUND, `Method not found: ${request.method}`);
175 > }
177 > private _handleNotification(notification: IJsonRpcNotification): void {
178 > this._logService.debug(`[McpGateway][session ${this.id}] <-- notification: ${notification.method}`); mcpGatewaySession.ts ×4
179 >
180 > if (notification.method === 'notifications/initialized') {
181 > this._isInitialized = true;
182 > this._logService.info(`[McpGateway][session ${this.id}] Session initialized`);
183 > this._rpc.sendNotification({ method: 'notifications/tools/list_changed' });
184 > this._rpc.sendNotification({ method: 'notifications/resources/list_changed' });
185 > }
186 > }
188 > private _handleInitialize(request: IJsonRpcRequest): MCP.InitializeResult {
189 > const params = typeof request.params === 'object' && request.params ? request.params as Record<string, unknown> : undefined; mcpGatewaySession.ts ×4
190 > const clientVersion = typeof params?.protocolVersion === 'string' ? params.protocolVersion : undefined;
191 > const clientInfo = params?.clientInfo as { name?: string; version?: string } | undefined;
192 > const negotiatedVersion = clientVersion && MCP_SUPPORTED_PROTOCOL_VERSIONS.includes(clientVersion)
193 > ? clientVersion mcpGatewaySession.ts ×1
194 > : MCP_LATEST_PROTOCOL_VERSION; mcpGatewaySession.ts ×1
196 > this._logService.info(`[McpGateway] Initialize: client=${clientInfo?.name ?? 'unknown'}/${clientInfo?.version ?? '?'}, clientProtocol=${clientVersion ?? '(none)'}, negotiated=${negotiatedVersion}`);
197 > if (clientVersion && clientVersion !== negotiatedVersion) {
198 > this._logService.warn(`[McpGateway] Client requested unsupported protocol version '${clientVersion}', falling back to '${negotiatedVersion}'`); mcpGatewaySession.ts ×1
199 > }
201 > return {
202 > protocolVersion: negotiatedVersion,
203 > capabilities: {
204 > tools: {
205 > listChanged: true,
206 > },
207 > resources: {
208 > listChanged: true,
209 > },
210 > },
211 > serverInfo: {
212 > name: 'VS Code MCP Gateway',
213 > version: '1.0.0',
214 > }
215 > };
216 > }
218 > private async _handleCallTool(request: IJsonRpcRequest): Promise<MCP.CallToolResult> {
219 > const params = typeof request.params === 'object' && request.params ? request.params as Record<string, unknown> : undefined; mcpGatewaySession.ts ×8
220 > if (!params || typeof params.name !== 'string') {
221 throw new JsonRpcError(MCP_INVALID_PARAMS, 'Missing tool call params');
222 }
224 > if (params.arguments && typeof params.arguments !== 'object') {
225 throw new JsonRpcError(MCP_INVALID_PARAMS, 'Invalid tool call arguments');
226 }
228 > const argumentsValue = (params.arguments && typeof params.arguments === 'object')
229 > ? params.arguments as Record<string, unknown>
230 : {};
232 > this._logService.debug(`[McpGateway][session ${this.id}] Calling tool '${params.name}' with args: ${JSON.stringify(argumentsValue)}`);
233 >
234 > try {
235 > const result = await this._serverInvoker.callTool(params.name, argumentsValue);
236 > this._logService.debug(`[McpGateway][session ${this.id}] Tool '${params.name}' completed (isError=${result.isError ?? false}, content blocks=${result.content.length})`);
237 > return result;
238 > } catch (error) {
239 this._logService.error(`[McpGateway][session ${this.id}] Tool '${params.name}' invocation failed`, error);
240 throw new JsonRpcError(MCP_INVALID_PARAMS, String(error));
241 }
244 > private async _handleListTools(): Promise<MCP.ListToolsResult> {
245 > const tools = await this._serverInvoker.listTools(); mcpGatewaySession.ts ×8
246 > this._logService.debug(`[McpGateway][session ${this.id}] Listed ${tools.length} tool(s): [${tools.map(t => t.name).join(', ')}]`);
247 > return { tools: tools as MCP.Tool[] };
248 > }
250 > private async _handleListResources(): Promise<MCP.ListResourcesResult> {
251 > const resources = await this._serverInvoker.listResources(); mcpGatewaySession.ts ×2
252 > this._logService.debug(`[McpGateway][session ${this.id}] Listed ${resources.length} resource(s)`);
253 > return { resources: resources as MCP.Resource[] };
254 > }
256 > private async _handleReadResource(request: IJsonRpcRequest): Promise<MCP.ReadResourceResult> {
257 > const params = typeof request.params === 'object' && request.params ? request.params as Record<string, unknown> : undefined; mcpGatewaySession.ts ×4
258 > if (!params || typeof params.uri !== 'string') {
259 throw new JsonRpcError(MCP_INVALID_PARAMS, 'Missing resource URI');
260 }
262 > this._logService.debug(`[McpGateway][session ${this.id}] Reading resource '${params.uri}'`);
263 > try {
264 > const result = await this._serverInvoker.readResource(params.uri);
265 > this._logService.debug(`[McpGateway][session ${this.id}] Resource read returned ${result.contents.length} content(s)`);
266 > return result;
267 > } catch (error) {
268 this._logService.error(`[McpGateway][session ${this.id}] Resource read failed for '${params.uri}'`, error);
269 throw new JsonRpcError(MCP_INVALID_PARAMS, String(error));
270 }
273 > private async _handleListResourceTemplates(): Promise<MCP.ListResourceTemplatesResult> {
274 > const resourceTemplates = await this._serverInvoker.listResourceTemplates(); mcpGatewaySession.ts ×2
275 > this._logService.debug(`[McpGateway][session ${this.id}] Listed ${resourceTemplates.length} resource template(s)`);
276 > return { resourceTemplates: resourceTemplates as MCP.ResourceTemplate[] };
277 > }
279 >
280 > export function isInitializeMessage(message: JsonRpcMessage | JsonRpcMessage[]): boolean {
281 const first = Array.isArray(message) ? message[0] : message;
282 if (!first || !hasKey(first, { method: true })) {
283 return false;
284 }
285
286 return first.method === 'initialize';
287 }