mcpGatewayToolBrokerChannel.ts ×13

Frontier kind: Code frontier

unlabeled · c_3dc63c05df5d

13 tests · 36959 LOC · 182 files · introduces 0 tests · 124 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
13 ranges124 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
3177 ranges36959 lines · 182 files · Browse complete extent
All tests (intent)
13 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

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 native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 124 introduced LOC across 13 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/mcp/common/mcpGatewayToolBrokerChannel.ts 124 introduced LOC · 13 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- mcpGatewayToolBrokerChannel.ts
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 { CancellationToken } from '../../../../base/common/cancellation.js';
7 > import { Emitter, Event } from '../../../../base/common/event.js';
8 > import { Disposable } from '../../../../base/common/lifecycle.js';
9 > import { autorun } from '../../../../base/common/observable.js';
10 > import { IServerChannel } from '../../../../base/parts/ipc/common/ipc.js';
11 > import { ILogService } from '../../../../platform/log/common/log.js';
12 > import { IMcpGatewayServerDescriptor } from '../../../../platform/mcp/common/mcpGateway.js';
13 > import { MCP } from '../../../../platform/mcp/common/modelContextProtocol.js';
14 > import { URI } from '../../../../base/common/uri.js';
15 > import { McpServer } from './mcpServer.js';
16 > import { IMcpServer, IMcpService, McpCapability, McpServerCacheState, McpToolVisibility } from './mcpTypes.js';
17 > import { startServerAndWaitForLiveTools } from './mcpTypesUtils.js';
18 >
19 > interface ICallToolForServerArgs {
20 > serverId: string;
21 > name: string;
22 > args: Record<string, unknown>;
23 > chatSessionResource?: string;
24 > }
25 >
26 > interface IReadResourceForServerArgs {
27 > serverId: string;
28 > uri: string;
29 > }
30 >
31 > interface IServerIdArg {
32 > serverId: string;
33 > }
34 >
35 > export class McpGatewayToolBrokerChannel extends Disposable implements IServerChannel<unknown> {
36 > private readonly _onDidChangeTools = this._register(new Emitter<void>());
37 > private readonly _onDidChangeResources = this._register(new Emitter<void>());
38 > private readonly _onDidChangeServers = this._register(new Emitter<readonly IMcpGatewayServerDescriptor[]>());
39 >
40 > /**
41 > * Per-server promise that races server startup against the grace period timeout.
42 > * Once set for a server, subsequent list calls await the already-resolved promise
43 > * and return immediately instead of waiting again.
44 > *
45 > * The `resolved` flag tracks whether the promise has settled. If a server's
46 > * cacheState regresses to Unknown/Outdated after the promise resolved (e.g.
47 > * after a cache reset), `_waitForStartup` discards the stale entry and creates
48 > * a fresh race so the server gets another chance to start.
49 > */
50 > private readonly _startupGrace = new Map<string, { promise: Promise<boolean>; resolved: boolean }>();
51 >
52 > constructor(
53 > private readonly _mcpService: IMcpService,
54 > private readonly _logService: ILogService,
55 > private readonly _startupGracePeriodMs = 5000,
56 > ) {
57 > super();
58 > this._logService.debug('[McpGateway][ToolBroker] Initialized');
59 >
60 > let toolsInitialized = false;
61 > this._register(autorun(reader => {
62 > for (const server of this._mcpService.servers.read(reader)) {
63 > server.tools.read(reader);
64 > }
65 >
66 > if (toolsInitialized) {
67 > this._logService.debug('[McpGateway][ToolBroker] Tools changed, firing onDidChangeTools');
68 > this._onDidChangeTools.fire();
69 > } else {
70 > toolsInitialized = true;
71 > }
72 > }));
73 >
74 > let resourcesInitialized = false;
75 > this._register(autorun(reader => {
76 > for (const server of this._mcpService.servers.read(reader)) {
77 > server.capabilities.read(reader);
78 > }
79 >
80 > if (resourcesInitialized) {
81 > this._logService.debug('[McpGateway][ToolBroker] Resources changed, firing onDidChangeResources');
82 > this._onDidChangeResources.fire();
83 > } else {
84 > resourcesInitialized = true;
85 > }
86 > }));
87 >
88 > let serversInitialized = false;
89 > this._register(autorun(reader => {
90 > const servers = this._mcpService.servers.read(reader);
91 >
92 > if (serversInitialized) {
93 > this._logService.debug('[McpGateway][ToolBroker] Servers changed, firing onDidChangeServers');
94 > this._onDidChangeServers.fire(servers.map(s => ({ id: s.definition.id, label: s.definition.label })));
95 > } else {
96 > serversInitialized = true;
97 > }
98 > }));
99 > }
100 >
101 > private _getServerById(serverId: string): IMcpServer | undefined {
102 for (const server of this._mcpService.servers.get()) {
103 if (server.definition.id === serverId) {
107 return undefined;
108 }
110 > private _waitForStartup(server: IMcpServer): Promise<boolean> {
111 const id = server.definition.id;
112 const existing = this._startupGrace.get(id);
133 return this._startupGrace.get(id)!.promise;
134 }
136 > private async _shouldUseCachedData(server: IMcpServer): Promise<boolean> {
137 const cacheState = server.cacheState.get();
138 if (cacheState === McpServerCacheState.Unknown || cacheState === McpServerCacheState.Outdated) {
147 || cacheState === McpServerCacheState.RefreshingFromCached;
148 }
150 > listen<T>(_ctx: unknown, event: string): Event<T> {
151 switch (event) {
152 case 'onDidChangeTools':
160 throw new Error(`Invalid listen: ${event}`);
161 }
163 > async call<T>(_ctx: unknown, command: string, arg?: unknown, cancellationToken?: CancellationToken): Promise<T> {
164 this._logService.debug(`[McpGateway][ToolBroker] IPC call: ${command}`);
165
198 throw new Error(`Invalid call: ${command}`);
199 }
201 > private _listServers(): readonly IMcpGatewayServerDescriptor[] {
202 const servers = this._mcpService.servers.get();
203 const result: IMcpGatewayServerDescriptor[] = [];
208 return result;
209 }
211 > private async _listToolsForServer(serverId: string): Promise<readonly MCP.Tool[]> {
212 const server = this._getServerById(serverId);
213 if (!server) {
225 return tools;
226 }
228 > private async _callToolForServer(serverId: string, name: string, args: Record<string, unknown>, chatSessionResource?: string, token: CancellationToken = CancellationToken.None): Promise<MCP.CallToolResult> {
229 this._logService.debug(`[McpGateway][ToolBroker] callToolForServer '${serverId}' tool '${name}' with args: ${JSON.stringify(args)}`);
230
246 return result;
247 }
249 > private async _listResourcesForServer(serverId: string): Promise<readonly MCP.Resource[]> {
250 const server = this._getServerById(serverId);
251 if (!server) {
272 }
273 }
275 > private async _readResourceForServer(serverId: string, uri: string, token: CancellationToken = CancellationToken.None): Promise<MCP.ReadResourceResult> {
276 const server = this._getServerById(serverId);
277 if (!server) {
284 return result;
285 }
287 > private async _listResourceTemplatesForServer(serverId: string): Promise<readonly MCP.ResourceTemplate[]> {
288 const server = this._getServerById(serverId);
289 if (!server) {
309 }
310 }
312 > private async _ensureServerReady(server: IMcpServer): Promise<boolean> {
313 const cacheState = server.cacheState.get();
314 if (cacheState !== McpServerCacheState.Unknown && cacheState !== McpServerCacheState.Outdated) {