@flowdot.ai/mcp-server 1.0.1 → 1.0.3

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.
Files changed (106) hide show
  1. package/bin/flowdot-mcp.js +1 -1
  2. package/dist/api-client.d.ts +2 -557
  3. package/dist/api-client.d.ts.map +1 -1
  4. package/dist/api-client.js +2 -1264
  5. package/dist/api-client.js.map +1 -1
  6. package/dist/resources/index.d.ts +12 -0
  7. package/dist/resources/index.d.ts.map +1 -0
  8. package/dist/resources/index.js +2204 -0
  9. package/dist/resources/index.js.map +1 -0
  10. package/dist/server.d.ts.map +1 -1
  11. package/dist/server.js +11 -1
  12. package/dist/server.js.map +1 -1
  13. package/dist/tools/add-recipe-step.d.ts +23 -0
  14. package/dist/tools/add-recipe-step.d.ts.map +1 -0
  15. package/dist/tools/add-recipe-step.js +196 -0
  16. package/dist/tools/add-recipe-step.js.map +1 -0
  17. package/dist/tools/add-recipe-store.d.ts +19 -0
  18. package/dist/tools/add-recipe-store.d.ts.map +1 -0
  19. package/dist/tools/add-recipe-store.js +116 -0
  20. package/dist/tools/add-recipe-store.js.map +1 -0
  21. package/dist/tools/agent-toolkits.d.ts.map +1 -1
  22. package/dist/tools/agent-toolkits.js +8 -1
  23. package/dist/tools/agent-toolkits.js.map +1 -1
  24. package/dist/tools/browse-recipes.d.ts +16 -0
  25. package/dist/tools/browse-recipes.d.ts.map +1 -0
  26. package/dist/tools/browse-recipes.js +102 -0
  27. package/dist/tools/browse-recipes.js.map +1 -0
  28. package/dist/tools/create-recipe.d.ts +16 -0
  29. package/dist/tools/create-recipe.d.ts.map +1 -0
  30. package/dist/tools/create-recipe.js +92 -0
  31. package/dist/tools/create-recipe.js.map +1 -0
  32. package/dist/tools/delete-recipe-step.d.ts +14 -0
  33. package/dist/tools/delete-recipe-step.d.ts.map +1 -0
  34. package/dist/tools/delete-recipe-step.js +65 -0
  35. package/dist/tools/delete-recipe-step.js.map +1 -0
  36. package/dist/tools/delete-recipe-store.d.ts +14 -0
  37. package/dist/tools/delete-recipe-store.d.ts.map +1 -0
  38. package/dist/tools/delete-recipe-store.js +65 -0
  39. package/dist/tools/delete-recipe-store.js.map +1 -0
  40. package/dist/tools/delete-recipe.d.ts +13 -0
  41. package/dist/tools/delete-recipe.d.ts.map +1 -0
  42. package/dist/tools/delete-recipe.js +59 -0
  43. package/dist/tools/delete-recipe.js.map +1 -0
  44. package/dist/tools/favorite-recipe.d.ts +13 -0
  45. package/dist/tools/favorite-recipe.d.ts.map +1 -0
  46. package/dist/tools/favorite-recipe.js +53 -0
  47. package/dist/tools/favorite-recipe.js.map +1 -0
  48. package/dist/tools/fork-recipe.d.ts +13 -0
  49. package/dist/tools/fork-recipe.d.ts.map +1 -0
  50. package/dist/tools/fork-recipe.js +56 -0
  51. package/dist/tools/fork-recipe.js.map +1 -0
  52. package/dist/tools/get-recipe-definition.d.ts +15 -0
  53. package/dist/tools/get-recipe-definition.d.ts.map +1 -0
  54. package/dist/tools/get-recipe-definition.js +70 -0
  55. package/dist/tools/get-recipe-definition.js.map +1 -0
  56. package/dist/tools/get-recipe.d.ts +12 -0
  57. package/dist/tools/get-recipe.d.ts.map +1 -0
  58. package/dist/tools/get-recipe.js +88 -0
  59. package/dist/tools/get-recipe.js.map +1 -0
  60. package/dist/tools/index.d.ts.map +1 -1
  61. package/dist/tools/index.js +87 -0
  62. package/dist/tools/index.js.map +1 -1
  63. package/dist/tools/link-recipe.d.ts +14 -0
  64. package/dist/tools/link-recipe.d.ts.map +1 -0
  65. package/dist/tools/link-recipe.js +76 -0
  66. package/dist/tools/link-recipe.js.map +1 -0
  67. package/dist/tools/list-recipe-steps.d.ts +12 -0
  68. package/dist/tools/list-recipe-steps.d.ts.map +1 -0
  69. package/dist/tools/list-recipe-steps.js +69 -0
  70. package/dist/tools/list-recipe-steps.js.map +1 -0
  71. package/dist/tools/list-recipe-stores.d.ts +12 -0
  72. package/dist/tools/list-recipe-stores.d.ts.map +1 -0
  73. package/dist/tools/list-recipe-stores.js +87 -0
  74. package/dist/tools/list-recipe-stores.js.map +1 -0
  75. package/dist/tools/list-recipes.d.ts +12 -0
  76. package/dist/tools/list-recipes.d.ts.map +1 -0
  77. package/dist/tools/list-recipes.js +92 -0
  78. package/dist/tools/list-recipes.js.map +1 -0
  79. package/dist/tools/update-recipe-step.d.ts +22 -0
  80. package/dist/tools/update-recipe-step.d.ts.map +1 -0
  81. package/dist/tools/update-recipe-step.js +97 -0
  82. package/dist/tools/update-recipe-step.js.map +1 -0
  83. package/dist/tools/update-recipe-store.d.ts +20 -0
  84. package/dist/tools/update-recipe-store.d.ts.map +1 -0
  85. package/dist/tools/update-recipe-store.js +98 -0
  86. package/dist/tools/update-recipe-store.js.map +1 -0
  87. package/dist/tools/update-recipe.d.ts +18 -0
  88. package/dist/tools/update-recipe.d.ts.map +1 -0
  89. package/dist/tools/update-recipe.js +98 -0
  90. package/dist/tools/update-recipe.js.map +1 -0
  91. package/dist/tools/vote-recipe.d.ts +13 -0
  92. package/dist/tools/vote-recipe.d.ts.map +1 -0
  93. package/dist/tools/vote-recipe.js +54 -0
  94. package/dist/tools/vote-recipe.js.map +1 -0
  95. package/dist/types.d.ts +3 -1092
  96. package/dist/types.d.ts.map +1 -1
  97. package/dist/types.js +3 -1
  98. package/dist/types.js.map +1 -1
  99. package/dist/utils/script-validator.d.ts.map +1 -1
  100. package/dist/utils/script-validator.js +5 -1
  101. package/dist/utils/script-validator.js.map +1 -1
  102. package/package.json +11 -3
  103. package/dist/tools/convert-app-to-multifile.d.ts +0 -11
  104. package/dist/tools/convert-app-to-multifile.d.ts.map +0 -1
  105. package/dist/tools/convert-app-to-multifile.js +0 -69
  106. package/dist/tools/convert-app-to-multifile.js.map +0 -1
@@ -0,0 +1,2204 @@
1
+ /**
2
+ * MCP Resources Registry
3
+ *
4
+ * Provides learning resources that agents can read to understand FlowDot concepts.
5
+ * These are designed to be the FIRST thing agents check when asked about FlowDot features.
6
+ */
7
+ import { ListResourcesRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
8
+ /**
9
+ * Learning resources for FlowDot system components.
10
+ * Use URI format: learn://component-name
11
+ */
12
+ const LEARN_RESOURCES = {
13
+ 'learn://overview': {
14
+ name: 'FlowDot Platform Overview',
15
+ description: 'High-level overview of all FlowDot components and how they work together',
16
+ mimeType: 'text/markdown',
17
+ content: `# FlowDot Platform Overview
18
+
19
+ ## What is FlowDot?
20
+
21
+ FlowDot is a visual workflow automation platform that lets you build, execute, and share automation workflows. It combines visual programming with AI-powered agents, custom nodes, and knowledge bases.
22
+
23
+ ## Core Components
24
+
25
+ ### 1. **Workflows**
26
+ Visual automation workflows with nodes and connections.
27
+ - **Learn more:** \`learn://workflows\`
28
+ - **Quick start:** Use \`list_workflows\` to see your workflows
29
+
30
+ ### 2. **Recipes**
31
+ Agent orchestration workflows for complex automation.
32
+ - **Learn more:** \`learn://recipes\`
33
+ - **Quick start:** Use \`list_recipes\` to see your recipes
34
+
35
+ ### 3. **Custom Nodes**
36
+ Reusable JavaScript nodes you can create and share.
37
+ - **Learn more:** \`learn://custom-nodes\`
38
+ - **Quick start:** Use \`list_custom_nodes\` to see available nodes
39
+
40
+ ### 4. **Apps**
41
+ React frontend applications that can invoke workflows.
42
+ - **Learn more:** \`learn://apps\`
43
+ - **Quick start:** Use \`list_apps\` to see your apps
44
+
45
+ ### 5. **Agent Toolkits**
46
+ MCP toolkits for extending AI agents with new capabilities.
47
+ - **Learn more:** \`learn://toolkits\`
48
+ - **Quick start:** Use \`mcp__flowdot__list_agent_toolkits\`
49
+
50
+ ### 6. **Knowledge Base**
51
+ Document storage and RAG-powered search.
52
+ - **Learn more:** \`learn://knowledge-base\`
53
+ - **Quick start:** Use \`list_knowledge_documents\`
54
+
55
+ ## Common Workflows
56
+
57
+ ### Creating a Simple Workflow
58
+ 1. \`create_workflow\` - Create empty workflow
59
+ 2. \`list_available_nodes\` - See what nodes you can add
60
+ 3. \`add_node\` - Add nodes to your workflow
61
+ 4. \`add_connection\` - Connect nodes together
62
+ 5. \`execute_workflow\` - Run the workflow
63
+
64
+ ### Creating a Recipe
65
+ 1. \`create_recipe\` - Create empty recipe
66
+ 2. \`add_recipe_store\` - Define inputs/outputs
67
+ 3. \`add_recipe_step\` - Add agent, loop, gate, or other steps
68
+ 4. \`update_recipe_step\` - Connect steps via "next"
69
+ 5. \`update_recipe\` - Set entry_step_id
70
+ 6. \`link_recipe\` - Create CLI alias
71
+ 7. Run via CLI: \`npx flowdot recipes run <alias>\`
72
+
73
+ ## Where to Start
74
+
75
+ - **New to FlowDot?** Read \`learn://workflows\` first
76
+ - **Building agents?** Read \`learn://recipes\` first
77
+ - **Extending functionality?** Read \`learn://custom-nodes\` or \`learn://toolkits\`
78
+ - **Building UIs?** Read \`learn://apps\` first
79
+
80
+ ## Getting Help
81
+
82
+ - Each \`learn://\` resource has detailed examples
83
+ - Tool descriptions include usage examples
84
+ - Visit https://flowdot.ai for web interface
85
+ `,
86
+ },
87
+ 'learn://workflows': {
88
+ name: 'Workflows Complete Guide',
89
+ description: 'Complete guide to creating, managing, and executing FlowDot workflows',
90
+ mimeType: 'text/markdown',
91
+ content: `# FlowDot Workflows - Complete Guide
92
+
93
+ ## What Are Workflows?
94
+
95
+ Workflows are visual automation workflows composed of **nodes** (processing units) and **connections** (data flow). Think of them like a flowchart where each box does something and arrows show how data moves between them.
96
+
97
+ ## Key Concepts
98
+
99
+ ### Nodes
100
+ Processing units that perform actions:
101
+ - **Input nodes:** Receive data from workflow inputs
102
+ - **Processing nodes:** Transform, analyze, or manipulate data
103
+ - **Output nodes:** Return results
104
+ - **LLM nodes:** AI-powered processing
105
+ - **Custom nodes:** Your own JavaScript logic
106
+
107
+ ### Connections
108
+ Data flows between nodes via **sockets**:
109
+ - **Output sockets:** Where a node sends data
110
+ - **Input sockets:** Where a node receives data
111
+ - Connect output socket → input socket to flow data
112
+
113
+ ### Execution
114
+ When you execute a workflow:
115
+ 1. Provide input values via \`inputs\` parameter
116
+ 2. Workflow processes nodes in dependency order
117
+ 3. Results are returned with output node data
118
+
119
+ ## Building a Workflow
120
+
121
+ ### Step 1: Create Workflow
122
+ \`\`\`javascript
123
+ // Create empty workflow
124
+ create_workflow({
125
+ name: "My First Workflow",
126
+ description: "Processes text with AI"
127
+ })
128
+ // Returns: { id: "abc123", ... }
129
+ \`\`\`
130
+
131
+ ### Step 2: See Available Nodes
132
+ \`\`\`javascript
133
+ list_available_nodes()
134
+ // Returns categories and node types
135
+ \`\`\`
136
+
137
+ Common node types:
138
+ - \`TextInput\` - Accept text input
139
+ - \`LLMNode\` - AI processing
140
+ - \`TextOutput\` - Return text result
141
+ - \`HTTPRequest\` - API calls
142
+ - \`custom_node_xxx\` - Custom nodes
143
+
144
+ ### Step 3: Add Nodes
145
+ \`\`\`javascript
146
+ // Add input node
147
+ add_node({
148
+ workflow_id: "abc123",
149
+ node_type: "TextInput",
150
+ position: { x: 100, y: 100 },
151
+ properties: {
152
+ label: "User Input",
153
+ inputName: "user_text"
154
+ }
155
+ })
156
+ // Returns: { id: "node-1", ... }
157
+
158
+ // Add LLM processing node
159
+ add_node({
160
+ workflow_id: "abc123",
161
+ node_type: "LLMNode",
162
+ position: { x: 300, y: 100 },
163
+ properties: {
164
+ prompt: "Summarize: {{text}}",
165
+ model: "claude-haiku-4.5"
166
+ }
167
+ })
168
+ // Returns: { id: "node-2", ... }
169
+
170
+ // Add output node
171
+ add_node({
172
+ workflow_id: "abc123",
173
+ node_type: "TextOutput",
174
+ position: { x: 500, y: 100 }
175
+ })
176
+ // Returns: { id: "node-3", ... }
177
+ \`\`\`
178
+
179
+ ### Step 4: Connect Nodes
180
+ \`\`\`javascript
181
+ // Connect input → LLM
182
+ add_connection({
183
+ workflow_id: "abc123",
184
+ source_node_id: "node-1",
185
+ source_socket_id: "text",
186
+ target_node_id: "node-2",
187
+ target_socket_id: "text"
188
+ })
189
+
190
+ // Connect LLM → output
191
+ add_connection({
192
+ workflow_id: "abc123",
193
+ source_node_id: "node-2",
194
+ source_socket_id: "response",
195
+ target_node_id: "node-3",
196
+ target_socket_id: "Consolidated Text"
197
+ })
198
+ \`\`\`
199
+
200
+ ### Step 5: Execute
201
+ \`\`\`javascript
202
+ execute_workflow({
203
+ workflow_id: "abc123",
204
+ inputs: {
205
+ user_text: "Long text to summarize..."
206
+ }
207
+ })
208
+ // Returns execution results
209
+ \`\`\`
210
+
211
+ ## Advanced Features
212
+
213
+ ### Getting Workflow Structure
214
+ \`\`\`javascript
215
+ // See all nodes and connections
216
+ get_workflow_graph({ workflow_id: "abc123" })
217
+
218
+ // Get input requirements
219
+ get_workflow_inputs_schema({ workflow_id: "abc123" })
220
+
221
+ // Validate workflow
222
+ validate_workflow({ workflow_id: "abc123" })
223
+ \`\`\`
224
+
225
+ ### Managing Workflows
226
+ \`\`\`javascript
227
+ // List your workflows
228
+ list_workflows()
229
+
230
+ // Search workflows
231
+ search_workflows({ query: "summarize" })
232
+
233
+ // Duplicate a workflow
234
+ duplicate_workflow({ workflow_id: "abc123", name: "Copy of My Workflow" })
235
+
236
+ // Make workflow public
237
+ toggle_workflow_public({ workflow_id: "abc123", is_public: true })
238
+
239
+ // Delete workflow
240
+ delete_workflow({ workflow_id: "abc123" })
241
+ \`\`\`
242
+
243
+ ### Execution Management
244
+ \`\`\`javascript
245
+ // Get execution status
246
+ get_execution_status({ execution_id: "exec-123" })
247
+
248
+ // Cancel running execution
249
+ cancel_execution({ execution_id: "exec-123" })
250
+
251
+ // Retry failed execution
252
+ retry_execution({ execution_id: "exec-123" })
253
+
254
+ // View execution history
255
+ get_execution_history({ workflow_id: "abc123" })
256
+ \`\`\`
257
+
258
+ ## Common Patterns
259
+
260
+ ### Pattern 1: Linear Processing
261
+ Input → Process 1 → Process 2 → Output
262
+
263
+ ### Pattern 2: Branching
264
+ Input → Condition → Path A or Path B → Output
265
+
266
+ ### Pattern 3: Parallel Processing
267
+ Input → [Process A, Process B, Process C] → Merge → Output
268
+
269
+ ### Pattern 4: Loop Processing
270
+ Input → Loop Over Items → Process Each → Collect Results → Output
271
+
272
+ ## Best Practices
273
+
274
+ 1. **Name things clearly:** Node labels should describe what they do
275
+ 2. **Use input nodes:** Define workflow inputs explicitly
276
+ 3. **Test incrementally:** Execute after each major change
277
+ 4. **Validate early:** Run \`validate_workflow\` before executing
278
+ 5. **Handle errors:** Consider error paths for critical workflows
279
+
280
+ ## Troubleshooting
281
+
282
+ ### Workflow Won't Execute
283
+ - Check \`validate_workflow\` for errors
284
+ - Ensure all required inputs are provided
285
+ - Verify all nodes are connected properly
286
+
287
+ ### Unexpected Results
288
+ - Check \`get_execution_history\` for execution logs
289
+ - Verify node configurations are correct
290
+ - Test individual nodes in isolation
291
+
292
+ ### Can't Find Nodes
293
+ - Use \`list_available_nodes\` to see all available types
294
+ - Check if custom nodes are published
295
+ - Verify node names match exactly (case-sensitive)
296
+
297
+ ## Related Resources
298
+
299
+ - **Custom Nodes:** \`learn://custom-nodes\` - Create your own node types
300
+ - **Apps:** \`learn://apps\` - Build UIs that execute workflows
301
+ - **Recipes:** \`learn://recipes\` - Agent-driven workflow orchestration
302
+ `,
303
+ },
304
+ 'learn://recipes': {
305
+ name: 'Recipes Complete Guide',
306
+ description: 'Complete guide to agent recipes - orchestration workflows for complex automation',
307
+ mimeType: 'text/markdown',
308
+ content: `# FlowDot Recipes - Complete Guide
309
+
310
+ ## What Are Recipes?
311
+
312
+ Recipes are **agent orchestration workflows** that combine AI agents, conditional logic, loops, approvals, and parallel execution to handle complex tasks. Unlike visual workflows, recipes are designed for programmatic agent-driven automation.
313
+
314
+ **CRITICAL:** MCP tools can only **DESIGN** recipes. To **RUN** a recipe, use the FlowDot CLI:
315
+ \`\`\`bash
316
+ npx flowdot recipes run <alias> --input '{"key":"value"}'
317
+ \`\`\`
318
+
319
+ ## Key Concepts
320
+
321
+ ### Steps
322
+ The building blocks of a recipe:
323
+ - **agent:** AI agent with tools (read, search, edit, etc.)
324
+ - **loop:** Iterate over arrays (sequential or parallel)
325
+ - **parallel:** Run multiple steps concurrently
326
+ - **gate:** Require approval or user input
327
+ - **branch:** Conditional routing based on data
328
+ - **invoke:** Call another recipe (subroutines)
329
+
330
+ ### Stores
331
+ Variables that hold data throughout execution:
332
+ - **Input stores:** Values provided when running
333
+ - **Output stores:** Values returned after completion
334
+ - **Internal stores:** Temporary data storage
335
+
336
+ ### Connections
337
+ Steps connect via:
338
+ - **next:** The step to run after success
339
+ - **on_error:** The step to run if error occurs
340
+
341
+ ### Interpolation
342
+ Reference data in prompts and conditions:
343
+ - \`{{inputs.request}}\` - The CLI task argument
344
+ - \`{{store_key}}\` - Any store value
345
+ - \`{{step.step_id}}\` - Output from a previous step
346
+
347
+ ## Building a Recipe
348
+
349
+ ### Step 1: Create Recipe
350
+ \`\`\`javascript
351
+ create_recipe({
352
+ name: "code-reviewer",
353
+ description: "Reviews code changes and suggests improvements"
354
+ })
355
+ // Returns: { hash: "abc123xyz", ... }
356
+ // SAVE THIS HASH - you need it for all operations!
357
+ \`\`\`
358
+
359
+ ### Step 2: Define Stores (Inputs/Outputs)
360
+
361
+ **CRITICAL:** Name your primary input store \`request\` - the CLI passes the task argument as \`inputs.request\`.
362
+
363
+ \`\`\`javascript
364
+ // Primary input (receives CLI task)
365
+ add_recipe_store({
366
+ hash: "abc123xyz",
367
+ key: "request",
368
+ label: "Task Request",
369
+ description: "The task provided by the user",
370
+ schema_type: "string",
371
+ is_input: true
372
+ })
373
+
374
+ // Output store
375
+ add_recipe_store({
376
+ hash: "abc123xyz",
377
+ key: "review_result",
378
+ label: "Review Result",
379
+ schema_type: "string",
380
+ is_output: true
381
+ })
382
+
383
+ // Internal stores for intermediate data
384
+ add_recipe_store({
385
+ hash: "abc123xyz",
386
+ key: "file_list",
387
+ schema_type: "array"
388
+ })
389
+ \`\`\`
390
+
391
+ ### Step 3: Add Steps
392
+
393
+ #### Agent Step (AI Processing)
394
+ \`\`\`javascript
395
+ add_recipe_step({
396
+ hash: "abc123xyz",
397
+ name: "analyze-code",
398
+ type: "agent",
399
+ config: {
400
+ user_prompt: "Review the code: {{inputs.request}}. List files to check.",
401
+ tools: ["read", "search", "analyze"],
402
+ output_store: "file_list",
403
+ max_iterations: 10
404
+ }
405
+ })
406
+ // Returns: { id: "step-1", ... }
407
+ // SAVE THE STEP ID!
408
+ \`\`\`
409
+
410
+ **IMPORTANT:** Use \`user_prompt\` (NOT \`prompt\`) - this is the field the runtime expects.
411
+
412
+ **Available tools:**
413
+ - \`read\` - Read files
414
+ - \`search\` - Search codebase
415
+ - \`analyze\` - Analyze code
416
+ - \`find-definition\` - Find function/class definitions
417
+ - \`web-search\` - Search the web
418
+ - \`edit\` - Edit files
419
+ - \`execute-command\` - Run shell commands
420
+ - \`create-file\` - Create new files
421
+
422
+ #### Loop Step (Iterate)
423
+ \`\`\`javascript
424
+ add_recipe_step({
425
+ hash: "abc123xyz",
426
+ name: "review-each-file",
427
+ type: "loop",
428
+ config: {
429
+ loop_over: "file_list", // Store with array
430
+ loop_variable: "current_file", // Name for current item
431
+ loop_step_id: "step-3", // Step to run per item
432
+ parallel: true, // Run iterations concurrently
433
+ max_concurrent: 5 // Max 5 at once
434
+ }
435
+ })
436
+ // Returns: { id: "step-2", ... }
437
+ \`\`\`
438
+
439
+ #### Parallel Step (Concurrent)
440
+ \`\`\`javascript
441
+ add_recipe_step({
442
+ hash: "abc123xyz",
443
+ name: "run-checks",
444
+ type: "parallel",
445
+ config: {
446
+ parallel_step_ids: ["step-4", "step-5", "step-6"]
447
+ }
448
+ })
449
+ \`\`\`
450
+
451
+ #### Gate Step (Approval/Input)
452
+ \`\`\`javascript
453
+ add_recipe_step({
454
+ hash: "abc123xyz",
455
+ name: "approve-changes",
456
+ type: "gate",
457
+ config: {
458
+ requires_approval: true,
459
+ approval_prompt: "Review findings:\\n{{findings}}\\n\\nApprove?",
460
+ input_options: {
461
+ button_mode: "preset",
462
+ preset: "approve_deny",
463
+ allow_comment: true,
464
+ comment_required: false
465
+ },
466
+ input_output_store: "approval_decision"
467
+ }
468
+ })
469
+ \`\`\`
470
+
471
+ **Gate Input Presets:**
472
+ - \`approve_deny\` - Approve or Deny buttons
473
+ - \`yes_no\` - Yes or No buttons
474
+ - \`continue_cancel\` - Continue or Cancel buttons
475
+
476
+ **Custom buttons:**
477
+ \`\`\`json
478
+ {
479
+ "button_mode": "custom",
480
+ "buttons": [
481
+ { "label": "Fix Now", "value": "fix", "isApproval": true },
482
+ { "label": "Skip", "value": "skip", "isApproval": false }
483
+ ]
484
+ }
485
+ \`\`\`
486
+
487
+ #### Branch Step (Conditional)
488
+ \`\`\`javascript
489
+ add_recipe_step({
490
+ hash: "abc123xyz",
491
+ name: "route-by-severity",
492
+ type: "branch",
493
+ config: {
494
+ conditions: [
495
+ { expression: "{{severity}} === 'high'", then: "step-urgent" },
496
+ { expression: "{{severity}} === 'medium'", then: "step-normal" }
497
+ ],
498
+ default: "step-low-priority"
499
+ }
500
+ })
501
+ \`\`\`
502
+
503
+ #### Invoke Step (Subroutine)
504
+ \`\`\`javascript
505
+ add_recipe_step({
506
+ hash: "abc123xyz",
507
+ name: "call-linter",
508
+ type: "invoke",
509
+ config: {
510
+ recipe_hash: "linter-recipe-hash",
511
+ input_mapping: {
512
+ "file_path": "{{current_file}}"
513
+ },
514
+ output_mapping: {
515
+ "lint_result": "lint_output"
516
+ }
517
+ }
518
+ })
519
+ \`\`\`
520
+
521
+ ### Step 4: Connect Steps
522
+ \`\`\`javascript
523
+ // Set the "next" step for sequential flow
524
+ update_recipe_step({
525
+ hash: "abc123xyz",
526
+ step_id: "step-1",
527
+ next: "step-2", // Run step-2 after step-1 succeeds
528
+ on_error: "step-error" // Run step-error if step-1 fails
529
+ })
530
+ \`\`\`
531
+
532
+ ### Step 5: Set Entry Point
533
+
534
+ **CRITICAL:** Recipe won't run until you set the entry_step_id!
535
+
536
+ \`\`\`javascript
537
+ update_recipe({
538
+ hash: "abc123xyz",
539
+ entry_step_id: "step-1" // First step to run
540
+ })
541
+ \`\`\`
542
+
543
+ ### Step 6: Link for CLI Access
544
+ \`\`\`javascript
545
+ link_recipe({
546
+ hash: "abc123xyz",
547
+ alias: "code-reviewer" // Use hyphens, not underscores!
548
+ })
549
+ \`\`\`
550
+
551
+ **CRITICAL Alias Rules:**
552
+ - Use HYPHENS: \`my-recipe\` ✓
553
+ - NO underscores: \`my_recipe\` ✗ (causes 422 error)
554
+ - Lowercase, alphanumeric + hyphens only
555
+
556
+ ### Step 7: Run via CLI
557
+ \`\`\`bash
558
+ # Run with alias
559
+ npx flowdot recipes run code-reviewer --input '{"request":"Review src/app.js"}'
560
+
561
+ # Or with hash
562
+ npx flowdot recipes run abc123xyz --input '{"request":"Review src/app.js"}'
563
+ \`\`\`
564
+
565
+ ## Complete Example
566
+
567
+ \`\`\`javascript
568
+ // 1. Create recipe
569
+ const recipe = await create_recipe({
570
+ name: "code-reviewer",
571
+ description: "Reviews code and suggests improvements"
572
+ });
573
+ const hash = recipe.hash;
574
+
575
+ // 2. Define stores
576
+ await add_recipe_store({ hash, key: "request", is_input: true });
577
+ await add_recipe_store({ hash, key: "review", is_output: true });
578
+ await add_recipe_store({ hash, key: "files" });
579
+
580
+ // 3. Add agent step
581
+ const step1 = await add_recipe_step({
582
+ hash,
583
+ name: "find-files",
584
+ type: "agent",
585
+ config: {
586
+ user_prompt: "Find files to review: {{inputs.request}}",
587
+ tools: ["search"],
588
+ output_store: "files"
589
+ }
590
+ });
591
+
592
+ // 4. Add review step
593
+ const step2 = await add_recipe_step({
594
+ hash,
595
+ name: "review-code",
596
+ type: "agent",
597
+ config: {
598
+ user_prompt: "Review these files: {{files}}",
599
+ tools: ["read", "analyze"],
600
+ output_store: "review"
601
+ }
602
+ });
603
+
604
+ // 5. Connect steps
605
+ await update_recipe_step({ hash, step_id: step1.id, next: step2.id });
606
+
607
+ // 6. Set entry point
608
+ await update_recipe({ hash, entry_step_id: step1.id });
609
+
610
+ // 7. Link for CLI
611
+ await link_recipe({ hash, alias: "code-reviewer" });
612
+ \`\`\`
613
+
614
+ ## Managing Recipes
615
+
616
+ \`\`\`javascript
617
+ // List recipes
618
+ list_recipes()
619
+
620
+ // Get recipe details
621
+ get_recipe({ hash: "abc123xyz" })
622
+
623
+ // Get full definition (YAML/JSON)
624
+ get_recipe_definition({ hash: "abc123xyz", format: "yaml" })
625
+
626
+ // List steps
627
+ list_recipe_steps({ hash: "abc123xyz" })
628
+
629
+ // List stores
630
+ list_recipe_stores({ hash: "abc123xyz" })
631
+
632
+ // Browse public recipes
633
+ browse_recipes({ search: "code", sort: "popular" })
634
+
635
+ // Fork a public recipe
636
+ fork_recipe({ hash: "public-recipe-hash", name: "My Fork" })
637
+
638
+ // Delete recipe
639
+ delete_recipe({ hash: "abc123xyz", confirm: true })
640
+ \`\`\`
641
+
642
+ ## Best Practices
643
+
644
+ 1. **Name primary input \`request\`** - CLI convention
645
+ 2. **Use \`user_prompt\` not \`prompt\`** - Runtime requirement
646
+ 3. **Set entry_step_id** - Recipe won't run without it
647
+ 4. **Use hyphens in aliases** - Not underscores
648
+ 5. **Save all IDs** - You need hashes and step IDs for updates
649
+ 6. **Test incrementally** - Build one step at a time
650
+ 7. **Handle errors** - Use \`on_error\` for critical steps
651
+
652
+ ## Troubleshooting
653
+
654
+ ### Recipe Won't Execute
655
+ - Check entry_step_id is set: \`update_recipe\`
656
+ - Verify alias is linked: \`link_recipe\`
657
+ - Ensure stores are defined (especially \`request\`)
658
+
659
+ ### Steps Not Connecting
660
+ - Verify step IDs are correct
661
+ - Use \`list_recipe_steps\` to see all step IDs
662
+ - Check \`next\` and \`on_error\` are valid step IDs
663
+
664
+ ### Agent Steps Failing
665
+ - Use \`user_prompt\` not \`prompt\`
666
+ - Verify tool names are correct
667
+ - Check interpolation syntax: \`{{store_key}}\`
668
+
669
+ ## Related Resources
670
+
671
+ - **Workflows:** \`learn://workflows\` - Visual automation workflows
672
+ - **Custom Nodes:** \`learn://custom-nodes\` - Extend agent capabilities
673
+ - **Toolkits:** \`learn://toolkits\` - MCP toolkit integration
674
+ `,
675
+ },
676
+ 'learn://custom-nodes': {
677
+ name: 'Custom Nodes Complete Guide',
678
+ description: 'Complete guide to creating and managing custom nodes in FlowDot',
679
+ mimeType: 'text/markdown',
680
+ content: `# FlowDot Custom Nodes - Complete Guide
681
+
682
+ ## What Are Custom Nodes?
683
+
684
+ Custom Nodes are **reusable JavaScript processing units** that you can create, share, and use in workflows. They extend FlowDot's built-in nodes with your own custom logic.
685
+
686
+ ## Key Concepts
687
+
688
+ ### Inputs
689
+ Data the node receives:
690
+ - Define name, data type, and description
691
+ - Access via \`inputs.InputName\` in script
692
+ - **Valid types:** text, number, boolean, json, array, any
693
+
694
+ ### Outputs
695
+ Data the node produces:
696
+ - Define name, data type, and description
697
+ - Return via \`return { OutputName: value }\`
698
+ - **Must match exactly** (case-sensitive)
699
+
700
+ ### Properties
701
+ Configuration values:
702
+ - Set by user in node UI
703
+ - Access via \`properties.propertyKey\`
704
+ - Examples: API URLs, prompts, thresholds
705
+
706
+ ### Script
707
+ JavaScript code that processes inputs:
708
+ - **Must define:** \`function processData(inputs, properties, llm)\`
709
+ - **Must return:** Object with output names as keys
710
+ - **Sandboxed:** No imports, eval, or file system access
711
+
712
+ ### LLM Capability (Optional)
713
+ Enable AI features:
714
+ - Users see Quick Select buttons (FlowDot, Simple, Capable, Complex)
715
+ - Script can call \`llm.call()\` to make LLM requests
716
+ - Useful for AI-powered processing
717
+
718
+ ## Creating a Custom Node
719
+
720
+ ### Step 1: Get Template (Optional)
721
+ \`\`\`javascript
722
+ get_custom_node_template({
723
+ inputs: [
724
+ { name: "Text", dataType: "text" },
725
+ { name: "MaxLength", dataType: "number" }
726
+ ],
727
+ outputs: [
728
+ { name: "Summary", dataType: "text" }
729
+ ],
730
+ llm_enabled: true
731
+ })
732
+ // Returns template code you can customize
733
+ \`\`\`
734
+
735
+ ### Step 2: Write Your Script
736
+
737
+ **REQUIRED FORMAT:**
738
+ \`\`\`javascript
739
+ function processData(inputs, properties, llm) {
740
+ // Access inputs by exact names
741
+ const text = inputs.Text || '';
742
+ const maxLength = inputs.MaxLength || 100;
743
+
744
+ // Access properties
745
+ const apiUrl = properties.apiUrl || 'https://api.example.com';
746
+
747
+ // Your logic here
748
+ const summary = text.substring(0, maxLength);
749
+
750
+ // Return object with keys matching output names EXACTLY
751
+ return {
752
+ Summary: summary
753
+ };
754
+ }
755
+ \`\`\`
756
+
757
+ **With LLM:**
758
+ \`\`\`javascript
759
+ function processData(inputs, properties, llm) {
760
+ const text = inputs.Text || '';
761
+
762
+ // Call LLM
763
+ const result = llm.call({
764
+ prompt: \`Summarize: \${text}\`,
765
+ temperature: 0.7,
766
+ maxTokens: 500
767
+ });
768
+
769
+ return {
770
+ Summary: result.success ? result.response : result.error
771
+ };
772
+ }
773
+ \`\`\`
774
+
775
+ **Important Rules:**
776
+ - ✅ processData function is REQUIRED
777
+ - ✅ Input/output names are case-sensitive
778
+ - ✅ Return keys must match output names exactly
779
+ - ❌ No top-level return statements
780
+ - ❌ No require/import, eval, process, global
781
+ - ❌ No file system access
782
+ - ✅ Available: console, JSON, Math, String, Array methods
783
+
784
+ ### Step 3: Create Node
785
+ \`\`\`javascript
786
+ create_custom_node({
787
+ name: "text-summarizer",
788
+ title: "Text Summarizer",
789
+ description: "Summarizes text to a specified length",
790
+ inputs: [
791
+ {
792
+ name: "Text",
793
+ dataType: "text",
794
+ description: "The text to summarize"
795
+ },
796
+ {
797
+ name: "MaxLength",
798
+ dataType: "number",
799
+ description: "Maximum summary length"
800
+ }
801
+ ],
802
+ outputs: [
803
+ {
804
+ name: "Summary",
805
+ dataType: "text",
806
+ description: "The summarized text"
807
+ }
808
+ ],
809
+ script_code: "function processData(inputs, properties, llm) { ... }",
810
+ llm_enabled: false,
811
+ execution_timeout: 5000,
812
+ memory_limit: 128
813
+ })
814
+ // Returns: { hash: "node-abc123", ... }
815
+ \`\`\`
816
+
817
+ ### Step 4: Test Your Node
818
+ \`\`\`javascript
819
+ // Add to a workflow
820
+ add_node({
821
+ workflow_id: "workflow-123",
822
+ node_type: "custom_node_abc123", // custom_node_{hash}
823
+ position: { x: 100, y: 100 }
824
+ })
825
+ \`\`\`
826
+
827
+ ## LLM-Enabled Nodes
828
+
829
+ Enable AI capabilities in your custom nodes:
830
+
831
+ \`\`\`javascript
832
+ create_custom_node({
833
+ name: "ai-analyzer",
834
+ title: "AI Analyzer",
835
+ description: "Analyzes data with AI",
836
+ inputs: [{ name: "Data", dataType: "text" }],
837
+ outputs: [{ name: "Analysis", dataType: "text" }],
838
+ llm_enabled: true, // Enable LLM
839
+ script_code: \`
840
+ function processData(inputs, properties, llm) {
841
+ const result = llm.call({
842
+ prompt: "Analyze: " + inputs.Data,
843
+ systemPrompt: "You are an expert analyst.",
844
+ temperature: 0.7,
845
+ maxTokens: 1000
846
+ });
847
+
848
+ return {
849
+ Analysis: result.success ? result.response : "Error: " + result.error
850
+ };
851
+ }
852
+ \`
853
+ })
854
+ \`\`\`
855
+
856
+ **LLM Response Structure:**
857
+ \`\`\`javascript
858
+ {
859
+ success: boolean, // true if call succeeded
860
+ response: string, // The LLM's response text
861
+ error: string | null, // Error message if failed
862
+ provider: string, // Provider used (e.g., "openai")
863
+ model: string, // Model used (e.g., "gpt-4")
864
+ tokens: { prompt, response, total }
865
+ }
866
+ \`\`\`
867
+
868
+ ## Managing Custom Nodes
869
+
870
+ \`\`\`javascript
871
+ // List your nodes
872
+ list_custom_nodes({ search: "summarizer" })
873
+
874
+ // Search public nodes
875
+ search_public_custom_nodes({
876
+ query: "text processing",
877
+ verified_only: true
878
+ })
879
+
880
+ // Get node details
881
+ get_custom_node({ node_id: "node-abc123" })
882
+
883
+ // Update node
884
+ update_custom_node({
885
+ node_id: "node-abc123",
886
+ description: "New description",
887
+ script_code: "function processData(...) { ... }"
888
+ })
889
+
890
+ // Delete node
891
+ delete_custom_node({ node_id: "node-abc123" })
892
+
893
+ // Copy public node to your library
894
+ copy_custom_node({
895
+ node_id: "public-node-xyz",
896
+ name: "my-custom-analyzer"
897
+ })
898
+ \`\`\`
899
+
900
+ ## Sharing Custom Nodes
901
+
902
+ \`\`\`javascript
903
+ // Make public
904
+ toggle_custom_node_visibility({
905
+ node_id: "node-abc123",
906
+ visibility: "public"
907
+ })
908
+
909
+ // Make private
910
+ toggle_custom_node_visibility({
911
+ node_id: "node-abc123",
912
+ visibility: "private"
913
+ })
914
+
915
+ // Unlisted (accessible via link only)
916
+ toggle_custom_node_visibility({
917
+ node_id: "node-abc123",
918
+ visibility: "unlisted"
919
+ })
920
+ \`\`\`
921
+
922
+ ## Common Patterns
923
+
924
+ ### Pattern 1: Data Transformation
925
+ \`\`\`javascript
926
+ function processData(inputs, properties, llm) {
927
+ const data = inputs.Data || {};
928
+
929
+ // Transform
930
+ const transformed = Object.keys(data).reduce((acc, key) => {
931
+ acc[key.toUpperCase()] = data[key];
932
+ return acc;
933
+ }, {});
934
+
935
+ return { Transformed: transformed };
936
+ }
937
+ \`\`\`
938
+
939
+ ### Pattern 2: API Integration
940
+ \`\`\`javascript
941
+ function processData(inputs, properties, llm) {
942
+ const query = inputs.Query || '';
943
+ const apiKey = properties.apiKey || '';
944
+
945
+ // Note: No fetch() in sandbox - use HTTPRequest node instead
946
+ // This pattern shows data preparation
947
+
948
+ const requestData = {
949
+ query: query,
950
+ apiKey: apiKey
951
+ };
952
+
953
+ return { RequestData: requestData };
954
+ }
955
+ \`\`\`
956
+
957
+ ### Pattern 3: Conditional Logic
958
+ \`\`\`javascript
959
+ function processData(inputs, properties, llm) {
960
+ const value = inputs.Value || 0;
961
+ const threshold = properties.threshold || 50;
962
+
963
+ let category;
964
+ if (value > threshold * 2) {
965
+ category = 'high';
966
+ } else if (value > threshold) {
967
+ category = 'medium';
968
+ } else {
969
+ category = 'low';
970
+ }
971
+
972
+ return { Category: category };
973
+ }
974
+ \`\`\`
975
+
976
+ ### Pattern 4: Array Processing
977
+ \`\`\`javascript
978
+ function processData(inputs, properties, llm) {
979
+ const items = inputs.Items || [];
980
+
981
+ const filtered = items.filter(item => item.active);
982
+ const mapped = filtered.map(item => ({
983
+ id: item.id,
984
+ name: item.name.toUpperCase()
985
+ }));
986
+
987
+ return { ProcessedItems: mapped };
988
+ }
989
+ \`\`\`
990
+
991
+ ## Best Practices
992
+
993
+ 1. **Validate inputs:** Always provide defaults
994
+ 2. **Clear naming:** Use descriptive input/output names
995
+ 3. **Handle errors:** Try-catch for risky operations
996
+ 4. **Test thoroughly:** Test with edge cases
997
+ 5. **Document:** Add clear descriptions
998
+ 6. **Keep it simple:** One clear purpose per node
999
+ 7. **Use LLM wisely:** Only when AI adds value
1000
+
1001
+ ## Troubleshooting
1002
+
1003
+ ### Script Validation Errors
1004
+ - Check function name: must be \`processData\`
1005
+ - Verify return object keys match output names exactly
1006
+ - Remove any top-level code outside function
1007
+ - No imports or require statements
1008
+
1009
+ ### Runtime Errors
1010
+ - Check input names match exactly (case-sensitive)
1011
+ - Verify all inputs have defaults: \`inputs.X || defaultValue\`
1012
+ - Console.log for debugging: \`console.log("Debug:", value)\`
1013
+
1014
+ ### LLM Calls Failing
1015
+ - Ensure \`llm_enabled: true\` when creating node
1016
+ - Check LLM response: \`result.success\` before using
1017
+ - Handle errors: \`result.error\` when \`success\` is false
1018
+
1019
+ ## Related Resources
1020
+
1021
+ - **Workflows:** \`learn://workflows\` - Use custom nodes in workflows
1022
+ - **Templates:** Use \`get_custom_node_template\` for starter code
1023
+ - **Public Nodes:** Browse with \`search_public_custom_nodes\`
1024
+ `,
1025
+ },
1026
+ 'learn://apps': {
1027
+ name: 'Apps Complete Guide',
1028
+ description: 'Complete guide to building multi-file React applications with FlowDot',
1029
+ mimeType: 'text/markdown',
1030
+ content: `# FlowDot Apps - Complete Guide
1031
+
1032
+ ## What Are Apps?
1033
+
1034
+ Apps are **React frontend applications** that run in a sandboxed browser environment. They can invoke FlowDot workflows as backends to create full-stack applications.
1035
+
1036
+ **Use cases:**
1037
+ - Custom UIs for workflows
1038
+ - Dashboards and data visualization
1039
+ - Interactive forms and wizards
1040
+ - Chat interfaces with workflow backends
1041
+ - Data exploration tools
1042
+
1043
+ ## Key Concepts
1044
+
1045
+ ### Execution Environment
1046
+ Apps run in a sandboxed iframe with:
1047
+ - **React 18** (global - no imports needed)
1048
+ - **Tailwind CSS** (full utility classes)
1049
+ - **FlowDot color tokens:** primary-50 to primary-900, secondary-50 to secondary-900
1050
+ - **invokeWorkflow()** function to call linked workflows
1051
+
1052
+ ### Multi-File Structure
1053
+ All apps are multi-file by default:
1054
+ - **Entry file:** Main component (App.jsx)
1055
+ - **Components:** Reusable UI components
1056
+ - **Utilities:** Helper functions
1057
+ - **Hooks:** Custom React hooks
1058
+ - **Styles:** CSS files
1059
+
1060
+ ### Display Modes
1061
+ - **windowed:** Standard view with FlowDot header (default)
1062
+ - **fullscreen:** Full viewport, minimal control bar
1063
+ - **embedded:** No FlowDot UI, for iframe embedding
1064
+
1065
+ ## CRITICAL CODE RULES
1066
+
1067
+ **These rules are MANDATORY due to sandbox constraints:**
1068
+
1069
+ 1. **NO IMPORTS** - React is global
1070
+ \`\`\`javascript
1071
+ // ❌ WRONG
1072
+ import React from 'react';
1073
+ import { useState } from 'react';
1074
+
1075
+ // ✅ CORRECT
1076
+ function MyApp() {
1077
+ const [state, setState] = React.useState(null);
1078
+ }
1079
+ \`\`\`
1080
+
1081
+ 2. **MUST export default**
1082
+ \`\`\`javascript
1083
+ function MyApp() {
1084
+ // component code
1085
+ }
1086
+
1087
+ export default MyApp; // REQUIRED!
1088
+ \`\`\`
1089
+
1090
+ 3. **Function must be named**
1091
+ \`\`\`javascript
1092
+ // ❌ WRONG
1093
+ export default function() { ... }
1094
+
1095
+ // ✅ CORRECT
1096
+ function MyApp() { ... }
1097
+ export default MyApp;
1098
+ \`\`\`
1099
+
1100
+ 4. **Use Tailwind CSS only**
1101
+ \`\`\`javascript
1102
+ // ❌ WRONG (no inline styles)
1103
+ <div style={{ color: 'red' }}>Text</div>
1104
+
1105
+ // ✅ CORRECT
1106
+ <div className="text-red-500">Text</div>
1107
+ \`\`\`
1108
+
1109
+ 5. **NO FORM ELEMENTS**
1110
+ \`\`\`javascript
1111
+ // ❌ WRONG (sandbox blocks forms)
1112
+ <form onSubmit={handleSubmit}>
1113
+ <button type="submit">Submit</button>
1114
+ </form>
1115
+
1116
+ // ✅ CORRECT
1117
+ <div>
1118
+ <input onKeyDown={(e) => e.key === 'Enter' && handleClick()} />
1119
+ <button type="button" onClick={handleClick}>Submit</button>
1120
+ </div>
1121
+ \`\`\`
1122
+
1123
+ 6. **ALL BUTTONS need type="button"**
1124
+ \`\`\`javascript
1125
+ <button type="button" onClick={handleClick}>Click Me</button>
1126
+ \`\`\`
1127
+
1128
+ ## Creating an App
1129
+
1130
+ ### Step 1: Create App
1131
+ \`\`\`javascript
1132
+ create_app({
1133
+ name: "my-dashboard",
1134
+ description: "Interactive dashboard with workflow backend",
1135
+ category: "productivity",
1136
+ tags: ["dashboard", "analytics"]
1137
+ })
1138
+ // Returns: { id: "app-abc123", ... }
1139
+ \`\`\`
1140
+
1141
+ This creates an app with a default Hello World App.jsx entry file.
1142
+
1143
+ ### Step 2: Update Entry File
1144
+ \`\`\`javascript
1145
+ update_app_file({
1146
+ app_id: "app-abc123",
1147
+ file_path: "App.jsx",
1148
+ content: \`
1149
+ function MyDashboard() {
1150
+ const [data, setData] = React.useState(null);
1151
+ const [loading, setLoading] = React.useState(false);
1152
+
1153
+ const handleFetch = async () => {
1154
+ setLoading(true);
1155
+ try {
1156
+ const result = await invokeWorkflow('workflow-hash', {
1157
+ input: 'fetch data'
1158
+ });
1159
+ const output = getNodeOutput(result, 'Output Node');
1160
+ setData(output);
1161
+ } catch (error) {
1162
+ console.error('Error:', error);
1163
+ } finally {
1164
+ setLoading(false);
1165
+ }
1166
+ };
1167
+
1168
+ return (
1169
+ <div className="min-h-screen bg-gray-50 p-8">
1170
+ <h1 className="text-3xl font-bold text-gray-900 mb-4">
1171
+ My Dashboard
1172
+ </h1>
1173
+
1174
+ <button
1175
+ type="button"
1176
+ onClick={handleFetch}
1177
+ disabled={loading}
1178
+ className="px-4 py-2 bg-primary-600 text-white rounded hover:bg-primary-700 disabled:opacity-50"
1179
+ >
1180
+ {loading ? 'Loading...' : 'Fetch Data'}
1181
+ </button>
1182
+
1183
+ {data && (
1184
+ <div className="mt-4 p-4 bg-white rounded shadow">
1185
+ <pre>{JSON.stringify(data, null, 2)}</pre>
1186
+ </div>
1187
+ )}
1188
+ </div>
1189
+ );
1190
+ }
1191
+
1192
+ export default MyDashboard;
1193
+ \`
1194
+ })
1195
+ \`\`\`
1196
+
1197
+ ### Step 3: Add Additional Files
1198
+ \`\`\`javascript
1199
+ // Create a component
1200
+ create_app_file({
1201
+ app_id: "app-abc123",
1202
+ path: "components/DataCard.jsx",
1203
+ type: "component",
1204
+ content: \`
1205
+ function DataCard({ title, value }) {
1206
+ return (
1207
+ <div className="p-6 bg-white rounded-lg shadow">
1208
+ <h3 className="text-lg font-semibold text-gray-700">{title}</h3>
1209
+ <p className="text-3xl font-bold text-primary-600">{value}</p>
1210
+ </div>
1211
+ );
1212
+ }
1213
+
1214
+ export default DataCard;
1215
+ \`
1216
+ })
1217
+
1218
+ // Create a utility
1219
+ create_app_file({
1220
+ app_id: "app-abc123",
1221
+ path: "utils/helpers.js",
1222
+ type: "utility",
1223
+ content: \`
1224
+ export function formatNumber(num) {
1225
+ return new Intl.NumberFormat().format(num);
1226
+ }
1227
+
1228
+ export function formatDate(date) {
1229
+ return new Date(date).toLocaleDateString();
1230
+ }
1231
+ \`
1232
+ })
1233
+ \`\`\`
1234
+
1235
+ ### Step 4: Link Workflow
1236
+ \`\`\`javascript
1237
+ link_app_workflow({
1238
+ app_id: "app-abc123",
1239
+ workflow_hash: "workflow-xyz",
1240
+ alias: "dataFetcher"
1241
+ })
1242
+ \`\`\`
1243
+
1244
+ ## Workflow Integration
1245
+
1246
+ ### Invoking Workflows
1247
+ \`\`\`javascript
1248
+ const result = await invokeWorkflow('workflow-hash', {
1249
+ inputName: 'value'
1250
+ });
1251
+ \`\`\`
1252
+
1253
+ ### Workflow Response Structure
1254
+ \`\`\`javascript
1255
+ {
1256
+ success: boolean,
1257
+ data: {
1258
+ "[nodeId]": {
1259
+ nodeId: "uuid",
1260
+ nodeTitle: "My Output Node",
1261
+ nodeType: "text_output",
1262
+ outputs: {
1263
+ "Consolidated Text": {
1264
+ value: "the actual data",
1265
+ metadata: {...}
1266
+ }
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+ \`\`\`
1272
+
1273
+ ### Extract Output Helper
1274
+ **CRITICAL:** Use this helper to safely extract workflow outputs:
1275
+
1276
+ \`\`\`javascript
1277
+ const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
1278
+ if (!result?.data) return null;
1279
+ const node = Object.values(result.data).find(n => n.nodeTitle === nodeTitle);
1280
+ return node?.outputs?.[socketName]?.value;
1281
+ };
1282
+
1283
+ // Usage
1284
+ const result = await invokeWorkflow('hash', { input });
1285
+ const data = getNodeOutput(result, 'Output Node');
1286
+ if (data) {
1287
+ // use data
1288
+ }
1289
+ \`\`\`
1290
+
1291
+ ## Managing Apps
1292
+
1293
+ \`\`\`javascript
1294
+ // List your apps
1295
+ list_apps({ search: "dashboard" })
1296
+
1297
+ // Search public apps
1298
+ search_apps({ query: "analytics", sort: "trending" })
1299
+
1300
+ // Get app details
1301
+ get_app({ app_id: "app-abc123" })
1302
+
1303
+ // List files in app
1304
+ list_app_files({ app_id: "app-abc123" })
1305
+
1306
+ // Get specific file
1307
+ get_app_file({ app_id: "app-abc123", file_path: "App.jsx" })
1308
+
1309
+ // Update app metadata
1310
+ update_app({
1311
+ app_id: "app-abc123",
1312
+ name: "New Name",
1313
+ description: "New description",
1314
+ config: { displayMode: "fullscreen" }
1315
+ })
1316
+
1317
+ // Delete file
1318
+ delete_app_file({ app_id: "app-abc123", file_path: "old-file.jsx" })
1319
+
1320
+ // Rename/move file
1321
+ rename_app_file({
1322
+ app_id: "app-abc123",
1323
+ file_path: "Button.jsx",
1324
+ new_path: "components/Button.jsx"
1325
+ })
1326
+ \`\`\`
1327
+
1328
+ ## Code Editing Tools
1329
+
1330
+ ### Surgical Edits (Find/Replace)
1331
+ \`\`\`javascript
1332
+ edit_app_code({
1333
+ app_id: "app-abc123",
1334
+ old_string: "const [count, setCount] = React.useState(0);",
1335
+ new_string: "const [count, setCount] = React.useState(10);"
1336
+ })
1337
+ \`\`\`
1338
+
1339
+ ### Insert Code
1340
+ \`\`\`javascript
1341
+ insert_app_code({
1342
+ app_id: "app-abc123",
1343
+ after_pattern: "const [data, setData] = React.useState(null);",
1344
+ content: "\\n const [error, setError] = React.useState(null);"
1345
+ })
1346
+ \`\`\`
1347
+
1348
+ ### Append Code
1349
+ \`\`\`javascript
1350
+ append_app_code({
1351
+ app_id: "app-abc123",
1352
+ content: "\\n\\n// Helper functions\\nfunction formatData(d) { return d; }"
1353
+ })
1354
+ \`\`\`
1355
+
1356
+ ### Prepend Code
1357
+ \`\`\`javascript
1358
+ prepend_app_code({
1359
+ app_id: "app-abc123",
1360
+ content: "// Dashboard Configuration\\nconst API_URL = 'https://api.example.com';\\n\\n"
1361
+ })
1362
+ \`\`\`
1363
+
1364
+ ## Templates
1365
+
1366
+ Get starter code for common patterns:
1367
+
1368
+ \`\`\`javascript
1369
+ get_app_template({ template: "basic" })
1370
+ get_app_template({ template: "chat" })
1371
+ get_app_template({ template: "dashboard" })
1372
+ get_app_template({ template: "form-builder" })
1373
+ get_app_template({ template: "data-viewer" })
1374
+ get_app_template({ template: "all" }) // See all templates
1375
+ \`\`\`
1376
+
1377
+ ## Publishing Apps
1378
+
1379
+ \`\`\`javascript
1380
+ // Publish to marketplace
1381
+ publish_app({ app_id: "app-abc123" })
1382
+
1383
+ // Unpublish (make private)
1384
+ unpublish_app({ app_id: "app-abc123" })
1385
+
1386
+ // Clone public app
1387
+ clone_app({
1388
+ app_id: "public-app-xyz",
1389
+ name: "My Custom Dashboard"
1390
+ })
1391
+ \`\`\`
1392
+
1393
+ ## Best Practices
1394
+
1395
+ 1. **Start with templates:** Use \`get_app_template\`
1396
+ 2. **One component per file:** Keep files focused
1397
+ 3. **Use getNodeOutput helper:** Always for workflow results
1398
+ 4. **Type="button" everywhere:** Prevent form behavior
1399
+ 5. **Tailwind only:** No inline styles
1400
+ 6. **Test incrementally:** Build piece by piece
1401
+ 7. **Link workflows first:** Before invoking them
1402
+
1403
+ ## Common Patterns
1404
+
1405
+ ### Loading States
1406
+ \`\`\`javascript
1407
+ const [loading, setLoading] = React.useState(false);
1408
+
1409
+ const handleAction = async () => {
1410
+ setLoading(true);
1411
+ try {
1412
+ const result = await invokeWorkflow('hash', { input });
1413
+ // handle result
1414
+ } finally {
1415
+ setLoading(false);
1416
+ }
1417
+ };
1418
+ \`\`\`
1419
+
1420
+ ### Error Handling
1421
+ \`\`\`javascript
1422
+ const [error, setError] = React.useState(null);
1423
+
1424
+ try {
1425
+ const result = await invokeWorkflow('hash', { input });
1426
+ setError(null);
1427
+ } catch (err) {
1428
+ setError(err.message);
1429
+ }
1430
+ \`\`\`
1431
+
1432
+ ### Enter Key Submission
1433
+ \`\`\`javascript
1434
+ <input
1435
+ onKeyDown={(e) => {
1436
+ if (e.key === 'Enter') {
1437
+ handleSubmit();
1438
+ }
1439
+ }}
1440
+ />
1441
+ \`\`\`
1442
+
1443
+ ## Troubleshooting
1444
+
1445
+ ### Import Errors
1446
+ - Remove all import statements
1447
+ - Use React.useState, React.useEffect, etc.
1448
+
1449
+ ### Form Not Working
1450
+ - Remove <form> tags
1451
+ - Add type="button" to all buttons
1452
+ - Use onKeyDown for Enter key
1453
+
1454
+ ### Workflow Not Responding
1455
+ - Check workflow is linked: \`link_app_workflow\`
1456
+ - Use getNodeOutput helper to extract data
1457
+ - Verify node titles match exactly
1458
+
1459
+ ## Related Resources
1460
+
1461
+ - **Workflows:** \`learn://workflows\` - Create workflow backends
1462
+ - **Templates:** \`get_app_template\` - Starter code patterns
1463
+ `,
1464
+ },
1465
+ 'learn://toolkits': {
1466
+ name: 'Agent Toolkits Complete Guide',
1467
+ description: 'Complete guide to creating and managing MCP agent toolkits',
1468
+ mimeType: 'text/markdown',
1469
+ content: `# FlowDot Agent Toolkits - Complete Guide
1470
+
1471
+ ## What Are Agent Toolkits?
1472
+
1473
+ Agent Toolkits are **collections of MCP tools** that extend AI agents with new capabilities. They bundle related tools (API integrations, data processors, etc.) with shared credentials and configuration.
1474
+
1475
+ **Think of it as:** Creating mini MCP servers that can be installed and used by agents.
1476
+
1477
+ ## Key Concepts
1478
+
1479
+ ### Toolkits
1480
+ Collections of related tools:
1481
+ - **Name:** Unique identifier (e.g., "spotify-api")
1482
+ - **Tools:** Array of HTTP or Workflow-based tools
1483
+ - **Credentials:** Shared API keys, OAuth tokens, etc.
1484
+ - **Visibility:** public, private, or unlisted
1485
+
1486
+ ### Tools
1487
+ Individual capabilities within a toolkit:
1488
+ - **HTTP tools:** Make REST API calls
1489
+ - **Workflow tools:** Execute FlowDot workflows
1490
+ - **Input schema:** Define required parameters
1491
+ - **Output schema:** Define expected responses
1492
+
1493
+ ### Credentials
1494
+ Authentication requirements:
1495
+ - **api_key:** Standard API keys
1496
+ - **oauth:** OAuth 2.0 tokens (auto-refreshable)
1497
+ - **bearer:** Bearer tokens
1498
+ - **basic:** Basic auth
1499
+ - **custom:** Custom authentication
1500
+
1501
+ ### Installation
1502
+ Users install toolkits to their account:
1503
+ - Map toolkit credentials to their stored API keys
1504
+ - Enable/disable as needed
1505
+ - Invoke tools with credentials applied
1506
+
1507
+ ## Creating a Toolkit
1508
+
1509
+ ### Step 1: Create Toolkit
1510
+ \`\`\`javascript
1511
+ create_agent_toolkit({
1512
+ name: "spotify-api",
1513
+ title: "Spotify API",
1514
+ description: "Access Spotify music data and playback",
1515
+ category: "api-integration",
1516
+ tags: ["music", "api", "streaming"],
1517
+ credential_requirements: [
1518
+ {
1519
+ key_name: "SPOTIFY_CLIENT_ID",
1520
+ label: "Spotify Client ID",
1521
+ credential_type: "api_key",
1522
+ is_required: true,
1523
+ description: "Your Spotify app client ID"
1524
+ },
1525
+ {
1526
+ key_name: "SPOTIFY_CLIENT_SECRET",
1527
+ label: "Spotify Client Secret",
1528
+ credential_type: "api_key",
1529
+ is_required: true,
1530
+ description: "Your Spotify app client secret"
1531
+ }
1532
+ ]
1533
+ })
1534
+ // Returns: { id: "toolkit-abc123", ... }
1535
+ \`\`\`
1536
+
1537
+ ### Step 2: Add HTTP Tool
1538
+ \`\`\`javascript
1539
+ create_toolkit_tool({
1540
+ toolkit_id: "toolkit-abc123",
1541
+ name: "search-tracks",
1542
+ title: "Search Tracks",
1543
+ description: "Search for tracks on Spotify",
1544
+ tool_type: "http",
1545
+ endpoint_config: {
1546
+ method: "GET",
1547
+ url: "https://api.spotify.com/v1/search"
1548
+ },
1549
+ input_schema: {
1550
+ type: "object",
1551
+ properties: {
1552
+ query: {
1553
+ type: "string",
1554
+ description: "Search query"
1555
+ },
1556
+ type: {
1557
+ type: "string",
1558
+ enum: ["track", "album", "artist"],
1559
+ description: "Type of content to search"
1560
+ },
1561
+ limit: {
1562
+ type: "number",
1563
+ description: "Number of results (1-50)"
1564
+ }
1565
+ },
1566
+ required: ["query", "type"]
1567
+ },
1568
+ credential_keys: ["SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET"]
1569
+ })
1570
+ \`\`\`
1571
+
1572
+ ### Step 3: Add Workflow Tool
1573
+ \`\`\`javascript
1574
+ create_toolkit_tool({
1575
+ toolkit_id: "toolkit-abc123",
1576
+ name: "analyze-playlist",
1577
+ title: "Analyze Playlist",
1578
+ description: "Analyze a Spotify playlist with AI",
1579
+ tool_type: "workflow",
1580
+ workflow_hash: "workflow-xyz",
1581
+ input_schema: {
1582
+ type: "object",
1583
+ properties: {
1584
+ playlist_id: {
1585
+ type: "string",
1586
+ description: "Spotify playlist ID"
1587
+ }
1588
+ },
1589
+ required: ["playlist_id"]
1590
+ }
1591
+ })
1592
+ \`\`\`
1593
+
1594
+ ## OAuth Configuration
1595
+
1596
+ For APIs requiring OAuth 2.0:
1597
+
1598
+ \`\`\`javascript
1599
+ create_agent_toolkit({
1600
+ name: "schwab-trading",
1601
+ title: "Schwab Trading API",
1602
+ description: "Access Schwab brokerage data",
1603
+ credential_requirements: [
1604
+ {
1605
+ key_name: "SCHWAB_APP_KEY",
1606
+ label: "Schwab App Key (Client ID)",
1607
+ credential_type: "api_key",
1608
+ is_required: true,
1609
+ description: "Your Schwab Developer App Key"
1610
+ },
1611
+ {
1612
+ key_name: "SCHWAB_APP_SECRET",
1613
+ label: "Schwab App Secret (Client Secret)",
1614
+ credential_type: "api_key",
1615
+ is_required: true,
1616
+ description: "Your Schwab Developer App Secret"
1617
+ },
1618
+ {
1619
+ key_name: "SCHWAB_ACCESS_TOKEN",
1620
+ label: "Schwab Access Token",
1621
+ credential_type: "oauth",
1622
+ is_required: true,
1623
+ description: "OAuth access token (auto-refreshed via Reconnect)",
1624
+ oauth_config: {
1625
+ authorization_url: "https://api.schwabapi.com/v1/oauth/authorize",
1626
+ token_endpoint: "https://api.schwabapi.com/v1/oauth/token",
1627
+ scopes: ["api"],
1628
+ client_id_credential_key: "SCHWAB_APP_KEY",
1629
+ client_secret_credential_key: "SCHWAB_APP_SECRET",
1630
+ pkce_enabled: true,
1631
+ auth_error_codes: [401, 403],
1632
+ auth_error_patterns: ["invalid_token", "expired_token"]
1633
+ }
1634
+ }
1635
+ ]
1636
+ })
1637
+ \`\`\`
1638
+
1639
+ **OAuth Config Fields:**
1640
+ - **authorization_url:** OAuth authorization endpoint
1641
+ - **token_endpoint:** Token exchange endpoint
1642
+ - **scopes:** Array of OAuth scopes
1643
+ - **client_id_credential_key:** Key name of credential with client ID
1644
+ - **client_secret_credential_key:** Key name of credential with client secret
1645
+ - **pkce_enabled:** Enable PKCE (recommended)
1646
+ - **auth_error_codes:** HTTP codes indicating auth failure
1647
+ - **auth_error_patterns:** Error message patterns for auth failure
1648
+
1649
+ ## Installing & Using Toolkits
1650
+
1651
+ ### Install Toolkit
1652
+ \`\`\`javascript
1653
+ install_toolkit({ toolkit_id: "toolkit-abc123" })
1654
+ // Returns: { installation_id: "install-xyz", ... }
1655
+ \`\`\`
1656
+
1657
+ ### Configure Credentials
1658
+ \`\`\`javascript
1659
+ update_toolkit_installation({
1660
+ installation_id: "install-xyz",
1661
+ credential_mapping: {
1662
+ "SPOTIFY_CLIENT_ID": "my-spotify-client-id",
1663
+ "SPOTIFY_CLIENT_SECRET": "my-spotify-secret"
1664
+ }
1665
+ })
1666
+ \`\`\`
1667
+
1668
+ ### Invoke Tool
1669
+ \`\`\`javascript
1670
+ invoke_toolkit_tool({
1671
+ installation_id: "install-xyz",
1672
+ tool_name: "search-tracks",
1673
+ inputs: {
1674
+ query: "Miles Davis",
1675
+ type: "track",
1676
+ limit: 10
1677
+ }
1678
+ })
1679
+ \`\`\`
1680
+
1681
+ ### Dynamic Credentials (OAuth)
1682
+ \`\`\`javascript
1683
+ // Pass fresh tokens from a token refresh call
1684
+ invoke_toolkit_tool({
1685
+ installation_id: "install-xyz",
1686
+ tool_name: "get-account",
1687
+ credential_overrides: {
1688
+ "SCHWAB_ACCESS_TOKEN": freshTokenValue
1689
+ }
1690
+ })
1691
+ \`\`\`
1692
+
1693
+ ## Managing Toolkits
1694
+
1695
+ \`\`\`javascript
1696
+ // List your toolkits
1697
+ mcp__flowdot__list_agent_toolkits({ search: "api" })
1698
+
1699
+ // Search public toolkits
1700
+ mcp__flowdot__search_agent_toolkits({
1701
+ query: "spotify",
1702
+ verified_only: true
1703
+ })
1704
+
1705
+ // Get toolkit details
1706
+ mcp__flowdot__get_agent_toolkit({ toolkit_id: "toolkit-abc123" })
1707
+
1708
+ // List tools in toolkit
1709
+ mcp__flowdot__list_toolkit_tools({ toolkit_id: "toolkit-abc123" })
1710
+
1711
+ // Update toolkit
1712
+ mcp__flowdot__update_agent_toolkit({
1713
+ toolkit_id: "toolkit-abc123",
1714
+ description: "Updated description"
1715
+ })
1716
+
1717
+ // Delete toolkit
1718
+ mcp__flowdot__delete_agent_toolkit({ toolkit_id: "toolkit-abc123" })
1719
+ \`\`\`
1720
+
1721
+ ## Managing Tools
1722
+
1723
+ \`\`\`javascript
1724
+ // Get tool details
1725
+ mcp__flowdot__get_toolkit_tool({
1726
+ toolkit_id: "toolkit-abc123",
1727
+ tool_id: "tool-xyz"
1728
+ })
1729
+
1730
+ // Update tool
1731
+ mcp__flowdot__update_toolkit_tool({
1732
+ toolkit_id: "toolkit-abc123",
1733
+ tool_id: "tool-xyz",
1734
+ description: "Updated description",
1735
+ input_schema: { /* new schema */ }
1736
+ })
1737
+
1738
+ // Delete tool
1739
+ mcp__flowdot__delete_toolkit_tool({
1740
+ toolkit_id: "toolkit-abc123",
1741
+ tool_id: "tool-xyz"
1742
+ })
1743
+ \`\`\`
1744
+
1745
+ ## Managing Installations
1746
+
1747
+ \`\`\`javascript
1748
+ // List installed toolkits
1749
+ mcp__flowdot__list_installed_toolkits()
1750
+
1751
+ // Check credentials
1752
+ mcp__flowdot__check_toolkit_credentials({
1753
+ installation_id: "install-xyz"
1754
+ })
1755
+
1756
+ // Enable/disable installation
1757
+ mcp__flowdot__toggle_toolkit_active({
1758
+ installation_id: "install-xyz",
1759
+ is_active: false
1760
+ })
1761
+
1762
+ // Uninstall
1763
+ mcp__flowdot__uninstall_toolkit({
1764
+ installation_id: "install-xyz"
1765
+ })
1766
+ \`\`\`
1767
+
1768
+ ## Sharing Toolkits
1769
+
1770
+ \`\`\`javascript
1771
+ // Make public
1772
+ mcp__flowdot__toggle_toolkit_visibility({
1773
+ toolkit_id: "toolkit-abc123",
1774
+ visibility: "public"
1775
+ })
1776
+
1777
+ // Vote on toolkit
1778
+ mcp__flowdot__vote_toolkit({
1779
+ toolkit_id: "toolkit-abc123",
1780
+ vote: "up"
1781
+ })
1782
+
1783
+ // Favorite toolkit
1784
+ mcp__flowdot__favorite_toolkit({
1785
+ toolkit_id: "toolkit-abc123",
1786
+ favorite: true
1787
+ })
1788
+
1789
+ // Add comment
1790
+ mcp__flowdot__add_toolkit_comment({
1791
+ toolkit_id: "toolkit-abc123",
1792
+ content: "Great toolkit for music APIs!"
1793
+ })
1794
+ \`\`\`
1795
+
1796
+ ## Best Practices
1797
+
1798
+ 1. **Group related tools:** Keep toolkits focused on one domain
1799
+ 2. **Clear credential names:** Use descriptive key names
1800
+ 3. **OAuth when possible:** Enables auto-refresh
1801
+ 4. **Document thoroughly:** Add descriptions to everything
1802
+ 5. **Test credentials:** Verify all required credentials work
1803
+ 6. **Version carefully:** Breaking changes need new toolkit
1804
+ 7. **Security first:** Never expose credentials in tool configs
1805
+
1806
+ ## Common Patterns
1807
+
1808
+ ### Pattern 1: RESTful API Toolkit
1809
+ - Multiple HTTP tools for different endpoints
1810
+ - Shared API key credentials
1811
+ - Input schemas matching API parameters
1812
+
1813
+ ### Pattern 2: AI-Powered Toolkit
1814
+ - Workflow tools calling LLM workflows
1815
+ - Pre-configured prompts and processing
1816
+ - Abstracted complexity
1817
+
1818
+ ### Pattern 3: Hybrid Toolkit
1819
+ - HTTP tools for data fetching
1820
+ - Workflow tools for processing
1821
+ - Combined capabilities
1822
+
1823
+ ## Troubleshooting
1824
+
1825
+ ### Credentials Not Working
1826
+ - Check \`check_toolkit_credentials\`
1827
+ - Verify credential mapping is correct
1828
+ - Ensure API keys are valid
1829
+
1830
+ ### OAuth Tokens Expiring
1831
+ - Verify oauth_config is complete
1832
+ - Check auth_error_codes and patterns
1833
+ - Ensure client credentials are correct
1834
+
1835
+ ### Tool Invocation Failing
1836
+ - Verify input schema requirements
1837
+ - Check endpoint configuration
1838
+ - Test with credential_overrides
1839
+
1840
+ ## Related Resources
1841
+
1842
+ - **Workflows:** \`learn://workflows\` - Build workflow-based tools
1843
+ - **Custom Nodes:** \`learn://custom-nodes\` - Extend processing capabilities
1844
+ `,
1845
+ },
1846
+ 'learn://knowledge-base': {
1847
+ name: 'Knowledge Base Complete Guide',
1848
+ description: 'Complete guide to using the FlowDot knowledge base with RAG',
1849
+ mimeType: 'text/markdown',
1850
+ content: `# FlowDot Knowledge Base - Complete Guide
1851
+
1852
+ ## What Is the Knowledge Base?
1853
+
1854
+ The Knowledge Base is a **document storage and RAG (Retrieval-Augmented Generation) system** that lets you:
1855
+ - Upload documents (PDF, DOCX, TXT, Markdown, CSV, JSON)
1856
+ - Organize with categories and teams
1857
+ - Search with semantic + keyword search
1858
+ - Use in workflows and agents for context
1859
+
1860
+ ## Key Concepts
1861
+
1862
+ ### Documents
1863
+ Files uploaded to your knowledge base:
1864
+ - **Max size:** 50MB per file
1865
+ - **Formats:** PDF, DOCX, TXT, MD, CSV, JSON
1866
+ - **Processing:** Auto-chunked and embedded
1867
+ - **Status:** pending → processing → ready or failed
1868
+
1869
+ ### Categories
1870
+ Organization for documents:
1871
+ - Create categories to group related docs
1872
+ - Color-coded for visual organization
1873
+ - Can be personal or team-based
1874
+
1875
+ ### Teams
1876
+ Shared knowledge bases:
1877
+ - Share documents with team members
1878
+ - Team-specific categories
1879
+ - Access control per team
1880
+
1881
+ ### Chunking
1882
+ Documents are split into chunks:
1883
+ - Each chunk is embedded for semantic search
1884
+ - Optimized chunk size for context
1885
+ - Preserves document structure
1886
+
1887
+ ### RAG Search
1888
+ Retrieval-Augmented Generation:
1889
+ - Semantic search (meaning-based)
1890
+ - Keyword search (exact matches)
1891
+ - Returns ranked chunks with sources
1892
+ - Use results to ground AI responses
1893
+
1894
+ ## Uploading Documents
1895
+
1896
+ ### Upload Text Content
1897
+ \`\`\`javascript
1898
+ upload_text_document({
1899
+ title: "Project Overview",
1900
+ content: "This is the content of my document...",
1901
+ mime_type: "text/markdown",
1902
+ category_id: 123 // optional
1903
+ })
1904
+ // Returns: { id: 456, status: "processing", ... }
1905
+ \`\`\`
1906
+
1907
+ ### Upload from URL
1908
+ \`\`\`javascript
1909
+ upload_document_from_url({
1910
+ url: "https://example.com/whitepaper.pdf",
1911
+ title: "Company Whitepaper",
1912
+ category_id: 123 // optional
1913
+ })
1914
+ \`\`\`
1915
+
1916
+ ### Check Processing Status
1917
+ \`\`\`javascript
1918
+ get_knowledge_document({ document_id: 456 })
1919
+ // Returns: { status: "ready", chunk_count: 42, ... }
1920
+ \`\`\`
1921
+
1922
+ ## Organizing Documents
1923
+
1924
+ ### Create Categories
1925
+ \`\`\`javascript
1926
+ create_knowledge_category({
1927
+ name: "Product Documentation",
1928
+ description: "Official product docs and guides",
1929
+ color: "#3B82F6"
1930
+ })
1931
+ // Returns: { id: 123, ... }
1932
+ \`\`\`
1933
+
1934
+ ### List Categories
1935
+ \`\`\`javascript
1936
+ list_knowledge_categories()
1937
+ list_knowledge_categories({ team_id: 5 }) // Team-specific
1938
+ list_knowledge_categories({ personal: true }) // Personal only
1939
+ \`\`\`
1940
+
1941
+ ### Move Document to Category
1942
+ \`\`\`javascript
1943
+ move_document_to_category({
1944
+ document_id: 456,
1945
+ category_id: 123
1946
+ })
1947
+
1948
+ // Or remove from category
1949
+ move_document_to_category({
1950
+ document_id: 456,
1951
+ category_id: null
1952
+ })
1953
+ \`\`\`
1954
+
1955
+ ### Update Category
1956
+ \`\`\`javascript
1957
+ update_knowledge_category({
1958
+ category_id: 123,
1959
+ name: "Updated Name",
1960
+ description: "New description",
1961
+ color: "#EF4444"
1962
+ })
1963
+ \`\`\`
1964
+
1965
+ ## Searching the Knowledge Base
1966
+
1967
+ ### Basic Search
1968
+ \`\`\`javascript
1969
+ query_knowledge_base({
1970
+ query: "How do I configure OAuth authentication?",
1971
+ top_k: 5
1972
+ })
1973
+ // Returns: Array of matching chunks with sources
1974
+ \`\`\`
1975
+
1976
+ ### Search Specific Category
1977
+ \`\`\`javascript
1978
+ query_knowledge_base({
1979
+ query: "deployment procedures",
1980
+ category_id: 123,
1981
+ top_k: 10
1982
+ })
1983
+ \`\`\`
1984
+
1985
+ ### Search Team Documents
1986
+ \`\`\`javascript
1987
+ query_knowledge_base({
1988
+ query: "security policies",
1989
+ team_id: 5,
1990
+ include_personal: false,
1991
+ top_k: 5
1992
+ })
1993
+ \`\`\`
1994
+
1995
+ ### Search Response Structure
1996
+ \`\`\`javascript
1997
+ [
1998
+ {
1999
+ chunk_text: "...relevant text...",
2000
+ document_title: "Security Guidelines",
2001
+ document_id: 456,
2002
+ score: 0.89,
2003
+ metadata: {
2004
+ page: 3,
2005
+ section: "OAuth Configuration"
2006
+ }
2007
+ },
2008
+ // ... more results
2009
+ ]
2010
+ \`\`\`
2011
+
2012
+ ## Managing Documents
2013
+
2014
+ ### List Documents
2015
+ \`\`\`javascript
2016
+ list_knowledge_documents()
2017
+ list_knowledge_documents({ category_id: 123 })
2018
+ list_knowledge_documents({ status: "ready" })
2019
+ list_knowledge_documents({ team_id: 5 })
2020
+ \`\`\`
2021
+
2022
+ ### Get Document Details
2023
+ \`\`\`javascript
2024
+ get_knowledge_document({ document_id: 456 })
2025
+ // Returns: Full metadata, chunks, processing status
2026
+ \`\`\`
2027
+
2028
+ ### Reprocess Failed Document
2029
+ \`\`\`javascript
2030
+ reprocess_document({ document_id: 456 })
2031
+ \`\`\`
2032
+
2033
+ ### Delete Document
2034
+ \`\`\`javascript
2035
+ delete_knowledge_document({ document_id: 456 })
2036
+ \`\`\`
2037
+
2038
+ ## Team Features
2039
+
2040
+ ### List Your Teams
2041
+ \`\`\`javascript
2042
+ list_user_teams()
2043
+ // Returns: Teams you belong to with roles
2044
+ \`\`\`
2045
+
2046
+ ### Upload to Team
2047
+ \`\`\`javascript
2048
+ upload_text_document({
2049
+ title: "Team Playbook",
2050
+ content: "...",
2051
+ team_id: 5
2052
+ })
2053
+ \`\`\`
2054
+
2055
+ ### Transfer Document Ownership
2056
+ \`\`\`javascript
2057
+ // Personal → Team
2058
+ transfer_document_ownership({
2059
+ document_id: 456,
2060
+ team_id: 5,
2061
+ category_id: 789 // optional team category
2062
+ })
2063
+
2064
+ // Team → Personal
2065
+ transfer_document_ownership({
2066
+ document_id: 456,
2067
+ team_id: null // or omit
2068
+ })
2069
+ \`\`\`
2070
+
2071
+ ## Storage Management
2072
+
2073
+ ### Check Storage Usage
2074
+ \`\`\`javascript
2075
+ get_knowledge_storage()
2076
+ // Returns: {
2077
+ // used_bytes: 12345678,
2078
+ // limit_bytes: 1073741824,
2079
+ // document_count: 42,
2080
+ // percentage_used: 1.15
2081
+ // }
2082
+ \`\`\`
2083
+
2084
+ ## Using in Workflows & Agents
2085
+
2086
+ ### In Agent Steps
2087
+ \`\`\`javascript
2088
+ add_recipe_step({
2089
+ hash: "recipe-xyz",
2090
+ name: "research",
2091
+ type: "agent",
2092
+ config: {
2093
+ user_prompt: \`
2094
+ Search knowledge base for: {{inputs.request}}
2095
+
2096
+ Use query_knowledge_base tool to find relevant information.
2097
+ Summarize the findings.
2098
+ \`,
2099
+ tools: ["query_knowledge_base", "search"],
2100
+ output_store: "research_result"
2101
+ }
2102
+ })
2103
+ \`\`\`
2104
+
2105
+ ### In Workflows
2106
+ Use a custom node or LLM node that:
2107
+ 1. Calls \`query_knowledge_base\`
2108
+ 2. Retrieves relevant chunks
2109
+ 3. Uses chunks as context for generation
2110
+
2111
+ ## Best Practices
2112
+
2113
+ 1. **Categorize from the start:** Easier to find later
2114
+ 2. **Descriptive titles:** Help with search ranking
2115
+ 3. **Check status:** Wait for "ready" before using
2116
+ 4. **Team vs personal:** Decide visibility upfront
2117
+ 5. **Regular cleanup:** Delete outdated docs
2118
+ 6. **Optimize queries:** Specific questions work best
2119
+ 7. **Use top_k wisely:** 5-10 results usually enough
2120
+
2121
+ ## Common Patterns
2122
+
2123
+ ### Pattern 1: Documentation Assistant
2124
+ 1. Upload product documentation
2125
+ 2. Create "docs" category
2126
+ 3. Agent searches knowledge base
2127
+ 4. Returns specific answers with sources
2128
+
2129
+ ### Pattern 2: Team Knowledge
2130
+ 1. Upload team playbooks, procedures
2131
+ 2. Share via team knowledge base
2132
+ 3. Team members query for guidance
2133
+ 4. Consistent information across team
2134
+
2135
+ ### Pattern 3: Research Assistant
2136
+ 1. Upload research papers, articles
2137
+ 2. Categorize by topic
2138
+ 3. Agent finds relevant passages
2139
+ 4. Synthesizes insights from multiple sources
2140
+
2141
+ ## Troubleshooting
2142
+
2143
+ ### Document Stuck in "processing"
2144
+ - Wait a few minutes (large docs take time)
2145
+ - Check \`get_knowledge_document\` for status
2146
+ - If stuck >10min, try \`reprocess_document\`
2147
+
2148
+ ### Search Returns No Results
2149
+ - Verify document status is "ready"
2150
+ - Try more general query terms
2151
+ - Check document is in expected category
2152
+ - Ensure team/personal filters are correct
2153
+
2154
+ ### Upload Fails
2155
+ - Check file size (<50MB)
2156
+ - Verify file format is supported
2157
+ - Try simpler filename (no special chars)
2158
+
2159
+ ## Related Resources
2160
+
2161
+ - **Recipes:** \`learn://recipes\` - Use knowledge base in agent workflows
2162
+ - **Custom Nodes:** \`learn://custom-nodes\` - Build RAG-powered nodes
2163
+ - **Workflows:** \`learn://workflows\` - Integrate knowledge base lookups
2164
+ `,
2165
+ },
2166
+ };
2167
+ /**
2168
+ * Register learning resources with the MCP server.
2169
+ */
2170
+ export function registerResources(server) {
2171
+ // Handle list resources request
2172
+ server.setRequestHandler(ListResourcesRequestSchema, async () => {
2173
+ return {
2174
+ resources: Object.entries(LEARN_RESOURCES).map(([uri, resource]) => ({
2175
+ uri,
2176
+ name: resource.name,
2177
+ description: resource.description,
2178
+ mimeType: resource.mimeType,
2179
+ })),
2180
+ };
2181
+ });
2182
+ // Handle read resource request
2183
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
2184
+ const uri = request.params.uri;
2185
+ const resource = LEARN_RESOURCES[uri];
2186
+ if (!resource) {
2187
+ throw new Error(`Resource not found: ${uri}`);
2188
+ }
2189
+ return {
2190
+ contents: [
2191
+ {
2192
+ uri,
2193
+ mimeType: resource.mimeType,
2194
+ text: resource.content,
2195
+ },
2196
+ ],
2197
+ };
2198
+ });
2199
+ console.error('Learning resources registered. Available resources:');
2200
+ Object.keys(LEARN_RESOURCES).forEach((uri) => {
2201
+ console.error(` • ${uri}`);
2202
+ });
2203
+ }
2204
+ //# sourceMappingURL=index.js.map