@loomcycle/n8n-nodes-loomcycle 1.0.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/LICENSE +201 -0
- package/README.md +190 -0
- package/dist/credentials/LoomCycleApi.credentials.d.ts +21 -0
- package/dist/credentials/LoomCycleApi.credentials.js +81 -0
- package/dist/credentials/LoomCycleApi.credentials.js.map +1 -0
- package/dist/nodes/LoomCycle/LoomCycle.node.d.ts +22 -0
- package/dist/nodes/LoomCycle/LoomCycle.node.js +403 -0
- package/dist/nodes/LoomCycle/LoomCycle.node.js.map +1 -0
- package/dist/nodes/LoomCycle/LoomCycle.node.json +13 -0
- package/dist/nodes/LoomCycle/LoomCycle.svg +13 -0
- package/dist/nodes/LoomCycle/descriptions/agentdef.d.ts +21 -0
- package/dist/nodes/LoomCycle/descriptions/agentdef.js +155 -0
- package/dist/nodes/LoomCycle/descriptions/agentdef.js.map +1 -0
- package/dist/nodes/LoomCycle/descriptions/channels.d.ts +19 -0
- package/dist/nodes/LoomCycle/descriptions/channels.js +164 -0
- package/dist/nodes/LoomCycle/descriptions/channels.js.map +1 -0
- package/dist/nodes/LoomCycle/descriptions/index.d.ts +6 -0
- package/dist/nodes/LoomCycle/descriptions/index.js +16 -0
- package/dist/nodes/LoomCycle/descriptions/index.js.map +1 -0
- package/dist/nodes/LoomCycle/descriptions/mcpserverdef.d.ts +29 -0
- package/dist/nodes/LoomCycle/descriptions/mcpserverdef.js +241 -0
- package/dist/nodes/LoomCycle/descriptions/mcpserverdef.js.map +1 -0
- package/dist/nodes/LoomCycle/descriptions/memory.d.ts +20 -0
- package/dist/nodes/LoomCycle/descriptions/memory.js +119 -0
- package/dist/nodes/LoomCycle/descriptions/memory.js.map +1 -0
- package/dist/nodes/LoomCycle/descriptions/runs.d.ts +17 -0
- package/dist/nodes/LoomCycle/descriptions/runs.js +226 -0
- package/dist/nodes/LoomCycle/descriptions/runs.js.map +1 -0
- package/dist/nodes/LoomCycle/descriptions/skilldef.d.ts +10 -0
- package/dist/nodes/LoomCycle/descriptions/skilldef.js +144 -0
- package/dist/nodes/LoomCycle/descriptions/skilldef.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/capability.d.ts +42 -0
- package/dist/nodes/LoomCycle/helpers/capability.js +54 -0
- package/dist/nodes/LoomCycle/helpers/capability.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/client.d.ts +15 -0
- package/dist/nodes/LoomCycle/helpers/client.js +30 -0
- package/dist/nodes/LoomCycle/helpers/client.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/envVarHints.d.ts +30 -0
- package/dist/nodes/LoomCycle/helpers/envVarHints.js +67 -0
- package/dist/nodes/LoomCycle/helpers/envVarHints.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/errors.d.ts +23 -0
- package/dist/nodes/LoomCycle/helpers/errors.js +189 -0
- package/dist/nodes/LoomCycle/helpers/errors.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/loadOptions.d.ts +24 -0
- package/dist/nodes/LoomCycle/helpers/loadOptions.js +89 -0
- package/dist/nodes/LoomCycle/helpers/loadOptions.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/segments.d.ts +13 -0
- package/dist/nodes/LoomCycle/helpers/segments.js +31 -0
- package/dist/nodes/LoomCycle/helpers/segments.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/staticData.d.ts +5 -0
- package/dist/nodes/LoomCycle/helpers/staticData.js +31 -0
- package/dist/nodes/LoomCycle/helpers/staticData.js.map +1 -0
- package/dist/nodes/LoomCycle/helpers/streaming.d.ts +31 -0
- package/dist/nodes/LoomCycle/helpers/streaming.js +48 -0
- package/dist/nodes/LoomCycle/helpers/streaming.js.map +1 -0
- package/dist/nodes/LoomCycleChannelMessage/LoomCycleChannelMessage.node.d.ts +24 -0
- package/dist/nodes/LoomCycleChannelMessage/LoomCycleChannelMessage.node.js +162 -0
- package/dist/nodes/LoomCycleChannelMessage/LoomCycleChannelMessage.node.js.map +1 -0
- package/dist/nodes/LoomCycleChannelMessage/LoomCycleChannelMessage.node.json +13 -0
- package/dist/nodes/LoomCycleChannelMessage/LoomCycleChannelMessage.svg +9 -0
- package/dist/nodes/LoomCycleChannelMessage/helpers/subscribe.d.ts +20 -0
- package/dist/nodes/LoomCycleChannelMessage/helpers/subscribe.js +106 -0
- package/dist/nodes/LoomCycleChannelMessage/helpers/subscribe.js.map +1 -0
- package/dist/nodes/LoomCycleChannelTool/LoomCycleChannelTool.node.d.ts +5 -0
- package/dist/nodes/LoomCycleChannelTool/LoomCycleChannelTool.node.js +114 -0
- package/dist/nodes/LoomCycleChannelTool/LoomCycleChannelTool.node.js.map +1 -0
- package/dist/nodes/LoomCycleChannelTool/LoomCycleChannelTool.node.json +13 -0
- package/dist/nodes/LoomCycleChannelTool/LoomCycleChannelTool.svg +13 -0
- package/dist/nodes/LoomCycleMcpServerTool/LoomCycleMcpServerTool.node.d.ts +5 -0
- package/dist/nodes/LoomCycleMcpServerTool/LoomCycleMcpServerTool.node.js +259 -0
- package/dist/nodes/LoomCycleMcpServerTool/LoomCycleMcpServerTool.node.js.map +1 -0
- package/dist/nodes/LoomCycleMcpServerTool/LoomCycleMcpServerTool.node.json +13 -0
- package/dist/nodes/LoomCycleMcpServerTool/LoomCycleMcpServerTool.svg +14 -0
- package/dist/nodes/LoomCycleMemoryTool/LoomCycleMemoryTool.node.d.ts +5 -0
- package/dist/nodes/LoomCycleMemoryTool/LoomCycleMemoryTool.node.js +108 -0
- package/dist/nodes/LoomCycleMemoryTool/LoomCycleMemoryTool.node.js.map +1 -0
- package/dist/nodes/LoomCycleMemoryTool/LoomCycleMemoryTool.node.json +13 -0
- package/dist/nodes/LoomCycleMemoryTool/LoomCycleMemoryTool.svg +11 -0
- package/dist/nodes/LoomCycleRunCompleted/LoomCycleRunCompleted.node.d.ts +18 -0
- package/dist/nodes/LoomCycleRunCompleted/LoomCycleRunCompleted.node.js +188 -0
- package/dist/nodes/LoomCycleRunCompleted/LoomCycleRunCompleted.node.js.map +1 -0
- package/dist/nodes/LoomCycleRunCompleted/LoomCycleRunCompleted.node.json +13 -0
- package/dist/nodes/LoomCycleRunCompleted/LoomCycleRunCompleted.svg +10 -0
- package/dist/nodes/LoomCycleRunCompleted/helpers/poll.d.ts +12 -0
- package/dist/nodes/LoomCycleRunCompleted/helpers/poll.js +69 -0
- package/dist/nodes/LoomCycleRunCompleted/helpers/poll.js.map +1 -0
- package/dist/nodes/LoomCycleRunCompleted/helpers/sse.d.ts +29 -0
- package/dist/nodes/LoomCycleRunCompleted/helpers/sse.js +61 -0
- package/dist/nodes/LoomCycleRunCompleted/helpers/sse.js.map +1 -0
- package/dist/nodes/LoomCycleSubAgentTool/LoomCycleSubAgentTool.node.d.ts +5 -0
- package/dist/nodes/LoomCycleSubAgentTool/LoomCycleSubAgentTool.node.js +131 -0
- package/dist/nodes/LoomCycleSubAgentTool/LoomCycleSubAgentTool.node.js.map +1 -0
- package/dist/nodes/LoomCycleSubAgentTool/LoomCycleSubAgentTool.node.json +13 -0
- package/dist/nodes/LoomCycleSubAgentTool/LoomCycleSubAgentTool.svg +12 -0
- package/dist/nodes/_shared/clusterTool.d.ts +19 -0
- package/dist/nodes/_shared/clusterTool.js +36 -0
- package/dist/nodes/_shared/clusterTool.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoomCycleMcpServerTool = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const client_1 = require("@loomcycle/client");
|
|
7
|
+
const client_2 = require("../LoomCycle/helpers/client");
|
|
8
|
+
const segments_1 = require("../LoomCycle/helpers/segments");
|
|
9
|
+
const streaming_1 = require("../LoomCycle/helpers/streaming");
|
|
10
|
+
const envVarHints_1 = require("../LoomCycle/helpers/envVarHints");
|
|
11
|
+
const clusterTool_1 = require("../_shared/clusterTool");
|
|
12
|
+
/**
|
|
13
|
+
* `LoomCycle MCP Server Tool` — **the strategic differentiator** for the
|
|
14
|
+
* n8n integration.
|
|
15
|
+
*
|
|
16
|
+
* Wires a single MCP server registration into loomcycle's substrate
|
|
17
|
+
* idempotently (get-then-create), then exposes a tool the parent AI
|
|
18
|
+
* Agent can call to delegate work to a loomcycle agent that has access
|
|
19
|
+
* to that MCP server's tool surface.
|
|
20
|
+
*
|
|
21
|
+
* Flow on `supplyData`:
|
|
22
|
+
* 1. Read tool config (name, transport, URL, headers, the loomcycle
|
|
23
|
+
* agent to spawn).
|
|
24
|
+
* 2. Refuse stdio transport with `NodeOperationError` (stdio MCP
|
|
25
|
+
* servers must live in loomcycle.yaml — the substrate enforces
|
|
26
|
+
* this; we surface it pre-wire for a cleaner error).
|
|
27
|
+
* 3. Idempotent ensure: `mcpServerDef({op:"get", name})` → on
|
|
28
|
+
* `NotFoundError`, `mcpServerDef({op:"create", ...})`. This means
|
|
29
|
+
* the second-and-Nth canvas runs are no-ops; the first run does
|
|
30
|
+
* the actual registration.
|
|
31
|
+
* 4. Return a tool that, when invoked, spawns the configured
|
|
32
|
+
* loomcycle agent with `allowed_tools: ["mcp__<name>__*"]` so
|
|
33
|
+
* the agent has access to the just-registered MCP server's tools.
|
|
34
|
+
*
|
|
35
|
+
* Lifecycle: **`cleanupOnEnd: false` default** — registrations persist
|
|
36
|
+
* across workflow executions so multiple agentic teams share stable
|
|
37
|
+
* MCP fleets without churn. Opt in to retire-on-workflow-end via the
|
|
38
|
+
* sub-node parameter when you want ephemeral / scoped registrations.
|
|
39
|
+
*/
|
|
40
|
+
const McpServerInputSchema = zod_1.z.object({
|
|
41
|
+
prompt: zod_1.z.string().describe('Prompt sent to the loomcycle agent (which has access to the MCP server\'s tools)'),
|
|
42
|
+
});
|
|
43
|
+
class LoomCycleMcpServerTool {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.description = {
|
|
46
|
+
displayName: 'LoomCycle MCP Server Tool',
|
|
47
|
+
name: 'loomCycleMcpServerTool',
|
|
48
|
+
icon: 'file:LoomCycleMcpServerTool.svg',
|
|
49
|
+
group: ['transform'],
|
|
50
|
+
version: 1,
|
|
51
|
+
description: 'Registers an MCP server in the loomcycle substrate (idempotent), then exposes a tool that delegates to a loomcycle agent using that MCP server',
|
|
52
|
+
defaults: { name: 'LoomCycle MCP Server Tool' },
|
|
53
|
+
codex: { categories: ['AI'], subcategories: { AI: ['Tools'] } },
|
|
54
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
|
55
|
+
inputs: [],
|
|
56
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
|
57
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
|
|
58
|
+
outputNames: ['Tool'],
|
|
59
|
+
credentials: [{ name: 'loomCycleApi', required: true }],
|
|
60
|
+
properties: [
|
|
61
|
+
{
|
|
62
|
+
displayName: 'Tool Name',
|
|
63
|
+
name: 'toolName',
|
|
64
|
+
type: 'string',
|
|
65
|
+
default: 'loomcycle_mcp',
|
|
66
|
+
required: true,
|
|
67
|
+
description: 'Name of the tool surfaced to the parent AI Agent',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
displayName: 'Tool Description',
|
|
71
|
+
name: 'toolDescription',
|
|
72
|
+
type: 'string',
|
|
73
|
+
typeOptions: { rows: 3 },
|
|
74
|
+
default: 'Delegate a task to a loomcycle agent that has access to the configured MCP server. Pass the task as the prompt.',
|
|
75
|
+
description: 'Description the AI Agent sees when deciding whether to call the tool',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
displayName: 'MCP Server Name',
|
|
79
|
+
name: 'mcpName',
|
|
80
|
+
type: 'string',
|
|
81
|
+
default: '',
|
|
82
|
+
required: true,
|
|
83
|
+
description: 'Registration name in loomcycle — referenced by agents as `mcp__<name>__<tool>`. Must be unique across the substrate; collisions with static yaml entries are refused.',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
displayName: 'Transport',
|
|
87
|
+
name: 'transport',
|
|
88
|
+
type: 'options',
|
|
89
|
+
default: 'streamable-http',
|
|
90
|
+
required: true,
|
|
91
|
+
options: [
|
|
92
|
+
{ name: 'HTTP', value: 'http', description: 'Classic JSON-RPC over HTTP POST' },
|
|
93
|
+
{ name: 'Streamable HTTP', value: 'streamable-http', description: 'MCP Streamable HTTP transport (recommended)' },
|
|
94
|
+
],
|
|
95
|
+
description: 'Transport for the MCP server. Stdio is intentionally not supported here — register stdio MCPs in loomcycle.yaml.',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
displayName: 'URL',
|
|
99
|
+
name: 'mcpUrl',
|
|
100
|
+
type: 'string',
|
|
101
|
+
default: '',
|
|
102
|
+
required: true,
|
|
103
|
+
placeholder: 'https://mcp.example.com/v1',
|
|
104
|
+
description: 'MCP server endpoint URL. Hostname must be in loomcycle\'s HTTPHostAllowlist.',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
displayName: 'Headers',
|
|
108
|
+
name: 'headers',
|
|
109
|
+
type: 'fixedCollection',
|
|
110
|
+
placeholder: 'Add Header',
|
|
111
|
+
default: {},
|
|
112
|
+
typeOptions: { multipleValues: true },
|
|
113
|
+
options: [
|
|
114
|
+
{
|
|
115
|
+
name: 'header',
|
|
116
|
+
displayName: 'Header',
|
|
117
|
+
values: [
|
|
118
|
+
{
|
|
119
|
+
displayName: 'Name',
|
|
120
|
+
name: 'name',
|
|
121
|
+
type: 'string',
|
|
122
|
+
default: '',
|
|
123
|
+
required: true,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
displayName: 'Value',
|
|
127
|
+
name: 'value',
|
|
128
|
+
type: 'string',
|
|
129
|
+
default: '',
|
|
130
|
+
required: true,
|
|
131
|
+
description: 'Supports `${LOOMCYCLE_FOO}` env-var substitution + `${run.user_bearer:-FALLBACK}` per-run substitution',
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
},
|
|
135
|
+
],
|
|
136
|
+
description: 'Headers attached to every MCP call. Use template strings (`${LOOMCYCLE_FOO_TOKEN}`) — plaintext credentials never travel through this wire path.',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
displayName: 'Loomcycle Agent',
|
|
140
|
+
name: 'agent',
|
|
141
|
+
type: 'string',
|
|
142
|
+
default: '',
|
|
143
|
+
required: true,
|
|
144
|
+
description: 'Loomcycle agent name to spawn when the parent AI Agent invokes this tool. The spawn will include `mcp__<name>__*` in allowed_tools so the agent can reach the MCP server.',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
displayName: 'Cleanup On Workflow End',
|
|
148
|
+
name: 'cleanupOnEnd',
|
|
149
|
+
type: 'boolean',
|
|
150
|
+
default: false,
|
|
151
|
+
description: 'Whether to retire the MCP server registration when the workflow execution ends. Default false: registrations persist so multiple agentic teams share stable MCP fleets without churn.',
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
async supplyData() {
|
|
157
|
+
const toolName = this.getNodeParameter('toolName', 0, 'loomcycle_mcp');
|
|
158
|
+
const toolDescription = this.getNodeParameter('toolDescription', 0, '');
|
|
159
|
+
const mcpName = this.getNodeParameter('mcpName', 0);
|
|
160
|
+
const transport = this.getNodeParameter('transport', 0);
|
|
161
|
+
const mcpUrl = this.getNodeParameter('mcpUrl', 0);
|
|
162
|
+
const headersParam = this.getNodeParameter('headers', 0, {});
|
|
163
|
+
const agent = this.getNodeParameter('agent', 0);
|
|
164
|
+
const cleanupOnEnd = this.getNodeParameter('cleanupOnEnd', 0, false);
|
|
165
|
+
if (transport !== 'http' && transport !== 'streamable-http') {
|
|
166
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Transport must be HTTP or Streamable-HTTP. Stdio MCP servers must be declared in loomcycle.yaml — dynamic registration does not support stdio.`);
|
|
167
|
+
}
|
|
168
|
+
const headers = collectHeaders(headersParam);
|
|
169
|
+
const client = await (0, client_2.getClient)(this);
|
|
170
|
+
const userIdDefault = await (0, client_2.getCredentialDefault)(this, 'userId');
|
|
171
|
+
const userTierDefault = await (0, client_2.getCredentialDefault)(this, 'userTier');
|
|
172
|
+
// Idempotent ensure: try get; on NotFoundError, create.
|
|
173
|
+
try {
|
|
174
|
+
await client.mcpServerDef({ op: 'get', name: mcpName });
|
|
175
|
+
}
|
|
176
|
+
catch (err) {
|
|
177
|
+
if (!(err instanceof client_1.NotFoundError)) {
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
const createInput = {
|
|
181
|
+
op: 'create',
|
|
182
|
+
name: mcpName,
|
|
183
|
+
promote: true,
|
|
184
|
+
// transport / url / headers ride on the index-signature
|
|
185
|
+
// (SubstrateToolInput has `[extra: string]: unknown`).
|
|
186
|
+
transport,
|
|
187
|
+
url: mcpUrl,
|
|
188
|
+
};
|
|
189
|
+
if (Object.keys(headers).length > 0)
|
|
190
|
+
createInput.headers = headers;
|
|
191
|
+
await client.mcpServerDef(createInput);
|
|
192
|
+
}
|
|
193
|
+
// Env-var hints (defence-in-depth — surface in the node's log so
|
|
194
|
+
// operators see which env vars must exist on the loomcycle side).
|
|
195
|
+
const envVars = (0, envVarHints_1.extractEnvVarsFromHeaders)(headersParam);
|
|
196
|
+
if (envVars.length > 0) {
|
|
197
|
+
this.logger.info?.(`[LoomCycleMcpServerTool] MCP server ${mcpName} registered. Required env vars on loomcycle: ${envVars.join(', ')}`);
|
|
198
|
+
}
|
|
199
|
+
const allowedToolGlob = `mcp__${mcpName}__*`;
|
|
200
|
+
const tool = (0, clusterTool_1.buildTool)({
|
|
201
|
+
name: toolName,
|
|
202
|
+
description: toolDescription,
|
|
203
|
+
schema: McpServerInputSchema,
|
|
204
|
+
fn: async (args) => {
|
|
205
|
+
const runOpts = {
|
|
206
|
+
agent,
|
|
207
|
+
segments: (0, segments_1.buildSegments)(args.prompt, true), // model-supplied prompt → untrusted
|
|
208
|
+
allowedTools: [allowedToolGlob],
|
|
209
|
+
};
|
|
210
|
+
if (userIdDefault)
|
|
211
|
+
runOpts.userId = userIdDefault;
|
|
212
|
+
if (userTierDefault)
|
|
213
|
+
runOpts.userTier = userTierDefault;
|
|
214
|
+
const result = await (0, streaming_1.drainRunStream)(client.runStreaming(runOpts));
|
|
215
|
+
return result.finalText;
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
const supplyData = { response: tool };
|
|
219
|
+
if (cleanupOnEnd) {
|
|
220
|
+
supplyData.closeFunction = async () => {
|
|
221
|
+
try {
|
|
222
|
+
await client.mcpServerDef({ op: 'retire', name: mcpName });
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// Best-effort cleanup; n8n's lifecycle calls closeFunction
|
|
226
|
+
// at workflow-end and we don't want a failed retire to
|
|
227
|
+
// taint the workflow's success state.
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return supplyData;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
exports.LoomCycleMcpServerTool = LoomCycleMcpServerTool;
|
|
235
|
+
/**
|
|
236
|
+
* Collect headers from the n8n fixedCollection shape into a map.
|
|
237
|
+
* Identical to the helper inside the umbrella node's executeMcpServerDef
|
|
238
|
+
* — kept local because the umbrella's lives in a closure and we want
|
|
239
|
+
* the cluster sub-node to be standalone.
|
|
240
|
+
*/
|
|
241
|
+
function collectHeaders(raw) {
|
|
242
|
+
const out = {};
|
|
243
|
+
if (!raw || typeof raw !== 'object')
|
|
244
|
+
return out;
|
|
245
|
+
const headerCollection = raw.header;
|
|
246
|
+
if (!Array.isArray(headerCollection))
|
|
247
|
+
return out;
|
|
248
|
+
for (const entry of headerCollection) {
|
|
249
|
+
if (!entry || typeof entry !== 'object')
|
|
250
|
+
continue;
|
|
251
|
+
const name = entry.name;
|
|
252
|
+
const value = entry.value;
|
|
253
|
+
if (typeof name === 'string' && name && typeof value === 'string') {
|
|
254
|
+
out[name] = value;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return out;
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=LoomCycleMcpServerTool.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoomCycleMcpServerTool.node.js","sourceRoot":"","sources":["../../../nodes/LoomCycleMcpServerTool/LoomCycleMcpServerTool.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "@loomcycle/n8n-nodes-loomcycle.loomCycleMcpServerTool",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["AI"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"primaryDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/denn-gubsky/n8n-nodes-loomcycle#cluster-sub-nodes"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
|
|
3
|
+
<title>LoomCycle MCP Server Tool</title>
|
|
4
|
+
<circle cx="30" cy="30" r="28" fill="#0f172a"/>
|
|
5
|
+
<g fill="none" stroke="#22d3ee" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
6
|
+
<rect x="14" y="16" width="32" height="14" rx="2"/>
|
|
7
|
+
<path d="M14 22 L46 22"/>
|
|
8
|
+
<circle cx="18" cy="19" r="1" fill="#22d3ee"/>
|
|
9
|
+
<circle cx="22" cy="19" r="1" fill="#22d3ee"/>
|
|
10
|
+
<path d="M22 30 L22 38 L30 38"/>
|
|
11
|
+
<path d="M38 30 L38 38 L30 38"/>
|
|
12
|
+
<circle cx="30" cy="44" r="3" fill="#22d3ee"/>
|
|
13
|
+
</g>
|
|
14
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { INodeType, INodeTypeDescription, ISupplyDataFunctions, SupplyData } from 'n8n-workflow';
|
|
2
|
+
export declare class LoomCycleMemoryTool implements INodeType {
|
|
3
|
+
description: INodeTypeDescription;
|
|
4
|
+
supplyData(this: ISupplyDataFunctions): Promise<SupplyData>;
|
|
5
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoomCycleMemoryTool = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const client_1 = require("../LoomCycle/helpers/client");
|
|
7
|
+
const clusterTool_1 = require("../_shared/clusterTool");
|
|
8
|
+
/**
|
|
9
|
+
* `LoomCycle Memory Tool` — cluster sub-node that plugs into n8n's AI
|
|
10
|
+
* Agent. Exposes loomcycle's Memory read surface as a single discriminated
|
|
11
|
+
* tool the agent can call.
|
|
12
|
+
*
|
|
13
|
+
* Read-only in this sub-phase (mirroring the action-node Memory resource;
|
|
14
|
+
* Set/Delete/Search land when `@loomcycle/client` exposes admin write
|
|
15
|
+
* endpoints).
|
|
16
|
+
*/
|
|
17
|
+
const MemoryInputSchema = zod_1.z.object({
|
|
18
|
+
op: zod_1.z
|
|
19
|
+
.enum(['listScopes', 'listScopeIDs', 'listEntries', 'getEntry'])
|
|
20
|
+
.describe('Which Memory operation to invoke'),
|
|
21
|
+
scope: zod_1.z
|
|
22
|
+
.string()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe('Memory scope (e.g. "agent", "user"). Required for listScopeIDs / listEntries / getEntry.'),
|
|
25
|
+
scopeID: zod_1.z
|
|
26
|
+
.string()
|
|
27
|
+
.optional()
|
|
28
|
+
.describe('Identifier within the scope. Required for listEntries / getEntry.'),
|
|
29
|
+
key: zod_1.z.string().optional().describe('Entry key. Required for getEntry.'),
|
|
30
|
+
prefix: zod_1.z.string().optional().describe('Key prefix filter for listEntries'),
|
|
31
|
+
limit: zod_1.z.number().int().positive().optional().describe('Max number of entries to return (listEntries)'),
|
|
32
|
+
});
|
|
33
|
+
class LoomCycleMemoryTool {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.description = {
|
|
36
|
+
displayName: 'LoomCycle Memory Tool',
|
|
37
|
+
name: 'loomCycleMemoryTool',
|
|
38
|
+
icon: 'file:LoomCycleMemoryTool.svg',
|
|
39
|
+
group: ['transform'],
|
|
40
|
+
version: 1,
|
|
41
|
+
description: 'Loomcycle Memory ops (read-only) as a tool the AI Agent can call',
|
|
42
|
+
defaults: { name: 'LoomCycle Memory Tool' },
|
|
43
|
+
codex: { categories: ['AI'], subcategories: { AI: ['Tools'] } },
|
|
44
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
|
45
|
+
inputs: [],
|
|
46
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
|
|
47
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.AiTool],
|
|
48
|
+
outputNames: ['Tool'],
|
|
49
|
+
credentials: [{ name: 'loomCycleApi', required: true }],
|
|
50
|
+
properties: [
|
|
51
|
+
{
|
|
52
|
+
displayName: 'Tool Name',
|
|
53
|
+
name: 'toolName',
|
|
54
|
+
type: 'string',
|
|
55
|
+
default: 'loomcycle_memory',
|
|
56
|
+
required: true,
|
|
57
|
+
description: 'Name of the tool surfaced to the parent AI Agent — must be unique across sibling tools',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
displayName: 'Tool Description',
|
|
61
|
+
name: 'toolDescription',
|
|
62
|
+
type: 'string',
|
|
63
|
+
typeOptions: { rows: 3 },
|
|
64
|
+
default: 'Read loomcycle Memory entries. Use op=listScopes to list scopes, op=listScopeIDs+scope to list ids, op=listEntries+scope+scopeID to list keys (optionally prefix/limit), op=getEntry+scope+scopeID+key to read a value.',
|
|
65
|
+
description: 'Description the AI Agent sees when deciding whether to call the tool',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
async supplyData() {
|
|
71
|
+
const toolName = this.getNodeParameter('toolName', 0, 'loomcycle_memory');
|
|
72
|
+
const toolDescription = this.getNodeParameter('toolDescription', 0, '');
|
|
73
|
+
const client = await (0, client_1.getClient)(this);
|
|
74
|
+
const tool = (0, clusterTool_1.buildTool)({
|
|
75
|
+
name: toolName,
|
|
76
|
+
description: toolDescription,
|
|
77
|
+
schema: MemoryInputSchema,
|
|
78
|
+
fn: async (args) => {
|
|
79
|
+
switch (args.op) {
|
|
80
|
+
case 'listScopes':
|
|
81
|
+
return client.listMemoryScopes();
|
|
82
|
+
case 'listScopeIDs':
|
|
83
|
+
if (!args.scope)
|
|
84
|
+
throw new Error('scope is required for listScopeIDs');
|
|
85
|
+
return client.listMemoryScopeIDs(args.scope);
|
|
86
|
+
case 'listEntries': {
|
|
87
|
+
if (!args.scope || !args.scopeID)
|
|
88
|
+
throw new Error('scope + scopeID required for listEntries');
|
|
89
|
+
const opts = {};
|
|
90
|
+
if (args.prefix)
|
|
91
|
+
opts.prefix = args.prefix;
|
|
92
|
+
if (args.limit)
|
|
93
|
+
opts.limit = args.limit;
|
|
94
|
+
return client.listMemoryEntries(args.scope, args.scopeID, opts);
|
|
95
|
+
}
|
|
96
|
+
case 'getEntry':
|
|
97
|
+
if (!args.scope || !args.scopeID || !args.key) {
|
|
98
|
+
throw new Error('scope + scopeID + key required for getEntry');
|
|
99
|
+
}
|
|
100
|
+
return client.getMemoryEntry(args.scope, args.scopeID, args.key);
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
return { response: tool };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.LoomCycleMemoryTool = LoomCycleMemoryTool;
|
|
108
|
+
//# sourceMappingURL=LoomCycleMemoryTool.node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoomCycleMemoryTool.node.js","sourceRoot":"","sources":["../../../nodes/LoomCycleMemoryTool/LoomCycleMemoryTool.node.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"node": "@loomcycle/n8n-nodes-loomcycle.loomCycleMemoryTool",
|
|
3
|
+
"nodeVersion": "1.0",
|
|
4
|
+
"codexVersion": "1.0",
|
|
5
|
+
"categories": ["AI"],
|
|
6
|
+
"resources": {
|
|
7
|
+
"primaryDocumentation": [
|
|
8
|
+
{
|
|
9
|
+
"url": "https://github.com/denn-gubsky/n8n-nodes-loomcycle#cluster-sub-nodes"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
|
|
3
|
+
<title>LoomCycle Memory Tool</title>
|
|
4
|
+
<circle cx="30" cy="30" r="28" fill="#0f172a"/>
|
|
5
|
+
<g fill="none" stroke="#22d3ee" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
6
|
+
<rect x="14" y="18" width="32" height="24" rx="3"/>
|
|
7
|
+
<path d="M14 26 L46 26"/>
|
|
8
|
+
<path d="M14 34 L46 34"/>
|
|
9
|
+
<path d="M22 18 L22 42"/>
|
|
10
|
+
</g>
|
|
11
|
+
</svg>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { INodeType, INodeTypeDescription, ITriggerFunctions, ITriggerResponse } from 'n8n-workflow';
|
|
2
|
+
/**
|
|
3
|
+
* `LoomCycle: Run Completed` — trigger that fires when a loomcycle
|
|
4
|
+
* agent run reaches a terminal state (completed / failed / cancelled).
|
|
5
|
+
*
|
|
6
|
+
* Two transports, operator-selectable via the `mode` parameter:
|
|
7
|
+
* - `sse` (default): persistent stream via streamUserRunStates. The
|
|
8
|
+
* adapter handles the substrate's 30-min server-side cap by
|
|
9
|
+
* yielding cleanly; our loop transparently re-opens. parentAgentId
|
|
10
|
+
* filter and debug toggle are forwarded.
|
|
11
|
+
* - `poll`: periodic listUserAgents calls with workflow-static
|
|
12
|
+
* dedup. Use when the deployment can't sustain long-lived SSE
|
|
13
|
+
* (reverse-proxy issues, Cloudflare workers, etc.).
|
|
14
|
+
*/
|
|
15
|
+
export declare class LoomCycleRunCompleted implements INodeType {
|
|
16
|
+
description: INodeTypeDescription;
|
|
17
|
+
trigger(this: ITriggerFunctions): Promise<ITriggerResponse>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoomCycleRunCompleted = void 0;
|
|
4
|
+
const client_1 = require("../LoomCycle/helpers/client");
|
|
5
|
+
const errors_1 = require("../LoomCycle/helpers/errors");
|
|
6
|
+
const sse_1 = require("./helpers/sse");
|
|
7
|
+
const poll_1 = require("./helpers/poll");
|
|
8
|
+
/**
|
|
9
|
+
* `LoomCycle: Run Completed` — trigger that fires when a loomcycle
|
|
10
|
+
* agent run reaches a terminal state (completed / failed / cancelled).
|
|
11
|
+
*
|
|
12
|
+
* Two transports, operator-selectable via the `mode` parameter:
|
|
13
|
+
* - `sse` (default): persistent stream via streamUserRunStates. The
|
|
14
|
+
* adapter handles the substrate's 30-min server-side cap by
|
|
15
|
+
* yielding cleanly; our loop transparently re-opens. parentAgentId
|
|
16
|
+
* filter and debug toggle are forwarded.
|
|
17
|
+
* - `poll`: periodic listUserAgents calls with workflow-static
|
|
18
|
+
* dedup. Use when the deployment can't sustain long-lived SSE
|
|
19
|
+
* (reverse-proxy issues, Cloudflare workers, etc.).
|
|
20
|
+
*/
|
|
21
|
+
class LoomCycleRunCompleted {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.description = {
|
|
24
|
+
displayName: 'LoomCycle: Run Completed',
|
|
25
|
+
name: 'loomCycleRunCompleted',
|
|
26
|
+
icon: 'file:LoomCycleRunCompleted.svg',
|
|
27
|
+
group: ['trigger'],
|
|
28
|
+
version: 1,
|
|
29
|
+
description: 'Fires when a loomcycle agent run reaches a terminal state (completed/failed/cancelled)',
|
|
30
|
+
defaults: { name: 'LoomCycle: Run Completed' },
|
|
31
|
+
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
|
|
32
|
+
inputs: [],
|
|
33
|
+
outputs: ['main'],
|
|
34
|
+
credentials: [{ name: 'loomCycleApi', required: true }],
|
|
35
|
+
properties: [
|
|
36
|
+
{
|
|
37
|
+
displayName: 'User ID',
|
|
38
|
+
name: 'userId',
|
|
39
|
+
type: 'string',
|
|
40
|
+
default: '',
|
|
41
|
+
description: 'User_id to watch. Empty = use the credential\'s Default User ID.',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Statuses',
|
|
45
|
+
name: 'statuses',
|
|
46
|
+
type: 'multiOptions',
|
|
47
|
+
default: ['completed'],
|
|
48
|
+
options: [
|
|
49
|
+
{ name: 'Cancelled', value: 'cancelled' },
|
|
50
|
+
{ name: 'Completed', value: 'completed' },
|
|
51
|
+
{ name: 'Failed', value: 'failed' },
|
|
52
|
+
],
|
|
53
|
+
description: 'Which terminal statuses fire this trigger',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
displayName: 'Transport',
|
|
57
|
+
name: 'mode',
|
|
58
|
+
type: 'options',
|
|
59
|
+
default: 'sse',
|
|
60
|
+
options: [
|
|
61
|
+
{
|
|
62
|
+
name: 'SSE (Push)',
|
|
63
|
+
value: 'sse',
|
|
64
|
+
description: 'Persistent stream; near-zero latency. Requires reverse-proxy SSE support.',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Polling',
|
|
68
|
+
value: 'poll',
|
|
69
|
+
description: 'Periodic list calls with dedup. Use when SSE is not viable.',
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
displayName: 'Poll Interval (Seconds)',
|
|
75
|
+
name: 'intervalSec',
|
|
76
|
+
type: 'number',
|
|
77
|
+
default: 30,
|
|
78
|
+
typeOptions: { minValue: 5, maxValue: 3600 },
|
|
79
|
+
displayOptions: { show: { mode: ['poll'] } },
|
|
80
|
+
description: 'How frequently to poll for new terminal-state rows',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
displayName: 'Additional Fields',
|
|
84
|
+
name: 'additionalFields',
|
|
85
|
+
type: 'collection',
|
|
86
|
+
placeholder: 'Add Field',
|
|
87
|
+
default: {},
|
|
88
|
+
options: [
|
|
89
|
+
{
|
|
90
|
+
displayName: 'Agent Name',
|
|
91
|
+
name: 'agent',
|
|
92
|
+
type: 'string',
|
|
93
|
+
default: '',
|
|
94
|
+
description: 'Narrow to runs of a specific agent name (SSE mode only)',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
displayName: 'Parent Agent ID',
|
|
98
|
+
name: 'parentAgentId',
|
|
99
|
+
type: 'string',
|
|
100
|
+
default: '',
|
|
101
|
+
description: 'Narrow to sub-runs of a specific parent agent_id',
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
displayName: 'Surface Stream Open/Close Events',
|
|
105
|
+
name: 'emitClose',
|
|
106
|
+
type: 'boolean',
|
|
107
|
+
default: false,
|
|
108
|
+
description: 'Whether to emit synthetic `{__meta:"stream_close"}` rows when the SSE stream reconnects. SSE mode only.',
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
displayName: 'Reconnect Backoff (Ms)',
|
|
112
|
+
name: 'reconnectBackoffMs',
|
|
113
|
+
type: 'number',
|
|
114
|
+
default: 1000,
|
|
115
|
+
typeOptions: { minValue: 100, maxValue: 60000 },
|
|
116
|
+
description: 'Base backoff between SSE reconnects. Multiplied by attempt count (capped at 4×).',
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async trigger() {
|
|
124
|
+
const userIdParam = this.getNodeParameter('userId', '') ?? '';
|
|
125
|
+
const statuses = this.getNodeParameter('statuses', ['completed']) ?? ['completed'];
|
|
126
|
+
const mode = this.getNodeParameter('mode', 'sse') ?? 'sse';
|
|
127
|
+
const intervalSec = this.getNodeParameter('intervalSec', 30) ?? 30;
|
|
128
|
+
const additionalFields = this.getNodeParameter('additionalFields', {}) ?? {};
|
|
129
|
+
const userId = userIdParam || (await (0, client_1.getCredentialDefault)(this, 'userId'));
|
|
130
|
+
if (!userId) {
|
|
131
|
+
throw (0, errors_1.wrapLoomcycleError)(new Error('User ID required — set per-node or as Default User ID on the credential'), this.getNode());
|
|
132
|
+
}
|
|
133
|
+
const client = await (0, client_1.getClient)(this);
|
|
134
|
+
const ac = new AbortController();
|
|
135
|
+
const agent = additionalFields.agent || undefined;
|
|
136
|
+
const parentAgentId = additionalFields.parentAgentId || undefined;
|
|
137
|
+
const emitClose = additionalFields.emitClose ?? false;
|
|
138
|
+
const reconnectBackoffMs = additionalFields.reconnectBackoffMs ?? 1000;
|
|
139
|
+
// Background loop — fire-and-forget. n8n's closeFunction aborts
|
|
140
|
+
// the controller; both loops respect it cooperatively.
|
|
141
|
+
if (mode === 'poll') {
|
|
142
|
+
void poll_1.runPollLoop.call(this, {
|
|
143
|
+
client,
|
|
144
|
+
userId,
|
|
145
|
+
statuses: statuses,
|
|
146
|
+
parentAgentId,
|
|
147
|
+
intervalMs: intervalSec * 1000,
|
|
148
|
+
signal: ac.signal,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
void sse_1.runSseLoop.call(this, {
|
|
153
|
+
client,
|
|
154
|
+
userId,
|
|
155
|
+
statuses,
|
|
156
|
+
parentAgentId,
|
|
157
|
+
agent,
|
|
158
|
+
debug: emitClose,
|
|
159
|
+
emitClose,
|
|
160
|
+
signal: ac.signal,
|
|
161
|
+
reconnectBackoffMs,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Single-shot path used when the operator clicks "Listen for Test
|
|
165
|
+
// Event" in the n8n editor. We resolve as soon as the first event
|
|
166
|
+
// lands, then close.
|
|
167
|
+
async function manualTriggerFunction() {
|
|
168
|
+
const oneShotAc = new AbortController();
|
|
169
|
+
const userIdInner = userId;
|
|
170
|
+
await poll_1.pollOnce.call(this, {
|
|
171
|
+
client,
|
|
172
|
+
userId: userIdInner,
|
|
173
|
+
statuses: statuses,
|
|
174
|
+
parentAgentId,
|
|
175
|
+
intervalMs: 0,
|
|
176
|
+
signal: oneShotAc.signal,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
closeFunction: async () => {
|
|
181
|
+
ac.abort();
|
|
182
|
+
},
|
|
183
|
+
manualTriggerFunction: manualTriggerFunction.bind(this),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
exports.LoomCycleRunCompleted = LoomCycleRunCompleted;
|
|
188
|
+
//# sourceMappingURL=LoomCycleRunCompleted.node.js.map
|