@deepset-ai/n8n-nodes-haystack-enterprise-mcp 0.0.0-stage → 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 deepset
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 CHANGED
@@ -1,3 +1,106 @@
1
- # Temporary Holding Version
1
+ # @deepset-ai/n8n-nodes-haystack-enterprise-mcp
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ This is an n8n community node. It lets you use pipelines you've enabled as MCP tools on a
4
+ [Haystack Enterprise Platform](https://www.deepset.ai/haystack-platform) workspace as tools for an
5
+ n8n AI Agent.
6
+
7
+ New to Haystack Enterprise Platform? Start with the
8
+ [getting started guide](https://docs.cloud.deepset.ai/docs/getting-started).
9
+
10
+ [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/sustainable-use-license/)
11
+ workflow automation platform.
12
+
13
+ ## Contents
14
+
15
+ - [Installation](#installation)
16
+ - [Operations](#operations)
17
+ - [Example workflows](#example-workflows)
18
+ - [Credentials](#credentials)
19
+ - [Resources](#resources)
20
+ - [Version history](#version-history)
21
+ - [Contributing](#contributing)
22
+
23
+ ## Installation
24
+
25
+ This node works on **self-hosted n8n only** (2.16 or later in the 2.x series; we also check it
26
+ against the latest n8n release). It is not n8n-verified, so it can't run on n8n Cloud.
27
+
28
+ Enable community nodes on your instance, then follow the
29
+ [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) and enter the
30
+ package name `@deepset-ai/n8n-nodes-haystack-enterprise-mcp`.
31
+
32
+ Looking for the n8n-verified Haystack Enterprise nodes? See
33
+ [`@deepset-ai/n8n-nodes-haystack-enterprise`](https://www.npmjs.com/package/@deepset-ai/n8n-nodes-haystack-enterprise).
34
+
35
+ ## Operations
36
+
37
+ The node connects to the MCP server of a Haystack Enterprise Platform workspace and exposes its
38
+ enabled pipelines as tools. Add it to the **Tools** input of an AI Agent, pick your workspace, and
39
+ the Agent can call your pipelines.
40
+
41
+ - **Workspace**: the workspace whose pipelines to expose.
42
+ - **Tools to Include**: which of the workspace's tools the Agent can use:
43
+ - **All**: every tool (default).
44
+ - **Selected**: only the tools you pick.
45
+ - **All Except**: every tool except the ones you pick.
46
+ - **Options → Timeout**: how long to wait for a pipeline call, in ms (default 60000).
47
+
48
+ ## Example workflows
49
+
50
+ Try the
51
+ [AI Agent with MCP tools example](https://github.com/deepset-ai/deepset-n8n-nodes/blob/main/packages/haystack-enterprise-mcp/test/examples/ai-agent-with-mcp-tools-example.json):
52
+ an AI Agent with this node wired into its Tools input.
53
+
54
+ To use it, open the file on GitHub, click **Copy raw file**, and paste it onto the n8n canvas
55
+ (Ctrl/Cmd+V). Then add your own credentials (including for the OpenAI Chat Model node) and select
56
+ your workspace on the Haystack Enterprise MCP node.
57
+
58
+ ## Credentials
59
+
60
+ The node's **Authentication** dropdown offers two ways to authenticate.
61
+
62
+ ### API Key
63
+
64
+ Choose **API Key** on the node (the default) and create a "Haystack Enterprise API" credential:
65
+
66
+ 1. In [Haystack Enterprise Platform](https://cloud.deepset.ai/), go to Settings → API Keys and
67
+ generate a key. See [Generate an API key](https://docs.cloud.deepset.ai/docs/generate-api-key).
68
+ 2. In n8n, paste the key into the credential. **Server URL** defaults to the hosted Haystack
69
+ Enterprise Platform API (`https://api.cloud.deepset.ai`); for dev or private environments, set it
70
+ to your instance's API origin.
71
+
72
+ The key needs at least workspace and search permissions.
73
+
74
+ ### OAuth2 (sign in with your Haystack Enterprise account)
75
+
76
+ OAuth2 credentials require n8n 2.19.2 or later.
77
+
78
+ Choose **OAuth2** on the node and create a "Haystack Enterprise OAuth2 API" credential.
79
+
80
+ 1. Leave **Server URL** at `https://api.cloud.deepset.ai`. The Server URL is the API origin the
81
+ nodes use for requests and OAuth2 discovery; change it only for dev or private Haystack
82
+ Enterprise deployments.
83
+ 2. Click **Connect** and sign in with your Haystack Enterprise account.
84
+
85
+ **Manual OAuth2 configuration.** Turn **Use Dynamic Client Registration** off to configure or reuse
86
+ an existing OAuth2 client, including clients for private Haystack Enterprise deployments. Keep
87
+ **Server URL** pointed at the Haystack Enterprise API, fill in the OAuth2 settings, and register the
88
+ callback URL shown on the credential as a redirect URI on the client. Leave **Client Secret** empty
89
+ for a public PKCE client; confidential clients still require their secret.
90
+
91
+ ## Resources
92
+
93
+ - [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
94
+ - [Use your pipelines as MCP tools](https://docs.cloud.deepset.ai/docs/use-pipeline-as-mcp-tool)
95
+ - [Haystack Enterprise Platform](https://cloud.deepset.ai/)
96
+ - [Haystack Enterprise Platform docs](https://docs.cloud.deepset.ai/)
97
+
98
+ ## Version history
99
+
100
+ See the [changelog](./CHANGELOG.md).
101
+
102
+ ## Contributing
103
+
104
+ See [CONTRIBUTING.md](https://github.com/deepset-ai/deepset-n8n-nodes/blob/main/CONTRIBUTING.md).
105
+ Report security issues as described in
106
+ [SECURITY.md](https://github.com/deepset-ai/deepset-n8n-nodes/blob/main/SECURITY.md).
@@ -0,0 +1,10 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class HaystackEnterpriseApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: "file:../nodes/HaystackEnterpriseMcp/haystack-enterprise-electric-blue.svg";
6
+ documentationUrl: string;
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HaystackEnterpriseApi = void 0;
4
+ class HaystackEnterpriseApi {
5
+ name = 'haystackEnterpriseApi';
6
+ displayName = 'Haystack Enterprise API';
7
+ icon = 'file:../nodes/HaystackEnterpriseMcp/haystack-enterprise-electric-blue.svg';
8
+ documentationUrl = 'https://docs.cloud.deepset.ai/docs/generate-api-key';
9
+ properties = [
10
+ {
11
+ displayName: 'Server URL',
12
+ name: 'serverUrl',
13
+ type: 'string',
14
+ default: 'https://api.cloud.deepset.ai',
15
+ placeholder: 'https://api.cloud.deepset.ai',
16
+ description: 'The Haystack Enterprise API origin. Use the default for the hosted Haystack Enterprise Platform; for dev or private environments, enter your instance API origin (for example <code>https://haystack.example.com</code>).',
17
+ },
18
+ {
19
+ displayName: 'API Key',
20
+ name: 'apiKey',
21
+ type: 'string',
22
+ typeOptions: { password: true },
23
+ default: '',
24
+ required: true,
25
+ description: 'Your Haystack Enterprise <a href="https://docs.cloud.deepset.ai/docs/generate-api-key/" target="_blank" rel="noopener noreferrer">API key</a>. Generate one in Haystack Enterprise Platform under Settings → API Keys.',
26
+ },
27
+ ];
28
+ authenticate = {
29
+ type: 'generic',
30
+ properties: {
31
+ headers: {
32
+ Authorization: '=Bearer {{$credentials.apiKey}}',
33
+ },
34
+ },
35
+ };
36
+ test = {
37
+ request: {
38
+ baseURL: '={{ ((($credentials.serverUrl || "https://api.cloud.deepset.ai").trim().replace(/\\/+$/,"").replace(/\\/api\\/v[12]$/i,"")) || "https://api.cloud.deepset.ai") }}/api/v1',
39
+ url: '/workspaces',
40
+ method: 'GET',
41
+ },
42
+ rules: [
43
+ {
44
+ type: 'responseCode',
45
+ properties: {
46
+ value: 401,
47
+ message: 'Invalid API key. Check your key in Haystack Enterprise Platform under Settings → API Keys.',
48
+ },
49
+ },
50
+ {
51
+ type: 'responseCode',
52
+ properties: {
53
+ value: 403,
54
+ message: 'Access denied. The API key may not have permission to list workspaces.',
55
+ },
56
+ },
57
+ ],
58
+ };
59
+ }
60
+ exports.HaystackEnterpriseApi = HaystackEnterpriseApi;
61
+ //# sourceMappingURL=HaystackEnterpriseApi.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HaystackEnterpriseApi.credentials.js","sourceRoot":"","sources":["../../credentials/HaystackEnterpriseApi.credentials.ts"],"names":[],"mappings":";;;AASA,MAAa,qBAAqB;IACjC,IAAI,GAAG,uBAAuB,CAAC;IAE/B,WAAW,GAAG,yBAAyB,CAAC;IAExC,IAAI,GAAG,2EAAoF,CAAC;IAE5F,gBAAgB,GAAG,qDAAqD,CAAC;IAEzE,UAAU,GAAsB;QAC/B;YACC,WAAW,EAAE,YAAY;YACzB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,8BAA8B;YACvC,WAAW,EAAE,8BAA8B;YAC3C,WAAW,EACV,2NAA2N;SAC5N;QACD;YACC,WAAW,EAAE,SAAS;YACtB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC/B,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,IAAI;YACd,WAAW,EACV,wNAAwN;SACzN;KACD,CAAC;IAEF,YAAY,GAAyB;QACpC,IAAI,EAAE,SAAS;QACf,UAAU,EAAE;YACX,OAAO,EAAE;gBACR,aAAa,EAAE,iCAAiC;aAChD;SACD;KACD,CAAC;IAEF,IAAI,GAA2B;QAC9B,OAAO,EAAE;YACR,OAAO,EACN,0KAA0K;YAC3K,GAAG,EAAE,aAAa;YAClB,MAAM,EAAE,KAAK;SACb;QACD,KAAK,EAAE;YACN;gBACC,IAAI,EAAE,cAAc;gBACpB,UAAU,EAAE;oBACX,KAAK,EAAE,GAAG;oBACV,OAAO,EACN,4FAA4F;iBAC7F;aACD;YACD;gBACC,IAAI,EAAE,cAAc;gBACpB,UAAU,EAAE;oBACX,KAAK,EAAE,GAAG;oBACV,OAAO,EAAE,wEAAwE;iBACjF;aACD;SACD;KACD,CAAC;CACF;AAjED,sDAiEC"}
@@ -0,0 +1,9 @@
1
+ import type { ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class HaystackEnterpriseOAuth2Api implements ICredentialType {
3
+ name: string;
4
+ extends: string[];
5
+ displayName: string;
6
+ icon: "file:../nodes/HaystackEnterpriseMcp/haystack-enterprise-electric-blue.svg";
7
+ documentationUrl: string;
8
+ properties: INodeProperties[];
9
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HaystackEnterpriseOAuth2Api = void 0;
4
+ class HaystackEnterpriseOAuth2Api {
5
+ name = 'haystackEnterpriseOAuth2Api';
6
+ extends = ['oAuth2Api'];
7
+ displayName = 'Haystack Enterprise OAuth2 API';
8
+ icon = 'file:../nodes/HaystackEnterpriseMcp/haystack-enterprise-electric-blue.svg';
9
+ documentationUrl = 'https://github.com/deepset-ai/deepset-n8n-nodes/tree/main/packages/haystack-enterprise-mcp#credentials';
10
+ properties = [
11
+ {
12
+ displayName: 'Click <b>Connect</b> below and sign in with your Haystack Enterprise account. n8n registers itself with Haystack Enterprise automatically.',
13
+ name: 'dynamicClientRegistrationNotice',
14
+ type: 'notice',
15
+ default: '',
16
+ displayOptions: { show: { useDynamicClientRegistration: [true] } },
17
+ },
18
+ {
19
+ displayName: 'Use Dynamic Client Registration',
20
+ name: 'useDynamicClientRegistration',
21
+ type: 'boolean',
22
+ default: true,
23
+ description: 'Whether to let n8n register itself as an OAuth2 client automatically (RFC 7591); turn this off to configure or reuse an OAuth2 client manually.',
24
+ },
25
+ {
26
+ displayName: 'Server URL',
27
+ name: 'serverUrl',
28
+ type: 'string',
29
+ default: 'https://api.cloud.deepset.ai',
30
+ required: true,
31
+ description: 'The Haystack Enterprise API origin (for example, https://api.cloud.deepset.ai).',
32
+ },
33
+ {
34
+ displayName: 'Scope',
35
+ name: 'scope',
36
+ type: 'string',
37
+ default: 'openid profile email offline_access',
38
+ displayOptions: { show: { useDynamicClientRegistration: [false] } },
39
+ description: 'The OAuth2 scopes requested when using a manually configured client.',
40
+ },
41
+ {
42
+ displayName: 'Client Secret',
43
+ name: 'clientSecret',
44
+ type: 'string',
45
+ typeOptions: { password: true },
46
+ default: '',
47
+ required: false,
48
+ displayOptions: { show: { useDynamicClientRegistration: [false] } },
49
+ description: 'Optional for public PKCE clients; required for confidential OAuth2 clients.',
50
+ },
51
+ ];
52
+ }
53
+ exports.HaystackEnterpriseOAuth2Api = HaystackEnterpriseOAuth2Api;
54
+ //# sourceMappingURL=HaystackEnterpriseOAuth2Api.credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HaystackEnterpriseOAuth2Api.credentials.js","sourceRoot":"","sources":["../../credentials/HaystackEnterpriseOAuth2Api.credentials.ts"],"names":[],"mappings":";;;AAMA,MAAa,2BAA2B;IACvC,IAAI,GAAG,6BAA6B,CAAC;IAErC,OAAO,GAAG,CAAC,WAAW,CAAC,CAAC;IAExB,WAAW,GAAG,gCAAgC,CAAC;IAE/C,IAAI,GAAG,2EAAoF,CAAC;IAE5F,gBAAgB,GACf,wGAAwG,CAAC;IAE1G,UAAU,GAAsB;QAC/B;YACC,WAAW,EACV,4IAA4I;YAC7I,IAAI,EAAE,iCAAiC;YACvC,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,EAAE;YACX,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,4BAA4B,EAAE,CAAC,IAAI,CAAC,EAAE,EAAE;SAClE;QACD;YAEC,WAAW,EAAE,iCAAiC;YAC9C,IAAI,EAAE,8BAA8B;YACpC,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,IAAI;YACb,WAAW,EACV,iJAAiJ;SAClJ;QACD;YAEC,WAAW,EAAE,YAAY;YACzB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,8BAA8B;YACvC,QAAQ,EAAE,IAAI;YACd,WAAW,EACV,iFAAiF;SAClF;QACD;YACC,WAAW,EAAE,OAAO;YACpB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,qCAAqC;YAC9C,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,4BAA4B,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE;YACnE,WAAW,EAAE,sEAAsE;SACnF;QACD;YACC,WAAW,EAAE,eAAe;YAC5B,IAAI,EAAE,cAAc;YACpB,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;YAC/B,OAAO,EAAE,EAAE;YACX,QAAQ,EAAE,KAAK;YACf,cAAc,EAAE,EAAE,IAAI,EAAE,EAAE,4BAA4B,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE;YACnE,WAAW,EAAE,6EAA6E;SAC1F;KACD,CAAC;CACF;AA3DD,kEA2DC"}
@@ -0,0 +1,12 @@
1
+ import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription, ISupplyDataFunctions, SupplyData } from 'n8n-workflow';
2
+ export declare class HaystackEnterpriseMcp implements INodeType {
3
+ description: INodeTypeDescription;
4
+ methods: {
5
+ loadOptions: {
6
+ getWorkspaces(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
7
+ getAvailableMcpTools(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
8
+ };
9
+ };
10
+ supplyData(this: ISupplyDataFunctions, itemIndex: number): Promise<SupplyData>;
11
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
12
+ }
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HaystackEnterpriseMcp = void 0;
4
+ const tools_1 = require("@langchain/core/tools");
5
+ const n8n_core_1 = require("n8n-core");
6
+ const n8n_workflow_1 = require("n8n-workflow");
7
+ const zod_1 = require("zod");
8
+ const utils_1 = require("./utils");
9
+ const SELF_HOSTED_ONLY_MESSAGE = 'The Haystack Enterprise MCP node is only available on self-hosted n8n instances.';
10
+ function assertSelfHosted(node) {
11
+ if (process.env.N8N_DEPLOYMENT_TYPE === 'cloud') {
12
+ throw new n8n_workflow_1.NodeOperationError(node, SELF_HOSTED_ONLY_MESSAGE);
13
+ }
14
+ }
15
+ async function connectAndListFilteredTools(ctx, itemIndex) {
16
+ const signal = ctx.getExecutionCancelSignal();
17
+ if (signal?.aborted) {
18
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'Execution was cancelled', { itemIndex });
19
+ }
20
+ const workspaceId = ctx.getNodeParameter('workspaceId', itemIndex);
21
+ const auth = await (0, utils_1.resolveMcpAuth)(ctx, itemIndex);
22
+ const timeout = ctx.getNodeParameter('options.timeout', itemIndex, 60000);
23
+ if (!workspaceId || !auth.token) {
24
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'A workspace and a valid credential (API key or OAuth2) are required. Select a workspace above and make sure the Haystack Enterprise credential is set up correctly.', { itemIndex });
25
+ }
26
+ const { client, transport, mcpUrl } = (0, utils_1.createMcpTransport)(workspaceId, auth, signal);
27
+ try {
28
+ await client.connect(transport);
29
+ }
30
+ catch (error) {
31
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), (0, utils_1.describeTransportErrorHint)(error) ||
32
+ `Failed to connect to the Haystack Enterprise Platform MCP server at ${mcpUrl}: ${error.message}`, { itemIndex });
33
+ }
34
+ let mcpTools;
35
+ try {
36
+ mcpTools = await (0, utils_1.listAllMcpTools)(client, { signal });
37
+ }
38
+ catch (error) {
39
+ await client.close();
40
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), (0, utils_1.describeTransportErrorHint)(error) ||
41
+ `Failed to list tools from the Haystack Enterprise Platform MCP server: ${error.message}`, { itemIndex });
42
+ }
43
+ if (mcpTools.length === 0) {
44
+ await client.close();
45
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), 'No MCP tools found. Enable at least one pipeline as a tool in Haystack Enterprise Platform under Settings → Workspace → MCP.', { itemIndex });
46
+ }
47
+ const includeMode = ctx.getNodeParameter('include', itemIndex, 'all');
48
+ const includeTools = ctx.getNodeParameter('includeTools', itemIndex, []);
49
+ const excludeTools = ctx.getNodeParameter('excludeTools', itemIndex, []);
50
+ const filteredTools = (0, utils_1.getSelectedTools)({
51
+ mode: includeMode,
52
+ includeTools,
53
+ excludeTools,
54
+ tools: mcpTools,
55
+ });
56
+ if (filteredTools.length === 0) {
57
+ await client.close();
58
+ const message = includeMode === 'except'
59
+ ? 'All available tools were excluded by the "Tools to Exclude" selection. Remove some exclusions, or choose "All" to expose every tool to the AI Agent.'
60
+ : 'None of the selected tools are currently available from the MCP server. Adjust the "Tools to Include" selection, or choose "All" to use all available tools.';
61
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), message, { itemIndex });
62
+ }
63
+ if (includeMode === 'selected' && includeTools.length > 0) {
64
+ const availableToolNames = new Set(mcpTools.map((t) => t.name));
65
+ const missingSelectedTools = includeTools.filter((name) => !availableToolNames.has(name));
66
+ if (missingSelectedTools.length > 0) {
67
+ ctx.addExecutionHints({
68
+ message: `These selected tools are no longer available from the Haystack Enterprise Platform MCP server and were skipped: ${missingSelectedTools.join(', ')}. Refresh the "Tools to Include" list to update the selection.`,
69
+ type: 'warning',
70
+ location: 'outputPane',
71
+ });
72
+ }
73
+ }
74
+ return { client, mcpTools, filteredTools, timeout, signal };
75
+ }
76
+ class HaystackEnterpriseMcp {
77
+ description = {
78
+ displayName: 'Haystack Enterprise MCP',
79
+ name: 'haystackEnterpriseMcp',
80
+ icon: 'file:haystack-enterprise-electric-blue.svg',
81
+ group: ['transform'],
82
+ version: 1,
83
+ description: "Connects to a Haystack Enterprise Platform workspace's MCP server and exposes your enabled pipelines as tools for an AI Agent. Self-hosted n8n only.",
84
+ subtitle: '={{$parameter["workspaceId"]}}',
85
+ defaults: {
86
+ name: 'Haystack Enterprise MCP',
87
+ },
88
+ codex: {
89
+ categories: ['AI'],
90
+ subcategories: {
91
+ AI: ['Tools'],
92
+ },
93
+ resources: {
94
+ primaryDocumentation: [
95
+ {
96
+ url: 'https://docs.cloud.deepset.ai/docs/use-pipeline-as-mcp-tool',
97
+ },
98
+ ],
99
+ },
100
+ },
101
+ inputs: [],
102
+ outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
103
+ outputNames: ['Tool'],
104
+ credentials: [
105
+ {
106
+ name: 'haystackEnterpriseApi',
107
+ required: true,
108
+ displayOptions: { show: { authentication: ['apiKey'] } },
109
+ },
110
+ {
111
+ name: 'haystackEnterpriseOAuth2Api',
112
+ required: true,
113
+ displayOptions: { show: { authentication: ['oAuth2'] } },
114
+ },
115
+ ],
116
+ properties: [
117
+ {
118
+ displayName: 'This node is only available on self-hosted n8n instances. It does not work on n8n Cloud.',
119
+ name: 'selfHostedNotice',
120
+ type: 'notice',
121
+ default: '',
122
+ },
123
+ {
124
+ displayName: 'Authentication',
125
+ name: 'authentication',
126
+ type: 'options',
127
+ noDataExpression: true,
128
+ options: [
129
+ {
130
+ name: 'API Key',
131
+ value: 'apiKey',
132
+ description: 'Authenticate with a Haystack Enterprise API key',
133
+ },
134
+ {
135
+ name: 'OAuth2',
136
+ value: 'oAuth2',
137
+ description: 'Sign in with your Haystack Enterprise user account',
138
+ },
139
+ ],
140
+ default: 'apiKey',
141
+ },
142
+ {
143
+ displayName: 'Make sure you have created an MCP server and enabled at least one pipeline as a tool in Haystack Enterprise Platform under Settings → Workspace → MCP. See the <a href="https://docs.cloud.deepset.ai/docs/use-pipeline-as-mcp-tool/" target="_blank" rel="noopener noreferrer">docs on using a pipeline as an MCP tool</a>.',
144
+ name: 'mcpServerNotice',
145
+ type: 'notice',
146
+ default: '',
147
+ },
148
+ {
149
+ displayName: 'Workspace Name or ID',
150
+ name: 'workspaceId',
151
+ type: 'options',
152
+ typeOptions: {
153
+ loadOptionsMethod: 'getWorkspaces',
154
+ loadOptionsDependsOn: ['authentication'],
155
+ },
156
+ default: '',
157
+ required: true,
158
+ description: 'Select your <a href="https://docs.cloud.deepset.ai/docs/basic-concepts#workspace" target="_blank" rel="noopener noreferrer">workspace</a>, populated from your Haystack Enterprise Platform account. Choose from the list, or specify an ID using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
159
+ },
160
+ {
161
+ displayName: 'Tools to Include',
162
+ name: 'include',
163
+ type: 'options',
164
+ default: 'all',
165
+ description: 'How to select the <a href="https://docs.cloud.deepset.ai/docs/use-pipeline-as-mcp-tool/" target="_blank" rel="noopener noreferrer">tools</a> you want to be exposed to the AI Agent',
166
+ options: [
167
+ {
168
+ name: 'All',
169
+ value: 'all',
170
+ description: 'Expose every tool from the Haystack Enterprise Platform MCP server',
171
+ },
172
+ {
173
+ name: 'All Except',
174
+ value: 'except',
175
+ description: 'Expose all tools except those listed in "Tools to Exclude"',
176
+ },
177
+ {
178
+ name: 'Selected',
179
+ value: 'selected',
180
+ description: 'Only expose the tools listed in "Tools to Include"',
181
+ },
182
+ ],
183
+ },
184
+ {
185
+ displayName: 'Tools to Include',
186
+ name: 'includeTools',
187
+ type: 'multiOptions',
188
+ typeOptions: {
189
+ loadOptionsMethod: 'getAvailableMcpTools',
190
+ loadOptionsDependsOn: ['authentication', 'workspaceId'],
191
+ },
192
+ default: [],
193
+ description: 'The <a href="https://docs.cloud.deepset.ai/docs/use-pipeline-as-mcp-tool/" target="_blank" rel="noopener noreferrer">tools</a> currently available from your Haystack Enterprise Platform MCP server. Click the refresh icon to load the list. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
194
+ displayOptions: { show: { include: ['selected'] } },
195
+ },
196
+ {
197
+ displayName: 'Tools to Exclude',
198
+ name: 'excludeTools',
199
+ type: 'multiOptions',
200
+ typeOptions: {
201
+ loadOptionsMethod: 'getAvailableMcpTools',
202
+ loadOptionsDependsOn: ['authentication', 'workspaceId'],
203
+ },
204
+ default: [],
205
+ description: 'The <a href="https://docs.cloud.deepset.ai/docs/use-pipeline-as-mcp-tool/" target="_blank" rel="noopener noreferrer">tools</a> currently available from your Haystack Enterprise Platform MCP server. Click the refresh icon to load the list. Choose from the list, or specify IDs using an <a href="https://docs.n8n.io/code/expressions/">expression</a>.',
206
+ displayOptions: { show: { include: ['except'] } },
207
+ },
208
+ {
209
+ displayName: 'Options',
210
+ name: 'options',
211
+ placeholder: 'Add Option',
212
+ description: 'Additional options to add',
213
+ type: 'collection',
214
+ default: {},
215
+ options: [
216
+ {
217
+ displayName: 'Timeout',
218
+ name: 'timeout',
219
+ type: 'number',
220
+ typeOptions: { minValue: 1 },
221
+ default: 60000,
222
+ description: 'Time in ms to wait for a pipeline call to finish',
223
+ },
224
+ ],
225
+ },
226
+ ],
227
+ };
228
+ methods = {
229
+ loadOptions: {
230
+ async getWorkspaces() {
231
+ return (0, utils_1.loadWorkspaceOptions)(this);
232
+ },
233
+ async getAvailableMcpTools() {
234
+ assertSelfHosted(this.getNode());
235
+ const workspaceId = this.getNodeParameter('workspaceId');
236
+ if (!workspaceId) {
237
+ return [];
238
+ }
239
+ return (0, utils_1.loadMcpToolOptions)(this, workspaceId, await (0, utils_1.resolveMcpAuth)(this, 0));
240
+ },
241
+ },
242
+ };
243
+ async supplyData(itemIndex) {
244
+ assertSelfHosted(this.getNode());
245
+ const { client, filteredTools, timeout, signal } = await connectAndListFilteredTools(this, itemIndex);
246
+ const toolsMissingDescription = filteredTools.filter((t) => !t.description).map((t) => t.name);
247
+ if (toolsMissingDescription.length > 0) {
248
+ this.addExecutionHints({
249
+ message: `These tools have no description set in Haystack Enterprise Platform, so the AI agent only sees their name, which can make it pick the wrong tool: ${toolsMissingDescription.join(', ')}. Add a description under Settings → Workspace → MCP.`,
250
+ type: 'info',
251
+ location: 'outputPane',
252
+ });
253
+ }
254
+ const tools = filteredTools.map((mcpTool) => {
255
+ const tool = new tools_1.DynamicStructuredTool({
256
+ name: (0, utils_1.buildMcpToolName)(this.getNode().name, mcpTool.name),
257
+ description: mcpTool.description ?? mcpTool.name,
258
+ schema: zod_1.z.object({}),
259
+ func: async (input) => {
260
+ const { index: runIndex } = this.addInputData(n8n_workflow_1.NodeConnectionTypes.AiTool, [
261
+ [{ json: { tool: mcpTool.name, input: input } }],
262
+ ]);
263
+ let output;
264
+ try {
265
+ const result = await client.callTool({
266
+ name: mcpTool.name,
267
+ arguments: input,
268
+ }, undefined, { timeout, signal });
269
+ if (result.isError) {
270
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), (0, utils_1.contentToString)(result.content) ||
271
+ 'The pipeline reported an error with no message.', { itemIndex });
272
+ }
273
+ output = [
274
+ (0, utils_1.contentToString)(result.content),
275
+ (0, utils_1.isStructuredContent)(result.structuredContent)
276
+ ? JSON.stringify(result.structuredContent)
277
+ : '',
278
+ ]
279
+ .filter(Boolean)
280
+ .join('\n');
281
+ }
282
+ catch (err) {
283
+ const hint = (0, utils_1.describeToolCallErrorHint)(err);
284
+ const error = new n8n_workflow_1.NodeOperationError(this.getNode(), `Haystack Enterprise Platform tool "${mcpTool.name}" failed: ${err.message}${hint ? ` — ${hint}` : ''}`, { itemIndex });
285
+ this.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, runIndex, error);
286
+ throw error;
287
+ }
288
+ this.addOutputData(n8n_workflow_1.NodeConnectionTypes.AiTool, runIndex, [
289
+ [{ json: { tool: mcpTool.name, response: output } }],
290
+ ]);
291
+ this.logAiEvent('ai-tool-called', `Called Haystack Enterprise Platform tool "${mcpTool.name}"`);
292
+ return output;
293
+ },
294
+ });
295
+ tool.schema = (0, utils_1.normalizeMcpInputSchema)(mcpTool.inputSchema);
296
+ return tool;
297
+ });
298
+ return {
299
+ response: new n8n_core_1.StructuredToolkit(tools),
300
+ closeFunction: async () => {
301
+ await client.close();
302
+ },
303
+ };
304
+ }
305
+ async execute() {
306
+ assertSelfHosted(this.getNode());
307
+ const items = this.getInputData();
308
+ const returnData = [];
309
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
310
+ const item = items[itemIndex];
311
+ let client;
312
+ try {
313
+ const toolName = item.json.tool;
314
+ if (typeof toolName !== 'string' || !toolName) {
315
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Each input item must have a "tool" field naming which Haystack Enterprise Platform pipeline to call. Every other field on the item becomes that pipeline\'s arguments.', { itemIndex });
316
+ }
317
+ const connected = await connectAndListFilteredTools(this, itemIndex);
318
+ client = connected.client;
319
+ const { filteredTools, timeout, signal } = connected;
320
+ const mcpTool = filteredTools.find((t) => t.name === toolName || (0, utils_1.buildMcpToolName)(this.getNode().name, t.name) === toolName);
321
+ if (!mcpTool) {
322
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Tool "${toolName}" is not one of the tools currently exposed by this node (the "Tools to Include"/"Tools to Exclude" selection applies here too). Available: ${filteredTools.map((t) => t.name).join(', ')}.`, { itemIndex });
323
+ }
324
+ const toolArguments = { ...item.json };
325
+ delete toolArguments.tool;
326
+ let result;
327
+ try {
328
+ result = await client.callTool({ name: mcpTool.name, arguments: toolArguments }, undefined, { timeout, signal });
329
+ if (result.isError) {
330
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), (0, utils_1.contentToString)(result.content) || 'The pipeline reported an error with no message.', { itemIndex });
331
+ }
332
+ }
333
+ catch (err) {
334
+ const hint = (0, utils_1.describeToolCallErrorHint)(err);
335
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Haystack Enterprise Platform tool "${mcpTool.name}" failed: ${err.message}${hint ? ` — ${hint}` : ''}`, { itemIndex });
336
+ }
337
+ returnData.push({
338
+ json: {
339
+ tool: mcpTool.name,
340
+ response: (0, utils_1.contentToString)(result.content),
341
+ ...((0, utils_1.isStructuredContent)(result.structuredContent)
342
+ ? { structuredContent: result.structuredContent }
343
+ : {}),
344
+ },
345
+ pairedItem: { item: itemIndex },
346
+ });
347
+ }
348
+ catch (error) {
349
+ if (this.continueOnFail()) {
350
+ returnData.push({
351
+ json: { error: error.message },
352
+ pairedItem: { item: itemIndex },
353
+ });
354
+ continue;
355
+ }
356
+ throw error instanceof n8n_workflow_1.NodeOperationError
357
+ ? error
358
+ : new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
359
+ }
360
+ finally {
361
+ if (client) {
362
+ await client.close();
363
+ }
364
+ }
365
+ }
366
+ return [returnData];
367
+ }
368
+ }
369
+ exports.HaystackEnterpriseMcp = HaystackEnterpriseMcp;
370
+ //# sourceMappingURL=HaystackEnterpriseMcp.node.js.map