nodestorm-mcp 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,27 +1,26 @@
1
1
  # nodestorm-mcp
2
2
 
3
- MCP (Model Context Protocol) server for NodeStorm workflow automation.
3
+ MCP (Model Context Protocol) server for [NodeStorm](https://nodestorm.ai) workflow automation.
4
4
 
5
5
  ## Installation
6
6
 
7
- ```bash
8
- npx nodestorm-mcp
9
- ```
10
-
11
- Or install globally:
7
+ No install step needed — MCP clients run it via `npx`:
12
8
 
13
9
  ```bash
14
- npm install -g nodestorm-mcp
10
+ npx -y nodestorm-mcp
15
11
  ```
16
12
 
17
13
  ## Configuration
18
14
 
19
- ### Claude Code / Claude Desktop
15
+ ### Claude Code
20
16
 
21
- Add to your Claude configuration file:
17
+ From your project directory:
22
18
 
23
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
24
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
19
+ ```bash
20
+ claude mcp add nodestorm -e NODESTORM_API_KEY=ns_your_access_token_here -- npx -y nodestorm-mcp
21
+ ```
22
+
23
+ Or add to a `.mcp.json` in your project root (or `~/.claude.json` for all projects):
25
24
 
26
25
  ```json
27
26
  {
@@ -37,6 +36,13 @@ Add to your Claude configuration file:
37
36
  }
38
37
  ```
39
38
 
39
+ ### Claude Desktop
40
+
41
+ Add the same `mcpServers` block to:
42
+
43
+ - **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
44
+ - **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
45
+
40
46
  ### Environment Variables
41
47
 
42
48
  | Variable | Required | Description |
@@ -55,6 +61,8 @@ Note: Access tokens require an active NodeStorm subscription.
55
61
 
56
62
  ## Available Tools
57
63
 
64
+ At connect the server sends short instructions (0.1.2): what NodeStorm is, which tool a nodestorm.ai link maps to (`/files/N`, `/flows/N`, `/flow-runs/N`, `/help/slug`), and to read a node's doc with `get_doc` before `execute_node`. MCP clients such as Claude Code show them to the model at session start.
65
+
58
66
  ### create_flow
59
67
  Create a new workflow from a natural language description.
60
68
 
@@ -72,10 +80,18 @@ Output: { id, name, nodes, edges, url, updated_at }
72
80
  ```
73
81
 
74
82
  ### update_flow
75
- Modify an existing workflow.
83
+ Update workflow **metadata** (name, description, schedule). Does not modify nodes or edges — use `update_flow_structure` for that.
76
84
 
77
85
  ```
78
- Input: { id: "abc123", flow: { name, nodes, edges } }
86
+ Input: { id: "abc123", flow: { name, description, schedule_enabled, ... } }
87
+ Output: { id, name, nodes, edges, url, updated_at }
88
+ ```
89
+
90
+ ### update_flow_structure
91
+ Apply a list of structural operations (add/update/remove nodes and edges) atomically to an existing workflow.
92
+
93
+ ```
94
+ Input: { flow_id: "abc123", operations: [{ op: "add_node", ... }, ...] }
79
95
  Output: { id, name, nodes, edges, url, updated_at }
80
96
  ```
81
97
 
@@ -88,37 +104,75 @@ Output: { success: true }
88
104
  ```
89
105
 
90
106
  ### list_flows
91
- List all your workflows.
107
+ List or search your workflows (`scope: "public"` searches published community entries).
92
108
 
93
109
  ```
94
- Input: { limit: 20, offset: 0 }
110
+ Input: { query: "pricing", scope: "user", limit: 20 }
95
111
  Output: { flows: [...], total }
96
112
  ```
97
113
 
98
- ### validate_flow
99
- Check if a workflow is valid before saving.
114
+ ### get_flow_run
115
+ Get details of a specific flow run, including execution results and node outputs.
116
+
117
+ ```
118
+ Input: { id: "run123" }
119
+ Output: { id, flow_id, status, node_results, started_at, finished_at }
120
+ ```
121
+
122
+ ### list_flow_runs
123
+ List flow runs with optional filters, for debugging and monitoring.
124
+
125
+ ```
126
+ Input: { flow_id: "abc123", status: "failed", limit: 20 }
127
+ Output: { runs: [...], total }
128
+ ```
129
+
130
+ ### search_chat_history
131
+ Semantic (meaning-based) search over your past NodeStorm chat messages.
132
+
133
+ ```
134
+ Input: { query: "how did I set up the scraper flow", limit: 10 }
135
+ Output: { messages: [...] }
136
+ ```
137
+
138
+ ### execute_node
139
+ Run one node directly, without a flow. `config` is the node's `data` as in a flow; read the node's doc with `get_doc` first. Each call counts as one node execution against the monthly quota and is capped at about 90 seconds. Nodes that send or write take effect at once.
140
+
141
+ ```
142
+ Input: { node_type: "file", config: { mode: "read", file_id: 123 } } // read a file (nodestorm.ai/files/123)
143
+ Input: { node_type: "folder", config: { folder_id: "top" } } // list the files in a folder ("top" = no folder)
144
+ Input: { node_type: "prompt", config: { input: "Summarize" }, input: "..." }
145
+ Output: { success, node_type, output }
146
+ ```
147
+
148
+ ### list_docs
149
+ List NodeStorm product documentation pages. Pass `category` to filter (e.g. `"node"` for the node catalog).
150
+
151
+ ```
152
+ Input: { category: "node" } // optional filter
153
+ Output: { pages: [{ slug, title, category, description }] }
154
+ ```
155
+
156
+ ### get_doc
157
+ Get a documentation page by slug, including its full markdown body. Node pages use kebab-case slugs like `node-prompt` and `node-http-request`.
100
158
 
101
159
  ```
102
- Input: { flow: { nodes, edges } }
103
- Output: { valid: true } or { valid: false, errors: [...] }
160
+ Input: { slug: "workflow-execution" }
161
+ Output: { page: { slug, title, category, description, body, updated_at } }
104
162
  ```
105
163
 
106
- ### describe_nodes
107
- Get node type definitions (no API call).
164
+ ### search_docs
165
+ Semantic (meaning-based) search over the NodeStorm documentation.
108
166
 
109
167
  ```
110
- Input: { types: ["prompt", "list"] } // optional filter
111
- Output: { nodes: [{ type, description, category, config_schema, handles }] }
168
+ Input: { query: "how does iteration work" }
169
+ Output: { results: [{ slug, title, category, chunk_content, relevance }] }
112
170
  ```
113
171
 
114
172
  ## Resources
115
173
 
116
174
  ### workflow-guide
117
- Complete guide for building NodeStorm workflows. Read this resource to understand:
118
- - Node types and their configurations
119
- - Edge connection rules
120
- - Iteration patterns
121
- - Validation requirements
175
+ Pointer to the live NodeStorm docs. The bundled guide was replaced by the docs tools above — start with `get_doc("workflow-execution")` and `list_docs({ category: "node" })`.
122
176
 
123
177
  ## Example Usage
124
178
 
@@ -131,7 +185,7 @@ Created "Competitor Pricing Analysis"
131
185
 
132
186
  User: Add 3 more URLs to the list
133
187
 
134
- Claude: [Uses get_flow, modifies nodes, uses update_flow]
188
+ Claude: [Uses update_flow_structure with add_node/add_edge operations]
135
189
  Updated flow with 3 additional URLs.
136
190
  ```
137
191
 
package/dist/client.d.ts CHANGED
@@ -29,7 +29,7 @@ export interface FlowRun {
29
29
  flow_id: number;
30
30
  flow_title: string;
31
31
  user_id?: number;
32
- status: 'success' | 'error' | 'pending' | 'running';
32
+ status: 'success' | 'partial' | 'error' | 'pending' | 'running';
33
33
  node_count: number;
34
34
  duration_seconds: number | null;
35
35
  result?: {
@@ -50,7 +50,7 @@ export interface FlowRunListItem {
50
50
  id: number;
51
51
  flow_id: number;
52
52
  flow_title: string;
53
- status: 'success' | 'error' | 'pending' | 'running';
53
+ status: 'success' | 'partial' | 'error' | 'pending' | 'running';
54
54
  node_count: number;
55
55
  duration_seconds: number | null;
56
56
  created_at: string;
@@ -104,6 +104,10 @@ export declare class NodeStormClient {
104
104
  schedule_enabled?: boolean;
105
105
  schedule_cron?: string;
106
106
  schedule_timezone?: string;
107
+ settings?: {
108
+ on_step_failure?: 'continue' | 'stop';
109
+ notify_on_failure?: boolean;
110
+ };
107
111
  }): Promise<Flow>;
108
112
  /**
109
113
  * Apply atomic structural operations (edit/add/remove nodes and edges) to an existing flow.
@@ -136,10 +140,24 @@ export declare class NodeStormClient {
136
140
  */
137
141
  listFlowRuns(options?: {
138
142
  flowId?: number;
139
- status?: 'success' | 'error' | 'pending' | 'running';
143
+ status?: 'success' | 'partial' | 'error' | 'pending' | 'running';
140
144
  page?: number;
141
145
  perPage?: number;
142
146
  }): Promise<FlowRunListResponse>;
147
+ /**
148
+ * List documentation pages, optionally filtered by category
149
+ */
150
+ listDocs(options?: {
151
+ category?: string;
152
+ }): Promise<DocListResponse>;
153
+ /**
154
+ * Get a documentation page by slug, including its full markdown body
155
+ */
156
+ getDoc(slug: string): Promise<DocPageResponse>;
157
+ /**
158
+ * Search documentation using semantic search
159
+ */
160
+ searchDocs(query: string): Promise<DocSearchResponse>;
143
161
  /**
144
162
  * Search chat message history using semantic search
145
163
  */
@@ -151,6 +169,42 @@ export declare class NodeStormClient {
151
169
  from?: string;
152
170
  to?: string;
153
171
  }): Promise<ChatSearchResponse>;
172
+ /**
173
+ * Execute a single node directly, outside any flow. Capped at ~90s server-side;
174
+ * long-running work belongs in a real flow.
175
+ */
176
+ executeNode(params: {
177
+ node_type: string;
178
+ config?: object;
179
+ input?: unknown;
180
+ }): Promise<ExecuteNodeResponse>;
181
+ }
182
+ export interface DocListItem {
183
+ slug: string;
184
+ title: string;
185
+ category: string;
186
+ visibility: string;
187
+ description: string | null;
188
+ }
189
+ export interface DocListResponse {
190
+ pages: DocListItem[];
191
+ }
192
+ export interface DocPage extends DocListItem {
193
+ body: string;
194
+ updated_at: string;
195
+ }
196
+ export interface DocPageResponse {
197
+ page: DocPage;
198
+ }
199
+ export interface DocSearchResult {
200
+ slug: string;
201
+ title: string;
202
+ category: string;
203
+ chunk_content: string;
204
+ relevance: number;
205
+ }
206
+ export interface DocSearchResponse {
207
+ results: DocSearchResult[];
154
208
  }
155
209
  export interface ChatSearchResult {
156
210
  content: string;
@@ -162,6 +216,12 @@ export interface ChatSearchResult {
162
216
  export interface ChatSearchResponse {
163
217
  results: ChatSearchResult[];
164
218
  }
219
+ export interface ExecuteNodeResponse {
220
+ success: boolean;
221
+ node_type: string;
222
+ /** The worker's result (NodeResult shape: { data, data_type, success, error?, metadata? }) */
223
+ output: unknown;
224
+ }
165
225
  export declare class NodeStormApiError extends Error {
166
226
  code: string;
167
227
  statusCode: number;
package/dist/client.js CHANGED
@@ -110,6 +110,31 @@ export class NodeStormClient {
110
110
  const path = queryString ? `/flow-runs?${queryString}` : '/flow-runs';
111
111
  return this.request(path);
112
112
  }
113
+ /**
114
+ * List documentation pages, optionally filtered by category
115
+ */
116
+ async listDocs(options = {}) {
117
+ const params = new URLSearchParams();
118
+ if (options.category)
119
+ params.append('category', options.category);
120
+ const queryString = params.toString();
121
+ const path = queryString ? `/docs?${queryString}` : '/docs';
122
+ return this.request(path);
123
+ }
124
+ /**
125
+ * Get a documentation page by slug, including its full markdown body
126
+ */
127
+ async getDoc(slug) {
128
+ return this.request(`/docs/${encodeURIComponent(slug)}`);
129
+ }
130
+ /**
131
+ * Search documentation using semantic search
132
+ */
133
+ async searchDocs(query) {
134
+ const params = new URLSearchParams();
135
+ params.append('q', query);
136
+ return this.request(`/docs/search?${params.toString()}`);
137
+ }
113
138
  /**
114
139
  * Search chat message history using semantic search
115
140
  */
@@ -128,6 +153,16 @@ export class NodeStormClient {
128
153
  params.append('to', options.to);
129
154
  return this.request(`/chat/search?${params.toString()}`);
130
155
  }
156
+ /**
157
+ * Execute a single node directly, outside any flow. Capped at ~90s server-side;
158
+ * long-running work belongs in a real flow.
159
+ */
160
+ async executeNode(params) {
161
+ return this.request('/nodes/execute', {
162
+ method: 'POST',
163
+ body: JSON.stringify(params),
164
+ });
165
+ }
131
166
  }
132
167
  export class NodeStormApiError extends Error {
133
168
  code;
package/dist/index.js CHANGED
@@ -8,17 +8,15 @@
8
8
  * - NODESTORM_API_KEY (required): API key for authentication
9
9
  * - NODESTORM_API_URL (optional): API base URL (default: https://nodestorm.ai)
10
10
  */
11
+ import { createRequire } from 'node:module';
11
12
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
13
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
13
14
  import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
14
15
  import { NodeStormClient, NodeStormApiError } from './client.js';
15
- import { getNodeDefinitions } from './tools/describe.js';
16
- import { readFileSync } from 'fs';
17
- import { fileURLToPath } from 'url';
18
- import { dirname, join } from 'path';
19
- // Get the directory of this file
20
- const __filename = fileURLToPath(import.meta.url);
21
- const __dirname = dirname(__filename);
16
+ import { DOCS_TOOL_DEFINITIONS } from './tools/docs.js';
17
+ import { EXECUTE_NODE_DESCRIPTION, SERVER_INSTRUCTIONS } from './instructions.js';
18
+ // One version string: package.json's. Before 0.1.2 this was hard-coded and drifted.
19
+ const { version: VERSION } = createRequire(import.meta.url)('../package.json');
22
20
  // Read environment variables
23
21
  const apiKey = process.env.NODESTORM_API_KEY;
24
22
  const apiUrl = process.env.NODESTORM_API_URL || 'https://nodestorm.ai';
@@ -28,25 +26,29 @@ if (!apiKey) {
28
26
  }
29
27
  // Initialize client
30
28
  const client = new NodeStormClient(apiKey, apiUrl);
31
- // Load workflow guide (bundled with package)
32
- function loadWorkflowGuide() {
33
- try {
34
- const guidePath = join(__dirname, 'workflow-guide.md');
35
- return readFileSync(guidePath, 'utf-8');
36
- }
37
- catch {
38
- return '# Workflow Guide\n\nGuide file not found. Please reinstall the package.';
39
- }
40
- }
29
+ // The bundled workflow guide was retired in favor of the live docs (see the docs tools).
30
+ // This pointer keeps the resource URI working for existing clients.
31
+ const WORKFLOW_GUIDE_POINTER = [
32
+ '# Workflow Guide',
33
+ '',
34
+ 'The workflow guide moved into the live NodeStorm docs.',
35
+ 'Use the `list_docs` / `get_doc` / `search_docs` tools instead of this resource.',
36
+ '',
37
+ "Start with `get_doc('workflow-execution')` for the execution model, and",
38
+ "`list_docs({ category: 'node' })` for the node catalog (slugs are kebab-case,",
39
+ 'e.g. `node-prompt`, `node-http-request`).',
40
+ ].join('\n');
41
41
  // Create MCP server
42
42
  const server = new Server({
43
43
  name: 'nodestorm-mcp',
44
- version: '0.1.0',
44
+ version: VERSION,
45
45
  }, {
46
46
  capabilities: {
47
47
  tools: {},
48
48
  resources: {},
49
49
  },
50
+ // 099.9: shown to the model at session start (URL → tool map, read docs first).
51
+ instructions: SERVER_INSTRUCTIONS,
50
52
  });
51
53
  // Tool definitions
52
54
  server.setRequestHandler(ListToolsRequestSchema, async () => {
@@ -97,8 +99,9 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
97
99
  },
98
100
  {
99
101
  name: 'update_flow',
100
- description: 'Update flow METADATA: name, description, schedule_*. Does NOT modify nodes or edges. ' +
101
- 'For any structural change use update_flow_structure.',
102
+ description: 'Update flow METADATA: name, description, schedule_*, and the flow settings ' +
103
+ '(settings.on_step_failure: "continue" | "stop"; settings.notify_on_failure: boolean). ' +
104
+ 'Does NOT modify nodes or edges. For any structural change use update_flow_structure.',
102
105
  inputSchema: {
103
106
  type: 'object',
104
107
  properties: {
@@ -114,7 +117,15 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
114
117
  description: { type: 'string' },
115
118
  schedule_enabled: { type: 'boolean' },
116
119
  schedule_cron: { type: 'string' },
117
- schedule_timezone: { type: 'string' },
120
+ schedule_timezone: { type: 'string', description: 'IANA name, e.g. America/New_York' },
121
+ settings: {
122
+ type: 'object',
123
+ description: 'Flow settings; keys sent are merged into the stored ones',
124
+ properties: {
125
+ on_step_failure: { type: 'string', enum: ['continue', 'stop'] },
126
+ notify_on_failure: { type: 'boolean' },
127
+ },
128
+ },
118
129
  },
119
130
  },
120
131
  },
@@ -177,7 +188,10 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
177
188
  description: 'Node payload. Server generates id if not provided.',
178
189
  properties: {
179
190
  id: { type: 'string' },
180
- type: { type: 'string', description: 'Node type — see describe_nodes for the catalog' },
191
+ type: {
192
+ type: 'string',
193
+ description: "Node type — see list_docs({category: 'node'}) for the catalog and get_doc for each node's schema",
194
+ },
181
195
  data: { type: 'object' },
182
196
  position: {
183
197
  type: 'object',
@@ -265,20 +279,6 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
265
279
  },
266
280
  },
267
281
  },
268
- {
269
- name: 'describe_nodes',
270
- description: 'Get node type definitions for building flows',
271
- inputSchema: {
272
- type: 'object',
273
- properties: {
274
- types: {
275
- type: 'array',
276
- items: { type: 'string' },
277
- description: "Filter to specific node types (e.g., ['prompt', 'list'])",
278
- },
279
- },
280
- },
281
- },
282
282
  {
283
283
  name: 'get_flow_run',
284
284
  description: 'Get details of a specific flow run by ID, including execution results and node outputs',
@@ -341,7 +341,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
341
341
  },
342
342
  status: {
343
343
  type: 'string',
344
- enum: ['success', 'error', 'pending', 'running'],
344
+ enum: ['success', 'partial', 'error', 'pending', 'running'],
345
345
  description: 'Filter by status',
346
346
  },
347
347
  page: {
@@ -355,6 +355,28 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
355
355
  },
356
356
  },
357
357
  },
358
+ {
359
+ name: 'execute_node',
360
+ description: EXECUTE_NODE_DESCRIPTION,
361
+ inputSchema: {
362
+ type: 'object',
363
+ properties: {
364
+ node_type: {
365
+ type: 'string',
366
+ description: "Node type to run — see list_docs({category: 'node'}) for the catalog",
367
+ },
368
+ config: {
369
+ type: 'object',
370
+ description: "Node configuration, same shape as the node's `data` in a flow",
371
+ },
372
+ input: {
373
+ description: 'Optional input passed as the parent node result (any JSON value)',
374
+ },
375
+ },
376
+ required: ['node_type'],
377
+ },
378
+ },
379
+ ...DOCS_TOOL_DEFINITIONS,
358
380
  ],
359
381
  };
360
382
  });
@@ -435,18 +457,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
435
457
  ],
436
458
  };
437
459
  }
438
- case 'describe_nodes': {
439
- const { types } = args;
440
- const nodes = getNodeDefinitions(types);
441
- return {
442
- content: [
443
- {
444
- type: 'text',
445
- text: JSON.stringify({ nodes }, null, 2),
446
- },
447
- ],
448
- };
449
- }
450
460
  case 'get_flow_run': {
451
461
  const { id } = args;
452
462
  const result = await client.getFlowRun(id);
@@ -488,6 +498,54 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
488
498
  ],
489
499
  };
490
500
  }
501
+ case 'execute_node': {
502
+ const { node_type, config, input } = args;
503
+ const result = await client.executeNode({ node_type, config, input });
504
+ return {
505
+ content: [
506
+ {
507
+ type: 'text',
508
+ text: JSON.stringify(result, null, 2),
509
+ },
510
+ ],
511
+ };
512
+ }
513
+ case 'list_docs': {
514
+ const { category } = args;
515
+ const result = await client.listDocs({ category });
516
+ return {
517
+ content: [
518
+ {
519
+ type: 'text',
520
+ text: JSON.stringify(result, null, 2),
521
+ },
522
+ ],
523
+ };
524
+ }
525
+ case 'get_doc': {
526
+ const { slug } = args;
527
+ const result = await client.getDoc(slug);
528
+ return {
529
+ content: [
530
+ {
531
+ type: 'text',
532
+ text: JSON.stringify(result, null, 2),
533
+ },
534
+ ],
535
+ };
536
+ }
537
+ case 'search_docs': {
538
+ const { query } = args;
539
+ const result = await client.searchDocs(query);
540
+ return {
541
+ content: [
542
+ {
543
+ type: 'text',
544
+ text: JSON.stringify(result, null, 2),
545
+ },
546
+ ],
547
+ };
548
+ }
491
549
  default:
492
550
  return {
493
551
  content: [
@@ -537,7 +595,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
537
595
  {
538
596
  uri: 'nodestorm://workflow-guide',
539
597
  name: 'Workflow Builder Guide',
540
- description: 'Complete guide for building NodeStorm flows: node types, edge rules, iteration semantics, validation, patterns, and examples',
598
+ description: 'Pointer to the live NodeStorm docs — use the list_docs / get_doc / search_docs tools instead',
541
599
  mimeType: 'text/markdown',
542
600
  },
543
601
  ],
@@ -547,13 +605,12 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => {
547
605
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
548
606
  const { uri } = request.params;
549
607
  if (uri === 'nodestorm://workflow-guide') {
550
- const guide = loadWorkflowGuide();
551
608
  return {
552
609
  contents: [
553
610
  {
554
611
  uri,
555
612
  mimeType: 'text/markdown',
556
- text: guide,
613
+ text: WORKFLOW_GUIDE_POINTER,
557
614
  },
558
615
  ],
559
616
  };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * What the server tells a client at connect (099.9). Shown to the model at
3
+ * session start, so it reaches the right tool first try instead of trying
4
+ * curl or a hosting dashboard on a nodestorm.ai link. Kept here, outside
5
+ * index.ts, so tests can import it without starting the server.
6
+ */
7
+ export declare const SERVER_INSTRUCTIONS = "NodeStorm runs recurring reports and tasks as flows of nodes (LLM prompts, search, scraping, files, tables, email, integrations). You act on one user's account through their access token.\n\nLinks from nodestorm.ai map to tools:\n- /files/N: execute_node {node_type: \"file\", config: {mode: \"read\", file_id: N}} returns the file's text.\n- /flows/N: get_flow {id: N}. /flows/N/runs: list_flow_runs {flow_id: N}. /flow-runs/N: get_flow_run {id: N}.\n- /help/SLUG: get_doc {slug: SLUG}.\nThere is no HTTP download for files or flows; do not fetch nodestorm.ai URLs directly.\n\nBefore execute_node or update_flow_structure, read the node's doc with get_doc (list_docs {category: \"node\"} lists them): the config keys come from there. Each execute_node call counts as one node execution against the user's monthly quota and is capped at about 90 seconds. Nodes that send or write (email, integrations, file create or update) take effect at once; confirm with the user first.";
8
+ export declare const EXECUTE_NODE_DESCRIPTION: string;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * What the server tells a client at connect (099.9). Shown to the model at
3
+ * session start, so it reaches the right tool first try instead of trying
4
+ * curl or a hosting dashboard on a nodestorm.ai link. Kept here, outside
5
+ * index.ts, so tests can import it without starting the server.
6
+ */
7
+ export const SERVER_INSTRUCTIONS = `NodeStorm runs recurring reports and tasks as flows of nodes (LLM prompts, search, scraping, files, tables, email, integrations). You act on one user's account through their access token.
8
+
9
+ Links from nodestorm.ai map to tools:
10
+ - /files/N: execute_node {node_type: "file", config: {mode: "read", file_id: N}} returns the file's text.
11
+ - /flows/N: get_flow {id: N}. /flows/N/runs: list_flow_runs {flow_id: N}. /flow-runs/N: get_flow_run {id: N}.
12
+ - /help/SLUG: get_doc {slug: SLUG}.
13
+ There is no HTTP download for files or flows; do not fetch nodestorm.ai URLs directly.
14
+
15
+ Before execute_node or update_flow_structure, read the node's doc with get_doc (list_docs {category: "node"} lists them): the config keys come from there. Each execute_node call counts as one node execution against the user's monthly quota and is capped at about 90 seconds. Nodes that send or write (email, integrations, file create or update) take effect at once; confirm with the user first.`;
16
+ export const EXECUTE_NODE_DESCRIPTION = 'Run a single NodeStorm node directly, without creating a flow — any node type. ' +
17
+ "config is identical to the node's `data` in a flow (see the node's doc via get_doc); " +
18
+ 'optional `input` is passed to the node as its parent result. ' +
19
+ 'Recipes: read a file (a nodestorm.ai/files/N link): {node_type: "file", config: {mode: "read", file_id: N}}. ' +
20
+ 'List the files in a folder: {node_type: "folder", config: {folder_id: ID}}, with folder_id "top" for files in no folder. ' +
21
+ 'Each call counts as one node execution against the monthly quota. ' +
22
+ 'Runs that send or write take effect immediately; confirm with your user first. ' +
23
+ 'Capped at ~90 seconds — long-running work belongs in a real flow instead.';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Docs tool definitions for the docs endpoints
3
+ * NodeStorm product documentation — node behavior, workflow mechanics, guides.
4
+ * These wrap the backend's v1 docs endpoints via NodeStormClient.
5
+ */
6
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
7
+ export declare const DOCS_TOOL_DEFINITIONS: Tool[];