@flowdot.ai/mcp-server 1.0.0 → 1.0.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/LICENSE +21 -0
- package/README.md +162 -162
- package/bin/flowdot-mcp.js +15 -15
- package/dist/api-client.d.ts +2 -344
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +2 -784
- package/dist/api-client.js.map +1 -1
- package/dist/resources/index.d.ts +12 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +2204 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +19 -10
- package/dist/server.js.map +1 -1
- package/dist/tools/add-recipe-step.d.ts +23 -0
- package/dist/tools/add-recipe-step.d.ts.map +1 -0
- package/dist/tools/add-recipe-step.js +196 -0
- package/dist/tools/add-recipe-step.js.map +1 -0
- package/dist/tools/add-recipe-store.d.ts +19 -0
- package/dist/tools/add-recipe-store.d.ts.map +1 -0
- package/dist/tools/add-recipe-store.js +116 -0
- package/dist/tools/add-recipe-store.js.map +1 -0
- package/dist/tools/agent-toolkits.d.ts +57 -0
- package/dist/tools/agent-toolkits.d.ts.map +1 -0
- package/dist/tools/agent-toolkits.js +1712 -0
- package/dist/tools/agent-toolkits.js.map +1 -0
- package/dist/tools/append-app-code.d.ts +12 -0
- package/dist/tools/append-app-code.d.ts.map +1 -0
- package/dist/tools/append-app-code.js +83 -0
- package/dist/tools/append-app-code.js.map +1 -0
- package/dist/tools/browse-recipes.d.ts +16 -0
- package/dist/tools/browse-recipes.d.ts.map +1 -0
- package/dist/tools/browse-recipes.js +102 -0
- package/dist/tools/browse-recipes.js.map +1 -0
- package/dist/tools/clone-app.js +20 -20
- package/dist/tools/create-app-file.d.ts +11 -0
- package/dist/tools/create-app-file.d.ts.map +1 -0
- package/dist/tools/create-app-file.js +83 -0
- package/dist/tools/create-app-file.js.map +1 -0
- package/dist/tools/create-app.d.ts.map +1 -1
- package/dist/tools/create-app.js +47 -29
- package/dist/tools/create-app.js.map +1 -1
- package/dist/tools/create-knowledge-category.d.ts +16 -0
- package/dist/tools/create-knowledge-category.d.ts.map +1 -0
- package/dist/tools/create-knowledge-category.js +71 -0
- package/dist/tools/create-knowledge-category.js.map +1 -0
- package/dist/tools/create-recipe.d.ts +16 -0
- package/dist/tools/create-recipe.d.ts.map +1 -0
- package/dist/tools/create-recipe.js +92 -0
- package/dist/tools/create-recipe.js.map +1 -0
- package/dist/tools/delete-app-file.d.ts +11 -0
- package/dist/tools/delete-app-file.d.ts.map +1 -0
- package/dist/tools/delete-app-file.js +52 -0
- package/dist/tools/delete-app-file.js.map +1 -0
- package/dist/tools/delete-app.js +3 -3
- package/dist/tools/delete-knowledge-category.d.ts +12 -0
- package/dist/tools/delete-knowledge-category.d.ts.map +1 -0
- package/dist/tools/delete-knowledge-category.js +40 -0
- package/dist/tools/delete-knowledge-category.js.map +1 -0
- package/dist/tools/delete-knowledge-document.d.ts +12 -0
- package/dist/tools/delete-knowledge-document.d.ts.map +1 -0
- package/dist/tools/delete-knowledge-document.js +40 -0
- package/dist/tools/delete-knowledge-document.js.map +1 -0
- package/dist/tools/delete-recipe-step.d.ts +14 -0
- package/dist/tools/delete-recipe-step.d.ts.map +1 -0
- package/dist/tools/delete-recipe-step.js +65 -0
- package/dist/tools/delete-recipe-step.js.map +1 -0
- package/dist/tools/delete-recipe-store.d.ts +14 -0
- package/dist/tools/delete-recipe-store.d.ts.map +1 -0
- package/dist/tools/delete-recipe-store.js +65 -0
- package/dist/tools/delete-recipe-store.js.map +1 -0
- package/dist/tools/delete-recipe.d.ts +13 -0
- package/dist/tools/delete-recipe.d.ts.map +1 -0
- package/dist/tools/delete-recipe.js +59 -0
- package/dist/tools/delete-recipe.js.map +1 -0
- package/dist/tools/edit-app-code.d.ts +12 -0
- package/dist/tools/edit-app-code.d.ts.map +1 -0
- package/dist/tools/edit-app-code.js +110 -0
- package/dist/tools/edit-app-code.js.map +1 -0
- package/dist/tools/favorite-recipe.d.ts +13 -0
- package/dist/tools/favorite-recipe.d.ts.map +1 -0
- package/dist/tools/favorite-recipe.js +53 -0
- package/dist/tools/favorite-recipe.js.map +1 -0
- package/dist/tools/fork-recipe.d.ts +13 -0
- package/dist/tools/fork-recipe.d.ts.map +1 -0
- package/dist/tools/fork-recipe.js +56 -0
- package/dist/tools/fork-recipe.js.map +1 -0
- package/dist/tools/get-app-file.d.ts +11 -0
- package/dist/tools/get-app-file.d.ts.map +1 -0
- package/dist/tools/get-app-file.js +76 -0
- package/dist/tools/get-app-file.js.map +1 -0
- package/dist/tools/get-app-template.d.ts.map +1 -1
- package/dist/tools/get-app-template.js +65 -21
- package/dist/tools/get-app-template.js.map +1 -1
- package/dist/tools/get-app.js +54 -54
- package/dist/tools/get-knowledge-document.d.ts +12 -0
- package/dist/tools/get-knowledge-document.d.ts.map +1 -0
- package/dist/tools/get-knowledge-document.js +71 -0
- package/dist/tools/get-knowledge-document.js.map +1 -0
- package/dist/tools/get-knowledge-storage.d.ts +10 -0
- package/dist/tools/get-knowledge-storage.d.ts.map +1 -0
- package/dist/tools/get-knowledge-storage.js +54 -0
- package/dist/tools/get-knowledge-storage.js.map +1 -0
- package/dist/tools/get-recipe-definition.d.ts +15 -0
- package/dist/tools/get-recipe-definition.d.ts.map +1 -0
- package/dist/tools/get-recipe-definition.js +70 -0
- package/dist/tools/get-recipe-definition.js.map +1 -0
- package/dist/tools/get-recipe.d.ts +12 -0
- package/dist/tools/get-recipe.d.ts.map +1 -0
- package/dist/tools/get-recipe.js +88 -0
- package/dist/tools/get-recipe.js.map +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +301 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/insert-app-code.d.ts +12 -0
- package/dist/tools/insert-app-code.d.ts.map +1 -0
- package/dist/tools/insert-app-code.js +97 -0
- package/dist/tools/insert-app-code.js.map +1 -0
- package/dist/tools/link-app-workflow.js +21 -21
- package/dist/tools/link-recipe.d.ts +14 -0
- package/dist/tools/link-recipe.d.ts.map +1 -0
- package/dist/tools/link-recipe.js +76 -0
- package/dist/tools/link-recipe.js.map +1 -0
- package/dist/tools/list-app-files.d.ts +11 -0
- package/dist/tools/list-app-files.d.ts.map +1 -0
- package/dist/tools/list-app-files.js +84 -0
- package/dist/tools/list-app-files.js.map +1 -0
- package/dist/tools/list-apps.js +8 -8
- package/dist/tools/list-knowledge-categories.d.ts +14 -0
- package/dist/tools/list-knowledge-categories.d.ts.map +1 -0
- package/dist/tools/list-knowledge-categories.js +107 -0
- package/dist/tools/list-knowledge-categories.js.map +1 -0
- package/dist/tools/list-knowledge-documents.d.ts +15 -0
- package/dist/tools/list-knowledge-documents.d.ts.map +1 -0
- package/dist/tools/list-knowledge-documents.js +129 -0
- package/dist/tools/list-knowledge-documents.js.map +1 -0
- package/dist/tools/list-recipe-steps.d.ts +12 -0
- package/dist/tools/list-recipe-steps.d.ts.map +1 -0
- package/dist/tools/list-recipe-steps.js +69 -0
- package/dist/tools/list-recipe-steps.js.map +1 -0
- package/dist/tools/list-recipe-stores.d.ts +12 -0
- package/dist/tools/list-recipe-stores.d.ts.map +1 -0
- package/dist/tools/list-recipe-stores.js +87 -0
- package/dist/tools/list-recipe-stores.js.map +1 -0
- package/dist/tools/list-recipes.d.ts +12 -0
- package/dist/tools/list-recipes.d.ts.map +1 -0
- package/dist/tools/list-recipes.js +92 -0
- package/dist/tools/list-recipes.js.map +1 -0
- package/dist/tools/list-user-teams.d.ts +11 -0
- package/dist/tools/list-user-teams.d.ts.map +1 -0
- package/dist/tools/list-user-teams.js +56 -0
- package/dist/tools/list-user-teams.js.map +1 -0
- package/dist/tools/move-document-to-category.d.ts +13 -0
- package/dist/tools/move-document-to-category.d.ts.map +1 -0
- package/dist/tools/move-document-to-category.js +42 -0
- package/dist/tools/move-document-to-category.js.map +1 -0
- package/dist/tools/prepend-app-code.d.ts +12 -0
- package/dist/tools/prepend-app-code.d.ts.map +1 -0
- package/dist/tools/prepend-app-code.js +79 -0
- package/dist/tools/prepend-app-code.js.map +1 -0
- package/dist/tools/publish-app.js +7 -7
- package/dist/tools/query-knowledge-base.d.ts +18 -0
- package/dist/tools/query-knowledge-base.d.ts.map +1 -0
- package/dist/tools/query-knowledge-base.js +144 -0
- package/dist/tools/query-knowledge-base.js.map +1 -0
- package/dist/tools/rename-app-file.d.ts +11 -0
- package/dist/tools/rename-app-file.d.ts.map +1 -0
- package/dist/tools/rename-app-file.js +64 -0
- package/dist/tools/rename-app-file.js.map +1 -0
- package/dist/tools/reprocess-document.d.ts +12 -0
- package/dist/tools/reprocess-document.d.ts.map +1 -0
- package/dist/tools/reprocess-document.js +40 -0
- package/dist/tools/reprocess-document.js.map +1 -0
- package/dist/tools/search-apps.js +13 -13
- package/dist/tools/search.d.ts +14 -0
- package/dist/tools/search.d.ts.map +1 -0
- package/dist/tools/search.js +86 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/set-app-entry-file.d.ts +11 -0
- package/dist/tools/set-app-entry-file.d.ts.map +1 -0
- package/dist/tools/set-app-entry-file.js +56 -0
- package/dist/tools/set-app-entry-file.js.map +1 -0
- package/dist/tools/transfer-document-ownership.d.ts +15 -0
- package/dist/tools/transfer-document-ownership.d.ts.map +1 -0
- package/dist/tools/transfer-document-ownership.js +66 -0
- package/dist/tools/transfer-document-ownership.js.map +1 -0
- package/dist/tools/unlink-app-workflow.js +2 -2
- package/dist/tools/unpublish-app.js +2 -2
- package/dist/tools/update-app-file.d.ts +11 -0
- package/dist/tools/update-app-file.d.ts.map +1 -0
- package/dist/tools/update-app-file.js +73 -0
- package/dist/tools/update-app-file.js.map +1 -0
- package/dist/tools/update-app.d.ts.map +1 -1
- package/dist/tools/update-app.js +8 -1
- package/dist/tools/update-app.js.map +1 -1
- package/dist/tools/update-knowledge-category.d.ts +15 -0
- package/dist/tools/update-knowledge-category.d.ts.map +1 -0
- package/dist/tools/update-knowledge-category.js +74 -0
- package/dist/tools/update-knowledge-category.js.map +1 -0
- package/dist/tools/update-recipe-step.d.ts +22 -0
- package/dist/tools/update-recipe-step.d.ts.map +1 -0
- package/dist/tools/update-recipe-step.js +97 -0
- package/dist/tools/update-recipe-step.js.map +1 -0
- package/dist/tools/update-recipe-store.d.ts +20 -0
- package/dist/tools/update-recipe-store.d.ts.map +1 -0
- package/dist/tools/update-recipe-store.js +98 -0
- package/dist/tools/update-recipe-store.js.map +1 -0
- package/dist/tools/update-recipe.d.ts +18 -0
- package/dist/tools/update-recipe.d.ts.map +1 -0
- package/dist/tools/update-recipe.js +98 -0
- package/dist/tools/update-recipe.js.map +1 -0
- package/dist/tools/upload-document-from-url.d.ts +16 -0
- package/dist/tools/upload-document-from-url.d.ts.map +1 -0
- package/dist/tools/upload-document-from-url.js +73 -0
- package/dist/tools/upload-document-from-url.js.map +1 -0
- package/dist/tools/upload-text-document.d.ts +17 -0
- package/dist/tools/upload-text-document.d.ts.map +1 -0
- package/dist/tools/upload-text-document.js +77 -0
- package/dist/tools/upload-text-document.js.map +1 -0
- package/dist/tools/vote-recipe.d.ts +13 -0
- package/dist/tools/vote-recipe.d.ts.map +1 -0
- package/dist/tools/vote-recipe.js +54 -0
- package/dist/tools/vote-recipe.js.map +1 -0
- package/dist/types.d.ts +3 -666
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -1
- package/dist/types.js.map +1 -1
- package/dist/utils/script-validator.d.ts.map +1 -1
- package/dist/utils/script-validator.js +5 -1
- package/dist/utils/script-validator.js.map +1 -1
- package/package.json +62 -54
|
@@ -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
|