Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
"main": "./dist/extension.js",
"l10n": "./l10n",
"contributes": {
"mcpServerDefinitionProviders": [
{
"id": "aspire-mcp-server",
"label": "Aspire"
Comment thread
adamint marked this conversation as resolved.
}
],
"viewsContainers": {
"activitybar": [
{
Expand Down
3 changes: 2 additions & 1 deletion extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,5 +136,6 @@
"walkthrough.getStarted.dashboard.title": "Explore the dashboard",
"walkthrough.getStarted.dashboard.description": "The Aspire Dashboard shows your resources, endpoints, logs, traces, and metrics — all in one place.\n\n[Open dashboard](command:aspire-vscode.openDashboard)",
"walkthrough.getStarted.nextSteps.title": "Next steps",
"walkthrough.getStarted.nextSteps.description": "You're all set! Add integrations for databases, messaging, and cloud services, or deploy your app to production.\n\n[Add an integration](command:aspire-vscode.add)\n\n[Open Aspire docs](https://aspire.dev/docs/)"
"walkthrough.getStarted.nextSteps.description": "You're all set! Add integrations for databases, messaging, and cloud services, or deploy your app to production.\n\n[Add an integration](command:aspire-vscode.add)\n\n[Open Aspire docs](https://aspire.dev/docs/)",
"mcpServerDefinitionProviders.aspire.label": "Aspire"
}
10 changes: 10 additions & 0 deletions extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { AspireEditorCommandProvider } from './editor/AspireEditorCommandProvide
import { AspireAppHostTreeProvider } from './views/AspireAppHostTreeProvider';
import { installCliStableCommand, installCliDailyCommand, verifyCliInstalledCommand } from './commands/walkthroughCommands';
import { AspireStatusBarProvider } from './views/AspireStatusBarProvider';
import { AspireMcpServerDefinitionProvider } from './mcp/AspireMcpServerDefinitionProvider';

let aspireExtensionContext = new AspireExtensionContext();

Expand Down Expand Up @@ -117,6 +118,15 @@ export async function activate(context: vscode.ExtensionContext) {

aspireExtensionContext.initialize(rpcServer, context, debugConfigProvider, dcpServer, terminalProvider, editorCommandProvider);

// Register Aspire MCP server definition provider so the Aspire MCP server
// appears automatically in VS Code's MCP tools list for Aspire workspaces.
const mcpProvider = new AspireMcpServerDefinitionProvider();
if (typeof vscode.lm?.registerMcpServerDefinitionProvider === 'function') {
context.subscriptions.push(vscode.lm.registerMcpServerDefinitionProvider('aspire-mcp-server', mcpProvider));
context.subscriptions.push(mcpProvider);
mcpProvider.refresh();
}

const getEnableSettingsFileCreationPromptOnStartup = () => vscode.workspace.getConfiguration('aspire').get<boolean>('enableSettingsFileCreationPromptOnStartup', true);
const setEnableSettingsFileCreationPromptOnStartup = async (value: boolean) => await vscode.workspace.getConfiguration('aspire').update('enableSettingsFileCreationPromptOnStartup', value, vscode.ConfigurationTarget.Workspace);
const appHostDisposablePromise = checkForExistingAppHostPathInWorkspace(
Expand Down
69 changes: 69 additions & 0 deletions extension/src/mcp/AspireMcpServerDefinitionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import * as vscode from 'vscode';
import { resolveCliPath } from '../utils/cliPath';
import { extensionLogOutputChannel } from '../utils/logging';
import { findAspireSettingsFiles } from '../utils/workspace';

/**
* Provides the Aspire MCP server definition to VS Code so it appears
* automatically in the MCP tools list when the Aspire CLI is available
* and the workspace contains an Aspire project.
*/
export class AspireMcpServerDefinitionProvider implements vscode.McpServerDefinitionProvider<vscode.McpStdioServerDefinition> {
private readonly _onDidChange = new vscode.EventEmitter<void>();
readonly onDidChangeMcpServerDefinitions = this._onDidChange.event;

private _cliPath: string | undefined;
private _cliAvailable: boolean = false;
private _isAspireWorkspace: boolean = false;

async refresh(): Promise<void> {
const [cliResult, isAspire] = await Promise.all([
resolveCliPath(),
checkIsAspireWorkspace(),
]);

const changed =
this._cliAvailable !== cliResult.available ||
this._cliPath !== cliResult.cliPath ||
this._isAspireWorkspace !== isAspire;

this._cliAvailable = cliResult.available;
this._cliPath = cliResult.cliPath;
this._isAspireWorkspace = isAspire;

if (changed) {
extensionLogOutputChannel.info(`Aspire MCP server definition changed: cliAvailable=${cliResult.available}, isAspireWorkspace=${isAspire}`);
this._onDidChange.fire();
}
}

provideMcpServerDefinitions(_token: vscode.CancellationToken): vscode.ProviderResult<vscode.McpStdioServerDefinition[]> {
if (!this._cliAvailable || !this._isAspireWorkspace || !this._cliPath) {
return [];
}
Comment thread
adamint marked this conversation as resolved.

return [new vscode.McpStdioServerDefinition('Aspire', this._cliPath, ['agent', 'mcp'])];
Comment thread
adamint marked this conversation as resolved.
}

dispose(): void {
this._onDidChange.dispose();
}
}

/**
* Checks whether the current workspace appears to be an Aspire workspace
* by looking for .aspire/settings.json files.
*/
async function checkIsAspireWorkspace(): Promise<boolean> {
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) {
return false;
}

// Check for .aspire/settings.json
const settingsFiles = await findAspireSettingsFiles();
if (settingsFiles.length > 0) {
return true;
}

return false;
}
40 changes: 40 additions & 0 deletions extension/src/vscode.proposed.mcpServerDefinitions.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

// Type declarations for the vscode proposed API: mcpServerDefinitions
// https://github.com/microsoft/vscode/blob/main/src/vscode-dts/vscode.proposed.mcpServerDefinitions.d.ts

declare module 'vscode' {

export class McpStdioServerDefinition {
readonly label: string;
cwd?: Uri;
command: string;
args: string[];
env: Record<string, string | number | null>;
version?: string;
constructor(label: string, command: string, args?: string[], env?: Record<string, string | number | null>, version?: string);
}

export class McpHttpServerDefinition {
readonly label: string;
uri: Uri;
headers: Record<string, string>;
version?: string;
constructor(label: string, uri: Uri, headers?: Record<string, string>, version?: string);
}

export type McpServerDefinition = McpStdioServerDefinition | McpHttpServerDefinition;

export interface McpServerDefinitionProvider<T extends McpServerDefinition = McpServerDefinition> {
readonly onDidChangeMcpServerDefinitions?: Event<void>;
provideMcpServerDefinitions(token: CancellationToken): ProviderResult<T[]>;
resolveMcpServerDefinition?(server: T, token: CancellationToken): ProviderResult<T>;
}

export namespace lm {
export function registerMcpServerDefinitionProvider(id: string, provider: McpServerDefinitionProvider): Disposable;
}
}