@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 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
+ [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/samchon/typia/blob/master/LICENSE)
4
+ [![NPM Version](https://img.shields.io/npm/v/typia.svg)](https://www.npmjs.com/package/typia)
5
+ [![NPM Downloads](https://img.shields.io/npm/dm/typia.svg)](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
@@ -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
+ }