510
511
constructor(
512
>
initialCollection: McpCollectionDefinition,
mcpServer.ts
513
>
public readonly definition: McpDefinitionReference,
514
>
explicitRoots: URI[] | undefined,
515
>
private readonly _requiresExtensionActivation: boolean | undefined,
516
>
private readonly _primitiveCache: McpServerMetadataCache,
517
>
prefixGenerator: McpPrefixGenerator,
518
>
enablementModel: IEnablementModel,
519
>
@IMcpRegistry private readonly _mcpRegistry: IMcpRegistry,
520
>
@IAllowedMcpServersService private readonly _allowedMcpServersService: IAllowedMcpServersService,
521
>
@IWorkspaceContextService workspacesService: IWorkspaceContextService,
522
>
@IExtensionService private readonly _extensionService: IExtensionService,
523
>
@ILoggerService private readonly _loggerService: ILoggerService,
524
>
@IOutputService private readonly _outputService: IOutputService,
525
>
@ITelemetryService private readonly _telemetryService: ITelemetryService,
526
>
@ICommandService private readonly _commandService: ICommandService,
527
>
@IInstantiationService private readonly _instantiationService: IInstantiationService,
528
>
@IDialogService private readonly _dialogService: IDialogService,
529
>
@INotificationService private readonly _notificationService: INotificationService,
530
>
@IOpenerService private readonly _openerService: IOpenerService,
531
>
@IMcpSamplingService private readonly _samplingService: IMcpSamplingService,
532
>
@IMcpElicitationService private readonly _elicitationService: IMcpElicitationService,
533
>
@IMcpSandboxService private readonly _mcpSandboxService: IMcpSandboxService,
534
>
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
535
>
) {
536
>
super();
537
>
538
>
this.collection = initialCollection;
539
>
this._fullDefinitions = this._mcpRegistry.getServerDefinition(this.collection, this.definition);
540
>
this.enablement = derived(r => enablementModel.readEnabled(definition.id, r));
541
>
542
>
this._policyEpoch = observableFromEvent(this, this._allowedMcpServersService.onDidChangeAllowedMcpServers, () => undefined);
543
>
this._policyBlock = derived<McpConnectionState.Error | undefined>(this, reader => {
544
>
this._policyEpoch.read(reader);
545
>
const connection = this._connection.read(reader);
546
>
if (connection) {
547
// Authoritative: the connection carries the fully resolved launch.
548
return this._evaluatePolicy(this._identityFromLaunch(connection.launchDefinition));
549
}
550
>
// At rest, only decide when we have a concrete, fully-resolved launch. If the definition
mcpServer.ts
551
>
// has not been provided yet (e.g. a lazy/extension server before activation) or the launch
552
>
// still contains unresolved `${...}` variables (inputs, workspace or env vars), a
553
>
// URL/command allow/deny rule cannot be matched reliably, so defer the decision to start()
554
>
// — which re-checks the fully resolved launch — to avoid over-eagerly blocking (and hiding
555
>
// the cached tools of) a server that will actually be allowed once resolved. `chat.mcp.access`
556
>
// and deny-by-name are still enforced at start(), and access also by the enablement layer.
557
>
const launch = this._fullDefinitions.read(reader).server?.launch;
558
>
if (!launch) {
559
return undefined;
560
}
561
>
const identity = this._identityFromLaunch(launch);
mcpServer.ts
562
>
if (McpServer._hasUnresolvedVariables(identity)) {
563
return undefined;
564
}
566
>
});
567
>
568
>
// Stop a live connection when the policy blocks it (e.g. the policy was tightened while the
569
>
// server was running). The block itself is evaluated reactively by `_policyBlock`, which also
570
>
// hides cached tools/prompts and surfaces the reason in the UI.
571
>
this._register(autorun(reader => {
572
>
if (this._policyBlock.read(reader) && this._connection.read(undefined)) {
573
this._connection.set(undefined, undefined); // disposes and stops the connection
574
}
576
>
577
>
this._loggerId = `mcpServer.${definition.id}`;
578
>
this._logger = this._register(_loggerService.createLogger(this._loggerId, { hidden: true, name: `MCP: ${definition.label}` }));
579
>
580
>
const that = this;
581
>
this._register(this._instantiationService.createInstance(McpDevModeServerAttache, this, { get lastModeDebugged() { return that._lastModeDebugged; } }));
582
>
583
>
// If the logger is disposed but not deregistered, then the disposed instance
584
>
// is reused and no-ops. todo@sandy081 this seems like a bug.
585
>
this._register(toDisposable(() => _loggerService.deregisterLogger(this._loggerId)));
586
>
587
>
// 1. Reflect workspaces into the MCP roots
588
>
const workspaces = explicitRoots
589
? observableValue(this, explicitRoots.map(uri => ({ uri, name: basename(uri) })))
591
>
this,
592
>
workspacesService.onDidChangeWorkspaceFolders,
593
>
() => workspacesService.getWorkspace().folders,
594
>
);
595
>
596
>
const uriTransformer = environmentService.remoteAuthority ? createURITransformer(environmentService.remoteAuthority) : undefined;
597
>
598
>
this._register(autorun(reader => {
599
>
const cnx = this._connection.read(reader)?.handler.read(reader);
600
>
if (!cnx) {
601
>
return;
602
>
}
603
604
cnx.roots = workspaces.read(reader)