@typia/mcp 12.0.0-dev.20260225
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +63 -0
- package/lib/index.d.ts +60 -0
- package/lib/index.js +35 -0
- package/lib/index.js.map +1 -0
- package/lib/index.mjs +35 -0
- package/lib/index.mjs.map +1 -0
- package/lib/internal/McpControllerRegistrar.d.ts +10 -0
- package/lib/internal/McpControllerRegistrar.js +259 -0
- package/lib/internal/McpControllerRegistrar.js.map +1 -0
- package/lib/internal/McpControllerRegistrar.mjs +247 -0
- package/lib/internal/McpControllerRegistrar.mjs.map +1 -0
- package/package.json +77 -0
- package/src/index.ts +67 -0
- package/src/internal/McpControllerRegistrar.ts +329 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Jeongho Nam
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# `@typia/mcp`
|
|
2
|
+
|
|
3
|
+
[](https://github.com/samchon/typia/blob/master/LICENSE)
|
|
4
|
+
[](https://www.npmjs.com/package/typia)
|
|
5
|
+
[](https://www.npmjs.com/package/typia)
|
|
6
|
+
|
|
7
|
+
[MCP (Model Context Protocol)](https://modelcontextprotocol.io) integration for [`typia`](https://github.com/samchon/typia).
|
|
8
|
+
|
|
9
|
+
Registers typia controllers as MCP tools with automatic validation.
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @typia/mcp @modelcontextprotocol/sdk
|
|
15
|
+
npm install typia
|
|
16
|
+
npx typia setup
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
### From TypeScript class
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
25
|
+
import { registerMcpControllers } from "@typia/mcp";
|
|
26
|
+
import typia from "typia";
|
|
27
|
+
|
|
28
|
+
const server: McpServer = new McpServer({
|
|
29
|
+
name: "my-server",
|
|
30
|
+
version: "1.0.0",
|
|
31
|
+
});
|
|
32
|
+
registerMcpControllers({
|
|
33
|
+
server,
|
|
34
|
+
controllers: [
|
|
35
|
+
typia.llm.controller<Calculator>("Calculator", new Calculator()),
|
|
36
|
+
],
|
|
37
|
+
});
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### From OpenAPI document
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import { registerMcpControllers } from "@typia/mcp";
|
|
44
|
+
import { HttpLlm } from "@typia/utils";
|
|
45
|
+
|
|
46
|
+
registerMcpControllers({
|
|
47
|
+
server,
|
|
48
|
+
controllers: [
|
|
49
|
+
HttpLlm.controller({
|
|
50
|
+
name: "petStore",
|
|
51
|
+
document: yourOpenApiDocument,
|
|
52
|
+
connection: { host: "https://api.example.com" },
|
|
53
|
+
}),
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Features
|
|
59
|
+
|
|
60
|
+
- No manual schema definition — generates everything from TypeScript types or OpenAPI
|
|
61
|
+
- Automatic argument validation with LLM-friendly error feedback
|
|
62
|
+
- Supports both class-based (`typia.llm.controller`) and HTTP-based (`HttpLlm.controller`) controllers
|
|
63
|
+
- `preserve` option to coexist with `McpServer.registerTool()`
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { IHttpLlmController, ILlmController } from "@typia/interface";
|
|
4
|
+
/**
|
|
5
|
+
* Register MCP tools from controllers.
|
|
6
|
+
*
|
|
7
|
+
* Registers TypeScript class methods via `typia.llm.controller<Class>()` or
|
|
8
|
+
* OpenAPI operations via `HttpLlm.controller()` as MCP tools.
|
|
9
|
+
*
|
|
10
|
+
* Every tool call is validated by typia. If LLM provides invalid arguments,
|
|
11
|
+
* returns {@link IValidation.IFailure} formatted by
|
|
12
|
+
* {@link stringifyValidationFailure} so that LLM can correct them automatically.
|
|
13
|
+
* Below is an example of the validation error format:
|
|
14
|
+
*
|
|
15
|
+
* ```json
|
|
16
|
+
* {
|
|
17
|
+
* "name": "John",
|
|
18
|
+
* "age": "twenty", // ❌ [{"path":"$input.age","expected":"number & Type<\"uint32\">"}]
|
|
19
|
+
* "email": "not-an-email", // ❌ [{"path":"$input.email","expected":"string & Format<\"email\">"}]
|
|
20
|
+
* "hobbies": "reading" // ❌ [{"path":"$input.hobbies","expected":"Array<string>"}]
|
|
21
|
+
* }
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* If you use `McpServer.registerTool()` instead, you have to define Zod schema,
|
|
25
|
+
* function name, and description string manually for each tool. Also, without
|
|
26
|
+
* typia's validation feedback, LLM cannot auto-correct its mistakes, which
|
|
27
|
+
* significantly degrades tool calling performance.
|
|
28
|
+
*
|
|
29
|
+
* @param props Registration properties
|
|
30
|
+
*/
|
|
31
|
+
export declare function registerMcpControllers(props: {
|
|
32
|
+
/**
|
|
33
|
+
* Target MCP server to register tools.
|
|
34
|
+
*
|
|
35
|
+
* Both {@link McpServer} and raw {@link Server} are supported. To combine with
|
|
36
|
+
* `McpServer.registerTool()`, set `preserve: true`.
|
|
37
|
+
*/
|
|
38
|
+
server: McpServer | Server;
|
|
39
|
+
/**
|
|
40
|
+
* List of controllers to register as MCP tools.
|
|
41
|
+
*
|
|
42
|
+
* - {@link ILlmController}: from `typia.llm.controller<Class>()`, registers all
|
|
43
|
+
* methods of the class as tools
|
|
44
|
+
* - {@link IHttpLlmController}: from `HttpLlm.controller()`, registers all
|
|
45
|
+
* operations from OpenAPI document as tools
|
|
46
|
+
*/
|
|
47
|
+
controllers: Array<ILlmController | IHttpLlmController>;
|
|
48
|
+
/**
|
|
49
|
+
* Preserve existing tools registered via `McpServer.registerTool()`.
|
|
50
|
+
*
|
|
51
|
+
* If `true`, typia tools coexist with existing McpServer tools. This uses MCP
|
|
52
|
+
* SDK's internal (private) API which may break on SDK updates.
|
|
53
|
+
*
|
|
54
|
+
* If `false`, typia tools completely replace the tool handlers, ignoring any
|
|
55
|
+
* tools registered via `McpServer.registerTool()`.
|
|
56
|
+
*
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
preserve?: boolean | undefined;
|
|
60
|
+
}): void;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerMcpControllers = registerMcpControllers;
|
|
4
|
+
const McpControllerRegistrar_1 = require("./internal/McpControllerRegistrar");
|
|
5
|
+
/**
|
|
6
|
+
* Register MCP tools from controllers.
|
|
7
|
+
*
|
|
8
|
+
* Registers TypeScript class methods via `typia.llm.controller<Class>()` or
|
|
9
|
+
* OpenAPI operations via `HttpLlm.controller()` as MCP tools.
|
|
10
|
+
*
|
|
11
|
+
* Every tool call is validated by typia. If LLM provides invalid arguments,
|
|
12
|
+
* returns {@link IValidation.IFailure} formatted by
|
|
13
|
+
* {@link stringifyValidationFailure} so that LLM can correct them automatically.
|
|
14
|
+
* Below is an example of the validation error format:
|
|
15
|
+
*
|
|
16
|
+
* ```json
|
|
17
|
+
* {
|
|
18
|
+
* "name": "John",
|
|
19
|
+
* "age": "twenty", // ❌ [{"path":"$input.age","expected":"number & Type<\"uint32\">"}]
|
|
20
|
+
* "email": "not-an-email", // ❌ [{"path":"$input.email","expected":"string & Format<\"email\">"}]
|
|
21
|
+
* "hobbies": "reading" // ❌ [{"path":"$input.hobbies","expected":"Array<string>"}]
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* If you use `McpServer.registerTool()` instead, you have to define Zod schema,
|
|
26
|
+
* function name, and description string manually for each tool. Also, without
|
|
27
|
+
* typia's validation feedback, LLM cannot auto-correct its mistakes, which
|
|
28
|
+
* significantly degrades tool calling performance.
|
|
29
|
+
*
|
|
30
|
+
* @param props Registration properties
|
|
31
|
+
*/
|
|
32
|
+
function registerMcpControllers(props) {
|
|
33
|
+
return McpControllerRegistrar_1.McpControllerRegistrar.register(props);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
package/lib/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAiCA,wDAiCC;AA9DD,8EAA2E;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,SAAgB,sBAAsB,CAAC,KA+BtC;IACC,OAAO,+CAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC"}
|
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { McpControllerRegistrar } from './internal/McpControllerRegistrar.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Register MCP tools from controllers.
|
|
5
|
+
*
|
|
6
|
+
* Registers TypeScript class methods via `typia.llm.controller<Class>()` or
|
|
7
|
+
* OpenAPI operations via `HttpLlm.controller()` as MCP tools.
|
|
8
|
+
*
|
|
9
|
+
* Every tool call is validated by typia. If LLM provides invalid arguments,
|
|
10
|
+
* returns {@link IValidation.IFailure} formatted by
|
|
11
|
+
* {@link stringifyValidationFailure} so that LLM can correct them automatically.
|
|
12
|
+
* Below is an example of the validation error format:
|
|
13
|
+
*
|
|
14
|
+
* ```json
|
|
15
|
+
* {
|
|
16
|
+
* "name": "John",
|
|
17
|
+
* "age": "twenty", // ❌ [{"path":"$input.age","expected":"number & Type<\"uint32\">"}]
|
|
18
|
+
* "email": "not-an-email", // ❌ [{"path":"$input.email","expected":"string & Format<\"email\">"}]
|
|
19
|
+
* "hobbies": "reading" // ❌ [{"path":"$input.hobbies","expected":"Array<string>"}]
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* If you use `McpServer.registerTool()` instead, you have to define Zod schema,
|
|
24
|
+
* function name, and description string manually for each tool. Also, without
|
|
25
|
+
* typia's validation feedback, LLM cannot auto-correct its mistakes, which
|
|
26
|
+
* significantly degrades tool calling performance.
|
|
27
|
+
*
|
|
28
|
+
* @param props Registration properties
|
|
29
|
+
*/
|
|
30
|
+
function registerMcpControllers(props) {
|
|
31
|
+
return McpControllerRegistrar.register(props);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { registerMcpControllers };
|
|
35
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;AA0BG;AACG,SAAU,sBAAsB,CAAC,KA+BtC,EAAA;AACC,IAAA,OAAO,sBAAsB,CAAC,QAAQ,CAAC,KAAK,CAAC;AAC/C;;;;"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { IHttpLlmController, ILlmController } from "@typia/interface";
|
|
4
|
+
export declare namespace McpControllerRegistrar {
|
|
5
|
+
const register: (props: {
|
|
6
|
+
server: McpServer | Server;
|
|
7
|
+
controllers: Array<ILlmController | IHttpLlmController>;
|
|
8
|
+
preserve?: boolean | undefined;
|
|
9
|
+
}) => void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.McpControllerRegistrar = void 0;
|
|
13
|
+
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
14
|
+
const utils_1 = require("@typia/utils");
|
|
15
|
+
const zod_to_json_schema_1 = require("zod-to-json-schema");
|
|
16
|
+
var McpControllerRegistrar;
|
|
17
|
+
(function (McpControllerRegistrar) {
|
|
18
|
+
McpControllerRegistrar.register = (props) => {
|
|
19
|
+
var _a;
|
|
20
|
+
// McpServer wraps raw Server - we need raw Server for JSON Schema support
|
|
21
|
+
const server = "server" in props.server
|
|
22
|
+
? props.server.server
|
|
23
|
+
: props.server;
|
|
24
|
+
// Build tool registry from controllers
|
|
25
|
+
const registry = new Map();
|
|
26
|
+
for (const controller of props.controllers) {
|
|
27
|
+
if (controller.protocol === "class") {
|
|
28
|
+
registerClassController(registry, controller);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
registerHttpController(registry, controller);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Determine preserve mode (default: false)
|
|
35
|
+
const preserve = (_a = props.preserve) !== null && _a !== void 0 ? _a : false;
|
|
36
|
+
if (preserve) {
|
|
37
|
+
// PRESERVE MODE: Coexist with McpServer.registerTool()
|
|
38
|
+
// Uses MCP SDK internal API (_registeredTools, _toolHandlersInitialized)
|
|
39
|
+
registerWithPreserve(server, registry, props.server);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
// STANDALONE MODE: Typia tools only, no private API dependency
|
|
43
|
+
registerStandalone(server, registry);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Standalone registration without private API. Typia tools completely replace
|
|
48
|
+
* any existing tool handlers.
|
|
49
|
+
*/
|
|
50
|
+
const registerStandalone = (server, registry) => {
|
|
51
|
+
// tools/list handler
|
|
52
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () {
|
|
53
|
+
return ({
|
|
54
|
+
tools: Array.from(registry.values()).map((entry) => ({
|
|
55
|
+
name: entry.function.name,
|
|
56
|
+
description: entry.function.description,
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: "object",
|
|
59
|
+
properties: entry.function.parameters.properties,
|
|
60
|
+
required: entry.function.parameters.required,
|
|
61
|
+
additionalProperties: false,
|
|
62
|
+
$defs: entry.function.parameters.$defs,
|
|
63
|
+
},
|
|
64
|
+
})),
|
|
65
|
+
});
|
|
66
|
+
}));
|
|
67
|
+
// tools/call handler
|
|
68
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, (request) => __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
const name = request.params.name;
|
|
70
|
+
const args = request.params.arguments;
|
|
71
|
+
const entry = registry.get(name);
|
|
72
|
+
if (entry !== undefined) {
|
|
73
|
+
return handleToolCall(entry, args);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
isError: true,
|
|
77
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
78
|
+
};
|
|
79
|
+
}));
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Preserve mode registration with private API. Coexists with tools registered
|
|
83
|
+
* via McpServer.registerTool().
|
|
84
|
+
*/
|
|
85
|
+
const registerWithPreserve = (server, registry, originalServer) => {
|
|
86
|
+
// Get McpServer reference for coexistence with McpServer.registerTool()
|
|
87
|
+
const mcpServer = "server" in originalServer ? originalServer : null;
|
|
88
|
+
// Helper to get existing tools dynamically (supports tools registered after this call)
|
|
89
|
+
const getExistingTools = () => { var _a;
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
return mcpServer ? ((_a = mcpServer._registeredTools) !== null && _a !== void 0 ? _a : {}) : {}; };
|
|
92
|
+
// Check for conflicts with existing McpServer tools at registration time
|
|
93
|
+
for (const pair of Object.entries(getExistingTools())) {
|
|
94
|
+
if (pair[1].enabled && registry.has(pair[0])) {
|
|
95
|
+
throw new Error(`Duplicate function name "${pair[0]}" between McpServer.registerTool() and controller "${registry.get(pair[0]).controller}"`);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// tools/list handler
|
|
99
|
+
server.setRequestHandler(types_js_1.ListToolsRequestSchema, () => __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
const existingTools = getExistingTools();
|
|
101
|
+
return {
|
|
102
|
+
tools: [
|
|
103
|
+
// Typia controller tools
|
|
104
|
+
...Array.from(registry.values()).map((entry) => {
|
|
105
|
+
return {
|
|
106
|
+
name: entry.function.name,
|
|
107
|
+
description: entry.function.description,
|
|
108
|
+
inputSchema: {
|
|
109
|
+
type: "object",
|
|
110
|
+
properties: entry.function.parameters.properties,
|
|
111
|
+
required: entry.function.parameters.required,
|
|
112
|
+
additionalProperties: false,
|
|
113
|
+
$defs: entry.function.parameters.$defs,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}),
|
|
117
|
+
// Existing McpServer tools
|
|
118
|
+
...Object.entries(existingTools)
|
|
119
|
+
.filter((pair) => !registry.has(pair[0]) && pair[1].enabled)
|
|
120
|
+
.map((pair) => {
|
|
121
|
+
return {
|
|
122
|
+
name: pair[0],
|
|
123
|
+
description: pair[1].description,
|
|
124
|
+
inputSchema: convertZodToJsonSchema(pair[1].inputSchema),
|
|
125
|
+
};
|
|
126
|
+
}),
|
|
127
|
+
],
|
|
128
|
+
};
|
|
129
|
+
}));
|
|
130
|
+
// tools/call handler
|
|
131
|
+
server.setRequestHandler(types_js_1.CallToolRequestSchema, (request, extra) => __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
const name = request.params.name;
|
|
133
|
+
const args = request.params.arguments;
|
|
134
|
+
// Check typia registry first
|
|
135
|
+
const entry = registry.get(name);
|
|
136
|
+
if (entry !== undefined) {
|
|
137
|
+
return handleToolCall(entry, args);
|
|
138
|
+
}
|
|
139
|
+
// Fall back to existing McpServer tools
|
|
140
|
+
const existingTools = getExistingTools();
|
|
141
|
+
const existingTool = existingTools[name];
|
|
142
|
+
if (existingTool && existingTool.enabled) {
|
|
143
|
+
return existingTool.handler(args, extra);
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
isError: true,
|
|
147
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
148
|
+
};
|
|
149
|
+
}));
|
|
150
|
+
// Mark handlers as initialized to prevent McpServer from overwriting
|
|
151
|
+
if (mcpServer) {
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
153
|
+
mcpServer._toolHandlersInitialized = true;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
const registerClassController = (registry, controller) => {
|
|
157
|
+
const execute = controller.execute;
|
|
158
|
+
for (const func of controller.application.functions) {
|
|
159
|
+
const existing = registry.get(func.name);
|
|
160
|
+
if (existing !== undefined) {
|
|
161
|
+
throw new Error(`Duplicate function name "${func.name}" between controllers "${existing.controller}" and "${controller.name}"`);
|
|
162
|
+
}
|
|
163
|
+
const method = execute[func.name];
|
|
164
|
+
if (typeof method !== "function") {
|
|
165
|
+
throw new Error(`Method "${func.name}" not found on controller "${controller.name}"`);
|
|
166
|
+
}
|
|
167
|
+
registry.set(func.name, {
|
|
168
|
+
controller: controller.name,
|
|
169
|
+
function: func,
|
|
170
|
+
execute: (args) => __awaiter(this, void 0, void 0, function* () { return method.call(execute, args); }),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const registerHttpController = (registry, controller) => {
|
|
175
|
+
const application = controller.application;
|
|
176
|
+
const connection = controller.connection;
|
|
177
|
+
for (const func of application.functions) {
|
|
178
|
+
const existing = registry.get(func.name);
|
|
179
|
+
if (existing !== undefined) {
|
|
180
|
+
throw new Error(`Duplicate function name "${func.name}" between controllers "${existing.controller}" and "${controller.name}"`);
|
|
181
|
+
}
|
|
182
|
+
registry.set(func.name, {
|
|
183
|
+
controller: controller.name,
|
|
184
|
+
function: func,
|
|
185
|
+
execute: (args) => __awaiter(this, void 0, void 0, function* () {
|
|
186
|
+
if (controller.execute !== undefined) {
|
|
187
|
+
const response = yield controller.execute({
|
|
188
|
+
connection,
|
|
189
|
+
application,
|
|
190
|
+
function: func,
|
|
191
|
+
arguments: args,
|
|
192
|
+
});
|
|
193
|
+
return response.body;
|
|
194
|
+
}
|
|
195
|
+
return utils_1.HttpLlm.execute({
|
|
196
|
+
application,
|
|
197
|
+
function: func,
|
|
198
|
+
connection,
|
|
199
|
+
input: args,
|
|
200
|
+
});
|
|
201
|
+
}),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const handleToolCall = (entry, args) => __awaiter(this, void 0, void 0, function* () {
|
|
206
|
+
const validation = entry.function.validate(args);
|
|
207
|
+
if (!validation.success) {
|
|
208
|
+
return {
|
|
209
|
+
isError: true,
|
|
210
|
+
content: [
|
|
211
|
+
{
|
|
212
|
+
type: "text",
|
|
213
|
+
text: (0, utils_1.stringifyValidationFailure)(validation),
|
|
214
|
+
},
|
|
215
|
+
],
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const result = yield entry.execute(validation.data);
|
|
220
|
+
return {
|
|
221
|
+
content: [
|
|
222
|
+
{
|
|
223
|
+
type: "text",
|
|
224
|
+
text: result === undefined
|
|
225
|
+
? "Success"
|
|
226
|
+
: JSON.stringify(result, null, 2),
|
|
227
|
+
},
|
|
228
|
+
],
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (error) {
|
|
232
|
+
return {
|
|
233
|
+
isError: true,
|
|
234
|
+
content: [
|
|
235
|
+
{
|
|
236
|
+
type: "text",
|
|
237
|
+
text: error instanceof Error
|
|
238
|
+
? `${error.name}: ${error.message}`
|
|
239
|
+
: String(error),
|
|
240
|
+
},
|
|
241
|
+
],
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
const convertZodToJsonSchema = (zodSchema) => {
|
|
246
|
+
if (zodSchema === undefined) {
|
|
247
|
+
return { type: "object", properties: {} };
|
|
248
|
+
}
|
|
249
|
+
// @todo: error TS2589: Type instantiation is excessively deep and possibly infinite.
|
|
250
|
+
const converted = zod_to_json_schema_1.zodToJsonSchema(zodSchema);
|
|
251
|
+
if (typeof converted === "object" &&
|
|
252
|
+
"type" in converted &&
|
|
253
|
+
converted.type === "object") {
|
|
254
|
+
return converted;
|
|
255
|
+
}
|
|
256
|
+
return { type: "object", properties: {} };
|
|
257
|
+
};
|
|
258
|
+
})(McpControllerRegistrar || (exports.McpControllerRegistrar = McpControllerRegistrar = {}));
|
|
259
|
+
//# sourceMappingURL=McpControllerRegistrar.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpControllerRegistrar.js","sourceRoot":"","sources":["../../src/internal/McpControllerRegistrar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { HttpLlm, stringifyValidationFailure } from '@typia/utils';
|
|
3
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
4
|
+
|
|
5
|
+
var McpControllerRegistrar;
|
|
6
|
+
(function (McpControllerRegistrar) {
|
|
7
|
+
McpControllerRegistrar.register = (props) => {
|
|
8
|
+
// McpServer wraps raw Server - we need raw Server for JSON Schema support
|
|
9
|
+
const server = "server" in props.server
|
|
10
|
+
? props.server.server
|
|
11
|
+
: props.server;
|
|
12
|
+
// Build tool registry from controllers
|
|
13
|
+
const registry = new Map();
|
|
14
|
+
for (const controller of props.controllers) {
|
|
15
|
+
if (controller.protocol === "class") {
|
|
16
|
+
registerClassController(registry, controller);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
registerHttpController(registry, controller);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Determine preserve mode (default: false)
|
|
23
|
+
const preserve = props.preserve ?? false;
|
|
24
|
+
if (preserve) {
|
|
25
|
+
// PRESERVE MODE: Coexist with McpServer.registerTool()
|
|
26
|
+
// Uses MCP SDK internal API (_registeredTools, _toolHandlersInitialized)
|
|
27
|
+
registerWithPreserve(server, registry, props.server);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// STANDALONE MODE: Typia tools only, no private API dependency
|
|
31
|
+
registerStandalone(server, registry);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Standalone registration without private API. Typia tools completely replace
|
|
36
|
+
* any existing tool handlers.
|
|
37
|
+
*/
|
|
38
|
+
const registerStandalone = (server, registry) => {
|
|
39
|
+
// tools/list handler
|
|
40
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
41
|
+
tools: Array.from(registry.values()).map((entry) => ({
|
|
42
|
+
name: entry.function.name,
|
|
43
|
+
description: entry.function.description,
|
|
44
|
+
inputSchema: {
|
|
45
|
+
type: "object",
|
|
46
|
+
properties: entry.function.parameters.properties,
|
|
47
|
+
required: entry.function.parameters.required,
|
|
48
|
+
additionalProperties: false,
|
|
49
|
+
$defs: entry.function.parameters.$defs,
|
|
50
|
+
},
|
|
51
|
+
})),
|
|
52
|
+
}));
|
|
53
|
+
// tools/call handler
|
|
54
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
55
|
+
const name = request.params.name;
|
|
56
|
+
const args = request.params.arguments;
|
|
57
|
+
const entry = registry.get(name);
|
|
58
|
+
if (entry !== undefined) {
|
|
59
|
+
return handleToolCall(entry, args);
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
isError: true,
|
|
63
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Preserve mode registration with private API. Coexists with tools registered
|
|
69
|
+
* via McpServer.registerTool().
|
|
70
|
+
*/
|
|
71
|
+
const registerWithPreserve = (server, registry, originalServer) => {
|
|
72
|
+
// Get McpServer reference for coexistence with McpServer.registerTool()
|
|
73
|
+
const mcpServer = "server" in originalServer ? originalServer : null;
|
|
74
|
+
// Helper to get existing tools dynamically (supports tools registered after this call)
|
|
75
|
+
const getExistingTools = () =>
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
77
|
+
mcpServer ? (mcpServer._registeredTools ?? {}) : {};
|
|
78
|
+
// Check for conflicts with existing McpServer tools at registration time
|
|
79
|
+
for (const pair of Object.entries(getExistingTools())) {
|
|
80
|
+
if (pair[1].enabled && registry.has(pair[0])) {
|
|
81
|
+
throw new Error(`Duplicate function name "${pair[0]}" between McpServer.registerTool() and controller "${registry.get(pair[0]).controller}"`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// tools/list handler
|
|
85
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
86
|
+
const existingTools = getExistingTools();
|
|
87
|
+
return {
|
|
88
|
+
tools: [
|
|
89
|
+
// Typia controller tools
|
|
90
|
+
...Array.from(registry.values()).map((entry) => {
|
|
91
|
+
return {
|
|
92
|
+
name: entry.function.name,
|
|
93
|
+
description: entry.function.description,
|
|
94
|
+
inputSchema: {
|
|
95
|
+
type: "object",
|
|
96
|
+
properties: entry.function.parameters.properties,
|
|
97
|
+
required: entry.function.parameters.required,
|
|
98
|
+
additionalProperties: false,
|
|
99
|
+
$defs: entry.function.parameters.$defs,
|
|
100
|
+
},
|
|
101
|
+
};
|
|
102
|
+
}),
|
|
103
|
+
// Existing McpServer tools
|
|
104
|
+
...Object.entries(existingTools)
|
|
105
|
+
.filter((pair) => !registry.has(pair[0]) && pair[1].enabled)
|
|
106
|
+
.map((pair) => {
|
|
107
|
+
return {
|
|
108
|
+
name: pair[0],
|
|
109
|
+
description: pair[1].description,
|
|
110
|
+
inputSchema: convertZodToJsonSchema(pair[1].inputSchema),
|
|
111
|
+
};
|
|
112
|
+
}),
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
// tools/call handler
|
|
117
|
+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
118
|
+
const name = request.params.name;
|
|
119
|
+
const args = request.params.arguments;
|
|
120
|
+
// Check typia registry first
|
|
121
|
+
const entry = registry.get(name);
|
|
122
|
+
if (entry !== undefined) {
|
|
123
|
+
return handleToolCall(entry, args);
|
|
124
|
+
}
|
|
125
|
+
// Fall back to existing McpServer tools
|
|
126
|
+
const existingTools = getExistingTools();
|
|
127
|
+
const existingTool = existingTools[name];
|
|
128
|
+
if (existingTool && existingTool.enabled) {
|
|
129
|
+
return existingTool.handler(args, extra);
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
isError: true,
|
|
133
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
// Mark handlers as initialized to prevent McpServer from overwriting
|
|
137
|
+
if (mcpServer) {
|
|
138
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
139
|
+
mcpServer._toolHandlersInitialized = true;
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const registerClassController = (registry, controller) => {
|
|
143
|
+
const execute = controller.execute;
|
|
144
|
+
for (const func of controller.application.functions) {
|
|
145
|
+
const existing = registry.get(func.name);
|
|
146
|
+
if (existing !== undefined) {
|
|
147
|
+
throw new Error(`Duplicate function name "${func.name}" between controllers "${existing.controller}" and "${controller.name}"`);
|
|
148
|
+
}
|
|
149
|
+
const method = execute[func.name];
|
|
150
|
+
if (typeof method !== "function") {
|
|
151
|
+
throw new Error(`Method "${func.name}" not found on controller "${controller.name}"`);
|
|
152
|
+
}
|
|
153
|
+
registry.set(func.name, {
|
|
154
|
+
controller: controller.name,
|
|
155
|
+
function: func,
|
|
156
|
+
execute: async (args) => method.call(execute, args),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const registerHttpController = (registry, controller) => {
|
|
161
|
+
const application = controller.application;
|
|
162
|
+
const connection = controller.connection;
|
|
163
|
+
for (const func of application.functions) {
|
|
164
|
+
const existing = registry.get(func.name);
|
|
165
|
+
if (existing !== undefined) {
|
|
166
|
+
throw new Error(`Duplicate function name "${func.name}" between controllers "${existing.controller}" and "${controller.name}"`);
|
|
167
|
+
}
|
|
168
|
+
registry.set(func.name, {
|
|
169
|
+
controller: controller.name,
|
|
170
|
+
function: func,
|
|
171
|
+
execute: async (args) => {
|
|
172
|
+
if (controller.execute !== undefined) {
|
|
173
|
+
const response = await controller.execute({
|
|
174
|
+
connection,
|
|
175
|
+
application,
|
|
176
|
+
function: func,
|
|
177
|
+
arguments: args,
|
|
178
|
+
});
|
|
179
|
+
return response.body;
|
|
180
|
+
}
|
|
181
|
+
return HttpLlm.execute({
|
|
182
|
+
application,
|
|
183
|
+
function: func,
|
|
184
|
+
connection,
|
|
185
|
+
input: args,
|
|
186
|
+
});
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
const handleToolCall = async (entry, args) => {
|
|
192
|
+
const validation = entry.function.validate(args);
|
|
193
|
+
if (!validation.success) {
|
|
194
|
+
return {
|
|
195
|
+
isError: true,
|
|
196
|
+
content: [
|
|
197
|
+
{
|
|
198
|
+
type: "text",
|
|
199
|
+
text: stringifyValidationFailure(validation),
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
const result = await entry.execute(validation.data);
|
|
206
|
+
return {
|
|
207
|
+
content: [
|
|
208
|
+
{
|
|
209
|
+
type: "text",
|
|
210
|
+
text: result === undefined
|
|
211
|
+
? "Success"
|
|
212
|
+
: JSON.stringify(result, null, 2),
|
|
213
|
+
},
|
|
214
|
+
],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
return {
|
|
219
|
+
isError: true,
|
|
220
|
+
content: [
|
|
221
|
+
{
|
|
222
|
+
type: "text",
|
|
223
|
+
text: error instanceof Error
|
|
224
|
+
? `${error.name}: ${error.message}`
|
|
225
|
+
: String(error),
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
const convertZodToJsonSchema = (zodSchema) => {
|
|
232
|
+
if (zodSchema === undefined) {
|
|
233
|
+
return { type: "object", properties: {} };
|
|
234
|
+
}
|
|
235
|
+
// @todo: error TS2589: Type instantiation is excessively deep and possibly infinite.
|
|
236
|
+
const converted = zodToJsonSchema(zodSchema);
|
|
237
|
+
if (typeof converted === "object" &&
|
|
238
|
+
"type" in converted &&
|
|
239
|
+
converted.type === "object") {
|
|
240
|
+
return converted;
|
|
241
|
+
}
|
|
242
|
+
return { type: "object", properties: {} };
|
|
243
|
+
};
|
|
244
|
+
})(McpControllerRegistrar || (McpControllerRegistrar = {}));
|
|
245
|
+
|
|
246
|
+
export { McpControllerRegistrar };
|
|
247
|
+
//# sourceMappingURL=McpControllerRegistrar.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"McpControllerRegistrar.mjs","sources":["../../src/internal/McpControllerRegistrar.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@typia/mcp",
|
|
3
|
+
"version": "12.0.0-dev.20260225",
|
|
4
|
+
"description": "MCP (Model Context Protocol) integration for typia",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./lib/index.d.ts",
|
|
9
|
+
"import": "./lib/index.mjs",
|
|
10
|
+
"default": "./lib/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./package.json": "./package.json"
|
|
13
|
+
},
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/samchon/typia"
|
|
17
|
+
},
|
|
18
|
+
"author": "Jeongho Nam",
|
|
19
|
+
"license": "MIT",
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/samchon/typia/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://typia.io",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"zod-to-json-schema": ">=3.24.0",
|
|
26
|
+
"@typia/interface": "^12.0.0-dev.20260225",
|
|
27
|
+
"@typia/utils": "^12.0.0-dev.20260225"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@modelcontextprotocol/sdk": ">=1.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
34
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
35
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
36
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
37
|
+
"rimraf": "^6.1.2",
|
|
38
|
+
"rollup": "^4.56.0",
|
|
39
|
+
"rollup-plugin-auto-external": "^2.0.0",
|
|
40
|
+
"rollup-plugin-node-externals": "^8.1.2",
|
|
41
|
+
"tinyglobby": "^0.2.12",
|
|
42
|
+
"typescript": "~5.9.3",
|
|
43
|
+
"zod": "^3.25.0"
|
|
44
|
+
},
|
|
45
|
+
"sideEffects": false,
|
|
46
|
+
"files": [
|
|
47
|
+
"LICENSE",
|
|
48
|
+
"README.md",
|
|
49
|
+
"package.json",
|
|
50
|
+
"lib",
|
|
51
|
+
"src"
|
|
52
|
+
],
|
|
53
|
+
"keywords": [
|
|
54
|
+
"mcp",
|
|
55
|
+
"model-context-protocol",
|
|
56
|
+
"typia",
|
|
57
|
+
"llm",
|
|
58
|
+
"llm-function-calling",
|
|
59
|
+
"ai",
|
|
60
|
+
"claude",
|
|
61
|
+
"openai",
|
|
62
|
+
"chatgpt",
|
|
63
|
+
"gemini",
|
|
64
|
+
"validation",
|
|
65
|
+
"json-schema",
|
|
66
|
+
"typescript"
|
|
67
|
+
],
|
|
68
|
+
"publishConfig": {
|
|
69
|
+
"access": "public"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"build": "rimraf lib && tsc && rollup -c",
|
|
73
|
+
"dev": "rimraf lib && tsc --watch"
|
|
74
|
+
},
|
|
75
|
+
"module": "lib/index.mjs",
|
|
76
|
+
"types": "lib/index.d.ts"
|
|
77
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { IHttpLlmController, ILlmController } from "@typia/interface";
|
|
4
|
+
|
|
5
|
+
import { McpControllerRegistrar } from "./internal/McpControllerRegistrar";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Register MCP tools from controllers.
|
|
9
|
+
*
|
|
10
|
+
* Registers TypeScript class methods via `typia.llm.controller<Class>()` or
|
|
11
|
+
* OpenAPI operations via `HttpLlm.controller()` as MCP tools.
|
|
12
|
+
*
|
|
13
|
+
* Every tool call is validated by typia. If LLM provides invalid arguments,
|
|
14
|
+
* returns {@link IValidation.IFailure} formatted by
|
|
15
|
+
* {@link stringifyValidationFailure} so that LLM can correct them automatically.
|
|
16
|
+
* Below is an example of the validation error format:
|
|
17
|
+
*
|
|
18
|
+
* ```json
|
|
19
|
+
* {
|
|
20
|
+
* "name": "John",
|
|
21
|
+
* "age": "twenty", // ❌ [{"path":"$input.age","expected":"number & Type<\"uint32\">"}]
|
|
22
|
+
* "email": "not-an-email", // ❌ [{"path":"$input.email","expected":"string & Format<\"email\">"}]
|
|
23
|
+
* "hobbies": "reading" // ❌ [{"path":"$input.hobbies","expected":"Array<string>"}]
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* If you use `McpServer.registerTool()` instead, you have to define Zod schema,
|
|
28
|
+
* function name, and description string manually for each tool. Also, without
|
|
29
|
+
* typia's validation feedback, LLM cannot auto-correct its mistakes, which
|
|
30
|
+
* significantly degrades tool calling performance.
|
|
31
|
+
*
|
|
32
|
+
* @param props Registration properties
|
|
33
|
+
*/
|
|
34
|
+
export function registerMcpControllers(props: {
|
|
35
|
+
/**
|
|
36
|
+
* Target MCP server to register tools.
|
|
37
|
+
*
|
|
38
|
+
* Both {@link McpServer} and raw {@link Server} are supported. To combine with
|
|
39
|
+
* `McpServer.registerTool()`, set `preserve: true`.
|
|
40
|
+
*/
|
|
41
|
+
server: McpServer | Server;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* List of controllers to register as MCP tools.
|
|
45
|
+
*
|
|
46
|
+
* - {@link ILlmController}: from `typia.llm.controller<Class>()`, registers all
|
|
47
|
+
* methods of the class as tools
|
|
48
|
+
* - {@link IHttpLlmController}: from `HttpLlm.controller()`, registers all
|
|
49
|
+
* operations from OpenAPI document as tools
|
|
50
|
+
*/
|
|
51
|
+
controllers: Array<ILlmController | IHttpLlmController>;
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Preserve existing tools registered via `McpServer.registerTool()`.
|
|
55
|
+
*
|
|
56
|
+
* If `true`, typia tools coexist with existing McpServer tools. This uses MCP
|
|
57
|
+
* SDK's internal (private) API which may break on SDK updates.
|
|
58
|
+
*
|
|
59
|
+
* If `false`, typia tools completely replace the tool handlers, ignoring any
|
|
60
|
+
* tools registered via `McpServer.registerTool()`.
|
|
61
|
+
*
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
preserve?: boolean | undefined;
|
|
65
|
+
}): void {
|
|
66
|
+
return McpControllerRegistrar.register(props);
|
|
67
|
+
}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import {
|
|
4
|
+
CallToolRequestSchema,
|
|
5
|
+
CallToolResult,
|
|
6
|
+
ListToolsRequestSchema,
|
|
7
|
+
Tool,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import {
|
|
10
|
+
IHttpLlmController,
|
|
11
|
+
IHttpLlmFunction,
|
|
12
|
+
ILlmController,
|
|
13
|
+
ILlmFunction,
|
|
14
|
+
IValidation,
|
|
15
|
+
} from "@typia/interface";
|
|
16
|
+
import { HttpLlm, stringifyValidationFailure } from "@typia/utils";
|
|
17
|
+
import { ZodObject, ZodType } from "zod";
|
|
18
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
19
|
+
|
|
20
|
+
export namespace McpControllerRegistrar {
|
|
21
|
+
export const register = (props: {
|
|
22
|
+
server: McpServer | Server;
|
|
23
|
+
controllers: Array<ILlmController | IHttpLlmController>;
|
|
24
|
+
preserve?: boolean | undefined;
|
|
25
|
+
}): void => {
|
|
26
|
+
// McpServer wraps raw Server - we need raw Server for JSON Schema support
|
|
27
|
+
const server: Server =
|
|
28
|
+
"server" in props.server
|
|
29
|
+
? (props.server as McpServer).server
|
|
30
|
+
: (props.server as Server);
|
|
31
|
+
|
|
32
|
+
// Build tool registry from controllers
|
|
33
|
+
const registry: Map<string, IToolEntry> = new Map();
|
|
34
|
+
|
|
35
|
+
for (const controller of props.controllers) {
|
|
36
|
+
if (controller.protocol === "class") {
|
|
37
|
+
registerClassController(registry, controller);
|
|
38
|
+
} else {
|
|
39
|
+
registerHttpController(registry, controller);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Determine preserve mode (default: false)
|
|
44
|
+
const preserve: boolean = props.preserve ?? false;
|
|
45
|
+
|
|
46
|
+
if (preserve) {
|
|
47
|
+
// PRESERVE MODE: Coexist with McpServer.registerTool()
|
|
48
|
+
// Uses MCP SDK internal API (_registeredTools, _toolHandlersInitialized)
|
|
49
|
+
registerWithPreserve(server, registry, props.server);
|
|
50
|
+
} else {
|
|
51
|
+
// STANDALONE MODE: Typia tools only, no private API dependency
|
|
52
|
+
registerStandalone(server, registry);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Standalone registration without private API. Typia tools completely replace
|
|
58
|
+
* any existing tool handlers.
|
|
59
|
+
*/
|
|
60
|
+
const registerStandalone = (
|
|
61
|
+
server: Server,
|
|
62
|
+
registry: Map<string, IToolEntry>,
|
|
63
|
+
): void => {
|
|
64
|
+
// tools/list handler
|
|
65
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
66
|
+
tools: Array.from(registry.values()).map((entry: IToolEntry) => ({
|
|
67
|
+
name: entry.function.name,
|
|
68
|
+
description: entry.function.description,
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: "object" as const,
|
|
71
|
+
properties: entry.function.parameters.properties,
|
|
72
|
+
required: entry.function.parameters.required,
|
|
73
|
+
additionalProperties: false,
|
|
74
|
+
$defs: entry.function.parameters.$defs,
|
|
75
|
+
},
|
|
76
|
+
})),
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
// tools/call handler
|
|
80
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
81
|
+
const name: string = request.params.name;
|
|
82
|
+
const args: Record<string, unknown> | undefined =
|
|
83
|
+
request.params.arguments;
|
|
84
|
+
|
|
85
|
+
const entry: IToolEntry | undefined = registry.get(name);
|
|
86
|
+
if (entry !== undefined) {
|
|
87
|
+
return handleToolCall(entry, args);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
isError: true,
|
|
92
|
+
content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Preserve mode registration with private API. Coexists with tools registered
|
|
99
|
+
* via McpServer.registerTool().
|
|
100
|
+
*/
|
|
101
|
+
const registerWithPreserve = (
|
|
102
|
+
server: Server,
|
|
103
|
+
registry: Map<string, IToolEntry>,
|
|
104
|
+
originalServer: McpServer | Server,
|
|
105
|
+
): void => {
|
|
106
|
+
// Get McpServer reference for coexistence with McpServer.registerTool()
|
|
107
|
+
const mcpServer: McpServer | null =
|
|
108
|
+
"server" in originalServer ? (originalServer as McpServer) : null;
|
|
109
|
+
|
|
110
|
+
// Helper to get existing tools dynamically (supports tools registered after this call)
|
|
111
|
+
const getExistingTools = (): Record<string, any> =>
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
mcpServer ? ((mcpServer as any)._registeredTools ?? {}) : {};
|
|
114
|
+
|
|
115
|
+
// Check for conflicts with existing McpServer tools at registration time
|
|
116
|
+
for (const pair of Object.entries(getExistingTools())) {
|
|
117
|
+
if (pair[1].enabled && registry.has(pair[0])) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`Duplicate function name "${pair[0]}" between McpServer.registerTool() and controller "${registry.get(pair[0])!.controller}"`,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// tools/list handler
|
|
125
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
126
|
+
const existingTools: Record<string, any> = getExistingTools();
|
|
127
|
+
return {
|
|
128
|
+
tools: [
|
|
129
|
+
// Typia controller tools
|
|
130
|
+
...Array.from(registry.values()).map((entry: IToolEntry) => {
|
|
131
|
+
return {
|
|
132
|
+
name: entry.function.name,
|
|
133
|
+
description: entry.function.description,
|
|
134
|
+
inputSchema: {
|
|
135
|
+
type: "object" as const,
|
|
136
|
+
properties: entry.function.parameters.properties,
|
|
137
|
+
required: entry.function.parameters.required,
|
|
138
|
+
additionalProperties: false,
|
|
139
|
+
$defs: entry.function.parameters.$defs,
|
|
140
|
+
},
|
|
141
|
+
} satisfies Tool;
|
|
142
|
+
}),
|
|
143
|
+
// Existing McpServer tools
|
|
144
|
+
...Object.entries(existingTools)
|
|
145
|
+
.filter(
|
|
146
|
+
(pair: [string, any]) =>
|
|
147
|
+
!registry.has(pair[0]) && pair[1].enabled,
|
|
148
|
+
)
|
|
149
|
+
.map((pair: [string, any]) => {
|
|
150
|
+
return {
|
|
151
|
+
name: pair[0],
|
|
152
|
+
description: pair[1].description,
|
|
153
|
+
inputSchema: convertZodToJsonSchema(pair[1].inputSchema),
|
|
154
|
+
} satisfies Tool;
|
|
155
|
+
}),
|
|
156
|
+
],
|
|
157
|
+
};
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// tools/call handler
|
|
161
|
+
server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
162
|
+
const name: string = request.params.name;
|
|
163
|
+
const args: Record<string, unknown> | undefined =
|
|
164
|
+
request.params.arguments;
|
|
165
|
+
|
|
166
|
+
// Check typia registry first
|
|
167
|
+
const entry: IToolEntry | undefined = registry.get(name);
|
|
168
|
+
if (entry !== undefined) {
|
|
169
|
+
return handleToolCall(entry, args);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Fall back to existing McpServer tools
|
|
173
|
+
const existingTools: Record<string, any> = getExistingTools();
|
|
174
|
+
const existingTool: any = existingTools[name];
|
|
175
|
+
if (existingTool && existingTool.enabled) {
|
|
176
|
+
return existingTool.handler(args, extra);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
isError: true,
|
|
181
|
+
content: [{ type: "text" as const, text: `Unknown tool: ${name}` }],
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// Mark handlers as initialized to prevent McpServer from overwriting
|
|
186
|
+
if (mcpServer) {
|
|
187
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
188
|
+
(mcpServer as any)._toolHandlersInitialized = true;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const registerClassController = (
|
|
193
|
+
registry: Map<string, IToolEntry>,
|
|
194
|
+
controller: ILlmController,
|
|
195
|
+
): void => {
|
|
196
|
+
const execute: Record<string, unknown> = controller.execute;
|
|
197
|
+
for (const func of controller.application.functions) {
|
|
198
|
+
const existing: IToolEntry | undefined = registry.get(func.name);
|
|
199
|
+
if (existing !== undefined) {
|
|
200
|
+
throw new Error(
|
|
201
|
+
`Duplicate function name "${func.name}" between controllers "${existing.controller}" and "${controller.name}"`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const method: unknown = execute[func.name];
|
|
206
|
+
if (typeof method !== "function") {
|
|
207
|
+
throw new Error(
|
|
208
|
+
`Method "${func.name}" not found on controller "${controller.name}"`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
registry.set(func.name, {
|
|
213
|
+
controller: controller.name,
|
|
214
|
+
function: func,
|
|
215
|
+
execute: async (args: unknown) => method.call(execute, args),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const registerHttpController = (
|
|
221
|
+
registry: Map<string, IToolEntry>,
|
|
222
|
+
controller: IHttpLlmController,
|
|
223
|
+
): void => {
|
|
224
|
+
const application: IHttpLlmController["application"] =
|
|
225
|
+
controller.application;
|
|
226
|
+
const connection: IHttpLlmController["connection"] = controller.connection;
|
|
227
|
+
|
|
228
|
+
for (const func of application.functions) {
|
|
229
|
+
const existing: IToolEntry | undefined = registry.get(func.name);
|
|
230
|
+
if (existing !== undefined) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`Duplicate function name "${func.name}" between controllers "${existing.controller}" and "${controller.name}"`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
registry.set(func.name, {
|
|
236
|
+
controller: controller.name,
|
|
237
|
+
function: func,
|
|
238
|
+
execute: async (args: unknown) => {
|
|
239
|
+
if (controller.execute !== undefined) {
|
|
240
|
+
const response = await controller.execute({
|
|
241
|
+
connection,
|
|
242
|
+
application,
|
|
243
|
+
function: func,
|
|
244
|
+
arguments: args as object,
|
|
245
|
+
});
|
|
246
|
+
return response.body;
|
|
247
|
+
}
|
|
248
|
+
return HttpLlm.execute({
|
|
249
|
+
application,
|
|
250
|
+
function: func,
|
|
251
|
+
connection,
|
|
252
|
+
input: args as object,
|
|
253
|
+
});
|
|
254
|
+
},
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const handleToolCall = async (
|
|
260
|
+
entry: IToolEntry,
|
|
261
|
+
args: unknown,
|
|
262
|
+
): Promise<CallToolResult> => {
|
|
263
|
+
const validation: IValidation<unknown> = entry.function.validate(args);
|
|
264
|
+
if (!validation.success) {
|
|
265
|
+
return {
|
|
266
|
+
isError: true,
|
|
267
|
+
content: [
|
|
268
|
+
{
|
|
269
|
+
type: "text" as const,
|
|
270
|
+
text: stringifyValidationFailure(validation),
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
const result: unknown = await entry.execute(validation.data);
|
|
278
|
+
return {
|
|
279
|
+
content: [
|
|
280
|
+
{
|
|
281
|
+
type: "text" as const,
|
|
282
|
+
text:
|
|
283
|
+
result === undefined
|
|
284
|
+
? "Success"
|
|
285
|
+
: JSON.stringify(result, null, 2),
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
};
|
|
289
|
+
} catch (error) {
|
|
290
|
+
return {
|
|
291
|
+
isError: true,
|
|
292
|
+
content: [
|
|
293
|
+
{
|
|
294
|
+
type: "text" as const,
|
|
295
|
+
text:
|
|
296
|
+
error instanceof Error
|
|
297
|
+
? `${error.name}: ${error.message}`
|
|
298
|
+
: String(error),
|
|
299
|
+
},
|
|
300
|
+
],
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
const convertZodToJsonSchema = (
|
|
306
|
+
zodSchema: ZodType | ZodObject<any> | undefined,
|
|
307
|
+
): Tool["inputSchema"] => {
|
|
308
|
+
if (zodSchema === undefined) {
|
|
309
|
+
return { type: "object", properties: {} };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// @todo: error TS2589: Type instantiation is excessively deep and possibly infinite.
|
|
313
|
+
const converted: object = (zodToJsonSchema as any)(zodSchema);
|
|
314
|
+
if (
|
|
315
|
+
typeof converted === "object" &&
|
|
316
|
+
"type" in converted &&
|
|
317
|
+
converted.type === "object"
|
|
318
|
+
) {
|
|
319
|
+
return converted as Tool["inputSchema"];
|
|
320
|
+
}
|
|
321
|
+
return { type: "object", properties: {} };
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
interface IToolEntry {
|
|
326
|
+
controller: string;
|
|
327
|
+
function: ILlmFunction | IHttpLlmFunction;
|
|
328
|
+
execute: (args: unknown) => Promise<unknown>;
|
|
329
|
+
}
|