Skip to content

Commit c92a1cf

Browse files
easy1090chenjiahan
andauthored
feat: rsdoctor mcp get auto port (#1072)
Co-authored-by: neverland <chenjiahan.jait@bytedance.com>
1 parent 1515827 commit c92a1cf

File tree

8 files changed

+146
-12
lines changed

8 files changed

+146
-12
lines changed

examples/rsbuild-minimal/rsbuild.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export default defineConfig({
1010
chain.plugin('Rsdoctor').use(RsdoctorRspackPlugin, [
1111
{
1212
disableClientServer: !process.env.ENABLE_CLIENT_SERVER,
13-
features: ['bundle', 'plugins', 'loader', 'resolver'],
1413
output: {
1514
reportCodeType: {
1615
noAssetsAndModuleSource: true,

packages/ai/src/server/server.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
getSimilarPackages,
1717
getLoaderTimeForAllFiles,
1818
getLoaderTimes,
19+
getPort,
1920
} from './tools.js';
2021
import { registerStaticResources } from './resource.js';
2122
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
@@ -274,6 +275,20 @@ server.tool(
274275
},
275276
);
276277

278+
server.tool(Tools.getPort, 'Get the port of the MCP server', {}, async () => {
279+
const res = await getPort();
280+
return {
281+
content: [
282+
{
283+
name: Tools.getPort,
284+
description: 'Get the port of the MCP server',
285+
type: 'text',
286+
text: res,
287+
},
288+
],
289+
};
290+
});
291+
277292
registerStaticResources(server);
278293

279294
export async function runServer() {

packages/ai/src/server/socket.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { Socket, io } from 'socket.io-client';
22
import { logger } from '@rsdoctor/utils/logger';
3+
import { GlobalConfig } from '@rsdoctor/utils/common';
4+
import fs from 'node:fs';
35

46
const map: Record<string, Socket> = {};
57

@@ -19,10 +21,21 @@ export const createSocket = (url: string): Socket => {
1921
export function getPortFromArgs(): number {
2022
const args = process.argv.slice(2); // Skip the first two elements
2123
const portIndex = args.indexOf('--port');
24+
const builderIndex = args.indexOf('--builder');
2225
if (portIndex !== -1 && args[portIndex + 1]) {
2326
return parseInt(args[portIndex + 1], 10);
2427
}
25-
return 3000; // Default port if not specified
28+
if (portIndex === -1) {
29+
const port = getMcpPort(
30+
builderIndex !== -1 ? args[builderIndex + 1] : undefined,
31+
);
32+
if (port) {
33+
return port;
34+
}
35+
}
36+
37+
// If no port is specified, use the default port.
38+
return 3000;
2639
}
2740

2841
export const getWsUrl = async () => {
@@ -42,3 +55,22 @@ export const sendRequest = async (api: string, params = {}) => {
4255
});
4356
return res;
4457
};
58+
59+
export const getMcpPort = (builder?: string) => {
60+
const mcpPortFilePath = GlobalConfig.getMcpConfigPath();
61+
62+
if (!fs.existsSync(mcpPortFilePath)) {
63+
return undefined;
64+
}
65+
66+
const mcpJson = JSON.parse(fs.readFileSync(mcpPortFilePath, 'utf8'));
67+
68+
if (builder) {
69+
const builderPort = mcpJson.portList[builder];
70+
if (builderPort) {
71+
return builderPort;
72+
}
73+
}
74+
75+
return mcpJson.port;
76+
};

packages/ai/src/server/tools.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { SDK } from '@rsdoctor/types';
2-
import { sendRequest } from './socket.js';
2+
import { getWsUrl, sendRequest } from './socket.js';
33
import { toolDescriptions } from '@/prompt/bundle.js';
44

55
export enum Tools {
@@ -19,6 +19,7 @@ export enum Tools {
1919
GetMediaAssetPrompt = 'get_media_asset_prompt',
2020
getLoaderTimeForAllFiles = 'get_loader_time_all_files',
2121
getLoaderTimes = 'get_loader_times',
22+
getPort = 'get_port',
2223
}
2324

2425
// Define the type for the response of getAllChunks
@@ -185,16 +186,22 @@ export const getPackageInfo = async (): Promise<
185186
};
186187

187188
export const getPackageDependency = async () => {
188-
return await sendRequest(SDK.ServerAPI.API.GetPackageDependency, {});
189+
const res = (await sendRequest(
190+
SDK.ServerAPI.API.GetPackageDependency,
191+
{},
192+
)) as SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetPackageDependency>;
193+
if (res.length > 300) {
194+
return res.slice(0, 300);
195+
}
196+
return res;
189197
};
190198

191199
export const getRuleInfo = async () => {
192200
return await sendRequest(SDK.ServerAPI.API.GetOverlayAlerts, {});
193201
};
194202

195203
export const getDuplicatePackages = async () => {
196-
const ruleInfo =
197-
(await getRuleInfo()) as SDK.ServerAPI.InferResponseType<SDK.ServerAPI.API.GetOverlayAlerts>;
204+
const ruleInfo = await sendRequest(SDK.ServerAPI.API.GetOverlayAlerts, {});
198205

199206
if (!ruleInfo) {
200207
return {
@@ -330,3 +337,7 @@ export const getLoaderTimeForAllFiles = async (): Promise<
330337
export const getLoaderTimes = async () => {
331338
return await sendRequest(SDK.ServerAPI.API.GetDirectoriesLoaders, {});
332339
};
340+
341+
export const getPort = async () => {
342+
return getWsUrl();
343+
};

packages/sdk/src/sdk/server/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { Common, SDK, Thirdparty, Client } from '@rsdoctor/types';
22
import { Server } from '@rsdoctor/utils/build';
33
import serve from 'sirv';
4-
import { Bundle } from '@rsdoctor/utils/common';
4+
import { Bundle, GlobalConfig } from '@rsdoctor/utils/common';
55
import assert from 'assert';
66
import bodyParser from 'body-parser';
77
import cors from 'cors';
88
import { PassThrough } from 'stream';
99
import { Socket } from './socket';
1010
import { Router } from './router';
1111
import * as APIs from './apis';
12-
import { chalk, logger } from '@rsdoctor/utils/logger';
12+
import { chalk, debug, logger } from '@rsdoctor/utils/logger';
1313
import { openBrowser } from '@/sdk/utils/openBrowser';
1414
import path from 'path';
1515
import { getLocalIpAddress } from './utils';
@@ -88,6 +88,13 @@ export class RsdoctorServer implements SDK.RsdoctorServerInstance {
8888
});
8989
await this._socket.bootstrap();
9090

91+
GlobalConfig.writeMcpPort(this.port, this.sdk.name);
92+
93+
debug(
94+
() =>
95+
`Successfully wrote mcp.json for ${chalk.cyan(this.sdk.name)} builder`,
96+
);
97+
9198
this.disposed = false;
9299

93100
this.app.use(cors());
@@ -223,9 +230,8 @@ export class RsdoctorServer implements SDK.RsdoctorServerInstance {
223230
const localhostUrl = `http://localhost:${this.port}${relativeUrl}`;
224231
await openBrowser(localhostUrl, !needEncodeURI);
225232
if (this._printServerUrl) {
226-
logger.info(`Rsdoctor analyze server running on: ${chalk.cyan(url)}`);
227233
logger.info(
228-
`Rsdoctor analyze server running on: ${chalk.cyan(localhostUrl)}`,
234+
`${chalk.green(`${this.sdk.name} compiler's`)} analyzer running on: ${chalk.cyan(url)}`,
229235
);
230236
}
231237
}

packages/utils/src/common/data/index.ts

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,8 +455,20 @@ export class APIDataLoader {
455455
return this.loader.loadData('moduleGraph').then((moduleGraph) => {
456456
const { moduleId } =
457457
body as SDK.ServerAPI.InferRequestBodyType<SDK.ServerAPI.API.GetModuleIssuerPath>;
458-
return (moduleGraph?.modules.find((m) => String(m.id) === moduleId)
459-
?.issuerPath || []) as R;
458+
const modules = moduleGraph?.modules || [];
459+
const issuerPath =
460+
modules.find((m) => String(m.id) === moduleId)?.issuerPath || [];
461+
if (
462+
Array.isArray(issuerPath) &&
463+
issuerPath.length > 0 &&
464+
typeof issuerPath[0] === 'number'
465+
) {
466+
return issuerPath
467+
.map((id) => modules.find((m) => m.id === id)?.path)
468+
.filter(Boolean) as R;
469+
}
470+
471+
return issuerPath as R;
460472
});
461473

462474
case SDK.ServerAPI.API.GetPackageInfo:
@@ -488,6 +500,9 @@ export class APIDataLoader {
488500
const chunkInfo = chunks.find(
489501
(c) => c.id === chunkId,
490502
) as SDK.ServerAPI.ExtendedChunkData;
503+
if (!chunkInfo) {
504+
return null as R;
505+
}
491506
const chunkModules = modules
492507
.filter((m) => chunkInfo.modules.includes(m.id))
493508
.map((module) => {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import os from 'os';
4+
5+
/**
6+
* @description Writes the builder port information to mcp.json.
7+
*
8+
* The mcp.json file uses the following format:
9+
* {
10+
* portList: {
11+
* builder1: portNumber,
12+
* builder2: portNumber,
13+
* },
14+
* port: portNumber // The port of the last builder is used by default
15+
* }
16+
*
17+
* @param {number} port - The port number to write.
18+
* @param {string} [builderName] - The name of the builder.
19+
*/
20+
export function writeMcpPort(port: number, builderName?: string) {
21+
const homeDir = os.homedir();
22+
const rsdoctorDir = path.join(homeDir, '.cache/rsdoctor');
23+
const mcpPortFilePath = path.join(rsdoctorDir, 'mcp.json');
24+
25+
if (!fs.existsSync(rsdoctorDir)) {
26+
fs.mkdirSync(rsdoctorDir, { recursive: true });
27+
}
28+
29+
let mcpJson: { portList: Record<string, number>; port: number } = {
30+
portList: {},
31+
port: 0,
32+
};
33+
if (fs.existsSync(mcpPortFilePath)) {
34+
mcpJson = JSON.parse(fs.readFileSync(mcpPortFilePath, 'utf8'));
35+
}
36+
37+
if (!mcpJson.portList) mcpJson.portList = {};
38+
mcpJson.portList[builderName || 'builder'] = port;
39+
40+
// Use the latest generated port.
41+
mcpJson.port = port;
42+
43+
fs.writeFileSync(mcpPortFilePath, JSON.stringify(mcpJson, null, 2), 'utf8');
44+
}
45+
46+
/**
47+
* @description Gets the path to the mcp.json file.
48+
* @returns {string} The path to the mcp.json file.
49+
*/
50+
export function getMcpConfigPath() {
51+
const homeDir = os.homedir();
52+
const rsdoctorDir = path.join(homeDir, '.cache/rsdoctor');
53+
const mcpPortFilePath = path.join(rsdoctorDir, 'mcp.json');
54+
return mcpPortFilePath;
55+
}

packages/utils/src/common/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@ export * as Alerts from './alerts';
1414
export * as Rspack from './rspack';
1515
export * as Package from './package';
1616
export * as Lodash from './lodash';
17+
export * as GlobalConfig from './global-config';

0 commit comments

Comments
 (0)