Skip to content

Follow-ups to #5215 #5216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
18 changes: 18 additions & 0 deletions apps/rush-mcp-server/rush-doc-fragment.mock.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[
{
"text": "---\ntitle: rush install-autoinstaller\n---\n\n```\nusage: rush install-autoinstaller [-h] --name AUTOINSTALLER_NAME\n\nUse this command to install dependencies for an autoinstaller folder.\n\nOptional arguments:\n -h, --help Show this help message and exit.\n --name AUTOINSTALLER_NAME\n The name of the autoinstaller, which must be one of\n the folders under common/autoinstallers.\n```\n\n## See also\n\n- [rush update-autoinstaller](../commands/rush_update-autoinstaller.md)\n- [rush init-autoinstaller](../commands/rush_init-autoinstaller.md)",
"score": 0.7231232
},
{
"text": "---\ntitle: rush install-autoinstaller\n---\n\n```\n用法:rush install-autoinstaller [-h] --name AUTOINSTALLER_NAME\n\n使用该指令给一个项目安装依赖。\n\n可选参数:\n -h, --help 展示帮助信息并退出\n --name AUTOINSTALLER_NAME\n 指定自动安装的包名,它必须是 common/autoinstallers\n 下的一个文件夹。\n```\n\n## See also\n\n- [rush update-autoinstaller](../commands/rush_update-autoinstaller.md)\n- [rush init-autoinstaller](../commands/rush_init-autoinstaller.md)",
"score": 0.7132133
},
{
"text": "---\ntitle: rush update-autoinstaller\n---\n\n```\nusage: rush update-autoinstaller [-h] --name AUTOINSTALLER_NAME\n\nUse this command to regenerate the shrinkwrap file for an autoinstaller\nfolder.\n\nOptional arguments:\n -h, --help Show this help message and exit.\n --name AUTOINSTALLER_NAME\n The name of the autoinstaller, which must be one of\n the folders under common/autoinstallers.\n```\n\n## See also\n\n- [rush install-autoinstaller](../commands/rush_install-autoinstaller.md)\n- [rush init-autoinstaller](../commands/rush_init-autoinstaller.md)",
"score": 0.6632131
},
{
"text": "---\ntitle: rush update-autoinstaller\n---\n\n```\nusage: rush update-autoinstaller [-h] --name AUTOINSTALLER_NAME\n\nUse this command to regenerate the shrinkwrap file for an autoinstaller\nfolder.\n\nOptional arguments:\n -h, --help Show this help message and exit.\n --name AUTOINSTALLER_NAME\n The name of the autoinstaller, which must be one of\n the folders under common/autoinstallers.\n```\n\n## See also\n\n- [rush install-autoinstaller](../commands/rush_install-autoinstaller.md)\n- [rush init-autoinstaller](../commands/rush_init-autoinstaller.md)",
"score": 0.6528328
}
]
33 changes: 21 additions & 12 deletions apps/rush-mcp-server/src/tools/docs.tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// See LICENSE in the project root for license information.

import { z } from 'zod';
import { JsonFile } from '@rushstack/node-core-library';
import path from 'path';

import { BaseTool, type CallToolResult } from './base.tool';

Expand All @@ -20,30 +22,37 @@ export class RushDocsTool extends BaseTool {
super({
name: 'rush_docs',
description:
'Search and retrieve relevant sections from Rush official documentation based on user queries.',
'Search and retrieve relevant sections from the official Rush documentation based on user queries.',
schema: {
userQuery: z.string().describe('The user query to search for relevant documentation sections.')
}
});
}

public async executeAsync({ userQuery }: { userQuery: string }): Promise<CallToolResult> {
// An example of a knowledge base that can run, but needs to be replaced with Microsoft’s service.
const response: Response = await fetch('http://47.120.46.115/search', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: userQuery, topK: 10 })
});
// TODO: replace with Microsoft's service
private _searchDocs(query: string): IDocsResult {
const startTime: number = Date.now();

const results: IDocsResult['results'] = JsonFile.load(
path.join(__dirname, '../rush-doc-fragment.mock.json')
);

return {
query,
results,
count: results.length,
searchTimeMs: Date.now() - startTime
};
}

const result: IDocsResult = (await response.json()) as IDocsResult;
public async executeAsync({ userQuery }: { userQuery: string }): Promise<CallToolResult> {
const docSearchResult: IDocsResult = this._searchDocs(userQuery);

return {
content: [
{
type: 'text',
text: result.results.map((item) => item.text).join('\n')
text: docSearchResult.results.map((item) => item.text).join('\n\n')
}
]
};
Expand Down
4 changes: 2 additions & 2 deletions apps/rush-mcp-server/src/tools/migrate-project.tool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ export class RushMigrateProjectTool extends BaseTool {
type: 'text',
text:
`Project "${projectName}" migrated to subspace "${targetSubspaceName}" successfully. ` +
`You can ask whether user wants to run "rush update --subspace ${targetSubspaceName}" to update the project. ` +
`If user says "yes" you can run "rush update --subspace ${targetSubspaceName}" directly for them.`
`You can ask whether the user wants to run "rush update --subspace ${targetSubspaceName}" to update the project. ` +
`If the user says "yes", you can run "rush update --subspace ${targetSubspaceName}" directly for them.`
}
]
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ export class RushCommandValidatorTool extends BaseTool {
content: [
{
type: 'text',
text: `The package "${packageName}" does not exist in the Rush workspace. You can retrive package name from 'package.json' file in the project folder.`
text: `The package "${packageName}" does not exist in the Rush workspace. You can retrieve the package name from the 'package.json' file in the project folder.`
}
]
};
Expand Down
49 changes: 33 additions & 16 deletions apps/rush-mcp-server/src/utilities/command-runner.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.

import type * as child_process from 'child_process';
import type { ChildProcess } from 'child_process';
import { Executable, type IExecutableSpawnSyncOptions } from '@rushstack/node-core-library';

interface ICommandResult {
Expand Down Expand Up @@ -49,21 +49,38 @@ export class CommandRunner {
): Promise<ICommandResult> {
const commandPath: string = this._resolveCommand(command);

const result: child_process.SpawnSyncReturns<string> = Executable.spawnSync(commandPath, args, options);

const status: number = result.status ?? 1;

if (status !== 0) {
throw new CommandExecutionError(command, args, result.stderr, status);
}

return {
status,
stdout: result.stdout,
stderr: result.stderr,
command,
args
};
return new Promise((resolve, reject) => {
const childProcess: ChildProcess = Executable.spawn(commandPath, args, options);
let stdout: string = '';
let stderr: string = '';

childProcess.stdout?.on('data', (data) => {
stdout += data.toString();
});

childProcess.stderr?.on('data', (data) => {
stderr += data.toString();
});

childProcess.on('close', (status) => {
if (status !== 0) {
reject(new CommandExecutionError(command, args, stderr, status ?? 1));
return;
}

resolve({
status: status ?? 0,
stdout,
stderr,
command,
args
});
});

childProcess.on('error', (error) => {
reject(error);
});
});
}

public static async runRushCommandAsync(
Expand Down
10 changes: 10 additions & 0 deletions common/changes/@rushstack/mcp-server/main_2025-05-02-03-43.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@rushstack/mcp-server",
"comment": "Follow-ups to #5215",
"type": "patch"
}
],
"packageName": "@rushstack/mcp-server"
}
4 changes: 4 additions & 0 deletions common/config/rush/nonbrowser-approved-packages.json
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,10 @@
{
"name": "xmldoc",
"allowedCategories": [ "libraries" ]
},
{
"name": "zod",
"allowedCategories": [ "libraries" ]
}
]
}