From 3832c767f98c67769cdea69dcea41c3a3adbc7e0 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:03:18 -0700 Subject: [PATCH 1/7] Improve error reporting for invalid plugin configurations --- .../src/pluginFramework/RushMcpPluginLoader.ts | 17 ++++++++++++++--- .../src/schemas/rush-mcp.schema.json | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts index 58fc212603f..4cb30fbbb7d 100644 --- a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts +++ b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts @@ -85,6 +85,10 @@ export class RushMcpPluginLoader { this._mcpServer = mcpServer; } + private static _formatError(e: Error): string { + return e.stack ?? RushMcpPluginLoader._formatError(e); + } + public async loadAsync(): Promise { const rushMcpFilePath: string = path.join( this._rushWorkspacePath, @@ -169,7 +173,10 @@ export class RushMcpPluginLoader { } pluginFactory = entryPointModule.default; } catch (e) { - throw new Error(`Unable to load plugin entry point at ${fullEntryPointPath}: ` + e.toString()); + throw new Error( + `Unable to load plugin entry point at ${fullEntryPointPath}:\n` + + RushMcpPluginLoader._formatError(e) + ); } const session: RushMcpPluginSessionInternal = new RushMcpPluginSessionInternal(this._mcpServer); @@ -178,14 +185,18 @@ export class RushMcpPluginLoader { try { plugin = pluginFactory(session, rushMcpPluginOptions); } catch (e) { - throw new Error(`Error invoking entry point for plugin ${jsonManifest.pluginName}:` + e.toString()); + throw new Error( + `Error invoking entry point for plugin ${jsonManifest.pluginName}:\n` + + RushMcpPluginLoader._formatError(e) + ); } try { await plugin.onInitializeAsync(); } catch (e) { throw new Error( - `Error occurred in onInitializeAsync() for plugin ${jsonManifest.pluginName}:` + e.toString() + `Error occurred in onInitializeAsync() for plugin ${jsonManifest.pluginName}:\n` + + RushMcpPluginLoader._formatError(e) ); } } diff --git a/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json b/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json index 14904b86cc3..3030981d4ef 100644 --- a/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json +++ b/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json @@ -22,7 +22,7 @@ "description": "The name of the plugin. This is used to identify the plugin in the MCP server." } }, - "required": ["packageName", "autoinstaller"], + "required": ["packageName", "autoinstaller", "pluginName"], "additionalProperties": false } } From 4c36dea87b1fec1a171965c355df98bcda335eb0 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:03:32 -0700 Subject: [PATCH 2/7] Fix an AJV error --- .../src/rush-mcp-example-plugin.schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json b/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json index 534b3291d31..55da2c56a0e 100644 --- a/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json +++ b/build-tests/rush-mcp-example-plugin/src/rush-mcp-example-plugin.schema.json @@ -1,5 +1,5 @@ { - "$schema": "https://json-schema.org/draft/2020-12/schema", + "$schema": "http://json-schema.org/draft-07/schema#", "title": "State Capital Map", "type": "object", "required": ["capitalsByState"], From 0da19515ced1bc02dc1b617148711f46068c2351 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:04:10 -0700 Subject: [PATCH 3/7] Fix an issue where "." isn't resolved properly on Windows --- apps/rush-mcp-server/src/start.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/rush-mcp-server/src/start.ts b/apps/rush-mcp-server/src/start.ts index ac6441963b5..4efc17caec7 100644 --- a/apps/rush-mcp-server/src/start.ts +++ b/apps/rush-mcp-server/src/start.ts @@ -7,11 +7,15 @@ import { log } from './utilities/log'; import { RushMCPServer } from './server'; const main = async (): Promise => { - const rushWorkspacePath: string | undefined = process.argv[2]; + let rushWorkspacePath: string | undefined = process.argv[2]; if (!rushWorkspacePath) { throw new Error('Please provide workspace root path as the first argument'); } + if (rushWorkspacePath === '.') { + rushWorkspacePath = process.cwd(); + } + const server: RushMCPServer = new RushMCPServer(rushWorkspacePath); await server.startAsync(); const transport: StdioServerTransport = new StdioServerTransport(); From e2fd2785c6e5fc357f93e4dd19f68d9020e83107 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:19:39 -0700 Subject: [PATCH 4/7] rush-mcp.json should not specify pluginName because there's only one plugin per NPM package, and its pluginName is found in the manifest --- .../src/pluginFramework/RushMcpPluginLoader.ts | 7 +------ apps/rush-mcp-server/src/schemas/rush-mcp.schema.json | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts index 4cb30fbbb7d..f0715d4d0fe 100644 --- a/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts +++ b/apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts @@ -39,11 +39,6 @@ export interface IJsonRushMcpPlugin { * @rushstack/mcp-server will ensure this folder is installed before loading the plugin. */ autoinstaller: string; - - /** - * The name of the plugin. This is used to identify the plugin in the MCP server. - */ - pluginName: string; } /** @@ -155,7 +150,7 @@ export class RushMcpPluginLoader { const mcpPluginSchema: JsonSchema = await JsonSchema.fromFile(mcpPluginSchemaFilePath); const rushMcpPluginOptionsFilePath: string = path.resolve( this._rushWorkspacePath, - `common/config/rush-mcp/${jsonMcpPlugin.pluginName}.json` + `common/config/rush-mcp/${jsonManifest.pluginName}.json` ); // Example: /path/to/my-repo/common/config/rush-mcp/rush-mcp-example-plugin.json rushMcpPluginOptions = await JsonFile.loadAndValidateAsync( diff --git a/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json b/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json index 3030981d4ef..0dd9a9546fe 100644 --- a/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json +++ b/apps/rush-mcp-server/src/schemas/rush-mcp.schema.json @@ -16,13 +16,9 @@ "autoinstaller": { "type": "string", "description": "The name of a Rush autoinstaller with this package as its dependency." - }, - "pluginName": { - "type": "string", - "description": "The name of the plugin. This is used to identify the plugin in the MCP server." } }, - "required": ["packageName", "autoinstaller", "pluginName"], + "required": ["packageName", "autoinstaller"], "additionalProperties": false } } From 41c6f37a3da4aaac5c7b6ec3c2740c25bb92795c Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:21:07 -0700 Subject: [PATCH 5/7] rush change --- .../octogonz-mcp-fixes_2025-06-06-07-20.json | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 common/changes/@rushstack/mcp-server/octogonz-mcp-fixes_2025-06-06-07-20.json diff --git a/common/changes/@rushstack/mcp-server/octogonz-mcp-fixes_2025-06-06-07-20.json b/common/changes/@rushstack/mcp-server/octogonz-mcp-fixes_2025-06-06-07-20.json new file mode 100644 index 00000000000..57999305204 --- /dev/null +++ b/common/changes/@rushstack/mcp-server/octogonz-mcp-fixes_2025-06-06-07-20.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@rushstack/mcp-server", + "comment": "Fix some errors when loading plugins", + "type": "patch" + } + ], + "packageName": "@rushstack/mcp-server" +} \ No newline at end of file From d202f4fc3487fa6407472d93b9b2944fd7051938 Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:37:06 -0700 Subject: [PATCH 6/7] Improve path resolution fix --- apps/rush-mcp-server/src/start.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/rush-mcp-server/src/start.ts b/apps/rush-mcp-server/src/start.ts index 4efc17caec7..f7863f52dde 100644 --- a/apps/rush-mcp-server/src/start.ts +++ b/apps/rush-mcp-server/src/start.ts @@ -1,22 +1,28 @@ // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. +import * as path from 'path'; +import { FileSystem } from '@rushstack/node-core-library'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { log } from './utilities/log'; import { RushMCPServer } from './server'; const main = async (): Promise => { - let rushWorkspacePath: string | undefined = process.argv[2]; + const rushWorkspacePath: string | undefined = process.argv[2]; if (!rushWorkspacePath) { throw new Error('Please provide workspace root path as the first argument'); } - if (rushWorkspacePath === '.') { - rushWorkspacePath = process.cwd(); + const rushWorkspaceFullPath: string = path.resolve(rushWorkspacePath); + + if (!(await FileSystem.existsAsync(rushWorkspaceFullPath))) { + throw new Error( + 'The specified workspace root path does not exist:\n ' + JSON.stringify(rushWorkspacePath) + ); } - const server: RushMCPServer = new RushMCPServer(rushWorkspacePath); + const server: RushMCPServer = new RushMCPServer(rushWorkspaceFullPath); await server.startAsync(); const transport: StdioServerTransport = new StdioServerTransport(); await server.connect(transport); From 98db57a5ab259552bdda2e73519fe7bc7fb7db1e Mon Sep 17 00:00:00 2001 From: Pete Gonzalez <4673363+octogonz@users.noreply.github.com> Date: Fri, 6 Jun 2025 00:55:29 -0700 Subject: [PATCH 7/7] Use RushSdkLoader to specify the rushJsonSearchFolder; this will also give us more control for managing its logging/progress --- apps/rush-mcp-server/src/start.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/apps/rush-mcp-server/src/start.ts b/apps/rush-mcp-server/src/start.ts index f7863f52dde..fa74da180ea 100644 --- a/apps/rush-mcp-server/src/start.ts +++ b/apps/rush-mcp-server/src/start.ts @@ -2,11 +2,13 @@ // See LICENSE in the project root for license information. import * as path from 'path'; +import * as process from 'process'; import { FileSystem } from '@rushstack/node-core-library'; +import { RushSdkLoader } from '@rushstack/rush-sdk/loader'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { log } from './utilities/log'; -import { RushMCPServer } from './server'; +import type { RushMCPServer } from './server'; const main = async (): Promise => { const rushWorkspacePath: string | undefined = process.argv[2]; @@ -22,7 +24,14 @@ const main = async (): Promise => { ); } - const server: RushMCPServer = new RushMCPServer(rushWorkspaceFullPath); + // Load rush-sdk from the specified repository + await RushSdkLoader.loadAsync({ + rushJsonSearchFolder: rushWorkspaceFullPath + }); + + const RushMCPServerClass: typeof RushMCPServer = (await import('./server')).RushMCPServer; + + const server: RushMCPServer = new RushMCPServerClass(rushWorkspaceFullPath); await server.startAsync(); const transport: StdioServerTransport = new StdioServerTransport(); await server.connect(transport);