Skip to content

Commit 3a2398d

Browse files
committed
feat: resolve the todos
1 parent fd86638 commit 3a2398d

File tree

6 files changed

+55
-13
lines changed

6 files changed

+55
-13
lines changed

apps/rush-mcp-server/src/pluginFramework/IRushMcpTool.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import type { CallToolResult } from './zodTypes';
99
* MCP plugins should implement this interface.
1010
* @public
1111
*/
12-
export interface IRushMcpTool<TSchema extends zod.ZodTypeAny = zod.ZodTypeAny> {
12+
export interface IRushMcpTool<
13+
TSchema extends zod.ZodObject<zod.ZodRawShape> = zod.ZodObject<zod.ZodRawShape>
14+
> {
1315
readonly schema: TSchema;
1416
executeAsync(input: zod.infer<TSchema>): Promise<CallToolResult>;
1517
}

apps/rush-mcp-server/src/pluginFramework/RushMcpPluginLoader.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// See LICENSE in the project root for license information.
33

44
import * as path from 'path';
5-
import { FileSystem, Import, JsonFile, JsonSchema } from '@rushstack/node-core-library';
5+
import { FileSystem, Import, JsonFile, type JsonObject, JsonSchema } from '@rushstack/node-core-library';
66
import { Autoinstaller } from '@rushstack/rush-sdk/lib/logic/Autoinstaller';
77
import { RushGlobalFolder } from '@rushstack/rush-sdk/lib/api/RushGlobalFolder';
88
import { RushConfiguration } from '@rushstack/rush-sdk/lib/api/RushConfiguration';
9+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
910

1011
import type { IRushMcpPlugin, RushMcpPluginFactory } from './IRushMcpPlugin';
1112
import { RushMcpPluginSessionInternal } from './RushMcpPluginSession';
@@ -38,6 +39,11 @@ export interface IJsonRushMcpPlugin {
3839
* @rushstack/mcp-server will ensure this folder is installed before loading the plugin.
3940
*/
4041
autoinstaller: string;
42+
43+
/**
44+
* The name of the plugin. This is used to identify the plugin in the MCP server.
45+
*/
46+
pluginName: string;
4147
}
4248

4349
/**
@@ -72,9 +78,11 @@ export class RushMcpPluginLoader {
7278
JsonSchema.fromLoadedObject(rushMcpPluginSchemaObject);
7379

7480
private readonly _rushWorkspacePath: string;
81+
private readonly _mcpServer: McpServer;
7582

76-
public constructor(rushWorkspacePath: string) {
83+
public constructor(rushWorkspacePath: string, mcpServer: McpServer) {
7784
this._rushWorkspacePath = rushWorkspacePath;
85+
this._mcpServer = mcpServer;
7886
}
7987

8088
public async loadAsync(): Promise<void> {
@@ -84,7 +92,6 @@ export class RushMcpPluginLoader {
8492
);
8593

8694
if (!(await FileSystem.existsAsync(rushMcpFilePath))) {
87-
// Should we report an error here?
8895
return;
8996
}
9097

@@ -135,7 +142,23 @@ export class RushMcpPluginLoader {
135142
RushMcpPluginLoader._rushMcpPluginSchemaObject
136143
);
137144

138-
// TODO: Load and validate config file if defined by the manifest
145+
let rushMcpPluginOptions: JsonObject = {};
146+
if (jsonManifest.configFileSchema) {
147+
const mcpPluginSchemaFilePath: string = path.resolve(
148+
installedPluginPackageFolder,
149+
jsonManifest.configFileSchema
150+
);
151+
const mcpPluginSchema: JsonSchema = await JsonSchema.fromFile(mcpPluginSchemaFilePath);
152+
const rushMcpPluginOptionsFilePath: string = path.resolve(
153+
this._rushWorkspacePath,
154+
`common/config/rush-mcp/${jsonMcpPlugin.pluginName}.json`
155+
);
156+
// Example: /path/to/my-repo/common/config/rush-mcp/rush-mcp-example-plugin.json
157+
rushMcpPluginOptions = await JsonFile.loadAndValidateAsync(
158+
rushMcpPluginOptionsFilePath,
159+
mcpPluginSchema
160+
);
161+
}
139162

140163
const fullEntryPointPath: string = path.join(installedPluginPackageFolder, jsonManifest.entryPoint);
141164
let pluginFactory: RushMcpPluginFactory;
@@ -149,12 +172,11 @@ export class RushMcpPluginLoader {
149172
throw new Error(`Unable to load plugin entry point at ${fullEntryPointPath}: ` + e.toString());
150173
}
151174

152-
const session: RushMcpPluginSessionInternal = new RushMcpPluginSessionInternal();
175+
const session: RushMcpPluginSessionInternal = new RushMcpPluginSessionInternal(this._mcpServer);
153176

154177
let plugin: IRushMcpPlugin;
155178
try {
156-
// TODO: Replace "{}" with the plugin's parsed config file JSON
157-
plugin = pluginFactory(session, {});
179+
plugin = pluginFactory(session, rushMcpPluginOptions);
158180
} catch (e) {
159181
throw new Error(`Error invoking entry point for plugin ${jsonManifest.pluginName}:` + e.toString());
160182
}

apps/rush-mcp-server/src/pluginFramework/RushMcpPluginSession.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
22
// See LICENSE in the project root for license information.
33

4-
import type { IRushMcpTool } from './IRushMcpTool';
54
import * as zod from 'zod';
5+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp';
6+
7+
import type { IRushMcpTool } from './IRushMcpTool';
68
import type { zodModule } from './zodTypes';
79

810
/**
@@ -26,11 +28,23 @@ export abstract class RushMcpPluginSession {
2628
}
2729

2830
export class RushMcpPluginSessionInternal extends RushMcpPluginSession {
29-
public constructor() {
31+
private readonly _mcpServer: McpServer;
32+
33+
public constructor(mcpServer: McpServer) {
3034
super();
35+
this._mcpServer = mcpServer;
3136
}
3237

3338
public override registerTool(options: IRegisterToolOptions, tool: IRushMcpTool): void {
34-
// TODO: Register the tool
39+
if (options.description) {
40+
this._mcpServer.tool(
41+
options.toolName,
42+
options.description,
43+
tool.schema.shape,
44+
tool.executeAsync.bind(tool)
45+
);
46+
} else {
47+
this._mcpServer.tool(options.toolName, tool.schema.shape, tool.executeAsync.bind(tool));
48+
}
3549
}
3650
}

apps/rush-mcp-server/src/schemas/rush-mcp.schema.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
"autoinstaller": {
1717
"type": "string",
1818
"description": "The name of a Rush autoinstaller with this package as its dependency."
19+
},
20+
"pluginName": {
21+
"type": "string",
22+
"description": "The name of the plugin. This is used to identify the plugin in the MCP server."
1923
}
2024
},
2125
"required": ["packageName", "autoinstaller"],

apps/rush-mcp-server/src/server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export class RushMCPServer extends McpServer {
2525
});
2626

2727
this._rushWorkspacePath = rushWorkspacePath;
28-
this._pluginLoader = new RushMcpPluginLoader(this._rushWorkspacePath);
28+
this._pluginLoader = new RushMcpPluginLoader(this._rushWorkspacePath, this);
2929
}
3030

3131
public async startAsync(): Promise<void> {

common/reviews/api/mcp-server.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export interface IRushMcpPlugin {
2727
}
2828

2929
// @public
30-
export interface IRushMcpTool<TSchema extends zodModule.ZodTypeAny = zodModule.ZodTypeAny> {
30+
export interface IRushMcpTool<TSchema extends zodModule.ZodObject<zodModule.ZodRawShape> = zodModule.ZodObject<zodModule.ZodRawShape>> {
3131
// (undocumented)
3232
executeAsync(input: zodModule.infer<TSchema>): Promise<CallToolResult>;
3333
// (undocumented)

0 commit comments

Comments
 (0)