Skip to content

Commit 115b553

Browse files
committed
refactor(cli): restructure MCP commands and tools directories, remove tsed-mcp binary, and enhance schema definitions
- Consolidated MCP-related tools and resources under `commands/mcp` for improved organization. - Removed `tsed-mcp` binary and updated `package.json` and integration scripts accordingly. - Enhanced support for dynamic input schemas with arrow function handlers in `defineTool`. - Improved error handling and logging during MCP tool execution. - Added `initProjectTool` and `McpCommand` for streamlined project initialization in MCP mode.
1 parent db0c875 commit 115b553

23 files changed

+172
-82
lines changed

packages/cli-mcp/src/fn/defineTool.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {McpServer} from "@modelcontextprotocol/sdk/server/mcp.js";
22
import type {RequestHandlerExtra} from "@modelcontextprotocol/sdk/shared/protocol.js";
33
import type {CallToolResult, ServerNotification, ServerRequest, Tool} from "@modelcontextprotocol/sdk/types.js";
44
import {injectable} from "@tsed/cli-core";
5+
import {isArrowFn} from "@tsed/core";
56
import {DIContext, injector, logger, runInContext, type TokenProvider} from "@tsed/di";
67
import {JsonSchema} from "@tsed/schema";
78
import {v4} from "uuid";
@@ -17,7 +18,7 @@ type ToolConfig = Parameters<McpServer["registerTool"]>[1];
1718
export type ToolProps<Input, Output> = Omit<ToolConfig, "inputSchema" | "outputSchema"> & {
1819
token?: TokenProvider;
1920
name: string;
20-
inputSchema?: JsonSchema<Input> | Tool["inputSchema"];
21+
inputSchema?: JsonSchema<Input> | (() => JsonSchema<Input>) | Tool["inputSchema"];
2122
outputSchema?: JsonSchema<Output> | Tool["outputSchema"];
2223
handler: ToolCallback<Input>;
2324
};
@@ -57,7 +58,7 @@ export function defineTool<Input, Output = undefined>(options: ToolProps<Input,
5758
.type("CLI_MCP_TOOLS")
5859
.factory(() => ({
5960
...options,
60-
inputSchema: toZod(options.inputSchema),
61+
inputSchema: toZod(isArrowFn(options.inputSchema) ? options.inputSchema() : options.inputSchema),
6162
outputSchema: toZod(options.outputSchema),
6263
async handler(args: Input, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) {
6364
const $ctx = new DIContext({
@@ -73,6 +74,21 @@ export function defineTool<Input, Output = undefined>(options: ToolProps<Input,
7374
return await runInContext($ctx, () => {
7475
return options.handler(args as Input, extra);
7576
});
77+
} catch (er) {
78+
$ctx.logger.error({
79+
event: "MCP_TOOL_ERROR",
80+
tool: options.name,
81+
error_message: er.message,
82+
stack: er.stack
83+
});
84+
85+
return {
86+
content: [],
87+
structuredContent: {
88+
code: "E_MCP_TOOL_ERROR",
89+
message: er.message
90+
}
91+
};
7692
} finally {
7793
// Ensure per-invocation context is destroyed to avoid leaks
7894
try {

packages/cli-mcp/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from "./fn/definePrompt.js";
22
export * from "./fn/defineResource.js";
33
export * from "./fn/defineTool.js";
44
export * from "./services/CLIMCPServer.js";
5+
export * from "./services/McpServerFactory.js";

packages/cli/package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@
2222
"test:ci": "vitest run --coverage.thresholds.autoUpdate=true"
2323
},
2424
"bin": {
25-
"tsed": "lib/esm/bin/tsed.js",
26-
"tsed-mcp": "lib/esm/bin/tsed-mcp.js"
25+
"tsed": "lib/esm/bin/tsed.js"
2726
},
2827
"files": [
2928
"lib/esm/bin/tsed.js",

packages/cli/src/bin/tsed-mcp.ts

Lines changed: 0 additions & 49 deletions
This file was deleted.

packages/cli/src/bin/tsed.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ register(pathToFileURL(join(import.meta.dirname, `../loaders/alias.hook.${EXT}`)
2121
transferList: []
2222
});
2323

24-
const {commands, CliCore, PKG, TEMPLATE_DIR, ArchitectureConvention, ProjectConvention} = await import("../index.js");
24+
const {tools, commands, resources, CliCore, PKG, TEMPLATE_DIR, ArchitectureConvention, ProjectConvention} = await import("../index.js");
2525

2626
CliCore.bootstrap({
2727
name: "tsed",
@@ -31,6 +31,8 @@ CliCore.bootstrap({
3131
updateNotifier: true,
3232
checkPrecondition: true,
3333
commands,
34+
tools,
35+
resources,
3436
defaultProjectPreferences() {
3537
return {
3638
convention: ProjectConvention.DEFAULT,

packages/cli/src/commands/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import {AddCmd} from "./add/AddCmd.js";
22
import {GenerateCmd} from "./generate/GenerateCmd.js";
33
import {InitCmd} from "./init/InitCmd.js";
4+
import {McpCommand} from "./mcp/McpCommand.js";
45
import {RunCmd} from "./run/RunCmd.js";
56
import {CreateTemplateCommand} from "./template/CreateTemplateCommand.js";
67
import {UpdateCmd} from "./update/UpdateCmd.js";
78

8-
export default [AddCmd, InitCmd, GenerateCmd, UpdateCmd, RunCmd, CreateTemplateCommand];
9+
export default [AddCmd, InitCmd, GenerateCmd, UpdateCmd, RunCmd, CreateTemplateCommand, McpCommand];

packages/cli/src/commands/init/InitCmd.ts

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import {BunRuntime} from "../../runtimes/supports/BunRuntime.js";
3737
import {NodeRuntime} from "../../runtimes/supports/NodeRuntime.js";
3838
import {CliProjectService} from "../../services/CliProjectService.js";
3939
import {FeaturesMap, FeatureType} from "./config/FeaturesPrompt.js";
40-
import {InitSchema} from "./config/InitSchema.js";
40+
import {InitFileSchema} from "./config/InitFileSchema.js";
4141
import {mapToContext} from "./mappers/mapToContext.js";
4242
import {getFeaturesPrompt} from "./prompts/getFeaturesPrompt.js";
4343

@@ -92,20 +92,16 @@ export class InitCmd implements CommandProvider {
9292
}
9393
}
9494

95-
async $beforePrompt(initialOptions: Partial<InitOptions>) {
95+
async $prompt(initialOptions: Partial<InitOptions>): Promise<QuestionOptions> {
9696
if (initialOptions.file) {
9797
const file = join(this.packageJson.cwd, initialOptions.file);
9898

99-
return {
99+
initialOptions = {
100100
...initialOptions,
101-
...(await this.cliLoadFile.loadFile(file, InitSchema))
101+
...(await this.cliLoadFile.loadFile(file, InitFileSchema))
102102
};
103103
}
104104

105-
return initialOptions;
106-
}
107-
108-
$prompt(initialOptions: Partial<InitOptions>): QuestionOptions {
109105
if (initialOptions.skipPrompt) {
110106
return [];
111107
}
@@ -153,7 +149,7 @@ export class InitCmd implements CommandProvider {
153149
} as InitOptions;
154150
}
155151

156-
async $beforeExec(ctx: InitOptions): Promise<any> {
152+
async $exec(ctx: InitOptions): Promise<Task[]> {
157153
this.fs.ensureDirSync(this.packageJson.cwd);
158154

159155
ctx.projectName && (this.packageJson.name = ctx.projectName);
@@ -199,9 +195,7 @@ export class InitCmd implements CommandProvider {
199195
],
200196
ctx
201197
);
202-
}
203198

204-
async $exec(ctx: InitOptions): Promise<Task[]> {
205199
this.checkPrecondition(ctx);
206200
const runtime = this.runtimes.get();
207201

packages/cli/src/commands/init/config/InitSchema.ts renamed to packages/cli/src/commands/init/config/InitFileSchema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import {PackageManager} from "@tsed/cli-core";
22
import {s} from "@tsed/schema";
33

4-
import {ArchitectureConvention, PlatformType, ProjectConvention} from "../../../interfaces/index.js";
4+
import {PlatformType, ProjectConvention} from "../../../interfaces/index.js";
55
import {FeatureType} from "./FeaturesPrompt.js";
66

7-
export const InitSchema = s
7+
export const InitFileSchema = s
88
.object({
99
tsedVersion: s.string().optional().description("The CLI will use the given tsed version to generate the project"),
1010
projectName: s.string().optional().description("The project name"),
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import {command} from "@tsed/cli-core";
2+
import {MCP_SERVER} from "@tsed/cli-mcp";
3+
import {inject} from "@tsed/di";
4+
5+
export const McpCommand = command({
6+
name: "mcp",
7+
description: "Run a MCP server",
8+
async handler() {
9+
await inject(MCP_SERVER).connect();
10+
return [];
11+
}
12+
}).token();
File renamed without changes.

0 commit comments

Comments
 (0)