@n8n/n8n-nodes-langchain 1.87.0 → 1.88.0
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/dist/credentials/MilvusApi.credentials.js +72 -0
- package/dist/credentials/MilvusApi.credentials.js.map +1 -0
- package/dist/known/credentials.json +7 -0
- package/dist/known/nodes.json +12 -0
- package/dist/methods/defined.json +3 -0
- package/dist/methods/referenced.json +3 -0
- package/dist/nodes/mcp/McpClientTool/McpClientTool.node.js +258 -0
- package/dist/nodes/mcp/McpClientTool/McpClientTool.node.js.map +1 -0
- package/dist/nodes/mcp/McpClientTool/loadOptions.js +51 -0
- package/dist/nodes/mcp/McpClientTool/loadOptions.js.map +1 -0
- package/dist/nodes/mcp/McpClientTool/types.js +17 -0
- package/dist/nodes/mcp/McpClientTool/types.js.map +1 -0
- package/dist/nodes/mcp/McpClientTool/utils.js +192 -0
- package/dist/nodes/mcp/McpClientTool/utils.js.map +1 -0
- package/dist/nodes/mcp/McpTrigger/FlushingSSEServerTransport.js +39 -0
- package/dist/nodes/mcp/McpTrigger/FlushingSSEServerTransport.js.map +1 -0
- package/dist/nodes/mcp/McpTrigger/McpServer.js +179 -0
- package/dist/nodes/mcp/McpTrigger/McpServer.js.map +1 -0
- package/dist/nodes/mcp/McpTrigger/McpTrigger.node.js +181 -0
- package/dist/nodes/mcp/McpTrigger/McpTrigger.node.js.map +1 -0
- package/dist/nodes/mcp/mcp.dark.svg +7 -0
- package/dist/nodes/mcp/mcp.svg +7 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/VectorStoreMilvus.node.js +106 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/VectorStoreMilvus.node.js.map +1 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/milvus-icon-black.svg +1 -0
- package/dist/nodes/vector_store/VectorStoreMilvus/milvus-icon-white.svg +1 -0
- package/dist/nodes/vector_store/shared/createVectorStoreNode/methods/listSearch.js +16 -0
- package/dist/nodes/vector_store/shared/createVectorStoreNode/methods/listSearch.js.map +1 -1
- package/dist/nodes/vector_store/shared/descriptions.js +24 -0
- package/dist/nodes/vector_store/shared/descriptions.js.map +1 -1
- package/dist/types/credentials.json +1 -0
- package/dist/types/nodes.json +3 -0
- package/dist/utils/helpers.js +7 -1
- package/dist/utils/helpers.js.map +1 -1
- package/dist/utils/logWrapper.js +9 -2
- package/dist/utils/logWrapper.js.map +1 -1
- package/package.json +10 -4
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var utils_exports = {};
|
|
20
|
+
__export(utils_exports, {
|
|
21
|
+
McpToolkit: () => McpToolkit,
|
|
22
|
+
connectMcpClient: () => connectMcpClient,
|
|
23
|
+
createCallTool: () => createCallTool,
|
|
24
|
+
getAllTools: () => getAllTools,
|
|
25
|
+
getAuthHeaders: () => getAuthHeaders,
|
|
26
|
+
getErrorDescriptionFromToolCall: () => getErrorDescriptionFromToolCall,
|
|
27
|
+
getSelectedTools: () => getSelectedTools,
|
|
28
|
+
mcpToolToDynamicTool: () => mcpToolToDynamicTool
|
|
29
|
+
});
|
|
30
|
+
module.exports = __toCommonJS(utils_exports);
|
|
31
|
+
var import_client = require("@modelcontextprotocol/sdk/client/index.js");
|
|
32
|
+
var import_sse = require("@modelcontextprotocol/sdk/client/sse.js");
|
|
33
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
34
|
+
var import_agents = require("langchain/agents");
|
|
35
|
+
var import_tools = require("langchain/tools");
|
|
36
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
37
|
+
var import_schemaParsing = require("../../../utils/schemaParsing");
|
|
38
|
+
async function getAllTools(client, cursor) {
|
|
39
|
+
const { tools, nextCursor } = await client.listTools({ cursor });
|
|
40
|
+
if (nextCursor) {
|
|
41
|
+
return tools.concat(await getAllTools(client, nextCursor));
|
|
42
|
+
}
|
|
43
|
+
return tools;
|
|
44
|
+
}
|
|
45
|
+
function getSelectedTools({
|
|
46
|
+
mode,
|
|
47
|
+
includeTools,
|
|
48
|
+
excludeTools,
|
|
49
|
+
tools
|
|
50
|
+
}) {
|
|
51
|
+
switch (mode) {
|
|
52
|
+
case "selected": {
|
|
53
|
+
if (!includeTools?.length) return tools;
|
|
54
|
+
const include = new Set(includeTools);
|
|
55
|
+
return tools.filter((tool) => include.has(tool.name));
|
|
56
|
+
}
|
|
57
|
+
case "except": {
|
|
58
|
+
const except = new Set(excludeTools ?? []);
|
|
59
|
+
return tools.filter((tool) => !except.has(tool.name));
|
|
60
|
+
}
|
|
61
|
+
case "all":
|
|
62
|
+
default:
|
|
63
|
+
return tools;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const getErrorDescriptionFromToolCall = (result) => {
|
|
67
|
+
if (result && typeof result === "object") {
|
|
68
|
+
if ("content" in result && Array.isArray(result.content)) {
|
|
69
|
+
const errorMessage = result.content.find(
|
|
70
|
+
(content) => content && typeof content === "object" && typeof content.text === "string"
|
|
71
|
+
)?.text;
|
|
72
|
+
return errorMessage;
|
|
73
|
+
} else if ("toolResult" in result && typeof result.toolResult === "string") {
|
|
74
|
+
return result.toolResult;
|
|
75
|
+
}
|
|
76
|
+
if ("message" in result && typeof result.message === "string") {
|
|
77
|
+
return result.message;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return void 0;
|
|
81
|
+
};
|
|
82
|
+
const createCallTool = (name, client, onError) => async (args) => {
|
|
83
|
+
let result;
|
|
84
|
+
try {
|
|
85
|
+
result = await client.callTool({ name, arguments: args }, import_types.CompatibilityCallToolResultSchema);
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return onError(getErrorDescriptionFromToolCall(error));
|
|
88
|
+
}
|
|
89
|
+
if (result.isError) {
|
|
90
|
+
return onError(getErrorDescriptionFromToolCall(result));
|
|
91
|
+
}
|
|
92
|
+
if (result.toolResult !== void 0) {
|
|
93
|
+
return result.toolResult;
|
|
94
|
+
}
|
|
95
|
+
if (result.content !== void 0) {
|
|
96
|
+
return result.content;
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
};
|
|
100
|
+
function mcpToolToDynamicTool(tool, onCallTool) {
|
|
101
|
+
return new import_tools.DynamicStructuredTool({
|
|
102
|
+
name: tool.name,
|
|
103
|
+
description: tool.description ?? "",
|
|
104
|
+
schema: (0, import_schemaParsing.convertJsonSchemaToZod)(tool.inputSchema),
|
|
105
|
+
func: onCallTool,
|
|
106
|
+
metadata: { isFromToolkit: true }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
class McpToolkit extends import_agents.Toolkit {
|
|
110
|
+
constructor(tools) {
|
|
111
|
+
super();
|
|
112
|
+
this.tools = tools;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function safeCreateUrl(url, baseUrl) {
|
|
116
|
+
try {
|
|
117
|
+
return (0, import_n8n_workflow.createResultOk)(new URL(url, baseUrl));
|
|
118
|
+
} catch (error) {
|
|
119
|
+
return (0, import_n8n_workflow.createResultError)(error);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function normalizeAndValidateUrl(input) {
|
|
123
|
+
const withProtocol = !/^https?:\/\//i.test(input) ? `https://${input}` : input;
|
|
124
|
+
const parsedUrl = safeCreateUrl(withProtocol);
|
|
125
|
+
if (!parsedUrl.ok) {
|
|
126
|
+
return (0, import_n8n_workflow.createResultError)(parsedUrl.error);
|
|
127
|
+
}
|
|
128
|
+
return parsedUrl;
|
|
129
|
+
}
|
|
130
|
+
async function connectMcpClient({
|
|
131
|
+
headers,
|
|
132
|
+
sseEndpoint,
|
|
133
|
+
name,
|
|
134
|
+
version
|
|
135
|
+
}) {
|
|
136
|
+
try {
|
|
137
|
+
const endpoint = normalizeAndValidateUrl(sseEndpoint);
|
|
138
|
+
if (!endpoint.ok) {
|
|
139
|
+
return (0, import_n8n_workflow.createResultError)({ type: "invalid_url", error: endpoint.error });
|
|
140
|
+
}
|
|
141
|
+
const transport = new import_sse.SSEClientTransport(endpoint.result, {
|
|
142
|
+
eventSourceInit: {
|
|
143
|
+
fetch: async (url, init) => await fetch(url, {
|
|
144
|
+
...init,
|
|
145
|
+
headers: {
|
|
146
|
+
...headers,
|
|
147
|
+
Accept: "text/event-stream"
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
},
|
|
151
|
+
requestInit: { headers }
|
|
152
|
+
});
|
|
153
|
+
const client = new import_client.Client(
|
|
154
|
+
{ name, version: version.toString() },
|
|
155
|
+
{ capabilities: { tools: {} } }
|
|
156
|
+
);
|
|
157
|
+
await client.connect(transport);
|
|
158
|
+
return (0, import_n8n_workflow.createResultOk)(client);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
return (0, import_n8n_workflow.createResultError)({ type: "connection", error });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function getAuthHeaders(ctx, authentication) {
|
|
164
|
+
switch (authentication) {
|
|
165
|
+
case "headerAuth": {
|
|
166
|
+
const header = await ctx.getCredentials("httpHeaderAuth").catch(() => null);
|
|
167
|
+
if (!header) return {};
|
|
168
|
+
return { headers: { [header.name]: header.value } };
|
|
169
|
+
}
|
|
170
|
+
case "bearerAuth": {
|
|
171
|
+
const result = await ctx.getCredentials("httpBearerAuth").catch(() => null);
|
|
172
|
+
if (!result) return {};
|
|
173
|
+
return { headers: { Authorization: `Bearer ${result.token}` } };
|
|
174
|
+
}
|
|
175
|
+
case "none":
|
|
176
|
+
default: {
|
|
177
|
+
return {};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
182
|
+
0 && (module.exports = {
|
|
183
|
+
McpToolkit,
|
|
184
|
+
connectMcpClient,
|
|
185
|
+
createCallTool,
|
|
186
|
+
getAllTools,
|
|
187
|
+
getAuthHeaders,
|
|
188
|
+
getErrorDescriptionFromToolCall,
|
|
189
|
+
getSelectedTools,
|
|
190
|
+
mcpToolToDynamicTool
|
|
191
|
+
});
|
|
192
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/mcp/McpClientTool/utils.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js';\nimport { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';\nimport { CompatibilityCallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';\nimport { Toolkit } from 'langchain/agents';\nimport { DynamicStructuredTool, type DynamicStructuredToolInput } from 'langchain/tools';\nimport {\n\tcreateResultError,\n\tcreateResultOk,\n\ttype IDataObject,\n\ttype IExecuteFunctions,\n\ttype Result,\n} from 'n8n-workflow';\nimport { type ZodTypeAny } from 'zod';\n\nimport { convertJsonSchemaToZod } from '@utils/schemaParsing';\n\nimport type { McpAuthenticationOption, McpTool, McpToolIncludeMode } from './types';\n\nexport async function getAllTools(client: Client, cursor?: string): Promise<McpTool[]> {\n\tconst { tools, nextCursor } = await client.listTools({ cursor });\n\n\tif (nextCursor) {\n\t\treturn (tools as McpTool[]).concat(await getAllTools(client, nextCursor));\n\t}\n\n\treturn tools as McpTool[];\n}\n\nexport function getSelectedTools({\n\tmode,\n\tincludeTools,\n\texcludeTools,\n\ttools,\n}: {\n\tmode: McpToolIncludeMode;\n\tincludeTools?: string[];\n\texcludeTools?: string[];\n\ttools: McpTool[];\n}) {\n\tswitch (mode) {\n\t\tcase 'selected': {\n\t\t\tif (!includeTools?.length) return tools;\n\t\t\tconst include = new Set(includeTools);\n\t\t\treturn tools.filter((tool) => include.has(tool.name));\n\t\t}\n\t\tcase 'except': {\n\t\t\tconst except = new Set(excludeTools ?? []);\n\t\t\treturn tools.filter((tool) => !except.has(tool.name));\n\t\t}\n\t\tcase 'all':\n\t\tdefault:\n\t\t\treturn tools;\n\t}\n}\n\nexport const getErrorDescriptionFromToolCall = (result: unknown): string | undefined => {\n\tif (result && typeof result === 'object') {\n\t\tif ('content' in result && Array.isArray(result.content)) {\n\t\t\tconst errorMessage = (result.content as Array<{ type: 'text'; text: string }>).find(\n\t\t\t\t(content) => content && typeof content === 'object' && typeof content.text === 'string',\n\t\t\t)?.text;\n\t\t\treturn errorMessage;\n\t\t} else if ('toolResult' in result && typeof result.toolResult === 'string') {\n\t\t\treturn result.toolResult;\n\t\t}\n\t\tif ('message' in result && typeof result.message === 'string') {\n\t\t\treturn result.message;\n\t\t}\n\t}\n\n\treturn undefined;\n};\n\nexport const createCallTool =\n\t(name: string, client: Client, onError: (error: string | undefined) => void) =>\n\tasync (args: IDataObject) => {\n\t\tlet result: Awaited<ReturnType<Client['callTool']>>;\n\t\ttry {\n\t\t\tresult = await client.callTool({ name, arguments: args }, CompatibilityCallToolResultSchema);\n\t\t} catch (error) {\n\t\t\treturn onError(getErrorDescriptionFromToolCall(error));\n\t\t}\n\n\t\tif (result.isError) {\n\t\t\treturn onError(getErrorDescriptionFromToolCall(result));\n\t\t}\n\n\t\tif (result.toolResult !== undefined) {\n\t\t\treturn result.toolResult;\n\t\t}\n\n\t\tif (result.content !== undefined) {\n\t\t\treturn result.content;\n\t\t}\n\n\t\treturn result;\n\t};\n\nexport function mcpToolToDynamicTool(\n\ttool: McpTool,\n\tonCallTool: DynamicStructuredToolInput['func'],\n) {\n\treturn new DynamicStructuredTool({\n\t\tname: tool.name,\n\t\tdescription: tool.description ?? '',\n\t\tschema: convertJsonSchemaToZod(tool.inputSchema),\n\t\tfunc: onCallTool,\n\t\tmetadata: { isFromToolkit: true },\n\t});\n}\n\nexport class McpToolkit extends Toolkit {\n\tconstructor(public tools: Array<DynamicStructuredTool<ZodTypeAny>>) {\n\t\tsuper();\n\t}\n}\n\nfunction safeCreateUrl(url: string, baseUrl?: string | URL): Result<URL, Error> {\n\ttry {\n\t\treturn createResultOk(new URL(url, baseUrl));\n\t} catch (error) {\n\t\treturn createResultError(error);\n\t}\n}\n\nfunction normalizeAndValidateUrl(input: string): Result<URL, Error> {\n\tconst withProtocol = !/^https?:\\/\\//i.test(input) ? `https://${input}` : input;\n\tconst parsedUrl = safeCreateUrl(withProtocol);\n\n\tif (!parsedUrl.ok) {\n\t\treturn createResultError(parsedUrl.error);\n\t}\n\n\treturn parsedUrl;\n}\n\ntype ConnectMcpClientError =\n\t| { type: 'invalid_url'; error: Error }\n\t| { type: 'connection'; error: Error };\nexport async function connectMcpClient({\n\theaders,\n\tsseEndpoint,\n\tname,\n\tversion,\n}: {\n\tsseEndpoint: string;\n\theaders?: Record<string, string>;\n\tname: string;\n\tversion: number;\n}): Promise<Result<Client, ConnectMcpClientError>> {\n\ttry {\n\t\tconst endpoint = normalizeAndValidateUrl(sseEndpoint);\n\n\t\tif (!endpoint.ok) {\n\t\t\treturn createResultError({ type: 'invalid_url', error: endpoint.error });\n\t\t}\n\n\t\tconst transport = new SSEClientTransport(endpoint.result, {\n\t\t\teventSourceInit: {\n\t\t\t\tfetch: async (url, init) =>\n\t\t\t\t\tawait fetch(url, {\n\t\t\t\t\t\t...init,\n\t\t\t\t\t\theaders: {\n\t\t\t\t\t\t\t...headers,\n\t\t\t\t\t\t\tAccept: 'text/event-stream',\n\t\t\t\t\t\t},\n\t\t\t\t\t}),\n\t\t\t},\n\t\t\trequestInit: { headers },\n\t\t});\n\n\t\tconst client = new Client(\n\t\t\t{ name, version: version.toString() },\n\t\t\t{ capabilities: { tools: {} } },\n\t\t);\n\n\t\tawait client.connect(transport);\n\t\treturn createResultOk(client);\n\t} catch (error) {\n\t\treturn createResultError({ type: 'connection', error });\n\t}\n}\n\nexport async function getAuthHeaders(\n\tctx: Pick<IExecuteFunctions, 'getCredentials'>,\n\tauthentication: McpAuthenticationOption,\n): Promise<{ headers?: Record<string, string> }> {\n\tswitch (authentication) {\n\t\tcase 'headerAuth': {\n\t\t\tconst header = await ctx\n\t\t\t\t.getCredentials<{ name: string; value: string }>('httpHeaderAuth')\n\t\t\t\t.catch(() => null);\n\n\t\t\tif (!header) return {};\n\n\t\t\treturn { headers: { [header.name]: header.value } };\n\t\t}\n\t\tcase 'bearerAuth': {\n\t\t\tconst result = await ctx\n\t\t\t\t.getCredentials<{ token: string }>('httpBearerAuth')\n\t\t\t\t.catch(() => null);\n\n\t\t\tif (!result) return {};\n\n\t\t\treturn { headers: { Authorization: `Bearer ${result.token}` } };\n\t\t}\n\t\tcase 'none':\n\t\tdefault: {\n\t\t\treturn {};\n\t\t}\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var FlushingSSEServerTransport_exports = {};
|
|
20
|
+
__export(FlushingSSEServerTransport_exports, {
|
|
21
|
+
FlushingSSEServerTransport: () => FlushingSSEServerTransport
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(FlushingSSEServerTransport_exports);
|
|
24
|
+
var import_sse = require("@modelcontextprotocol/sdk/server/sse.js");
|
|
25
|
+
class FlushingSSEServerTransport extends import_sse.SSEServerTransport {
|
|
26
|
+
constructor(_endpoint, response) {
|
|
27
|
+
super(_endpoint, response);
|
|
28
|
+
this.response = response;
|
|
29
|
+
}
|
|
30
|
+
async send(message) {
|
|
31
|
+
await super.send(message);
|
|
32
|
+
this.response.flush();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
36
|
+
0 && (module.exports = {
|
|
37
|
+
FlushingSSEServerTransport
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=FlushingSSEServerTransport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/mcp/McpTrigger/FlushingSSEServerTransport.ts"],"sourcesContent":["import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\nimport type { Response } from 'express';\n\nexport type CompressionResponse = Response & {\n\t/**\n\t * `flush()` is defined in the compression middleware.\n\t * This is necessary because the compression middleware sometimes waits\n\t * for a certain amount of data before sending the data to the client\n\t */\n\tflush: () => void;\n};\n\nexport class FlushingSSEServerTransport extends SSEServerTransport {\n\tconstructor(\n\t\t_endpoint: string,\n\t\tprivate response: CompressionResponse,\n\t) {\n\t\tsuper(_endpoint, response);\n\t}\n\n\tasync send(message: JSONRPCMessage): Promise<void> {\n\t\tawait super.send(message);\n\t\tthis.response.flush();\n\t}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,iBAAmC;AAa5B,MAAM,mCAAmC,8BAAmB;AAAA,EAClE,YACC,WACQ,UACP;AACD,UAAM,WAAW,QAAQ;AAFjB;AAAA,EAGT;AAAA,EAEA,MAAM,KAAK,SAAwC;AAClD,UAAM,MAAM,KAAK,OAAO;AACxB,SAAK,SAAS,MAAM;AAAA,EACrB;AACD;","names":[]}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __typeError = (msg) => {
|
|
7
|
+
throw TypeError(msg);
|
|
8
|
+
};
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
22
|
+
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
23
|
+
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
24
|
+
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
25
|
+
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
26
|
+
var McpServer_exports = {};
|
|
27
|
+
__export(McpServer_exports, {
|
|
28
|
+
McpServer: () => McpServer,
|
|
29
|
+
McpServerSingleton: () => McpServerSingleton
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(McpServer_exports);
|
|
32
|
+
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
33
|
+
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
34
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
35
|
+
var import_zod_to_json_schema = require("zod-to-json-schema");
|
|
36
|
+
var import_FlushingSSEServerTransport = require("./FlushingSSEServerTransport");
|
|
37
|
+
var _instance;
|
|
38
|
+
function wasToolCall(body) {
|
|
39
|
+
try {
|
|
40
|
+
const message = JSON.parse(body);
|
|
41
|
+
const parsedMessage = import_types.JSONRPCMessageSchema.parse(message);
|
|
42
|
+
return "method" in parsedMessage && "id" in parsedMessage && parsedMessage?.method === import_types.CallToolRequestSchema.shape.method.value;
|
|
43
|
+
} catch {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
class McpServer {
|
|
48
|
+
constructor(logger) {
|
|
49
|
+
this.servers = {};
|
|
50
|
+
this.transports = {};
|
|
51
|
+
this.tools = {};
|
|
52
|
+
this.resolveFunctions = {};
|
|
53
|
+
this.logger = logger;
|
|
54
|
+
this.logger.debug("MCP Server created");
|
|
55
|
+
}
|
|
56
|
+
async connectTransport(postUrl, resp) {
|
|
57
|
+
const transport = new import_FlushingSSEServerTransport.FlushingSSEServerTransport(postUrl, resp);
|
|
58
|
+
const server = this.setUpServer();
|
|
59
|
+
const { sessionId } = transport;
|
|
60
|
+
this.transports[sessionId] = transport;
|
|
61
|
+
this.servers[sessionId] = server;
|
|
62
|
+
resp.on("close", async () => {
|
|
63
|
+
this.logger.debug(`Deleting transport for ${sessionId}`);
|
|
64
|
+
delete this.tools[sessionId];
|
|
65
|
+
delete this.resolveFunctions[sessionId];
|
|
66
|
+
delete this.transports[sessionId];
|
|
67
|
+
delete this.servers[sessionId];
|
|
68
|
+
});
|
|
69
|
+
await server.connect(transport);
|
|
70
|
+
if (resp.flush) {
|
|
71
|
+
resp.flush();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async handlePostMessage(req, resp, connectedTools) {
|
|
75
|
+
const sessionId = req.query.sessionId;
|
|
76
|
+
const transport = this.transports[sessionId];
|
|
77
|
+
this.tools[sessionId] = connectedTools;
|
|
78
|
+
if (transport) {
|
|
79
|
+
await new Promise(async (resolve) => {
|
|
80
|
+
this.resolveFunctions[sessionId] = resolve;
|
|
81
|
+
await transport.handlePostMessage(req, resp, req.rawBody.toString());
|
|
82
|
+
});
|
|
83
|
+
delete this.resolveFunctions[sessionId];
|
|
84
|
+
} else {
|
|
85
|
+
this.logger.warn(`No transport found for session ${sessionId}`);
|
|
86
|
+
resp.status(401).send("No transport found for sessionId");
|
|
87
|
+
}
|
|
88
|
+
if (resp.flush) {
|
|
89
|
+
resp.flush();
|
|
90
|
+
}
|
|
91
|
+
delete this.tools[sessionId];
|
|
92
|
+
return wasToolCall(req.rawBody.toString());
|
|
93
|
+
}
|
|
94
|
+
setUpServer() {
|
|
95
|
+
const server = new import_server.Server(
|
|
96
|
+
{
|
|
97
|
+
name: "n8n-mcp-server",
|
|
98
|
+
version: "0.1.0"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
capabilities: { tools: {} }
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
server.setRequestHandler(import_types.ListToolsRequestSchema, async (_, extra) => {
|
|
105
|
+
if (!extra.sessionId) {
|
|
106
|
+
throw new import_n8n_workflow.OperationalError("Require a sessionId for the listing of tools");
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
tools: this.tools[extra.sessionId].map((tool) => {
|
|
110
|
+
return {
|
|
111
|
+
name: tool.name,
|
|
112
|
+
description: tool.description,
|
|
113
|
+
inputSchema: (0, import_zod_to_json_schema.zodToJsonSchema)(tool.schema)
|
|
114
|
+
};
|
|
115
|
+
})
|
|
116
|
+
};
|
|
117
|
+
});
|
|
118
|
+
server.setRequestHandler(import_types.CallToolRequestSchema, async (request, extra) => {
|
|
119
|
+
if (!request.params?.name || !request.params?.arguments) {
|
|
120
|
+
throw new import_n8n_workflow.OperationalError("Require a name and arguments for the tool call");
|
|
121
|
+
}
|
|
122
|
+
if (!extra.sessionId) {
|
|
123
|
+
throw new import_n8n_workflow.OperationalError("Require a sessionId for the tool call");
|
|
124
|
+
}
|
|
125
|
+
const requestedTool = this.tools[extra.sessionId].find(
|
|
126
|
+
(tool) => tool.name === request.params.name
|
|
127
|
+
);
|
|
128
|
+
if (!requestedTool) {
|
|
129
|
+
throw new import_n8n_workflow.OperationalError("Tool not found");
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const result = await requestedTool.invoke(request.params.arguments);
|
|
133
|
+
this.resolveFunctions[extra.sessionId]();
|
|
134
|
+
this.logger.debug(`Got request for ${requestedTool.name}, and executed it.`);
|
|
135
|
+
if (typeof result === "object") {
|
|
136
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
137
|
+
}
|
|
138
|
+
if (typeof result === "string") {
|
|
139
|
+
return { content: [{ type: "text", text: result }] };
|
|
140
|
+
}
|
|
141
|
+
return { content: [{ type: "text", text: String(result) }] };
|
|
142
|
+
} catch (error) {
|
|
143
|
+
this.logger.error(`Error while executing Tool ${requestedTool.name}: ${error}`);
|
|
144
|
+
return { isError: true, content: [{ type: "text", text: `Error: ${error.message}` }] };
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
server.onclose = () => {
|
|
148
|
+
this.logger.debug("Closing MCP Server");
|
|
149
|
+
};
|
|
150
|
+
server.onerror = (error) => {
|
|
151
|
+
this.logger.error(`MCP Error: ${error}`);
|
|
152
|
+
};
|
|
153
|
+
return server;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
const _McpServerSingleton = class _McpServerSingleton {
|
|
157
|
+
constructor(logger) {
|
|
158
|
+
this._serverData = new McpServer(logger);
|
|
159
|
+
}
|
|
160
|
+
static instance(logger) {
|
|
161
|
+
if (!__privateGet(_McpServerSingleton, _instance)) {
|
|
162
|
+
__privateSet(_McpServerSingleton, _instance, new _McpServerSingleton(logger));
|
|
163
|
+
logger.debug("Created singleton for MCP Servers");
|
|
164
|
+
}
|
|
165
|
+
return __privateGet(_McpServerSingleton, _instance).serverData;
|
|
166
|
+
}
|
|
167
|
+
get serverData() {
|
|
168
|
+
return this._serverData;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
_instance = new WeakMap();
|
|
172
|
+
__privateAdd(_McpServerSingleton, _instance);
|
|
173
|
+
let McpServerSingleton = _McpServerSingleton;
|
|
174
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
175
|
+
0 && (module.exports = {
|
|
176
|
+
McpServer,
|
|
177
|
+
McpServerSingleton
|
|
178
|
+
});
|
|
179
|
+
//# sourceMappingURL=McpServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/mcp/McpTrigger/McpServer.ts"],"sourcesContent":["import type { Tool } from '@langchain/core/tools';\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport type { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';\nimport type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';\nimport {\n\tJSONRPCMessageSchema,\n\tListToolsRequestSchema,\n\tCallToolRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport type * as express from 'express';\nimport { OperationalError, type Logger } from 'n8n-workflow';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\n\nimport { FlushingSSEServerTransport } from './FlushingSSEServerTransport';\nimport type { CompressionResponse } from './FlushingSSEServerTransport';\n\n/**\n * Parses the JSONRPC message and checks whether the method used was a tool\n * call. This is necessary in order to not have executions for listing tools\n * and other commands sent by the MCP client\n */\nfunction wasToolCall(body: string) {\n\ttry {\n\t\tconst message: unknown = JSON.parse(body);\n\t\tconst parsedMessage: JSONRPCMessage = JSONRPCMessageSchema.parse(message);\n\t\treturn (\n\t\t\t'method' in parsedMessage &&\n\t\t\t'id' in parsedMessage &&\n\t\t\tparsedMessage?.method === CallToolRequestSchema.shape.method.value\n\t\t);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport class McpServer {\n\tservers: { [sessionId: string]: Server } = {};\n\n\ttransports: { [sessionId: string]: FlushingSSEServerTransport } = {};\n\n\tlogger: Logger;\n\n\tprivate tools: { [sessionId: string]: Tool[] } = {};\n\n\tprivate resolveFunctions: { [sessionId: string]: CallableFunction } = {};\n\n\tconstructor(logger: Logger) {\n\t\tthis.logger = logger;\n\t\tthis.logger.debug('MCP Server created');\n\t}\n\n\tasync connectTransport(postUrl: string, resp: CompressionResponse): Promise<void> {\n\t\tconst transport = new FlushingSSEServerTransport(postUrl, resp);\n\t\tconst server = this.setUpServer();\n\t\tconst { sessionId } = transport;\n\t\tthis.transports[sessionId] = transport;\n\t\tthis.servers[sessionId] = server;\n\n\t\tresp.on('close', async () => {\n\t\t\tthis.logger.debug(`Deleting transport for ${sessionId}`);\n\t\t\tdelete this.tools[sessionId];\n\t\t\tdelete this.resolveFunctions[sessionId];\n\t\t\tdelete this.transports[sessionId];\n\t\t\tdelete this.servers[sessionId];\n\t\t});\n\n\t\tawait server.connect(transport);\n\n\t\t// Make sure we flush the compression middleware, so that it's not waiting for more content to be added to the buffer\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\t}\n\n\tasync handlePostMessage(req: express.Request, resp: CompressionResponse, connectedTools: Tool[]) {\n\t\tconst sessionId = req.query.sessionId as string;\n\t\tconst transport = this.transports[sessionId];\n\t\tthis.tools[sessionId] = connectedTools;\n\t\tif (transport) {\n\t\t\t// We need to add a promise here because the `handlePostMessage` will send something to the\n\t\t\t// MCP Server, that will run in a different context. This means that the return will happen\n\t\t\t// almost immediately, and will lead to marking the sub-node as \"running\" in the final execution\n\t\t\tawait new Promise(async (resolve) => {\n\t\t\t\tthis.resolveFunctions[sessionId] = resolve;\n\t\t\t\tawait transport.handlePostMessage(req, resp, req.rawBody.toString());\n\t\t\t});\n\t\t\tdelete this.resolveFunctions[sessionId];\n\t\t} else {\n\t\t\tthis.logger.warn(`No transport found for session ${sessionId}`);\n\t\t\tresp.status(401).send('No transport found for sessionId');\n\t\t}\n\n\t\tif (resp.flush) {\n\t\t\tresp.flush();\n\t\t}\n\n\t\tdelete this.tools[sessionId]; // Clean up to avoid keeping all tools in memory\n\n\t\treturn wasToolCall(req.rawBody.toString());\n\t}\n\n\tsetUpServer(): Server {\n\t\tconst server = new Server(\n\t\t\t{\n\t\t\t\tname: 'n8n-mcp-server',\n\t\t\t\tversion: '0.1.0',\n\t\t\t},\n\t\t\t{\n\t\t\t\tcapabilities: { tools: {} },\n\t\t\t},\n\t\t);\n\n\t\tserver.setRequestHandler(ListToolsRequestSchema, async (_, extra: RequestHandlerExtra) => {\n\t\t\tif (!extra.sessionId) {\n\t\t\t\tthrow new OperationalError('Require a sessionId for the listing of tools');\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\ttools: this.tools[extra.sessionId].map((tool) => {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\tinputSchema: zodToJsonSchema(tool.schema),\n\t\t\t\t\t};\n\t\t\t\t}),\n\t\t\t};\n\t\t});\n\n\t\tserver.setRequestHandler(CallToolRequestSchema, async (request, extra: RequestHandlerExtra) => {\n\t\t\tif (!request.params?.name || !request.params?.arguments) {\n\t\t\t\tthrow new OperationalError('Require a name and arguments for the tool call');\n\t\t\t}\n\t\t\tif (!extra.sessionId) {\n\t\t\t\tthrow new OperationalError('Require a sessionId for the tool call');\n\t\t\t}\n\n\t\t\tconst requestedTool: Tool | undefined = this.tools[extra.sessionId].find(\n\t\t\t\t(tool) => tool.name === request.params.name,\n\t\t\t);\n\t\t\tif (!requestedTool) {\n\t\t\t\tthrow new OperationalError('Tool not found');\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst result = await requestedTool.invoke(request.params.arguments);\n\n\t\t\t\tthis.resolveFunctions[extra.sessionId]();\n\n\t\t\t\tthis.logger.debug(`Got request for ${requestedTool.name}, and executed it.`);\n\n\t\t\t\tif (typeof result === 'object') {\n\t\t\t\t\treturn { content: [{ type: 'text', text: JSON.stringify(result) }] };\n\t\t\t\t}\n\t\t\t\tif (typeof result === 'string') {\n\t\t\t\t\treturn { content: [{ type: 'text', text: result }] };\n\t\t\t\t}\n\t\t\t\treturn { content: [{ type: 'text', text: String(result) }] };\n\t\t\t} catch (error) {\n\t\t\t\tthis.logger.error(`Error while executing Tool ${requestedTool.name}: ${error}`);\n\t\t\t\treturn { isError: true, content: [{ type: 'text', text: `Error: ${error.message}` }] };\n\t\t\t}\n\t\t});\n\n\t\tserver.onclose = () => {\n\t\t\tthis.logger.debug('Closing MCP Server');\n\t\t};\n\t\tserver.onerror = (error: unknown) => {\n\t\t\tthis.logger.error(`MCP Error: ${error}`);\n\t\t};\n\t\treturn server;\n\t}\n}\n\n/**\n * This singleton is shared across the instance, making sure we only have one server to worry about.\n * It needs to stay in memory to keep track of the long-lived connections.\n * It requires a logger at first creation to set everything up.\n */\nexport class McpServerSingleton {\n\tstatic #instance: McpServerSingleton;\n\n\tprivate _serverData: McpServer;\n\n\tprivate constructor(logger: Logger) {\n\t\tthis._serverData = new McpServer(logger);\n\t}\n\n\tstatic instance(logger: Logger): McpServer {\n\t\tif (!McpServerSingleton.#instance) {\n\t\t\tMcpServerSingleton.#instance = new McpServerSingleton(logger);\n\t\t\tlogger.debug('Created singleton for MCP Servers');\n\t\t}\n\n\t\treturn McpServerSingleton.#instance.serverData;\n\t}\n\n\tget serverData() {\n\t\treturn this._serverData;\n\t}\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var McpTrigger_node_exports = {};
|
|
20
|
+
__export(McpTrigger_node_exports, {
|
|
21
|
+
McpTrigger: () => McpTrigger
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(McpTrigger_node_exports);
|
|
24
|
+
var import_error = require("n8n-nodes-base/dist/nodes/Webhook/error");
|
|
25
|
+
var import_utils = require("n8n-nodes-base/dist/nodes/Webhook/utils");
|
|
26
|
+
var import_n8n_workflow = require("n8n-workflow");
|
|
27
|
+
var import_helpers = require("../../../utils/helpers");
|
|
28
|
+
var import_McpServer = require("./McpServer");
|
|
29
|
+
const MCP_SSE_SETUP_PATH = "sse";
|
|
30
|
+
const MCP_SSE_MESSAGES_PATH = "messages";
|
|
31
|
+
class McpTrigger extends import_n8n_workflow.Node {
|
|
32
|
+
constructor() {
|
|
33
|
+
super(...arguments);
|
|
34
|
+
this.description = {
|
|
35
|
+
displayName: "MCP Server Trigger",
|
|
36
|
+
name: "mcpTrigger",
|
|
37
|
+
icon: {
|
|
38
|
+
light: "file:../mcp.svg",
|
|
39
|
+
dark: "file:../mcp.dark.svg"
|
|
40
|
+
},
|
|
41
|
+
group: ["trigger"],
|
|
42
|
+
version: 1,
|
|
43
|
+
description: "Expose n8n tools as an MCP Server endpoint",
|
|
44
|
+
activationMessage: "You can now connect your MCP Clients to the SSE URL.",
|
|
45
|
+
defaults: {
|
|
46
|
+
name: "MCP Server Trigger"
|
|
47
|
+
},
|
|
48
|
+
codex: {
|
|
49
|
+
categories: ["AI", "Core Nodes"],
|
|
50
|
+
subcategories: {
|
|
51
|
+
AI: ["Root Nodes", "Model Context Protocol"],
|
|
52
|
+
"Core Nodes": ["Other Trigger Nodes"]
|
|
53
|
+
},
|
|
54
|
+
alias: ["Model Context Protocol", "MCP Server"],
|
|
55
|
+
resources: {
|
|
56
|
+
primaryDocumentation: [
|
|
57
|
+
{
|
|
58
|
+
url: "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.mcptrigger/"
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
triggerPanel: {
|
|
64
|
+
header: "Listen for MCP events",
|
|
65
|
+
executionsHelp: {
|
|
66
|
+
inactive: "This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. <a data-key='activate'>Activate</a> the workflow, then make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.",
|
|
67
|
+
active: "This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. Since your workflow is activated, you can make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor."
|
|
68
|
+
},
|
|
69
|
+
activationHint: "Once you\u2019ve finished building your workflow, run it without having to click this button by using the production URL."
|
|
70
|
+
},
|
|
71
|
+
inputs: [
|
|
72
|
+
{
|
|
73
|
+
type: import_n8n_workflow.NodeConnectionTypes.AiTool,
|
|
74
|
+
displayName: "Tools"
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
outputs: [],
|
|
78
|
+
credentials: [
|
|
79
|
+
{
|
|
80
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-credentials-name-unsuffixed
|
|
81
|
+
name: "httpBearerAuth",
|
|
82
|
+
required: true,
|
|
83
|
+
displayOptions: {
|
|
84
|
+
show: {
|
|
85
|
+
authentication: ["bearerAuth"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "httpHeaderAuth",
|
|
91
|
+
required: true,
|
|
92
|
+
displayOptions: {
|
|
93
|
+
show: {
|
|
94
|
+
authentication: ["headerAuth"]
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
properties: [
|
|
100
|
+
{
|
|
101
|
+
displayName: "Authentication",
|
|
102
|
+
name: "authentication",
|
|
103
|
+
type: "options",
|
|
104
|
+
options: [
|
|
105
|
+
{ name: "None", value: "none" },
|
|
106
|
+
{ name: "Bearer Auth", value: "bearerAuth" },
|
|
107
|
+
{ name: "Header Auth", value: "headerAuth" }
|
|
108
|
+
],
|
|
109
|
+
default: "none",
|
|
110
|
+
description: "The way to authenticate"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
displayName: "Path",
|
|
114
|
+
name: "path",
|
|
115
|
+
type: "string",
|
|
116
|
+
default: "",
|
|
117
|
+
placeholder: "webhook",
|
|
118
|
+
required: true,
|
|
119
|
+
description: "The base path for this MCP server"
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
webhooks: [
|
|
123
|
+
{
|
|
124
|
+
name: "setup",
|
|
125
|
+
httpMethod: "GET",
|
|
126
|
+
responseMode: "onReceived",
|
|
127
|
+
isFullPath: true,
|
|
128
|
+
path: `={{$parameter["path"]}}/${MCP_SSE_SETUP_PATH}`,
|
|
129
|
+
nodeType: "mcp",
|
|
130
|
+
ndvHideMethod: true,
|
|
131
|
+
ndvHideUrl: false
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "default",
|
|
135
|
+
httpMethod: "POST",
|
|
136
|
+
responseMode: "onReceived",
|
|
137
|
+
isFullPath: true,
|
|
138
|
+
path: `={{$parameter["path"]}}/${MCP_SSE_MESSAGES_PATH}`,
|
|
139
|
+
nodeType: "mcp",
|
|
140
|
+
ndvHideMethod: true,
|
|
141
|
+
ndvHideUrl: true
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async webhook(context) {
|
|
147
|
+
const webhookName = context.getWebhookName();
|
|
148
|
+
const req = context.getRequestObject();
|
|
149
|
+
const resp = context.getResponseObject();
|
|
150
|
+
try {
|
|
151
|
+
await (0, import_utils.validateWebhookAuthentication)(context, "authentication");
|
|
152
|
+
} catch (error) {
|
|
153
|
+
if (error instanceof import_error.WebhookAuthorizationError) {
|
|
154
|
+
resp.writeHead(error.responseCode);
|
|
155
|
+
resp.end(error.message);
|
|
156
|
+
return { noWebhookResponse: true };
|
|
157
|
+
}
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
const mcpServer = import_McpServer.McpServerSingleton.instance(context.logger);
|
|
161
|
+
if (webhookName === "setup") {
|
|
162
|
+
const postUrl = req.path.replace(
|
|
163
|
+
new RegExp(`/${MCP_SSE_SETUP_PATH}$`),
|
|
164
|
+
`/${MCP_SSE_MESSAGES_PATH}`
|
|
165
|
+
);
|
|
166
|
+
await mcpServer.connectTransport(postUrl, resp);
|
|
167
|
+
return { noWebhookResponse: true };
|
|
168
|
+
} else if (webhookName === "default") {
|
|
169
|
+
const connectedTools = await (0, import_helpers.getConnectedTools)(context, true);
|
|
170
|
+
const wasToolCall = await mcpServer.handlePostMessage(req, resp, connectedTools);
|
|
171
|
+
if (wasToolCall) return { noWebhookResponse: true, workflowData: [[{ json: {} }]] };
|
|
172
|
+
return { noWebhookResponse: true };
|
|
173
|
+
}
|
|
174
|
+
return { workflowData: [[{ json: {} }]] };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
178
|
+
0 && (module.exports = {
|
|
179
|
+
McpTrigger
|
|
180
|
+
});
|
|
181
|
+
//# sourceMappingURL=McpTrigger.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../nodes/mcp/McpTrigger/McpTrigger.node.ts"],"sourcesContent":["import { WebhookAuthorizationError } from 'n8n-nodes-base/dist/nodes/Webhook/error';\nimport { validateWebhookAuthentication } from 'n8n-nodes-base/dist/nodes/Webhook/utils';\nimport type { INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';\nimport { NodeConnectionTypes, Node } from 'n8n-workflow';\n\nimport { getConnectedTools } from '@utils/helpers';\n\nimport type { CompressionResponse } from './FlushingSSEServerTransport';\nimport { McpServerSingleton } from './McpServer';\nimport type { McpServer } from './McpServer';\n\nconst MCP_SSE_SETUP_PATH = 'sse';\nconst MCP_SSE_MESSAGES_PATH = 'messages';\n\nexport class McpTrigger extends Node {\n\tdescription: INodeTypeDescription = {\n\t\tdisplayName: 'MCP Server Trigger',\n\t\tname: 'mcpTrigger',\n\t\ticon: {\n\t\t\tlight: 'file:../mcp.svg',\n\t\t\tdark: 'file:../mcp.dark.svg',\n\t\t},\n\t\tgroup: ['trigger'],\n\t\tversion: 1,\n\t\tdescription: 'Expose n8n tools as an MCP Server endpoint',\n\t\tactivationMessage: 'You can now connect your MCP Clients to the SSE URL.',\n\t\tdefaults: {\n\t\t\tname: 'MCP Server Trigger',\n\t\t},\n\t\tcodex: {\n\t\t\tcategories: ['AI', 'Core Nodes'],\n\t\t\tsubcategories: {\n\t\t\t\tAI: ['Root Nodes', 'Model Context Protocol'],\n\t\t\t\t'Core Nodes': ['Other Trigger Nodes'],\n\t\t\t},\n\t\t\talias: ['Model Context Protocol', 'MCP Server'],\n\t\t\tresources: {\n\t\t\t\tprimaryDocumentation: [\n\t\t\t\t\t{\n\t\t\t\t\t\turl: 'https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-langchain.mcptrigger/',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t},\n\t\t},\n\t\ttriggerPanel: {\n\t\t\theader: 'Listen for MCP events',\n\t\t\texecutionsHelp: {\n\t\t\t\tinactive:\n\t\t\t\t\t\"This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. <a data-key='activate'>Activate</a> the workflow, then make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.\",\n\t\t\t\tactive:\n\t\t\t\t\t\"This trigger has two modes: test and production.<br /><br /><b>Use test mode while you build your workflow</b>. Click the 'test step' button, then make an MCP request to the test URL. The executions will show up in the editor.<br /><br /><b>Use production mode to run your workflow automatically</b>. Since your workflow is activated, you can make requests to the production URL. These executions will show up in the <a data-key='executions'>executions list</a>, but not the editor.\",\n\t\t\t},\n\t\t\tactivationHint:\n\t\t\t\t'Once you’ve finished building your workflow, run it without having to click this button by using the production URL.',\n\t\t},\n\t\tinputs: [\n\t\t\t{\n\t\t\t\ttype: NodeConnectionTypes.AiTool,\n\t\t\t\tdisplayName: 'Tools',\n\t\t\t},\n\t\t],\n\t\toutputs: [],\n\t\tcredentials: [\n\t\t\t{\n\t\t\t\t// eslint-disable-next-line n8n-nodes-base/node-class-description-credentials-name-unsuffixed\n\t\t\t\tname: 'httpBearerAuth',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tauthentication: ['bearerAuth'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'httpHeaderAuth',\n\t\t\t\trequired: true,\n\t\t\t\tdisplayOptions: {\n\t\t\t\t\tshow: {\n\t\t\t\t\t\tauthentication: ['headerAuth'],\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t\tproperties: [\n\t\t\t{\n\t\t\t\tdisplayName: 'Authentication',\n\t\t\t\tname: 'authentication',\n\t\t\t\ttype: 'options',\n\t\t\t\toptions: [\n\t\t\t\t\t{ name: 'None', value: 'none' },\n\t\t\t\t\t{ name: 'Bearer Auth', value: 'bearerAuth' },\n\t\t\t\t\t{ name: 'Header Auth', value: 'headerAuth' },\n\t\t\t\t],\n\t\t\t\tdefault: 'none',\n\t\t\t\tdescription: 'The way to authenticate',\n\t\t\t},\n\t\t\t{\n\t\t\t\tdisplayName: 'Path',\n\t\t\t\tname: 'path',\n\t\t\t\ttype: 'string',\n\t\t\t\tdefault: '',\n\t\t\t\tplaceholder: 'webhook',\n\t\t\t\trequired: true,\n\t\t\t\tdescription: 'The base path for this MCP server',\n\t\t\t},\n\t\t],\n\t\twebhooks: [\n\t\t\t{\n\t\t\t\tname: 'setup',\n\t\t\t\thttpMethod: 'GET',\n\t\t\t\tresponseMode: 'onReceived',\n\t\t\t\tisFullPath: true,\n\t\t\t\tpath: `={{$parameter[\"path\"]}}/${MCP_SSE_SETUP_PATH}`,\n\t\t\t\tnodeType: 'mcp',\n\t\t\t\tndvHideMethod: true,\n\t\t\t\tndvHideUrl: false,\n\t\t\t},\n\t\t\t{\n\t\t\t\tname: 'default',\n\t\t\t\thttpMethod: 'POST',\n\t\t\t\tresponseMode: 'onReceived',\n\t\t\t\tisFullPath: true,\n\t\t\t\tpath: `={{$parameter[\"path\"]}}/${MCP_SSE_MESSAGES_PATH}`,\n\t\t\t\tnodeType: 'mcp',\n\t\t\t\tndvHideMethod: true,\n\t\t\t\tndvHideUrl: true,\n\t\t\t},\n\t\t],\n\t};\n\n\tasync webhook(context: IWebhookFunctions): Promise<IWebhookResponseData> {\n\t\tconst webhookName = context.getWebhookName();\n\t\tconst req = context.getRequestObject();\n\t\tconst resp = context.getResponseObject() as unknown as CompressionResponse;\n\n\t\ttry {\n\t\t\tawait validateWebhookAuthentication(context, 'authentication');\n\t\t} catch (error) {\n\t\t\tif (error instanceof WebhookAuthorizationError) {\n\t\t\t\tresp.writeHead(error.responseCode);\n\t\t\t\tresp.end(error.message);\n\t\t\t\treturn { noWebhookResponse: true };\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tconst mcpServer: McpServer = McpServerSingleton.instance(context.logger);\n\n\t\tif (webhookName === 'setup') {\n\t\t\t// Sets up the transport and opens the long-lived connection. This resp\n\t\t\t// will stay streaming, and is the channel that sends the events\n\t\t\tconst postUrl = req.path.replace(\n\t\t\t\tnew RegExp(`/${MCP_SSE_SETUP_PATH}$`),\n\t\t\t\t`/${MCP_SSE_MESSAGES_PATH}`,\n\t\t\t);\n\t\t\tawait mcpServer.connectTransport(postUrl, resp);\n\n\t\t\treturn { noWebhookResponse: true };\n\t\t} else if (webhookName === 'default') {\n\t\t\t// This is the command-channel, and is actually executing the tools. This\n\t\t\t// sends the response back through the long-lived connection setup in the\n\t\t\t// 'setup' call\n\t\t\tconst connectedTools = await getConnectedTools(context, true);\n\n\t\t\tconst wasToolCall = await mcpServer.handlePostMessage(req, resp, connectedTools);\n\n\t\t\tif (wasToolCall) return { noWebhookResponse: true, workflowData: [[{ json: {} }]] };\n\t\t\treturn { noWebhookResponse: true };\n\t\t}\n\n\t\treturn { workflowData: [[{ json: {} }]] };\n\t}\n}\n"],"mappings":"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","names":[]}
|