@nestjs-adk/mcp 0.0.1
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/README.md +13 -0
- package/dist/index.cjs +215 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.mjs +212 -0
- package/dist/lib/json-schema-to-zod.d.ts +2 -0
- package/dist/lib/mcp-client.d.ts +15 -0
- package/dist/lib/mcp-options.d.ts +23 -0
- package/dist/lib/mcp.module.d.ts +5 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @nestjs-adk/mcp
|
|
2
|
+
|
|
3
|
+
Consume external MCP servers as agent tools for [`@nestjs-adk/core`](https://www.npmjs.com/package/@nestjs-adk/core), via the official `@modelcontextprotocol/sdk` (stdio, streamable HTTP, SSE).
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
McpModule.forRoot({ servers: [{ name: "github", transport: { type: "stdio", command: "npx", args: [...] } }] })
|
|
7
|
+
|
|
8
|
+
@Agent({ ..., tools: [toolset("github", ["create_issue"])] }) // the server's catalog becomes tools (JSON Schema → Zod)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
The catalog is cached at boot (fail-fast; `optional: true` for non-critical servers). Runtime tool failures return as `{ error }` to the LLM; connection errors become typed `McpConnectionError`s.
|
|
12
|
+
|
|
13
|
+
Full documentation: [github.com/gabrieljsilva/nestjs-adk](https://github.com/gabrieljsilva/nestjs-adk)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@nestjs-adk/core');
|
|
4
|
+
var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
5
|
+
var sse_js = require('@modelcontextprotocol/sdk/client/sse.js');
|
|
6
|
+
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
7
|
+
var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
8
|
+
var common = require('@nestjs/common');
|
|
9
|
+
var zod = require('zod');
|
|
10
|
+
|
|
11
|
+
/******************************************************************************
|
|
12
|
+
Copyright (c) Microsoft Corporation.
|
|
13
|
+
|
|
14
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
15
|
+
purpose with or without fee is hereby granted.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
18
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
19
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
20
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
21
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
22
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
23
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
24
|
+
***************************************************************************** */
|
|
25
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
function __decorate(decorators, target, key, desc) {
|
|
29
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
30
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
31
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
32
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function __param(paramIndex, decorator) {
|
|
36
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function __metadata(metadataKey, metadataValue) {
|
|
40
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
44
|
+
var e = new Error(message);
|
|
45
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
function jsonSchemaToZod(schema) {
|
|
49
|
+
const root = (schema ?? {});
|
|
50
|
+
const shape = {};
|
|
51
|
+
const required = new Set(root.required ?? []);
|
|
52
|
+
for (const [key, property] of Object.entries(root.properties ?? {})) {
|
|
53
|
+
let type = convert(property);
|
|
54
|
+
if (!required.has(key))
|
|
55
|
+
type = type.optional();
|
|
56
|
+
shape[key] = type;
|
|
57
|
+
}
|
|
58
|
+
return zod.z.object(shape);
|
|
59
|
+
}
|
|
60
|
+
function convert(schema) {
|
|
61
|
+
let type;
|
|
62
|
+
if (schema.enum) {
|
|
63
|
+
type = zod.z.enum(schema.enum.map(String));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
switch (schema.type) {
|
|
67
|
+
case "string":
|
|
68
|
+
type = zod.z.string();
|
|
69
|
+
break;
|
|
70
|
+
case "number":
|
|
71
|
+
type = zod.z.number();
|
|
72
|
+
break;
|
|
73
|
+
case "integer":
|
|
74
|
+
type = zod.z.number().int();
|
|
75
|
+
break;
|
|
76
|
+
case "boolean":
|
|
77
|
+
type = zod.z.boolean();
|
|
78
|
+
break;
|
|
79
|
+
case "array":
|
|
80
|
+
type = zod.z.array(schema.items ? convert(schema.items) : zod.z.any());
|
|
81
|
+
break;
|
|
82
|
+
case "object":
|
|
83
|
+
type = jsonSchemaToZod(schema);
|
|
84
|
+
break;
|
|
85
|
+
default:
|
|
86
|
+
type = zod.z.any();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return schema.description ? type.describe(schema.description) : type;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const MCP_OPTIONS = Symbol("adk:mcp-options");
|
|
93
|
+
|
|
94
|
+
var McpClient_1;
|
|
95
|
+
exports.McpClient = McpClient_1 = class McpClient extends core.ToolsetResolver {
|
|
96
|
+
constructor(options) {
|
|
97
|
+
super();
|
|
98
|
+
this.options = options;
|
|
99
|
+
this.logger = new common.Logger(McpClient_1.name);
|
|
100
|
+
this.catalogs = new Map();
|
|
101
|
+
}
|
|
102
|
+
async onModuleInit() {
|
|
103
|
+
for (const server of this.options.servers) {
|
|
104
|
+
try {
|
|
105
|
+
await this.connect(server);
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
if (server.optional) {
|
|
109
|
+
this.logger.warn(`MCP server "${server.name}" unavailable (optional) — tools ignored.`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
throw new core.McpConnectionError(server.name, error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async onModuleDestroy() {
|
|
117
|
+
for (const { client } of this.catalogs.values()) {
|
|
118
|
+
await client.close().catch(() => undefined);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async resolve(ref) {
|
|
122
|
+
const catalog = this.catalogs.get(ref.__adkToolset);
|
|
123
|
+
if (!catalog) {
|
|
124
|
+
const configured = this.options.servers.some((server) => server.name === ref.__adkToolset);
|
|
125
|
+
if (configured)
|
|
126
|
+
return [];
|
|
127
|
+
throw new core.McpConnectionError(ref.__adkToolset, new Error("server is not configured in McpModule.forRoot"));
|
|
128
|
+
}
|
|
129
|
+
const tools = ref.filter ? catalog.tools.filter((tool) => ref.filter?.includes(tool.name)) : catalog.tools;
|
|
130
|
+
return tools.map((tool) => ({
|
|
131
|
+
name: tool.name,
|
|
132
|
+
description: tool.description,
|
|
133
|
+
schema: jsonSchemaToZod(tool.inputSchema),
|
|
134
|
+
execute: async (input) => this.callTool(catalog.client, tool.name, input),
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
137
|
+
async callTool(client, name, input) {
|
|
138
|
+
try {
|
|
139
|
+
const result = await client.callTool({ name, arguments: (input ?? {}) });
|
|
140
|
+
const texts = (result.content ?? [])
|
|
141
|
+
.filter((part) => part.type === "text")
|
|
142
|
+
.map((part) => part.text ?? "");
|
|
143
|
+
if (result.isError)
|
|
144
|
+
return { error: texts.join("\n") || `MCP tool "${name}" failed.` };
|
|
145
|
+
if (result.structuredContent)
|
|
146
|
+
return result.structuredContent;
|
|
147
|
+
return texts.length === 1 ? texts[0] : texts.join("\n");
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
return { error: `MCP tool "${name}" failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async connect(server) {
|
|
154
|
+
const client = new index_js.Client({ name: "nestjs-adk", version: "1.0.0" });
|
|
155
|
+
await client.connect(this.createTransport(server.transport));
|
|
156
|
+
const { tools } = await client.listTools();
|
|
157
|
+
this.catalogs.set(server.name, {
|
|
158
|
+
client,
|
|
159
|
+
tools: tools.map((tool) => ({
|
|
160
|
+
name: tool.name,
|
|
161
|
+
description: tool.description ?? "",
|
|
162
|
+
inputSchema: tool.inputSchema,
|
|
163
|
+
})),
|
|
164
|
+
});
|
|
165
|
+
this.logger.log(`MCP server "${server.name}" connected — ${tools.length} tools in the catalog.`);
|
|
166
|
+
}
|
|
167
|
+
createTransport(transport) {
|
|
168
|
+
switch (transport.type) {
|
|
169
|
+
case "stdio":
|
|
170
|
+
return new stdio_js.StdioClientTransport({
|
|
171
|
+
command: transport.command,
|
|
172
|
+
args: transport.args,
|
|
173
|
+
env: transport.env ? { ...cleanEnv(process.env), ...transport.env } : undefined,
|
|
174
|
+
});
|
|
175
|
+
case "http":
|
|
176
|
+
return new streamableHttp_js.StreamableHTTPClientTransport(new URL(transport.url), {
|
|
177
|
+
requestInit: { headers: transport.headers },
|
|
178
|
+
});
|
|
179
|
+
case "sse":
|
|
180
|
+
return new sse_js.SSEClientTransport(new URL(transport.url), { requestInit: { headers: transport.headers } });
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
exports.McpClient = McpClient_1 = __decorate([
|
|
185
|
+
common.Injectable(),
|
|
186
|
+
__param(0, common.Inject(MCP_OPTIONS)),
|
|
187
|
+
__metadata("design:paramtypes", [Object])
|
|
188
|
+
], exports.McpClient);
|
|
189
|
+
function cleanEnv(env) {
|
|
190
|
+
return Object.fromEntries(Object.entries(env).filter(([, value]) => value !== undefined));
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
var McpModule_1;
|
|
194
|
+
exports.McpModule = McpModule_1 = class McpModule {
|
|
195
|
+
static forRoot(options) {
|
|
196
|
+
return {
|
|
197
|
+
module: McpModule_1,
|
|
198
|
+
providers: [
|
|
199
|
+
{ provide: MCP_OPTIONS, useValue: options },
|
|
200
|
+
exports.McpClient,
|
|
201
|
+
{ provide: core.ToolsetResolver, useExisting: exports.McpClient },
|
|
202
|
+
],
|
|
203
|
+
exports: [core.ToolsetResolver, exports.McpClient],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
exports.McpModule = McpModule_1 = __decorate([
|
|
208
|
+
common.Global(),
|
|
209
|
+
common.Module({})
|
|
210
|
+
], exports.McpModule);
|
|
211
|
+
|
|
212
|
+
const mcpTools = core.toolset;
|
|
213
|
+
|
|
214
|
+
exports.jsonSchemaToZod = jsonSchemaToZod;
|
|
215
|
+
exports.mcpTools = mcpTools;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { toolset } from "@nestjs-adk/core";
|
|
2
|
+
export { McpClient } from "./lib/mcp-client";
|
|
3
|
+
export { McpModule } from "./lib/mcp.module";
|
|
4
|
+
export type { McpModuleOptions, McpServerConfig, McpTransportConfig } from "./lib/mcp-options";
|
|
5
|
+
export { jsonSchemaToZod } from "./lib/json-schema-to-zod";
|
|
6
|
+
export declare const mcpTools: typeof toolset;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
import { ToolsetResolver, McpConnectionError, toolset } from '@nestjs-adk/core';
|
|
2
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
3
|
+
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
4
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
5
|
+
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
6
|
+
import { Injectable, Inject, Logger, Global, Module } from '@nestjs/common';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
/******************************************************************************
|
|
10
|
+
Copyright (c) Microsoft Corporation.
|
|
11
|
+
|
|
12
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
13
|
+
purpose with or without fee is hereby granted.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
16
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
17
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
18
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
19
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
20
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
21
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
22
|
+
***************************************************************************** */
|
|
23
|
+
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
function __decorate(decorators, target, key, desc) {
|
|
27
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
28
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
29
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
30
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function __param(paramIndex, decorator) {
|
|
34
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function __metadata(metadataKey, metadataValue) {
|
|
38
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
42
|
+
var e = new Error(message);
|
|
43
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function jsonSchemaToZod(schema) {
|
|
47
|
+
const root = (schema ?? {});
|
|
48
|
+
const shape = {};
|
|
49
|
+
const required = new Set(root.required ?? []);
|
|
50
|
+
for (const [key, property] of Object.entries(root.properties ?? {})) {
|
|
51
|
+
let type = convert(property);
|
|
52
|
+
if (!required.has(key))
|
|
53
|
+
type = type.optional();
|
|
54
|
+
shape[key] = type;
|
|
55
|
+
}
|
|
56
|
+
return z.object(shape);
|
|
57
|
+
}
|
|
58
|
+
function convert(schema) {
|
|
59
|
+
let type;
|
|
60
|
+
if (schema.enum) {
|
|
61
|
+
type = z.enum(schema.enum.map(String));
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
switch (schema.type) {
|
|
65
|
+
case "string":
|
|
66
|
+
type = z.string();
|
|
67
|
+
break;
|
|
68
|
+
case "number":
|
|
69
|
+
type = z.number();
|
|
70
|
+
break;
|
|
71
|
+
case "integer":
|
|
72
|
+
type = z.number().int();
|
|
73
|
+
break;
|
|
74
|
+
case "boolean":
|
|
75
|
+
type = z.boolean();
|
|
76
|
+
break;
|
|
77
|
+
case "array":
|
|
78
|
+
type = z.array(schema.items ? convert(schema.items) : z.any());
|
|
79
|
+
break;
|
|
80
|
+
case "object":
|
|
81
|
+
type = jsonSchemaToZod(schema);
|
|
82
|
+
break;
|
|
83
|
+
default:
|
|
84
|
+
type = z.any();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return schema.description ? type.describe(schema.description) : type;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const MCP_OPTIONS = Symbol("adk:mcp-options");
|
|
91
|
+
|
|
92
|
+
var McpClient_1;
|
|
93
|
+
let McpClient = McpClient_1 = class McpClient extends ToolsetResolver {
|
|
94
|
+
constructor(options) {
|
|
95
|
+
super();
|
|
96
|
+
this.options = options;
|
|
97
|
+
this.logger = new Logger(McpClient_1.name);
|
|
98
|
+
this.catalogs = new Map();
|
|
99
|
+
}
|
|
100
|
+
async onModuleInit() {
|
|
101
|
+
for (const server of this.options.servers) {
|
|
102
|
+
try {
|
|
103
|
+
await this.connect(server);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (server.optional) {
|
|
107
|
+
this.logger.warn(`MCP server "${server.name}" unavailable (optional) — tools ignored.`);
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
throw new McpConnectionError(server.name, error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async onModuleDestroy() {
|
|
115
|
+
for (const { client } of this.catalogs.values()) {
|
|
116
|
+
await client.close().catch(() => undefined);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
async resolve(ref) {
|
|
120
|
+
const catalog = this.catalogs.get(ref.__adkToolset);
|
|
121
|
+
if (!catalog) {
|
|
122
|
+
const configured = this.options.servers.some((server) => server.name === ref.__adkToolset);
|
|
123
|
+
if (configured)
|
|
124
|
+
return [];
|
|
125
|
+
throw new McpConnectionError(ref.__adkToolset, new Error("server is not configured in McpModule.forRoot"));
|
|
126
|
+
}
|
|
127
|
+
const tools = ref.filter ? catalog.tools.filter((tool) => ref.filter?.includes(tool.name)) : catalog.tools;
|
|
128
|
+
return tools.map((tool) => ({
|
|
129
|
+
name: tool.name,
|
|
130
|
+
description: tool.description,
|
|
131
|
+
schema: jsonSchemaToZod(tool.inputSchema),
|
|
132
|
+
execute: async (input) => this.callTool(catalog.client, tool.name, input),
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
async callTool(client, name, input) {
|
|
136
|
+
try {
|
|
137
|
+
const result = await client.callTool({ name, arguments: (input ?? {}) });
|
|
138
|
+
const texts = (result.content ?? [])
|
|
139
|
+
.filter((part) => part.type === "text")
|
|
140
|
+
.map((part) => part.text ?? "");
|
|
141
|
+
if (result.isError)
|
|
142
|
+
return { error: texts.join("\n") || `MCP tool "${name}" failed.` };
|
|
143
|
+
if (result.structuredContent)
|
|
144
|
+
return result.structuredContent;
|
|
145
|
+
return texts.length === 1 ? texts[0] : texts.join("\n");
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
return { error: `MCP tool "${name}" failed: ${error instanceof Error ? error.message : String(error)}` };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async connect(server) {
|
|
152
|
+
const client = new Client({ name: "nestjs-adk", version: "1.0.0" });
|
|
153
|
+
await client.connect(this.createTransport(server.transport));
|
|
154
|
+
const { tools } = await client.listTools();
|
|
155
|
+
this.catalogs.set(server.name, {
|
|
156
|
+
client,
|
|
157
|
+
tools: tools.map((tool) => ({
|
|
158
|
+
name: tool.name,
|
|
159
|
+
description: tool.description ?? "",
|
|
160
|
+
inputSchema: tool.inputSchema,
|
|
161
|
+
})),
|
|
162
|
+
});
|
|
163
|
+
this.logger.log(`MCP server "${server.name}" connected — ${tools.length} tools in the catalog.`);
|
|
164
|
+
}
|
|
165
|
+
createTransport(transport) {
|
|
166
|
+
switch (transport.type) {
|
|
167
|
+
case "stdio":
|
|
168
|
+
return new StdioClientTransport({
|
|
169
|
+
command: transport.command,
|
|
170
|
+
args: transport.args,
|
|
171
|
+
env: transport.env ? { ...cleanEnv(process.env), ...transport.env } : undefined,
|
|
172
|
+
});
|
|
173
|
+
case "http":
|
|
174
|
+
return new StreamableHTTPClientTransport(new URL(transport.url), {
|
|
175
|
+
requestInit: { headers: transport.headers },
|
|
176
|
+
});
|
|
177
|
+
case "sse":
|
|
178
|
+
return new SSEClientTransport(new URL(transport.url), { requestInit: { headers: transport.headers } });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
McpClient = McpClient_1 = __decorate([
|
|
183
|
+
Injectable(),
|
|
184
|
+
__param(0, Inject(MCP_OPTIONS)),
|
|
185
|
+
__metadata("design:paramtypes", [Object])
|
|
186
|
+
], McpClient);
|
|
187
|
+
function cleanEnv(env) {
|
|
188
|
+
return Object.fromEntries(Object.entries(env).filter(([, value]) => value !== undefined));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
var McpModule_1;
|
|
192
|
+
let McpModule = McpModule_1 = class McpModule {
|
|
193
|
+
static forRoot(options) {
|
|
194
|
+
return {
|
|
195
|
+
module: McpModule_1,
|
|
196
|
+
providers: [
|
|
197
|
+
{ provide: MCP_OPTIONS, useValue: options },
|
|
198
|
+
McpClient,
|
|
199
|
+
{ provide: ToolsetResolver, useExisting: McpClient },
|
|
200
|
+
],
|
|
201
|
+
exports: [ToolsetResolver, McpClient],
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
McpModule = McpModule_1 = __decorate([
|
|
206
|
+
Global(),
|
|
207
|
+
Module({})
|
|
208
|
+
], McpModule);
|
|
209
|
+
|
|
210
|
+
const mcpTools = toolset;
|
|
211
|
+
|
|
212
|
+
export { McpClient, McpModule, jsonSchemaToZod, mcpTools };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ResolvedTool, type ToolsetRef, ToolsetResolver } from "@nestjs-adk/core";
|
|
2
|
+
import { type OnModuleDestroy, type OnModuleInit } from "@nestjs/common";
|
|
3
|
+
import { type McpModuleOptions } from "./mcp-options";
|
|
4
|
+
export declare class McpClient extends ToolsetResolver implements OnModuleInit, OnModuleDestroy {
|
|
5
|
+
private readonly options;
|
|
6
|
+
private readonly logger;
|
|
7
|
+
private readonly catalogs;
|
|
8
|
+
constructor(options: McpModuleOptions);
|
|
9
|
+
onModuleInit(): Promise<void>;
|
|
10
|
+
onModuleDestroy(): Promise<void>;
|
|
11
|
+
resolve(ref: ToolsetRef): Promise<ResolvedTool[]>;
|
|
12
|
+
private callTool;
|
|
13
|
+
private connect;
|
|
14
|
+
private createTransport;
|
|
15
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type McpTransportConfig = {
|
|
2
|
+
type: "stdio";
|
|
3
|
+
command: string;
|
|
4
|
+
args?: string[];
|
|
5
|
+
env?: Record<string, string>;
|
|
6
|
+
} | {
|
|
7
|
+
type: "http";
|
|
8
|
+
url: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
} | {
|
|
11
|
+
type: "sse";
|
|
12
|
+
url: string;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
};
|
|
15
|
+
export interface McpServerConfig {
|
|
16
|
+
name: string;
|
|
17
|
+
transport: McpTransportConfig;
|
|
18
|
+
optional?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface McpModuleOptions {
|
|
21
|
+
servers: McpServerConfig[];
|
|
22
|
+
}
|
|
23
|
+
export declare const MCP_OPTIONS: unique symbol;
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nestjs-adk/mcp",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "MCP client integration for nestjs-adk: consume external MCP servers as agent tools.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "dist/index.cjs",
|
|
8
|
+
"module": "dist/index.mjs",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.mjs",
|
|
14
|
+
"require": "./dist/index.cjs"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"files": ["dist"],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=20"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"prebuild": "rimraf -g dist",
|
|
24
|
+
"build": "rollup -c"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"@nestjs-adk/core": "^0.0.1",
|
|
31
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
32
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
33
|
+
"zod": "^3.23.0 || ^4.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@nestjs-adk/core": "^0.0.1"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|