nodestorm-mcp 0.1.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/README.md +151 -0
- package/dist/client.d.ts +170 -0
- package/dist/client.js +143 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +572 -0
- package/dist/tools/describe.d.ts +18 -0
- package/dist/tools/describe.js +284 -0
- package/dist/workflow-guide.md +1655 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
# nodestorm-mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for NodeStorm workflow automation.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx nodestorm-mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install globally:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install -g nodestorm-mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
### Claude Code / Claude Desktop
|
|
20
|
+
|
|
21
|
+
Add to your Claude configuration file:
|
|
22
|
+
|
|
23
|
+
**macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
24
|
+
**Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"mcpServers": {
|
|
29
|
+
"nodestorm": {
|
|
30
|
+
"command": "npx",
|
|
31
|
+
"args": ["-y", "nodestorm-mcp"],
|
|
32
|
+
"env": {
|
|
33
|
+
"NODESTORM_API_KEY": "ns_your_access_token_here"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Environment Variables
|
|
41
|
+
|
|
42
|
+
| Variable | Required | Description |
|
|
43
|
+
|----------|----------|-------------|
|
|
44
|
+
| `NODESTORM_API_KEY` | Yes | Access token from NodeStorm Settings |
|
|
45
|
+
| `NODESTORM_API_URL` | No | API base URL (default: `https://nodestorm.ai`) |
|
|
46
|
+
|
|
47
|
+
## Getting Your Access Token
|
|
48
|
+
|
|
49
|
+
1. Log in to [NodeStorm](https://nodestorm.ai)
|
|
50
|
+
2. Go to Settings → Access Tokens
|
|
51
|
+
3. Click "Generate New Token"
|
|
52
|
+
4. Copy the token (you won't see it again!)
|
|
53
|
+
|
|
54
|
+
Note: Access tokens require an active NodeStorm subscription.
|
|
55
|
+
|
|
56
|
+
## Available Tools
|
|
57
|
+
|
|
58
|
+
### create_flow
|
|
59
|
+
Create a new workflow from a natural language description.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
Input: { prompt: "Scrape 5 URLs and summarize each page" }
|
|
63
|
+
Output: { id, name, nodes, edges, url }
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### get_flow
|
|
67
|
+
Retrieve an existing workflow by ID.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
Input: { id: "abc123" }
|
|
71
|
+
Output: { id, name, nodes, edges, url, updated_at }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### update_flow
|
|
75
|
+
Modify an existing workflow.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Input: { id: "abc123", flow: { name, nodes, edges } }
|
|
79
|
+
Output: { id, name, nodes, edges, url, updated_at }
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### delete_flow
|
|
83
|
+
Delete a workflow.
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Input: { id: "abc123" }
|
|
87
|
+
Output: { success: true }
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### list_flows
|
|
91
|
+
List all your workflows.
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
Input: { limit: 20, offset: 0 }
|
|
95
|
+
Output: { flows: [...], total }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### validate_flow
|
|
99
|
+
Check if a workflow is valid before saving.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Input: { flow: { nodes, edges } }
|
|
103
|
+
Output: { valid: true } or { valid: false, errors: [...] }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### describe_nodes
|
|
107
|
+
Get node type definitions (no API call).
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
Input: { types: ["prompt", "list"] } // optional filter
|
|
111
|
+
Output: { nodes: [{ type, description, category, config_schema, handles }] }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Resources
|
|
115
|
+
|
|
116
|
+
### 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
|
|
122
|
+
|
|
123
|
+
## Example Usage
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
User: Create a flow that scrapes competitor websites and analyzes pricing
|
|
127
|
+
|
|
128
|
+
Claude: [Uses create_flow with prompt]
|
|
129
|
+
Created "Competitor Pricing Analysis"
|
|
130
|
+
→ https://nodestorm.ai/flows/abc123
|
|
131
|
+
|
|
132
|
+
User: Add 3 more URLs to the list
|
|
133
|
+
|
|
134
|
+
Claude: [Uses get_flow, modifies nodes, uses update_flow]
|
|
135
|
+
Updated flow with 3 additional URLs.
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Error Codes
|
|
139
|
+
|
|
140
|
+
| Code | Meaning |
|
|
141
|
+
|------|---------|
|
|
142
|
+
| `unauthorized` | Invalid or missing API key |
|
|
143
|
+
| `forbidden` | Not authorized for this resource |
|
|
144
|
+
| `not_found` | Resource doesn't exist |
|
|
145
|
+
| `invalid` | Validation failed |
|
|
146
|
+
| `rate_limited` | Too many requests |
|
|
147
|
+
| `server_error` | Server error |
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NodeStorm API Client
|
|
3
|
+
* Handles communication with the NodeStorm backend
|
|
4
|
+
*/
|
|
5
|
+
export interface Flow {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
nodes: any[];
|
|
9
|
+
edges: any[];
|
|
10
|
+
url: string;
|
|
11
|
+
updated_at?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface FlowListItem {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
url: string;
|
|
17
|
+
updated_at: string;
|
|
18
|
+
}
|
|
19
|
+
export interface FlowListResponse {
|
|
20
|
+
flows: FlowListItem[];
|
|
21
|
+
total: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ValidationResult {
|
|
24
|
+
valid: boolean;
|
|
25
|
+
errors?: string[];
|
|
26
|
+
}
|
|
27
|
+
export interface FlowRun {
|
|
28
|
+
id: number;
|
|
29
|
+
flow_id: number;
|
|
30
|
+
flow_title: string;
|
|
31
|
+
user_id?: number;
|
|
32
|
+
status: 'success' | 'error' | 'pending' | 'running';
|
|
33
|
+
node_count: number;
|
|
34
|
+
duration_seconds: number | null;
|
|
35
|
+
result?: {
|
|
36
|
+
node_executions?: Array<{
|
|
37
|
+
node_id: string;
|
|
38
|
+
node_type: string;
|
|
39
|
+
result: any;
|
|
40
|
+
success?: boolean;
|
|
41
|
+
error?: string;
|
|
42
|
+
started_at?: string;
|
|
43
|
+
completed_at?: string;
|
|
44
|
+
}>;
|
|
45
|
+
};
|
|
46
|
+
created_at: string;
|
|
47
|
+
updated_at: string;
|
|
48
|
+
}
|
|
49
|
+
export interface FlowRunListItem {
|
|
50
|
+
id: number;
|
|
51
|
+
flow_id: number;
|
|
52
|
+
flow_title: string;
|
|
53
|
+
status: 'success' | 'error' | 'pending' | 'running';
|
|
54
|
+
node_count: number;
|
|
55
|
+
duration_seconds: number | null;
|
|
56
|
+
created_at: string;
|
|
57
|
+
updated_at: string;
|
|
58
|
+
}
|
|
59
|
+
export interface FlowRunListResponse {
|
|
60
|
+
flow_runs: FlowRunListItem[];
|
|
61
|
+
pagination: {
|
|
62
|
+
current_page: number;
|
|
63
|
+
per_page: number;
|
|
64
|
+
total_count: number;
|
|
65
|
+
total_pages: number;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface CreateFlowResponse extends Flow {
|
|
69
|
+
valid: boolean;
|
|
70
|
+
errors?: string[];
|
|
71
|
+
}
|
|
72
|
+
export interface ApiError {
|
|
73
|
+
error: string;
|
|
74
|
+
code: 'unauthorized' | 'forbidden' | 'not_found' | 'invalid' | 'rate_limited' | 'server_error';
|
|
75
|
+
errors?: string[];
|
|
76
|
+
}
|
|
77
|
+
export declare class NodeStormClient {
|
|
78
|
+
private apiKey;
|
|
79
|
+
private baseUrl;
|
|
80
|
+
constructor(apiKey: string, baseUrl?: string);
|
|
81
|
+
private request;
|
|
82
|
+
/**
|
|
83
|
+
* Create a new flow from a prompt or directly from JSON (admin-only).
|
|
84
|
+
*/
|
|
85
|
+
createFlow(params: {
|
|
86
|
+
prompt?: string;
|
|
87
|
+
name?: string;
|
|
88
|
+
workflow_json?: {
|
|
89
|
+
nodes: unknown[];
|
|
90
|
+
edges: unknown[];
|
|
91
|
+
};
|
|
92
|
+
description?: string;
|
|
93
|
+
}): Promise<CreateFlowResponse>;
|
|
94
|
+
/**
|
|
95
|
+
* Get a flow by ID
|
|
96
|
+
*/
|
|
97
|
+
getFlow(id: string): Promise<Flow>;
|
|
98
|
+
/**
|
|
99
|
+
* Update flow metadata. For structural changes (nodes/edges), use updateFlowStructure.
|
|
100
|
+
*/
|
|
101
|
+
updateFlow(id: string, flow: {
|
|
102
|
+
name?: string;
|
|
103
|
+
description?: string;
|
|
104
|
+
schedule_enabled?: boolean;
|
|
105
|
+
schedule_cron?: string;
|
|
106
|
+
schedule_timezone?: string;
|
|
107
|
+
}): Promise<Flow>;
|
|
108
|
+
/**
|
|
109
|
+
* Apply atomic structural operations (edit/add/remove nodes and edges) to an existing flow.
|
|
110
|
+
* All ops apply in one transaction; the whole batch rolls back if any op fails or the final
|
|
111
|
+
* state is invalid. See update_flow_structure tool docs for op shapes.
|
|
112
|
+
*/
|
|
113
|
+
updateFlowStructure(flowId: string, operations: any[]): Promise<any>;
|
|
114
|
+
/**
|
|
115
|
+
* Delete a flow
|
|
116
|
+
*/
|
|
117
|
+
deleteFlow(id: string): Promise<{
|
|
118
|
+
success: boolean;
|
|
119
|
+
}>;
|
|
120
|
+
/**
|
|
121
|
+
* List or search user's flows. Pass `query` to filter by title/description.
|
|
122
|
+
* scope='user' (default) searches user's own flows; scope='public' searches
|
|
123
|
+
* published community/official entries.
|
|
124
|
+
*/
|
|
125
|
+
listFlows(opts?: {
|
|
126
|
+
query?: string;
|
|
127
|
+
scope?: string;
|
|
128
|
+
limit?: number;
|
|
129
|
+
}): Promise<FlowListResponse>;
|
|
130
|
+
/**
|
|
131
|
+
* Get a flow run by ID
|
|
132
|
+
*/
|
|
133
|
+
getFlowRun(id: string): Promise<FlowRun>;
|
|
134
|
+
/**
|
|
135
|
+
* List flow runs with optional filters
|
|
136
|
+
*/
|
|
137
|
+
listFlowRuns(options?: {
|
|
138
|
+
flowId?: number;
|
|
139
|
+
status?: 'success' | 'error' | 'pending' | 'running';
|
|
140
|
+
page?: number;
|
|
141
|
+
perPage?: number;
|
|
142
|
+
}): Promise<FlowRunListResponse>;
|
|
143
|
+
/**
|
|
144
|
+
* Search chat message history using semantic search
|
|
145
|
+
*/
|
|
146
|
+
searchChatHistory(options: {
|
|
147
|
+
query: string;
|
|
148
|
+
limit?: number;
|
|
149
|
+
role?: 'user' | 'assistant';
|
|
150
|
+
source?: 'chat' | 'heartbeat' | 'telegram';
|
|
151
|
+
from?: string;
|
|
152
|
+
to?: string;
|
|
153
|
+
}): Promise<ChatSearchResponse>;
|
|
154
|
+
}
|
|
155
|
+
export interface ChatSearchResult {
|
|
156
|
+
content: string;
|
|
157
|
+
role: string;
|
|
158
|
+
source: string;
|
|
159
|
+
relevance: number;
|
|
160
|
+
created_at: string;
|
|
161
|
+
}
|
|
162
|
+
export interface ChatSearchResponse {
|
|
163
|
+
results: ChatSearchResult[];
|
|
164
|
+
}
|
|
165
|
+
export declare class NodeStormApiError extends Error {
|
|
166
|
+
code: string;
|
|
167
|
+
statusCode: number;
|
|
168
|
+
errors?: string[];
|
|
169
|
+
constructor(message: string, code: string, statusCode: number, errors?: string[]);
|
|
170
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NodeStorm API Client
|
|
3
|
+
* Handles communication with the NodeStorm backend
|
|
4
|
+
*/
|
|
5
|
+
export class NodeStormClient {
|
|
6
|
+
apiKey;
|
|
7
|
+
baseUrl;
|
|
8
|
+
constructor(apiKey, baseUrl = 'https://nodestorm.ai') {
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
this.baseUrl = baseUrl;
|
|
11
|
+
}
|
|
12
|
+
async request(path, options = {}) {
|
|
13
|
+
const url = `${this.baseUrl}/api/v1${path}`;
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
...options,
|
|
16
|
+
headers: {
|
|
17
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
'Accept': 'application/json',
|
|
20
|
+
...options.headers,
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
const data = await response.json();
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
const error = data;
|
|
26
|
+
throw new NodeStormApiError(error.error, error.code, response.status, error.errors);
|
|
27
|
+
}
|
|
28
|
+
return data;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a new flow from a prompt or directly from JSON (admin-only).
|
|
32
|
+
*/
|
|
33
|
+
async createFlow(params) {
|
|
34
|
+
return this.request('/flows', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
body: JSON.stringify(params),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get a flow by ID
|
|
41
|
+
*/
|
|
42
|
+
async getFlow(id) {
|
|
43
|
+
return this.request(`/flows/${id}`);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Update flow metadata. For structural changes (nodes/edges), use updateFlowStructure.
|
|
47
|
+
*/
|
|
48
|
+
async updateFlow(id, flow) {
|
|
49
|
+
return this.request(`/flows/${id}`, {
|
|
50
|
+
method: 'PUT',
|
|
51
|
+
body: JSON.stringify({ flow }),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Apply atomic structural operations (edit/add/remove nodes and edges) to an existing flow.
|
|
56
|
+
* All ops apply in one transaction; the whole batch rolls back if any op fails or the final
|
|
57
|
+
* state is invalid. See update_flow_structure tool docs for op shapes.
|
|
58
|
+
*/
|
|
59
|
+
async updateFlowStructure(flowId, operations) {
|
|
60
|
+
return this.request(`/flows/${flowId}/structure`, {
|
|
61
|
+
method: 'PATCH',
|
|
62
|
+
body: JSON.stringify({ operations }),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Delete a flow
|
|
67
|
+
*/
|
|
68
|
+
async deleteFlow(id) {
|
|
69
|
+
return this.request(`/flows/${id}`, {
|
|
70
|
+
method: 'DELETE',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* List or search user's flows. Pass `query` to filter by title/description.
|
|
75
|
+
* scope='user' (default) searches user's own flows; scope='public' searches
|
|
76
|
+
* published community/official entries.
|
|
77
|
+
*/
|
|
78
|
+
async listFlows(opts = {}) {
|
|
79
|
+
const { query, scope = 'user', limit = 20 } = opts;
|
|
80
|
+
if (query || scope === 'public') {
|
|
81
|
+
const params = new URLSearchParams();
|
|
82
|
+
if (query)
|
|
83
|
+
params.set('query', query);
|
|
84
|
+
params.set('scope', scope);
|
|
85
|
+
params.set('limit', String(limit));
|
|
86
|
+
return this.request(`/flows/search?${params.toString()}`);
|
|
87
|
+
}
|
|
88
|
+
return this.request(`/flows?limit=${limit}`);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get a flow run by ID
|
|
92
|
+
*/
|
|
93
|
+
async getFlowRun(id) {
|
|
94
|
+
return this.request(`/flow-runs/${id}`);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* List flow runs with optional filters
|
|
98
|
+
*/
|
|
99
|
+
async listFlowRuns(options = {}) {
|
|
100
|
+
const params = new URLSearchParams();
|
|
101
|
+
if (options.flowId)
|
|
102
|
+
params.append('flow_id', options.flowId.toString());
|
|
103
|
+
if (options.status)
|
|
104
|
+
params.append('status', options.status);
|
|
105
|
+
if (options.page)
|
|
106
|
+
params.append('page', options.page.toString());
|
|
107
|
+
if (options.perPage)
|
|
108
|
+
params.append('per_page', options.perPage.toString());
|
|
109
|
+
const queryString = params.toString();
|
|
110
|
+
const path = queryString ? `/flow-runs?${queryString}` : '/flow-runs';
|
|
111
|
+
return this.request(path);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Search chat message history using semantic search
|
|
115
|
+
*/
|
|
116
|
+
async searchChatHistory(options) {
|
|
117
|
+
const params = new URLSearchParams();
|
|
118
|
+
params.append('q', options.query);
|
|
119
|
+
if (options.limit)
|
|
120
|
+
params.append('limit', options.limit.toString());
|
|
121
|
+
if (options.role)
|
|
122
|
+
params.append('role', options.role);
|
|
123
|
+
if (options.source)
|
|
124
|
+
params.append('source', options.source);
|
|
125
|
+
if (options.from)
|
|
126
|
+
params.append('from', options.from);
|
|
127
|
+
if (options.to)
|
|
128
|
+
params.append('to', options.to);
|
|
129
|
+
return this.request(`/chat/search?${params.toString()}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export class NodeStormApiError extends Error {
|
|
133
|
+
code;
|
|
134
|
+
statusCode;
|
|
135
|
+
errors;
|
|
136
|
+
constructor(message, code, statusCode, errors) {
|
|
137
|
+
super(message);
|
|
138
|
+
this.name = 'NodeStormApiError';
|
|
139
|
+
this.code = code;
|
|
140
|
+
this.statusCode = statusCode;
|
|
141
|
+
this.errors = errors;
|
|
142
|
+
}
|
|
143
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* NodeStorm MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Provides tools for AI agents to create, read, update, and delete NodeStorm workflows.
|
|
6
|
+
*
|
|
7
|
+
* Environment variables:
|
|
8
|
+
* - NODESTORM_API_KEY (required): API key for authentication
|
|
9
|
+
* - NODESTORM_API_URL (optional): API base URL (default: https://nodestorm.ai)
|
|
10
|
+
*/
|
|
11
|
+
export {};
|