Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
11 changes: 9 additions & 2 deletions extension/loc/xlf/aspire-vscode.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 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"
}
],
"viewsContainers": {
"activitybar": [
{
Expand Down Expand Up @@ -461,6 +467,12 @@
"default": true,
"description": "%configuration.aspire.enableDebugConfigEnvironmentLogging%",
"scope": "window"
},
"aspire.registerMcpServerInWorkspace": {
"type": "boolean",
"default": false,
"description": "%configuration.aspire.registerMcpServerInWorkspace%",
"scope": "window"
}
}
},
Expand Down
4 changes: 3 additions & 1 deletion extension/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"configuration.aspire.dashboardBrowser.debugFirefox": "Launch Firefox as a debug session (requires Firefox Debugger extension).",
"configuration.aspire.closeDashboardOnDebugEnd": "Close the dashboard browser when the debug session ends. Works with debug browser options (Chrome, Edge, Firefox).",
"configuration.aspire.enableDebugConfigEnvironmentLogging": "Include environment variables when logging debug session configurations. This can help diagnose environment-related issues but may expose sensitive information in logs.",
"configuration.aspire.registerMcpServerInWorkspace": "Whether to register the Aspire MCP server when a workspace is open.",
"command.runAppHost": "Run Aspire apphost",
"command.debugAppHost": "Debug Aspire apphost",
"aspire-vscode.strings.noCsprojFound": "No apphost found in the current workspace.",
Expand Down Expand Up @@ -146,5 +147,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 @@ -29,6 +29,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 @@ -119,6 +120,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
83 changes: 83 additions & 0 deletions extension/src/mcp/AspireMcpServerDefinitionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as vscode from 'vscode';
import { resolveCliPath } from '../utils/cliPath';
import { extensionLogOutputChannel } from '../utils/logging';
import { getRegisterMcpServerInWorkspace, registerMcpServerInWorkspaceSetting } from '../utils/settings';

/**
* 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 _shouldProvide: boolean = false;
private _configChangeDisposable: vscode.Disposable | undefined;
private _workspaceFolderChangeDisposable: vscode.Disposable | undefined;

constructor() {
// Re-evaluate when the setting changes
this._configChangeDisposable = vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration(registerMcpServerInWorkspaceSetting)) {
this.refresh();
}
});

// Re-evaluate when workspace folders change
this._workspaceFolderChangeDisposable = vscode.workspace.onDidChangeWorkspaceFolders(() => {
this.refresh();
});
}

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

const changed =
this._cliAvailable !== cliResult.available ||
this._cliPath !== cliResult.cliPath ||
this._shouldProvide !== shouldProvide;

this._cliAvailable = cliResult.available;
this._cliPath = cliResult.cliPath;
this._shouldProvide = shouldProvide;

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

provideMcpServerDefinitions(_token: vscode.CancellationToken): vscode.ProviderResult<vscode.McpStdioServerDefinition[]> {
if (!this._cliAvailable || !this._shouldProvide || !this._cliPath) {
return [];
}

return [new vscode.McpStdioServerDefinition('Aspire', this._cliPath, ['agent', 'mcp'])];
}

dispose(): void {
this._configChangeDisposable?.dispose();
this._workspaceFolderChangeDisposable?.dispose();
this._onDidChange.dispose();
}
}

/**
* Determines whether the Aspire MCP server should be provided.
*
* The server is provided only when workspace folders are open and the
* "aspire.registerMcpServerInWorkspace" setting is enabled.
*/
async function checkShouldProvideMcpServer(): Promise<boolean> {
if (!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0) {
return false;
}

return getRegisterMcpServerInWorkspace();
}
17 changes: 17 additions & 0 deletions extension/src/utils/settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as vscode from 'vscode';

const aspireConfigSection = 'aspire';

const registerMcpServerInWorkspaceSettingName = 'registerMcpServerInWorkspace';
export const registerMcpServerInWorkspaceSetting = `${aspireConfigSection}.${registerMcpServerInWorkspaceSettingName}`;

/**
* Returns the Aspire extension configuration object.
*/
function getAspireConfig(): vscode.WorkspaceConfiguration {
return vscode.workspace.getConfiguration(aspireConfigSection);
}

export function getRegisterMcpServerInWorkspace(): boolean {
return getAspireConfig().get<boolean>(registerMcpServerInWorkspaceSettingName, 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;
}
}
Loading