@flowdot.ai/mcp-server 1.0.3 → 1.0.5
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 +45 -21
- package/README.md +439 -162
- package/bin/flowdot-mcp.js +15 -15
- package/dist/api-client.d.ts +0 -6
- package/dist/api-client.js +0 -6
- package/dist/index.d.ts +0 -23
- package/dist/index.js +0 -24
- package/dist/resources/index.d.ts +1 -11
- package/dist/resources/index.js +2882 -2127
- package/dist/server.d.ts +0 -12
- package/dist/server.js +23 -21
- package/dist/tools/add-connection.d.ts +2 -8
- package/dist/tools/add-connection.js +0 -6
- package/dist/tools/add-custom-node-comment.d.ts +2 -9
- package/dist/tools/add-custom-node-comment.js +0 -7
- package/dist/tools/add-node.d.ts +2 -8
- package/dist/tools/add-node.js +0 -6
- package/dist/tools/add-recipe-step.d.ts +3 -9
- package/dist/tools/add-recipe-step.js +95 -101
- package/dist/tools/add-recipe-store.d.ts +2 -8
- package/dist/tools/add-recipe-store.js +30 -36
- package/dist/tools/add-shared-result-comment.d.ts +2 -8
- package/dist/tools/add-shared-result-comment.js +0 -7
- package/dist/tools/add-workflow-comment.d.ts +2 -8
- package/dist/tools/add-workflow-comment.js +0 -7
- package/dist/tools/agent-chat.d.ts +2 -8
- package/dist/tools/agent-chat.js +0 -10
- package/dist/tools/agent-toolkits.d.ts +3 -10
- package/dist/tools/agent-toolkits.js +294 -336
- package/dist/tools/append-app-code.d.ts +2 -10
- package/dist/tools/append-app-code.js +27 -35
- package/dist/tools/browse-recipes.d.ts +2 -8
- package/dist/tools/browse-recipes.js +0 -7
- package/dist/tools/cancel-execution.d.ts +2 -8
- package/dist/tools/cancel-execution.js +0 -7
- package/dist/tools/clone-app.d.ts +2 -9
- package/dist/tools/clone-app.js +20 -27
- package/dist/tools/copy-custom-node.d.ts +2 -9
- package/dist/tools/copy-custom-node.js +0 -7
- package/dist/tools/create-app-file.d.ts +2 -9
- package/dist/tools/create-app-file.js +18 -25
- package/dist/tools/create-app.d.ts +2 -9
- package/dist/tools/create-app.js +123 -130
- package/dist/tools/create-custom-node.d.ts +3 -10
- package/dist/tools/create-custom-node.js +66 -75
- package/dist/tools/create-input-preset.d.ts +2 -8
- package/dist/tools/create-input-preset.js +0 -7
- package/dist/tools/create-knowledge-category.d.ts +2 -9
- package/dist/tools/create-knowledge-category.js +0 -7
- package/dist/tools/create-recipe.d.ts +2 -8
- package/dist/tools/create-recipe.js +21 -27
- package/dist/tools/create-shared-result.d.ts +2 -8
- package/dist/tools/create-shared-result.js +0 -6
- package/dist/tools/create-workflow.d.ts +2 -8
- package/dist/tools/create-workflow.js +0 -6
- package/dist/tools/delete-app-file.d.ts +2 -9
- package/dist/tools/delete-app-file.js +9 -16
- package/dist/tools/delete-app.d.ts +2 -9
- package/dist/tools/delete-app.js +3 -10
- package/dist/tools/delete-connection.d.ts +2 -8
- package/dist/tools/delete-connection.js +0 -7
- package/dist/tools/delete-custom-node.d.ts +2 -9
- package/dist/tools/delete-custom-node.js +0 -7
- package/dist/tools/delete-input-preset.d.ts +2 -8
- package/dist/tools/delete-input-preset.js +0 -6
- package/dist/tools/delete-knowledge-category.d.ts +2 -8
- package/dist/tools/delete-knowledge-category.js +0 -6
- package/dist/tools/delete-knowledge-document.d.ts +2 -8
- package/dist/tools/delete-knowledge-document.js +0 -6
- package/dist/tools/delete-node.d.ts +2 -8
- package/dist/tools/delete-node.js +0 -7
- package/dist/tools/delete-recipe-step.d.ts +2 -8
- package/dist/tools/delete-recipe-step.js +2 -8
- package/dist/tools/delete-recipe-store.d.ts +2 -8
- package/dist/tools/delete-recipe-store.js +2 -8
- package/dist/tools/delete-recipe.d.ts +2 -8
- package/dist/tools/delete-recipe.js +0 -6
- package/dist/tools/delete-workflow.d.ts +2 -8
- package/dist/tools/delete-workflow.js +0 -7
- package/dist/tools/duplicate-workflow.d.ts +2 -8
- package/dist/tools/duplicate-workflow.js +0 -6
- package/dist/tools/edit-app-code.d.ts +2 -10
- package/dist/tools/edit-app-code.js +34 -43
- package/dist/tools/execute-workflow.d.ts +2 -8
- package/dist/tools/execute-workflow.js +0 -11
- package/dist/tools/favorite-custom-node.d.ts +2 -9
- package/dist/tools/favorite-custom-node.js +0 -8
- package/dist/tools/favorite-recipe.d.ts +2 -8
- package/dist/tools/favorite-recipe.js +2 -8
- package/dist/tools/favorite-workflow.d.ts +2 -8
- package/dist/tools/favorite-workflow.js +0 -7
- package/dist/tools/fork-recipe.d.ts +2 -8
- package/dist/tools/fork-recipe.js +7 -13
- package/dist/tools/get-app-file.d.ts +2 -9
- package/dist/tools/get-app-file.js +20 -28
- package/dist/tools/get-app-template.d.ts +1 -8
- package/dist/tools/get-app-template.js +827 -834
- package/dist/tools/get-app.d.ts +2 -9
- package/dist/tools/get-app.js +54 -66
- package/dist/tools/get-custom-node-comments.d.ts +2 -9
- package/dist/tools/get-custom-node-comments.js +0 -7
- package/dist/tools/get-custom-node-template.d.ts +1 -8
- package/dist/tools/get-custom-node-template.js +60 -73
- package/dist/tools/get-custom-node.d.ts +2 -9
- package/dist/tools/get-custom-node.js +0 -12
- package/dist/tools/get-execution-history.d.ts +2 -8
- package/dist/tools/get-execution-history.js +0 -7
- package/dist/tools/get-execution.d.ts +2 -8
- package/dist/tools/get-execution.js +0 -7
- package/dist/tools/get-input-preset.d.ts +2 -8
- package/dist/tools/get-input-preset.js +0 -7
- package/dist/tools/get-knowledge-document-content.d.ts +6 -0
- package/dist/tools/get-knowledge-document-content.js +42 -0
- package/dist/tools/get-knowledge-document.d.ts +2 -8
- package/dist/tools/get-knowledge-document.js +0 -6
- package/dist/tools/get-knowledge-storage.d.ts +2 -8
- package/dist/tools/get-knowledge-storage.js +0 -7
- package/dist/tools/get-node-connections.d.ts +2 -8
- package/dist/tools/get-node-connections.js +0 -7
- package/dist/tools/get-node-schema.d.ts +2 -8
- package/dist/tools/get-node-schema.js +0 -13
- package/dist/tools/get-public-workflows.d.ts +2 -8
- package/dist/tools/get-public-workflows.js +0 -7
- package/dist/tools/get-recipe-definition.d.ts +2 -10
- package/dist/tools/get-recipe-definition.js +1 -10
- package/dist/tools/get-recipe.d.ts +2 -8
- package/dist/tools/get-recipe.js +10 -19
- package/dist/tools/get-shared-result-comments.d.ts +2 -8
- package/dist/tools/get-shared-result-comments.js +0 -6
- package/dist/tools/get-shared-result.d.ts +2 -8
- package/dist/tools/get-shared-result.js +0 -8
- package/dist/tools/get-workflow-comments.d.ts +2 -8
- package/dist/tools/get-workflow-comments.js +0 -7
- package/dist/tools/get-workflow-details.d.ts +2 -8
- package/dist/tools/get-workflow-details.js +0 -8
- package/dist/tools/get-workflow-graph.d.ts +2 -8
- package/dist/tools/get-workflow-graph.js +1 -10
- package/dist/tools/get-workflow-inputs-schema.d.ts +2 -8
- package/dist/tools/get-workflow-inputs-schema.js +0 -7
- package/dist/tools/get-workflow-metrics.d.ts +2 -8
- package/dist/tools/get-workflow-metrics.js +1 -9
- package/dist/tools/get-workflow-public-url.d.ts +2 -8
- package/dist/tools/get-workflow-public-url.js +0 -6
- package/dist/tools/get-workflow-tags.d.ts +2 -8
- package/dist/tools/get-workflow-tags.js +0 -7
- package/dist/tools/index.d.ts +2 -11
- package/dist/tools/index.js +12 -139
- package/dist/tools/insert-app-code.d.ts +2 -10
- package/dist/tools/insert-app-code.js +35 -43
- package/dist/tools/link-app-workflow.d.ts +2 -9
- package/dist/tools/link-app-workflow.js +21 -28
- package/dist/tools/link-recipe.d.ts +2 -8
- package/dist/tools/link-recipe.js +18 -24
- package/dist/tools/list-app-files.d.ts +2 -9
- package/dist/tools/list-app-files.js +10 -20
- package/dist/tools/list-apps.d.ts +2 -9
- package/dist/tools/list-apps.js +8 -15
- package/dist/tools/list-available-nodes.d.ts +2 -8
- package/dist/tools/list-available-nodes.js +0 -13
- package/dist/tools/list-custom-nodes.d.ts +2 -9
- package/dist/tools/list-custom-nodes.js +0 -7
- package/dist/tools/list-input-presets.d.ts +2 -8
- package/dist/tools/list-input-presets.js +0 -7
- package/dist/tools/list-knowledge-categories.d.ts +2 -9
- package/dist/tools/list-knowledge-categories.js +0 -11
- package/dist/tools/list-knowledge-documents.d.ts +2 -9
- package/dist/tools/list-knowledge-documents.js +0 -10
- package/dist/tools/list-recipe-steps.d.ts +2 -8
- package/dist/tools/list-recipe-steps.js +1 -8
- package/dist/tools/list-recipe-stores.d.ts +2 -8
- package/dist/tools/list-recipe-stores.js +0 -6
- package/dist/tools/list-recipes.d.ts +2 -8
- package/dist/tools/list-recipes.js +22 -31
- package/dist/tools/list-shared-results.d.ts +2 -8
- package/dist/tools/list-shared-results.js +0 -6
- package/dist/tools/list-user-teams.d.ts +2 -9
- package/dist/tools/list-user-teams.js +0 -7
- package/dist/tools/list-workflows.d.ts +2 -8
- package/dist/tools/list-workflows.js +0 -8
- package/dist/tools/move-document-to-category.d.ts +2 -8
- package/dist/tools/move-document-to-category.js +0 -6
- package/dist/tools/patch-knowledge-document-section.d.ts +8 -0
- package/dist/tools/patch-knowledge-document-section.js +47 -0
- package/dist/tools/prepend-app-code.d.ts +2 -10
- package/dist/tools/prepend-app-code.js +23 -31
- package/dist/tools/publish-app.d.ts +2 -9
- package/dist/tools/publish-app.js +7 -14
- package/dist/tools/query-knowledge-base.d.ts +2 -9
- package/dist/tools/query-knowledge-base.js +1 -15
- package/dist/tools/rename-app-file.d.ts +2 -9
- package/dist/tools/rename-app-file.js +16 -23
- package/dist/tools/reprocess-document.d.ts +2 -8
- package/dist/tools/reprocess-document.js +0 -6
- package/dist/tools/retry-execution.d.ts +2 -8
- package/dist/tools/retry-execution.js +0 -6
- package/dist/tools/search-apps.d.ts +2 -9
- package/dist/tools/search-apps.js +13 -20
- package/dist/tools/search-public-custom-nodes.d.ts +2 -9
- package/dist/tools/search-public-custom-nodes.js +0 -7
- package/dist/tools/search-workflows.d.ts +2 -8
- package/dist/tools/search-workflows.js +0 -8
- package/dist/tools/search.d.ts +2 -8
- package/dist/tools/search.js +1 -7
- package/dist/tools/set-app-entry-file.d.ts +2 -9
- package/dist/tools/set-app-entry-file.js +13 -20
- package/dist/tools/set-workflow-tags.d.ts +2 -8
- package/dist/tools/set-workflow-tags.js +0 -7
- package/dist/tools/stream-execution.d.ts +2 -9
- package/dist/tools/stream-execution.js +0 -8
- package/dist/tools/toggle-community-inputs.d.ts +2 -8
- package/dist/tools/toggle-community-inputs.js +0 -6
- package/dist/tools/toggle-custom-node-visibility.d.ts +2 -9
- package/dist/tools/toggle-custom-node-visibility.js +0 -8
- package/dist/tools/toggle-workflow-public.d.ts +2 -8
- package/dist/tools/toggle-workflow-public.js +0 -7
- package/dist/tools/transfer-document-ownership.d.ts +2 -9
- package/dist/tools/transfer-document-ownership.js +0 -7
- package/dist/tools/unlink-app-workflow.d.ts +2 -9
- package/dist/tools/unlink-app-workflow.js +2 -9
- package/dist/tools/unpublish-app.d.ts +2 -9
- package/dist/tools/unpublish-app.js +2 -9
- package/dist/tools/update-app-file.d.ts +2 -9
- package/dist/tools/update-app-file.js +15 -22
- package/dist/tools/update-app.d.ts +2 -9
- package/dist/tools/update-app.js +14 -21
- package/dist/tools/update-custom-node.d.ts +3 -10
- package/dist/tools/update-custom-node.js +11 -24
- package/dist/tools/update-input-preset.d.ts +2 -8
- package/dist/tools/update-input-preset.js +0 -7
- package/dist/tools/update-knowledge-category.d.ts +2 -8
- package/dist/tools/update-knowledge-category.js +0 -6
- package/dist/tools/update-knowledge-document-content.d.ts +7 -0
- package/dist/tools/update-knowledge-document-content.js +42 -0
- package/dist/tools/update-node.d.ts +2 -8
- package/dist/tools/update-node.js +0 -7
- package/dist/tools/update-recipe-step.d.ts +2 -8
- package/dist/tools/update-recipe-step.js +6 -12
- package/dist/tools/update-recipe-store.d.ts +2 -8
- package/dist/tools/update-recipe-store.js +5 -11
- package/dist/tools/update-recipe.d.ts +2 -8
- package/dist/tools/update-recipe.js +14 -20
- package/dist/tools/upload-document-from-url.d.ts +2 -9
- package/dist/tools/upload-document-from-url.js +0 -7
- package/dist/tools/upload-text-document.d.ts +2 -9
- package/dist/tools/upload-text-document.js +0 -7
- package/dist/tools/validate-workflow.d.ts +2 -8
- package/dist/tools/validate-workflow.js +0 -8
- package/dist/tools/vote-custom-node.d.ts +2 -9
- package/dist/tools/vote-custom-node.js +0 -7
- package/dist/tools/vote-input-preset.d.ts +2 -8
- package/dist/tools/vote-input-preset.js +0 -6
- package/dist/tools/vote-recipe.d.ts +2 -8
- package/dist/tools/vote-recipe.js +2 -8
- package/dist/tools/vote-shared-result.d.ts +2 -8
- package/dist/tools/vote-shared-result.js +0 -6
- package/dist/tools/vote-workflow.d.ts +2 -8
- package/dist/tools/vote-workflow.js +0 -6
- package/dist/types.d.ts +0 -6
- package/dist/types.js +0 -6
- package/dist/utils/script-validator.d.ts +1 -18
- package/dist/utils/script-validator.js +5 -44
- package/package.json +81 -62
- package/dist/api-client.d.ts.map +0 -1
- package/dist/api-client.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/resources/index.d.ts.map +0 -1
- package/dist/resources/index.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/tools/add-connection.d.ts.map +0 -1
- package/dist/tools/add-connection.js.map +0 -1
- package/dist/tools/add-custom-node-comment.d.ts.map +0 -1
- package/dist/tools/add-custom-node-comment.js.map +0 -1
- package/dist/tools/add-node.d.ts.map +0 -1
- package/dist/tools/add-node.js.map +0 -1
- package/dist/tools/add-recipe-step.d.ts.map +0 -1
- package/dist/tools/add-recipe-step.js.map +0 -1
- package/dist/tools/add-recipe-store.d.ts.map +0 -1
- package/dist/tools/add-recipe-store.js.map +0 -1
- package/dist/tools/add-shared-result-comment.d.ts.map +0 -1
- package/dist/tools/add-shared-result-comment.js.map +0 -1
- package/dist/tools/add-workflow-comment.d.ts.map +0 -1
- package/dist/tools/add-workflow-comment.js.map +0 -1
- package/dist/tools/agent-chat.d.ts.map +0 -1
- package/dist/tools/agent-chat.js.map +0 -1
- package/dist/tools/agent-toolkits.d.ts.map +0 -1
- package/dist/tools/agent-toolkits.js.map +0 -1
- package/dist/tools/append-app-code.d.ts.map +0 -1
- package/dist/tools/append-app-code.js.map +0 -1
- package/dist/tools/browse-recipes.d.ts.map +0 -1
- package/dist/tools/browse-recipes.js.map +0 -1
- package/dist/tools/cancel-execution.d.ts.map +0 -1
- package/dist/tools/cancel-execution.js.map +0 -1
- package/dist/tools/clone-app.d.ts.map +0 -1
- package/dist/tools/clone-app.js.map +0 -1
- package/dist/tools/copy-custom-node.d.ts.map +0 -1
- package/dist/tools/copy-custom-node.js.map +0 -1
- package/dist/tools/create-app-file.d.ts.map +0 -1
- package/dist/tools/create-app-file.js.map +0 -1
- package/dist/tools/create-app.d.ts.map +0 -1
- package/dist/tools/create-app.js.map +0 -1
- package/dist/tools/create-custom-node.d.ts.map +0 -1
- package/dist/tools/create-custom-node.js.map +0 -1
- package/dist/tools/create-input-preset.d.ts.map +0 -1
- package/dist/tools/create-input-preset.js.map +0 -1
- package/dist/tools/create-knowledge-category.d.ts.map +0 -1
- package/dist/tools/create-knowledge-category.js.map +0 -1
- package/dist/tools/create-recipe.d.ts.map +0 -1
- package/dist/tools/create-recipe.js.map +0 -1
- package/dist/tools/create-shared-result.d.ts.map +0 -1
- package/dist/tools/create-shared-result.js.map +0 -1
- package/dist/tools/create-workflow.d.ts.map +0 -1
- package/dist/tools/create-workflow.js.map +0 -1
- package/dist/tools/delete-app-file.d.ts.map +0 -1
- package/dist/tools/delete-app-file.js.map +0 -1
- package/dist/tools/delete-app.d.ts.map +0 -1
- package/dist/tools/delete-app.js.map +0 -1
- package/dist/tools/delete-connection.d.ts.map +0 -1
- package/dist/tools/delete-connection.js.map +0 -1
- package/dist/tools/delete-custom-node.d.ts.map +0 -1
- package/dist/tools/delete-custom-node.js.map +0 -1
- package/dist/tools/delete-input-preset.d.ts.map +0 -1
- package/dist/tools/delete-input-preset.js.map +0 -1
- package/dist/tools/delete-knowledge-category.d.ts.map +0 -1
- package/dist/tools/delete-knowledge-category.js.map +0 -1
- package/dist/tools/delete-knowledge-document.d.ts.map +0 -1
- package/dist/tools/delete-knowledge-document.js.map +0 -1
- package/dist/tools/delete-node.d.ts.map +0 -1
- package/dist/tools/delete-node.js.map +0 -1
- package/dist/tools/delete-recipe-step.d.ts.map +0 -1
- package/dist/tools/delete-recipe-step.js.map +0 -1
- package/dist/tools/delete-recipe-store.d.ts.map +0 -1
- package/dist/tools/delete-recipe-store.js.map +0 -1
- package/dist/tools/delete-recipe.d.ts.map +0 -1
- package/dist/tools/delete-recipe.js.map +0 -1
- package/dist/tools/delete-workflow.d.ts.map +0 -1
- package/dist/tools/delete-workflow.js.map +0 -1
- package/dist/tools/duplicate-workflow.d.ts.map +0 -1
- package/dist/tools/duplicate-workflow.js.map +0 -1
- package/dist/tools/edit-app-code.d.ts.map +0 -1
- package/dist/tools/edit-app-code.js.map +0 -1
- package/dist/tools/execute-workflow.d.ts.map +0 -1
- package/dist/tools/execute-workflow.js.map +0 -1
- package/dist/tools/favorite-custom-node.d.ts.map +0 -1
- package/dist/tools/favorite-custom-node.js.map +0 -1
- package/dist/tools/favorite-recipe.d.ts.map +0 -1
- package/dist/tools/favorite-recipe.js.map +0 -1
- package/dist/tools/favorite-workflow.d.ts.map +0 -1
- package/dist/tools/favorite-workflow.js.map +0 -1
- package/dist/tools/fork-recipe.d.ts.map +0 -1
- package/dist/tools/fork-recipe.js.map +0 -1
- package/dist/tools/get-app-file.d.ts.map +0 -1
- package/dist/tools/get-app-file.js.map +0 -1
- package/dist/tools/get-app-template.d.ts.map +0 -1
- package/dist/tools/get-app-template.js.map +0 -1
- package/dist/tools/get-app.d.ts.map +0 -1
- package/dist/tools/get-app.js.map +0 -1
- package/dist/tools/get-custom-node-comments.d.ts.map +0 -1
- package/dist/tools/get-custom-node-comments.js.map +0 -1
- package/dist/tools/get-custom-node-template.d.ts.map +0 -1
- package/dist/tools/get-custom-node-template.js.map +0 -1
- package/dist/tools/get-custom-node.d.ts.map +0 -1
- package/dist/tools/get-custom-node.js.map +0 -1
- package/dist/tools/get-execution-history.d.ts.map +0 -1
- package/dist/tools/get-execution-history.js.map +0 -1
- package/dist/tools/get-execution.d.ts.map +0 -1
- package/dist/tools/get-execution.js.map +0 -1
- package/dist/tools/get-input-preset.d.ts.map +0 -1
- package/dist/tools/get-input-preset.js.map +0 -1
- package/dist/tools/get-knowledge-document.d.ts.map +0 -1
- package/dist/tools/get-knowledge-document.js.map +0 -1
- package/dist/tools/get-knowledge-storage.d.ts.map +0 -1
- package/dist/tools/get-knowledge-storage.js.map +0 -1
- package/dist/tools/get-node-connections.d.ts.map +0 -1
- package/dist/tools/get-node-connections.js.map +0 -1
- package/dist/tools/get-node-schema.d.ts.map +0 -1
- package/dist/tools/get-node-schema.js.map +0 -1
- package/dist/tools/get-public-workflows.d.ts.map +0 -1
- package/dist/tools/get-public-workflows.js.map +0 -1
- package/dist/tools/get-recipe-definition.d.ts.map +0 -1
- package/dist/tools/get-recipe-definition.js.map +0 -1
- package/dist/tools/get-recipe.d.ts.map +0 -1
- package/dist/tools/get-recipe.js.map +0 -1
- package/dist/tools/get-shared-result-comments.d.ts.map +0 -1
- package/dist/tools/get-shared-result-comments.js.map +0 -1
- package/dist/tools/get-shared-result.d.ts.map +0 -1
- package/dist/tools/get-shared-result.js.map +0 -1
- package/dist/tools/get-workflow-comments.d.ts.map +0 -1
- package/dist/tools/get-workflow-comments.js.map +0 -1
- package/dist/tools/get-workflow-details.d.ts.map +0 -1
- package/dist/tools/get-workflow-details.js.map +0 -1
- package/dist/tools/get-workflow-graph.d.ts.map +0 -1
- package/dist/tools/get-workflow-graph.js.map +0 -1
- package/dist/tools/get-workflow-inputs-schema.d.ts.map +0 -1
- package/dist/tools/get-workflow-inputs-schema.js.map +0 -1
- package/dist/tools/get-workflow-metrics.d.ts.map +0 -1
- package/dist/tools/get-workflow-metrics.js.map +0 -1
- package/dist/tools/get-workflow-public-url.d.ts.map +0 -1
- package/dist/tools/get-workflow-public-url.js.map +0 -1
- package/dist/tools/get-workflow-tags.d.ts.map +0 -1
- package/dist/tools/get-workflow-tags.js.map +0 -1
- package/dist/tools/index.d.ts.map +0 -1
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/insert-app-code.d.ts.map +0 -1
- package/dist/tools/insert-app-code.js.map +0 -1
- package/dist/tools/link-app-workflow.d.ts.map +0 -1
- package/dist/tools/link-app-workflow.js.map +0 -1
- package/dist/tools/link-recipe.d.ts.map +0 -1
- package/dist/tools/link-recipe.js.map +0 -1
- package/dist/tools/list-app-files.d.ts.map +0 -1
- package/dist/tools/list-app-files.js.map +0 -1
- package/dist/tools/list-apps.d.ts.map +0 -1
- package/dist/tools/list-apps.js.map +0 -1
- package/dist/tools/list-available-nodes.d.ts.map +0 -1
- package/dist/tools/list-available-nodes.js.map +0 -1
- package/dist/tools/list-custom-nodes.d.ts.map +0 -1
- package/dist/tools/list-custom-nodes.js.map +0 -1
- package/dist/tools/list-input-presets.d.ts.map +0 -1
- package/dist/tools/list-input-presets.js.map +0 -1
- package/dist/tools/list-knowledge-categories.d.ts.map +0 -1
- package/dist/tools/list-knowledge-categories.js.map +0 -1
- package/dist/tools/list-knowledge-documents.d.ts.map +0 -1
- package/dist/tools/list-knowledge-documents.js.map +0 -1
- package/dist/tools/list-recipe-steps.d.ts.map +0 -1
- package/dist/tools/list-recipe-steps.js.map +0 -1
- package/dist/tools/list-recipe-stores.d.ts.map +0 -1
- package/dist/tools/list-recipe-stores.js.map +0 -1
- package/dist/tools/list-recipes.d.ts.map +0 -1
- package/dist/tools/list-recipes.js.map +0 -1
- package/dist/tools/list-shared-results.d.ts.map +0 -1
- package/dist/tools/list-shared-results.js.map +0 -1
- package/dist/tools/list-user-teams.d.ts.map +0 -1
- package/dist/tools/list-user-teams.js.map +0 -1
- package/dist/tools/list-workflows.d.ts.map +0 -1
- package/dist/tools/list-workflows.js.map +0 -1
- package/dist/tools/move-document-to-category.d.ts.map +0 -1
- package/dist/tools/move-document-to-category.js.map +0 -1
- package/dist/tools/prepend-app-code.d.ts.map +0 -1
- package/dist/tools/prepend-app-code.js.map +0 -1
- package/dist/tools/publish-app.d.ts.map +0 -1
- package/dist/tools/publish-app.js.map +0 -1
- package/dist/tools/query-knowledge-base.d.ts.map +0 -1
- package/dist/tools/query-knowledge-base.js.map +0 -1
- package/dist/tools/rename-app-file.d.ts.map +0 -1
- package/dist/tools/rename-app-file.js.map +0 -1
- package/dist/tools/reprocess-document.d.ts.map +0 -1
- package/dist/tools/reprocess-document.js.map +0 -1
- package/dist/tools/retry-execution.d.ts.map +0 -1
- package/dist/tools/retry-execution.js.map +0 -1
- package/dist/tools/search-apps.d.ts.map +0 -1
- package/dist/tools/search-apps.js.map +0 -1
- package/dist/tools/search-public-custom-nodes.d.ts.map +0 -1
- package/dist/tools/search-public-custom-nodes.js.map +0 -1
- package/dist/tools/search-workflows.d.ts.map +0 -1
- package/dist/tools/search-workflows.js.map +0 -1
- package/dist/tools/search.d.ts.map +0 -1
- package/dist/tools/search.js.map +0 -1
- package/dist/tools/set-app-entry-file.d.ts.map +0 -1
- package/dist/tools/set-app-entry-file.js.map +0 -1
- package/dist/tools/set-workflow-tags.d.ts.map +0 -1
- package/dist/tools/set-workflow-tags.js.map +0 -1
- package/dist/tools/stream-execution.d.ts.map +0 -1
- package/dist/tools/stream-execution.js.map +0 -1
- package/dist/tools/toggle-community-inputs.d.ts.map +0 -1
- package/dist/tools/toggle-community-inputs.js.map +0 -1
- package/dist/tools/toggle-custom-node-visibility.d.ts.map +0 -1
- package/dist/tools/toggle-custom-node-visibility.js.map +0 -1
- package/dist/tools/toggle-workflow-public.d.ts.map +0 -1
- package/dist/tools/toggle-workflow-public.js.map +0 -1
- package/dist/tools/transfer-document-ownership.d.ts.map +0 -1
- package/dist/tools/transfer-document-ownership.js.map +0 -1
- package/dist/tools/unlink-app-workflow.d.ts.map +0 -1
- package/dist/tools/unlink-app-workflow.js.map +0 -1
- package/dist/tools/unpublish-app.d.ts.map +0 -1
- package/dist/tools/unpublish-app.js.map +0 -1
- package/dist/tools/update-app-file.d.ts.map +0 -1
- package/dist/tools/update-app-file.js.map +0 -1
- package/dist/tools/update-app.d.ts.map +0 -1
- package/dist/tools/update-app.js.map +0 -1
- package/dist/tools/update-custom-node.d.ts.map +0 -1
- package/dist/tools/update-custom-node.js.map +0 -1
- package/dist/tools/update-input-preset.d.ts.map +0 -1
- package/dist/tools/update-input-preset.js.map +0 -1
- package/dist/tools/update-knowledge-category.d.ts.map +0 -1
- package/dist/tools/update-knowledge-category.js.map +0 -1
- package/dist/tools/update-node.d.ts.map +0 -1
- package/dist/tools/update-node.js.map +0 -1
- package/dist/tools/update-recipe-step.d.ts.map +0 -1
- package/dist/tools/update-recipe-step.js.map +0 -1
- package/dist/tools/update-recipe-store.d.ts.map +0 -1
- package/dist/tools/update-recipe-store.js.map +0 -1
- package/dist/tools/update-recipe.d.ts.map +0 -1
- package/dist/tools/update-recipe.js.map +0 -1
- package/dist/tools/upload-document-from-url.d.ts.map +0 -1
- package/dist/tools/upload-document-from-url.js.map +0 -1
- package/dist/tools/upload-text-document.d.ts.map +0 -1
- package/dist/tools/upload-text-document.js.map +0 -1
- package/dist/tools/validate-workflow.d.ts.map +0 -1
- package/dist/tools/validate-workflow.js.map +0 -1
- package/dist/tools/vote-custom-node.d.ts.map +0 -1
- package/dist/tools/vote-custom-node.js.map +0 -1
- package/dist/tools/vote-input-preset.d.ts.map +0 -1
- package/dist/tools/vote-input-preset.js.map +0 -1
- package/dist/tools/vote-recipe.d.ts.map +0 -1
- package/dist/tools/vote-recipe.js.map +0 -1
- package/dist/tools/vote-shared-result.d.ts.map +0 -1
- package/dist/tools/vote-shared-result.js.map +0 -1
- package/dist/tools/vote-workflow.d.ts.map +0 -1
- package/dist/tools/vote-workflow.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/utils/script-validator.d.ts.map +0 -1
- package/dist/utils/script-validator.js.map +0 -1
package/dist/resources/index.js
CHANGED
|
@@ -1,2174 +1,2931 @@
|
|
|
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
1
|
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
2
|
const LEARN_RESOURCES = {
|
|
13
3
|
'learn://overview': {
|
|
14
4
|
name: 'FlowDot Platform Overview',
|
|
15
5
|
description: 'High-level overview of all FlowDot components and how they work together',
|
|
16
6
|
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
|
|
7
|
+
content: `# FlowDot Platform Overview
|
|
8
|
+
|
|
9
|
+
## What is FlowDot?
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
## Core Components
|
|
14
|
+
|
|
15
|
+
### 1. **Workflows**
|
|
16
|
+
Visual automation workflows with nodes and connections.
|
|
17
|
+
- **Learn more:** \`learn://workflows\`
|
|
18
|
+
- **Quick start:** Use \`list_workflows\` to see your workflows
|
|
19
|
+
|
|
20
|
+
### 2. **Recipes**
|
|
21
|
+
Agent orchestration workflows for complex automation.
|
|
22
|
+
- **Learn more:** \`learn://recipes\`
|
|
23
|
+
- **Quick start:** Use \`list_recipes\` to see your recipes
|
|
24
|
+
|
|
25
|
+
### 3. **Custom Nodes**
|
|
26
|
+
Reusable JavaScript nodes you can create and share.
|
|
27
|
+
- **Learn more:** \`learn://custom-nodes\`
|
|
28
|
+
- **Quick start:** Use \`list_custom_nodes\` to see available nodes
|
|
29
|
+
|
|
30
|
+
### 4. **Apps**
|
|
31
|
+
React frontend applications that can invoke workflows.
|
|
32
|
+
- **Learn more:** \`learn://apps\`
|
|
33
|
+
- **Quick start:** Use \`list_apps\` to see your apps
|
|
34
|
+
|
|
35
|
+
### 5. **Agent Toolkits**
|
|
36
|
+
MCP toolkits for extending AI agents with new capabilities.
|
|
37
|
+
- **Learn more:** \`learn://toolkits\`
|
|
38
|
+
- **Quick start:** Use \`mcp__flowdot__list_agent_toolkits\`
|
|
39
|
+
|
|
40
|
+
### 6. **Knowledge Base**
|
|
41
|
+
Document storage and RAG-powered search.
|
|
42
|
+
- **Learn more:** \`learn://knowledge-base\`
|
|
43
|
+
- **Quick start:** Use \`list_knowledge_documents\`
|
|
44
|
+
|
|
45
|
+
## Common Workflows
|
|
46
|
+
|
|
47
|
+
### Creating a Simple Workflow
|
|
48
|
+
1. \`create_workflow\` - Create empty workflow
|
|
49
|
+
2. \`list_available_nodes\` - See what nodes you can add
|
|
50
|
+
3. \`add_node\` - Add nodes to your workflow
|
|
51
|
+
4. \`add_connection\` - Connect nodes together
|
|
52
|
+
5. \`execute_workflow\` - Run the workflow
|
|
53
|
+
|
|
54
|
+
### Creating a Recipe
|
|
55
|
+
1. \`create_recipe\` - Create empty recipe
|
|
56
|
+
2. \`add_recipe_store\` - Define inputs/outputs
|
|
57
|
+
3. \`add_recipe_step\` - Add agent, loop, gate, or other steps
|
|
58
|
+
4. \`update_recipe_step\` - Connect steps via "next"
|
|
59
|
+
5. \`update_recipe\` - Set entry_step_id
|
|
60
|
+
6. \`link_recipe\` - Create CLI alias
|
|
61
|
+
7. Run via CLI: \`npx flowdot recipes run <alias>\`
|
|
62
|
+
|
|
63
|
+
## Where to Start
|
|
64
|
+
|
|
65
|
+
- **New to FlowDot?** Read \`learn://workflows\` first
|
|
66
|
+
- **Building agents?** Read \`learn://recipes\` first
|
|
67
|
+
- **Extending functionality?** Read \`learn://custom-nodes\` or \`learn://toolkits\`
|
|
68
|
+
- **Building UIs?** Read \`learn://apps\` first
|
|
69
|
+
|
|
70
|
+
## Getting Help
|
|
71
|
+
|
|
72
|
+
- Each \`learn://\` resource has detailed examples
|
|
73
|
+
- Tool descriptions include usage examples
|
|
74
|
+
- Visit https://flowdot.ai for web interface
|
|
85
75
|
`,
|
|
86
76
|
},
|
|
87
77
|
'learn://workflows': {
|
|
88
78
|
name: 'Workflows Complete Guide',
|
|
89
79
|
description: 'Complete guide to creating, managing, and executing FlowDot workflows',
|
|
90
80
|
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
|
-
##
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
//
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
81
|
+
content: `# FlowDot Workflows - Complete Guide
|
|
82
|
+
|
|
83
|
+
## What Are Workflows?
|
|
84
|
+
|
|
85
|
+
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.
|
|
86
|
+
|
|
87
|
+
## Key Concepts
|
|
88
|
+
|
|
89
|
+
### Nodes
|
|
90
|
+
Processing units that perform actions:
|
|
91
|
+
- **Input nodes:** Receive data from workflow inputs
|
|
92
|
+
- **Processing nodes:** Transform, analyze, or manipulate data
|
|
93
|
+
- **Output nodes:** Return results
|
|
94
|
+
- **LLM nodes:** AI-powered processing
|
|
95
|
+
- **Custom nodes:** Your own JavaScript logic
|
|
96
|
+
|
|
97
|
+
### Connections
|
|
98
|
+
Data flows between nodes via **sockets**:
|
|
99
|
+
- **Output sockets:** Where a node sends data
|
|
100
|
+
- **Input sockets:** Where a node receives data
|
|
101
|
+
- Connect output socket → input socket to flow data
|
|
102
|
+
|
|
103
|
+
### Execution
|
|
104
|
+
When you execute a workflow:
|
|
105
|
+
1. Provide input values via \`inputs\` parameter
|
|
106
|
+
2. Workflow processes nodes in dependency order
|
|
107
|
+
3. Results are returned with output node data
|
|
108
|
+
|
|
109
|
+
## Building a Workflow
|
|
110
|
+
|
|
111
|
+
### Step 1: Create Workflow
|
|
112
|
+
\`\`\`javascript
|
|
113
|
+
// Create empty workflow
|
|
114
|
+
create_workflow({
|
|
115
|
+
name: "My First Workflow",
|
|
116
|
+
description: "Processes text with AI"
|
|
117
|
+
})
|
|
118
|
+
// Returns: { id: "abc123", ... }
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
### Step 2: See Available Nodes
|
|
122
|
+
\`\`\`javascript
|
|
123
|
+
list_available_nodes()
|
|
124
|
+
// Returns categories and node types
|
|
125
|
+
\`\`\`
|
|
126
|
+
|
|
127
|
+
Common node types:
|
|
128
|
+
- \`TextInput\` - Accept text input
|
|
129
|
+
- \`LLMNode\` - AI processing
|
|
130
|
+
- \`TextOutput\` - Return text result
|
|
131
|
+
- \`HTTPRequest\` - API calls
|
|
132
|
+
- \`custom_node_xxx\` - Custom nodes
|
|
133
|
+
|
|
134
|
+
### Step 3: Add Nodes
|
|
135
|
+
\`\`\`javascript
|
|
136
|
+
// Add input node
|
|
137
|
+
add_node({
|
|
138
|
+
workflow_id: "abc123",
|
|
139
|
+
node_type: "TextInput",
|
|
140
|
+
position: { x: 100, y: 100 },
|
|
141
|
+
properties: {
|
|
142
|
+
label: "User Input",
|
|
143
|
+
inputName: "user_text"
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
// Returns: { id: "node-1", ... }
|
|
147
|
+
|
|
148
|
+
// Add LLM processing node
|
|
149
|
+
add_node({
|
|
150
|
+
workflow_id: "abc123",
|
|
151
|
+
node_type: "LLMNode",
|
|
152
|
+
position: { x: 300, y: 100 },
|
|
153
|
+
properties: {
|
|
154
|
+
prompt: "Summarize: {{text}}",
|
|
155
|
+
model: "claude-haiku-4.5"
|
|
156
|
+
}
|
|
157
|
+
})
|
|
158
|
+
// Returns: { id: "node-2", ... }
|
|
159
|
+
|
|
160
|
+
// Add output node
|
|
161
|
+
add_node({
|
|
162
|
+
workflow_id: "abc123",
|
|
163
|
+
node_type: "TextOutput",
|
|
164
|
+
position: { x: 500, y: 100 }
|
|
165
|
+
})
|
|
166
|
+
// Returns: { id: "node-3", ... }
|
|
167
|
+
\`\`\`
|
|
168
|
+
|
|
169
|
+
### Step 4: Connect Nodes
|
|
170
|
+
\`\`\`javascript
|
|
171
|
+
// Connect input → LLM
|
|
172
|
+
add_connection({
|
|
173
|
+
workflow_id: "abc123",
|
|
174
|
+
source_node_id: "node-1",
|
|
175
|
+
source_socket_id: "text",
|
|
176
|
+
target_node_id: "node-2",
|
|
177
|
+
target_socket_id: "text"
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
// Connect LLM → output
|
|
181
|
+
add_connection({
|
|
182
|
+
workflow_id: "abc123",
|
|
183
|
+
source_node_id: "node-2",
|
|
184
|
+
source_socket_id: "response",
|
|
185
|
+
target_node_id: "node-3",
|
|
186
|
+
target_socket_id: "Consolidated Text"
|
|
187
|
+
})
|
|
188
|
+
\`\`\`
|
|
189
|
+
|
|
190
|
+
### Step 5: Execute
|
|
191
|
+
\`\`\`javascript
|
|
192
|
+
execute_workflow({
|
|
193
|
+
workflow_id: "abc123",
|
|
194
|
+
inputs: {
|
|
195
|
+
user_text: "Long text to summarize..."
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
// Returns execution results
|
|
199
|
+
\`\`\`
|
|
200
|
+
|
|
201
|
+
## Sync vs Async Execution
|
|
202
|
+
|
|
203
|
+
\`execute_workflow\` supports two modes:
|
|
204
|
+
|
|
205
|
+
| Mode | When To Use | Behavior |
|
|
206
|
+
|------|-------------|----------|
|
|
207
|
+
| **Sync** (default for fast workflows) | Quick workflows (<30s) where you need the result immediately | Call blocks until the workflow completes; result returned in the same response |
|
|
208
|
+
| **Async** | Long-running workflows, parallel work, fire-and-forget | Returns an \`execution_id\` immediately; poll \`get_execution_status\` or use \`stream_execution\` for real-time updates |
|
|
209
|
+
|
|
210
|
+
\`\`\`javascript
|
|
211
|
+
// Async pattern
|
|
212
|
+
const { execution_id } = await execute_workflow({
|
|
213
|
+
workflow_id: "abc123",
|
|
214
|
+
inputs: { /* ... */ },
|
|
215
|
+
wait_for_completion: false // Async mode
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Then poll
|
|
219
|
+
const status = await get_execution_status({ execution_id });
|
|
220
|
+
|
|
221
|
+
// Or stream events in real time
|
|
222
|
+
const stream = await stream_execution({ execution_id });
|
|
223
|
+
\`\`\`
|
|
224
|
+
|
|
225
|
+
**Why this matters:** AI clients have request timeouts (often 30-60 seconds). Sync mode will fail on workflows that exceed those limits. For any workflow involving multiple LLM calls, large file processing, or external API chains, **default to async**.
|
|
226
|
+
|
|
227
|
+
## Data Flow & Type Handling
|
|
228
|
+
|
|
229
|
+
Data flows from one node's output socket to another node's input socket. A few rules to remember when designing workflows:
|
|
230
|
+
|
|
231
|
+
- **Socket types must be compatible.** Connecting a number socket to a string socket will either fail validation or trigger an automatic coercion. Run \`validate_workflow\` to surface mismatches before execution.
|
|
232
|
+
- **Output socket names matter.** When you connect with \`add_connection\`, the \`source_socket_id\` and \`target_socket_id\` must match exact socket names from each node's schema. Use \`get_node_schema\` to see them.
|
|
233
|
+
- **Null/undefined propagation.** If an upstream node fails or produces no value, downstream nodes receive null. Design nodes to handle nulls gracefully or use a validator node early in the chain.
|
|
234
|
+
- **One source can fan out.** A single output socket can connect to multiple downstream input sockets — you don't need to duplicate the upstream node.
|
|
235
|
+
- **Convergence requires explicit merging.** If multiple nodes feed into one downstream node, that downstream node needs an input socket that accepts multiple sources, OR you need an explicit merge/join node.
|
|
236
|
+
|
|
237
|
+
## Advanced Features
|
|
238
|
+
|
|
239
|
+
### Getting Workflow Structure
|
|
240
|
+
\`\`\`javascript
|
|
241
|
+
// See all nodes and connections
|
|
242
|
+
get_workflow_graph({ workflow_id: "abc123" })
|
|
243
|
+
|
|
244
|
+
// Get input requirements
|
|
245
|
+
get_workflow_inputs_schema({ workflow_id: "abc123" })
|
|
246
|
+
|
|
247
|
+
// Validate workflow
|
|
248
|
+
validate_workflow({ workflow_id: "abc123" })
|
|
249
|
+
\`\`\`
|
|
250
|
+
|
|
251
|
+
### Managing Workflows
|
|
252
|
+
\`\`\`javascript
|
|
253
|
+
// List your workflows
|
|
254
|
+
list_workflows()
|
|
255
|
+
|
|
256
|
+
// Search workflows
|
|
257
|
+
search_workflows({ query: "summarize" })
|
|
258
|
+
|
|
259
|
+
// Duplicate a workflow
|
|
260
|
+
duplicate_workflow({ workflow_id: "abc123", name: "Copy of My Workflow" })
|
|
261
|
+
|
|
262
|
+
// Make workflow public
|
|
263
|
+
toggle_workflow_public({ workflow_id: "abc123", is_public: true })
|
|
264
|
+
|
|
265
|
+
// Delete workflow
|
|
266
|
+
delete_workflow({ workflow_id: "abc123" })
|
|
267
|
+
\`\`\`
|
|
268
|
+
|
|
269
|
+
### Execution Management
|
|
270
|
+
\`\`\`javascript
|
|
271
|
+
// Get execution status
|
|
272
|
+
get_execution_status({ execution_id: "exec-123" })
|
|
273
|
+
|
|
274
|
+
// Cancel running execution
|
|
275
|
+
cancel_execution({ execution_id: "exec-123" })
|
|
276
|
+
|
|
277
|
+
// Retry failed execution
|
|
278
|
+
retry_execution({ execution_id: "exec-123" })
|
|
279
|
+
|
|
280
|
+
// View execution history
|
|
281
|
+
get_execution_history({ workflow_id: "abc123" })
|
|
282
|
+
\`\`\`
|
|
283
|
+
|
|
284
|
+
## Common Patterns
|
|
285
|
+
|
|
286
|
+
Pick a shape before you start adding nodes. Most workflows fit one of these:
|
|
287
|
+
|
|
288
|
+
### Pattern 1: Linear Processing
|
|
289
|
+
\`\`\`
|
|
290
|
+
Input → Transform → LLM → Output
|
|
291
|
+
\`\`\`
|
|
292
|
+
The simplest case. Each node feeds the next. Use this for "take input, do N steps, return result."
|
|
293
|
+
|
|
294
|
+
### Pattern 2: Branching
|
|
295
|
+
\`\`\`
|
|
296
|
+
Input → Condition Node → Path A → Output A
|
|
297
|
+
→ Path B → Output B
|
|
298
|
+
\`\`\`
|
|
299
|
+
Use a condition or router node to send data down different paths based on its content. Each path can have its own downstream nodes. Useful for "if X, do Y; otherwise do Z."
|
|
300
|
+
|
|
301
|
+
### Pattern 3: Parallel Fan-Out + Merge
|
|
302
|
+
\`\`\`
|
|
303
|
+
┌→ Process A ─┐
|
|
304
|
+
Input ───────┼→ Process B ─┼→ Merge → Output
|
|
305
|
+
└→ Process C ─┘
|
|
306
|
+
\`\`\`
|
|
307
|
+
A single input feeds three independent processing chains, which then converge into a merge node. Useful for "do these N independent things and combine the results." All three branches execute in parallel.
|
|
308
|
+
|
|
309
|
+
### Pattern 4: Loop Processing
|
|
310
|
+
\`\`\`
|
|
311
|
+
Input → Generate List → Loop Node → Process Each Item → Collect → Output
|
|
312
|
+
\`\`\`
|
|
313
|
+
Use this when you need to process every item in an array. The loop node runs the inner subgraph once per item and collects the outputs. Pair with parallel execution for speed.
|
|
314
|
+
|
|
315
|
+
### Pattern 5: External API Chain
|
|
316
|
+
\`\`\`
|
|
317
|
+
Input → HTTP Request 1 → Transform → HTTP Request 2 → Output
|
|
318
|
+
\`\`\`
|
|
319
|
+
Each HTTP node makes an API call, the transform node reshapes the response into the next request's input. Use this for "fetch from API A, then use that to fetch from API B."
|
|
320
|
+
|
|
321
|
+
**Tip:** When designing a workflow, sketch the pattern first as ASCII or pseudocode. The visual graph is a faithful representation of that sketch — once you know the pattern, the node-and-connection setup is mechanical.
|
|
322
|
+
|
|
323
|
+
## Debugging Workflows
|
|
324
|
+
|
|
325
|
+
When a workflow doesn't behave the way you expect, work through these in order:
|
|
326
|
+
|
|
327
|
+
### 1. Validate before executing
|
|
328
|
+
\`\`\`javascript
|
|
329
|
+
validate_workflow({ workflow_id: "abc123" })
|
|
330
|
+
\`\`\`
|
|
331
|
+
Catches missing required inputs, disconnected nodes, cycles, and socket-type mismatches without spending tokens on a failed execution.
|
|
332
|
+
|
|
333
|
+
### 2. Inspect the graph
|
|
334
|
+
\`\`\`javascript
|
|
335
|
+
get_workflow_graph({ workflow_id: "abc123" })
|
|
336
|
+
\`\`\`
|
|
337
|
+
Returns every node and connection. Useful for confirming that the structure matches your mental model — especially after a series of \`add_node\` / \`add_connection\` calls where you might have lost track of step IDs.
|
|
338
|
+
|
|
339
|
+
### 3. Check the input schema
|
|
340
|
+
\`\`\`javascript
|
|
341
|
+
get_workflow_inputs_schema({ workflow_id: "abc123" })
|
|
342
|
+
\`\`\`
|
|
343
|
+
Confirms which input names the workflow expects, their types, and which are required. The most common execution failure is "input name doesn't match" — this prevents that.
|
|
344
|
+
|
|
345
|
+
### 4. Read execution history
|
|
346
|
+
\`\`\`javascript
|
|
347
|
+
get_execution_history({ workflow_id: "abc123" })
|
|
348
|
+
get_execution_status({ execution_id: "exec-123" })
|
|
349
|
+
\`\`\`
|
|
350
|
+
Past executions include per-node status, inputs, outputs, and error messages. If a recent run failed, this is where you find out why.
|
|
351
|
+
|
|
352
|
+
### 5. Stream a fresh execution
|
|
353
|
+
\`\`\`javascript
|
|
354
|
+
const { execution_id } = await execute_workflow({ /* ... */, wait_for_completion: false });
|
|
355
|
+
const stream = await stream_execution({ execution_id });
|
|
356
|
+
\`\`\`
|
|
357
|
+
Watch each node fire in real time. Lets you see exactly which node fails and what value it received.
|
|
358
|
+
|
|
359
|
+
### Common failure modes
|
|
360
|
+
|
|
361
|
+
| Symptom | Likely Cause |
|
|
362
|
+
|---------|--------------|
|
|
363
|
+
| "Input X is required" | Input name in \`execute_workflow\` doesn't match the input node's \`inputName\` property |
|
|
364
|
+
| "Node X has no incoming connection" | Forgot to call \`add_connection\` for one of the input sockets |
|
|
365
|
+
| "Socket type mismatch" | Connected an output socket to an input socket of an incompatible type |
|
|
366
|
+
| "Node X failed: undefined is not..." | An upstream node returned null and the downstream node didn't handle it |
|
|
367
|
+
| Workflow times out | Workflow is too long for sync mode — re-run with \`wait_for_completion: false\` |
|
|
368
|
+
| LLM node returns nothing | The prompt template references a variable that wasn't connected |
|
|
369
|
+
| Custom node returns wrong shape | Custom node's \`return\` keys don't match its declared output names |
|
|
370
|
+
|
|
371
|
+
## Best Practices
|
|
372
|
+
|
|
373
|
+
1. **Name things clearly:** Node labels should describe what they do
|
|
374
|
+
2. **Use input nodes:** Define workflow inputs explicitly
|
|
375
|
+
3. **Test incrementally:** Execute after each major change
|
|
376
|
+
4. **Validate early:** Run \`validate_workflow\` before executing
|
|
377
|
+
5. **Handle errors:** Consider error paths for critical workflows
|
|
378
|
+
|
|
379
|
+
## Troubleshooting
|
|
380
|
+
|
|
381
|
+
### Workflow Won't Execute
|
|
382
|
+
- Check \`validate_workflow\` for errors
|
|
383
|
+
- Ensure all required inputs are provided
|
|
384
|
+
- Verify all nodes are connected properly
|
|
385
|
+
|
|
386
|
+
### Unexpected Results
|
|
387
|
+
- Check \`get_execution_history\` for execution logs
|
|
388
|
+
- Verify node configurations are correct
|
|
389
|
+
- Test individual nodes in isolation
|
|
390
|
+
|
|
391
|
+
### Can't Find Nodes
|
|
392
|
+
- Use \`list_available_nodes\` to see all available types
|
|
393
|
+
- Check if custom nodes are published
|
|
394
|
+
- Verify node names match exactly (case-sensitive)
|
|
395
|
+
|
|
396
|
+
## Related Resources
|
|
397
|
+
|
|
398
|
+
- **Custom Nodes:** \`learn://custom-nodes\` - Create your own node types
|
|
399
|
+
- **Apps:** \`learn://apps\` - Build UIs that execute workflows
|
|
400
|
+
- **Recipes:** \`learn://recipes\` - Agent-driven workflow orchestration
|
|
302
401
|
`,
|
|
303
402
|
},
|
|
304
403
|
'learn://recipes': {
|
|
305
404
|
name: 'Recipes Complete Guide',
|
|
306
405
|
description: 'Complete guide to agent recipes - orchestration workflows for complex automation',
|
|
307
406
|
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
|
-
##
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
**
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
hash: "abc123xyz",
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
**
|
|
472
|
-
|
|
473
|
-
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
\`\`\`
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
//
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
407
|
+
content: `# FlowDot Recipes - Complete Guide
|
|
408
|
+
|
|
409
|
+
## What Are Recipes?
|
|
410
|
+
|
|
411
|
+
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.
|
|
412
|
+
|
|
413
|
+
**CRITICAL:** MCP tools can only **DESIGN** recipes. To **RUN** a recipe, use the FlowDot CLI:
|
|
414
|
+
\`\`\`bash
|
|
415
|
+
npx flowdot recipes run <alias> --input '{"key":"value"}'
|
|
416
|
+
\`\`\`
|
|
417
|
+
|
|
418
|
+
## Key Concepts
|
|
419
|
+
|
|
420
|
+
### Steps
|
|
421
|
+
The building blocks of a recipe:
|
|
422
|
+
- **agent:** AI agent with tools (read, search, edit, etc.)
|
|
423
|
+
- **loop:** Iterate over arrays (sequential or parallel)
|
|
424
|
+
- **parallel:** Run multiple steps concurrently
|
|
425
|
+
- **gate:** Require approval or user input
|
|
426
|
+
- **branch:** Conditional routing based on data
|
|
427
|
+
- **invoke:** Call another recipe (subroutines)
|
|
428
|
+
|
|
429
|
+
### Stores
|
|
430
|
+
Variables that hold data throughout execution:
|
|
431
|
+
- **Input stores:** Values provided when running
|
|
432
|
+
- **Output stores:** Values returned after completion
|
|
433
|
+
- **Internal stores:** Temporary data storage
|
|
434
|
+
|
|
435
|
+
### Connections
|
|
436
|
+
Steps connect via:
|
|
437
|
+
- **next:** The step to run after success
|
|
438
|
+
- **on_error:** The step to run if error occurs
|
|
439
|
+
|
|
440
|
+
### Interpolation
|
|
441
|
+
Reference data in prompts and conditions:
|
|
442
|
+
- \`{{inputs.request}}\` - The CLI task argument
|
|
443
|
+
- \`{{store_key}}\` - Any store value
|
|
444
|
+
- \`{{step.step_id}}\` - Output from a previous step
|
|
445
|
+
|
|
446
|
+
## Architecture Patterns
|
|
447
|
+
|
|
448
|
+
Before designing a recipe, pick the right *shape*. Most recipes fall into one of three patterns. Knowing the pattern up front determines which step types you need and how stores connect, which prevents you from painting yourself into a corner.
|
|
449
|
+
|
|
450
|
+
### Pattern 1: Orchestrator + Workers (most common)
|
|
451
|
+
|
|
452
|
+
For research, exploration, or any task where the work fans out into independent sub-tasks:
|
|
453
|
+
|
|
454
|
+
\`\`\`
|
|
455
|
+
1. Orchestrator (agent)
|
|
456
|
+
- Analyzes the request
|
|
457
|
+
- Generates sub-tasks as a JSON array
|
|
458
|
+
- Outputs to: questions[]
|
|
459
|
+
|
|
460
|
+
2. Worker Loop (loop step over questions[])
|
|
461
|
+
- Runs Worker Agent for each item
|
|
462
|
+
- parallel: true, max_concurrent: 5
|
|
463
|
+
- Collects results to: search_results[]
|
|
464
|
+
|
|
465
|
+
3. Synthesizer (agent)
|
|
466
|
+
- Reads all search_results[]
|
|
467
|
+
- Produces final coherent answer
|
|
468
|
+
- Outputs to: answer
|
|
469
|
+
\`\`\`
|
|
470
|
+
|
|
471
|
+
This is the canonical shape for any "research a topic", "review N files", or "analyze N items" task.
|
|
472
|
+
|
|
473
|
+
### Pattern 2: Sequential Pipeline
|
|
474
|
+
|
|
475
|
+
For linear transformation tasks where each step depends on the previous one:
|
|
476
|
+
|
|
477
|
+
\`\`\`
|
|
478
|
+
Parser → Validator → Transformer → Formatter
|
|
479
|
+
\`\`\`
|
|
480
|
+
|
|
481
|
+
Each step has a single \`next\` and no parallelism.
|
|
482
|
+
|
|
483
|
+
### Pattern 3: Parallel Fan-Out
|
|
484
|
+
|
|
485
|
+
For independent concurrent tasks that converge into one aggregator:
|
|
486
|
+
|
|
487
|
+
\`\`\`
|
|
488
|
+
┌→ Task A ─┐
|
|
489
|
+
Request ─┼→ Task B ─┼→ Aggregator
|
|
490
|
+
└→ Task C ─┘
|
|
491
|
+
\`\`\`
|
|
492
|
+
|
|
493
|
+
Use a \`parallel\` step containing the three tasks, with the aggregator as the \`next\` step.
|
|
494
|
+
|
|
495
|
+
## Building a Recipe
|
|
496
|
+
|
|
497
|
+
### Step 1: Create Recipe
|
|
498
|
+
\`\`\`javascript
|
|
499
|
+
create_recipe({
|
|
500
|
+
name: "code-reviewer",
|
|
501
|
+
description: "Reviews code changes and suggests improvements"
|
|
502
|
+
})
|
|
503
|
+
// Returns: { hash: "abc123xyz", ... }
|
|
504
|
+
// SAVE THIS HASH - you need it for all operations!
|
|
505
|
+
\`\`\`
|
|
506
|
+
|
|
507
|
+
### Step 2: Define Stores (Inputs/Outputs)
|
|
508
|
+
|
|
509
|
+
**CRITICAL:** Name your primary input store \`request\` - the CLI passes the task argument as \`inputs.request\`.
|
|
510
|
+
|
|
511
|
+
\`\`\`javascript
|
|
512
|
+
// Primary input (receives CLI task)
|
|
513
|
+
add_recipe_store({
|
|
514
|
+
hash: "abc123xyz",
|
|
515
|
+
key: "request",
|
|
516
|
+
label: "Task Request",
|
|
517
|
+
description: "The task provided by the user",
|
|
518
|
+
schema_type: "string",
|
|
519
|
+
is_input: true
|
|
520
|
+
})
|
|
521
|
+
|
|
522
|
+
// Output store
|
|
523
|
+
add_recipe_store({
|
|
524
|
+
hash: "abc123xyz",
|
|
525
|
+
key: "review_result",
|
|
526
|
+
label: "Review Result",
|
|
527
|
+
schema_type: "string",
|
|
528
|
+
is_output: true
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
// Internal stores for intermediate data
|
|
532
|
+
add_recipe_store({
|
|
533
|
+
hash: "abc123xyz",
|
|
534
|
+
key: "file_list",
|
|
535
|
+
schema_type: "array"
|
|
536
|
+
})
|
|
537
|
+
\`\`\`
|
|
538
|
+
|
|
539
|
+
### Step 3: Add Steps
|
|
540
|
+
|
|
541
|
+
#### Agent Step (AI Processing)
|
|
542
|
+
\`\`\`javascript
|
|
543
|
+
add_recipe_step({
|
|
544
|
+
hash: "abc123xyz",
|
|
545
|
+
name: "analyze-code",
|
|
546
|
+
type: "agent",
|
|
547
|
+
config: {
|
|
548
|
+
user_prompt: "Review the code: {{inputs.request}}. List files to check.",
|
|
549
|
+
tools: ["read", "search", "analyze"],
|
|
550
|
+
output_store: "file_list",
|
|
551
|
+
max_iterations: 10
|
|
552
|
+
}
|
|
553
|
+
})
|
|
554
|
+
// Returns: { id: "step-1", ... }
|
|
555
|
+
// SAVE THE STEP ID!
|
|
556
|
+
\`\`\`
|
|
557
|
+
|
|
558
|
+
**IMPORTANT:** Use \`user_prompt\` (NOT \`prompt\`) - this is the field the runtime expects.
|
|
559
|
+
|
|
560
|
+
**Built-in tools:**
|
|
561
|
+
- \`read\` - Read files
|
|
562
|
+
- \`search\` - Search codebase
|
|
563
|
+
- \`analyze\` - Analyze code
|
|
564
|
+
- \`find-definition\` - Find function/class definitions
|
|
565
|
+
- \`web-search\` - Search the web
|
|
566
|
+
- \`edit\` - Edit files
|
|
567
|
+
- \`execute-command\` - Run shell commands
|
|
568
|
+
- \`create-file\` - Create new files
|
|
569
|
+
|
|
570
|
+
**You are NOT limited to the built-in tools.** The \`tools\` array accepts arbitrary tool references, and the recipe runtime resolves them against three sources at execution time:
|
|
571
|
+
|
|
572
|
+
1. **Built-ins** — the 8 tools above
|
|
573
|
+
2. **Local MCP servers** — any MCP server the user has configured in their CLI environment (e.g. \`schwab\`, \`robinhood\`, \`electron-qa\`, \`playwright\`, a custom stdio server they wrote)
|
|
574
|
+
3. **FlowDot toolkits** — any agent toolkit the user has installed (see \`learn://toolkits\`)
|
|
575
|
+
|
|
576
|
+
This means you can design generic recipes like *"QA these websites using the electron-qa browser tool"* or *"Rebalance this portfolio using the Schwab trading tool"* without knowing which specific MCP server the user has — you just declare the namespace or a wildcard and the runtime wires it up.
|
|
577
|
+
|
|
578
|
+
### Tool Reference Naming
|
|
579
|
+
|
|
580
|
+
| Source | Syntax | Example |
|
|
581
|
+
|---|---|---|
|
|
582
|
+
| Built-in | \`<name>\` | \`read\`, \`search\`, \`edit\` |
|
|
583
|
+
| Specific MCP tool | \`mcp__<server>__<tool>\` | \`mcp__schwab__get_accounts\` |
|
|
584
|
+
| All tools from one MCP server | \`mcp__<server>__*\` | \`mcp__electron-qa__*\` |
|
|
585
|
+
| All MCP tools from every server | \`mcp__*\` | \`mcp__*\` |
|
|
586
|
+
| Specific toolkit tool | \`toolkit__<name>__<tool>\` | \`toolkit__spotify__search_tracks\` |
|
|
587
|
+
| All tools from one toolkit | \`toolkit__<name>__*\` | \`toolkit__trading__*\` |
|
|
588
|
+
|
|
589
|
+
You can freely mix built-ins, MCP references, and toolkit references in a single \`tools\` array:
|
|
590
|
+
|
|
591
|
+
\`\`\`javascript
|
|
592
|
+
add_recipe_step({
|
|
593
|
+
hash: "abc123xyz",
|
|
594
|
+
name: "qa-website",
|
|
595
|
+
type: "agent",
|
|
596
|
+
config: {
|
|
597
|
+
user_prompt: "QA the site at {{current_task.url}}: {{current_task.goal}}",
|
|
598
|
+
tools: [
|
|
599
|
+
"mcp__electron-qa__*", // all browser-automation tools
|
|
600
|
+
"create-file", // built-in, to write the evidence file
|
|
601
|
+
"web-search" // built-in, for lookups
|
|
602
|
+
],
|
|
603
|
+
output_store: "qa_results"
|
|
604
|
+
}
|
|
605
|
+
})
|
|
606
|
+
\`\`\`
|
|
607
|
+
|
|
608
|
+
### Prompt Lecturing for MCP Tools
|
|
609
|
+
|
|
610
|
+
MCP and toolkit tools follow the same prompt-lecturing rules as built-ins — maybe more so. Small models do NOT know what \`mcp__schwab__place_order\` does just because it's in the tool list. Name the tools explicitly in the prompt, give concrete example invocations, and spell out the expected output shape. Example:
|
|
611
|
+
|
|
612
|
+
\`\`\`
|
|
613
|
+
You have access to the electron-qa browser tools. To QA a page you MUST:
|
|
614
|
+
1. Call 'mcp__electron-qa__launch_app' with the URL
|
|
615
|
+
2. Call 'mcp__electron-qa__describe_screen' to see what's rendered
|
|
616
|
+
3. Call 'mcp__electron-qa__find_element' and 'mcp__electron-qa__perform_action' to exercise the feature
|
|
617
|
+
4. Call 'mcp__electron-qa__take_screenshot' and save the path to evidence
|
|
618
|
+
5. Call 'mcp__electron-qa__close_app' before reporting
|
|
619
|
+
|
|
620
|
+
DO NOT skip steps 1 or 5. DO NOT invent element selectors you haven't seen in a describe_screen result.
|
|
621
|
+
\`\`\`
|
|
622
|
+
|
|
623
|
+
### Runtime Prerequisite
|
|
624
|
+
|
|
625
|
+
MCP tools are resolved at **execution time** against the CLI user's MCP server configuration. The recipe definition itself stores the tool names as strings — there is no compile-time check that \`mcp__schwab__*\` exists. If the user runs your recipe without a \`schwab\` MCP server configured, the wildcard expands to nothing and the agent will have no Schwab tools available. Design your prompts defensively, and document required MCP servers / toolkits in the recipe description.
|
|
626
|
+
|
|
627
|
+
#### Loop Step (Iterate)
|
|
628
|
+
\`\`\`javascript
|
|
629
|
+
add_recipe_step({
|
|
630
|
+
hash: "abc123xyz",
|
|
631
|
+
name: "review-each-file",
|
|
632
|
+
type: "loop",
|
|
633
|
+
config: {
|
|
634
|
+
loop_over: "file_list", // Store with array
|
|
635
|
+
loop_variable: "current_file", // Name for current item
|
|
636
|
+
loop_step_id: "step-3", // Step to run per item
|
|
637
|
+
parallel: true, // Run iterations concurrently
|
|
638
|
+
max_concurrent: 5 // Max 5 at once
|
|
639
|
+
}
|
|
640
|
+
})
|
|
641
|
+
// Returns: { id: "step-2", ... }
|
|
642
|
+
\`\`\`
|
|
643
|
+
|
|
644
|
+
#### Parallel Step (Concurrent)
|
|
645
|
+
\`\`\`javascript
|
|
646
|
+
add_recipe_step({
|
|
647
|
+
hash: "abc123xyz",
|
|
648
|
+
name: "run-checks",
|
|
649
|
+
type: "parallel",
|
|
650
|
+
config: {
|
|
651
|
+
parallel_step_ids: ["step-4", "step-5", "step-6"]
|
|
652
|
+
}
|
|
653
|
+
})
|
|
654
|
+
\`\`\`
|
|
655
|
+
|
|
656
|
+
#### Gate Step (Approval/Input)
|
|
657
|
+
\`\`\`javascript
|
|
658
|
+
add_recipe_step({
|
|
659
|
+
hash: "abc123xyz",
|
|
660
|
+
name: "approve-changes",
|
|
661
|
+
type: "gate",
|
|
662
|
+
config: {
|
|
663
|
+
requires_approval: true,
|
|
664
|
+
approval_prompt: "Review findings:\\n{{findings}}\\n\\nApprove?",
|
|
665
|
+
input_options: {
|
|
666
|
+
button_mode: "preset",
|
|
667
|
+
preset: "approve_deny",
|
|
668
|
+
allow_comment: true,
|
|
669
|
+
comment_required: false
|
|
670
|
+
},
|
|
671
|
+
input_output_store: "approval_decision"
|
|
672
|
+
}
|
|
673
|
+
})
|
|
674
|
+
\`\`\`
|
|
675
|
+
|
|
676
|
+
**Gate Input Presets:**
|
|
677
|
+
- \`approve_deny\` - Approve or Deny buttons
|
|
678
|
+
- \`yes_no\` - Yes or No buttons
|
|
679
|
+
- \`continue_cancel\` - Continue or Cancel buttons
|
|
680
|
+
|
|
681
|
+
**Custom buttons:**
|
|
682
|
+
\`\`\`json
|
|
683
|
+
{
|
|
684
|
+
"button_mode": "custom",
|
|
685
|
+
"buttons": [
|
|
686
|
+
{ "label": "Fix Now", "value": "fix", "isApproval": true },
|
|
687
|
+
{ "label": "Skip", "value": "skip", "isApproval": false }
|
|
688
|
+
]
|
|
689
|
+
}
|
|
690
|
+
\`\`\`
|
|
691
|
+
|
|
692
|
+
#### Branch Step (Conditional)
|
|
693
|
+
\`\`\`javascript
|
|
694
|
+
add_recipe_step({
|
|
695
|
+
hash: "abc123xyz",
|
|
696
|
+
name: "route-by-severity",
|
|
697
|
+
type: "branch",
|
|
698
|
+
config: {
|
|
699
|
+
conditions: [
|
|
700
|
+
{ expression: "{{severity}} === 'high'", then: "step-urgent" },
|
|
701
|
+
{ expression: "{{severity}} === 'medium'", then: "step-normal" }
|
|
702
|
+
],
|
|
703
|
+
default: "step-low-priority"
|
|
704
|
+
}
|
|
705
|
+
})
|
|
706
|
+
\`\`\`
|
|
707
|
+
|
|
708
|
+
#### Invoke Step (Subroutine)
|
|
709
|
+
\`\`\`javascript
|
|
710
|
+
add_recipe_step({
|
|
711
|
+
hash: "abc123xyz",
|
|
712
|
+
name: "call-linter",
|
|
713
|
+
type: "invoke",
|
|
714
|
+
config: {
|
|
715
|
+
recipe_hash: "linter-recipe-hash",
|
|
716
|
+
input_mapping: {
|
|
717
|
+
"file_path": "{{current_file}}"
|
|
718
|
+
},
|
|
719
|
+
output_mapping: {
|
|
720
|
+
"lint_result": "lint_output"
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
})
|
|
724
|
+
\`\`\`
|
|
725
|
+
|
|
726
|
+
### Step 4: Connect Steps
|
|
727
|
+
\`\`\`javascript
|
|
728
|
+
// Set the "next" step for sequential flow
|
|
729
|
+
update_recipe_step({
|
|
730
|
+
hash: "abc123xyz",
|
|
731
|
+
step_id: "step-1",
|
|
732
|
+
next: "step-2", // Run step-2 after step-1 succeeds
|
|
733
|
+
on_error: "step-error" // Run step-error if step-1 fails
|
|
734
|
+
})
|
|
735
|
+
\`\`\`
|
|
736
|
+
|
|
737
|
+
### Step 5: Set Entry Point
|
|
738
|
+
|
|
739
|
+
**CRITICAL:** Recipe won't run until you set the entry_step_id!
|
|
740
|
+
|
|
741
|
+
\`\`\`javascript
|
|
742
|
+
update_recipe({
|
|
743
|
+
hash: "abc123xyz",
|
|
744
|
+
entry_step_id: "step-1" // First step to run
|
|
745
|
+
})
|
|
746
|
+
\`\`\`
|
|
747
|
+
|
|
748
|
+
### Step 6: Link for CLI Access
|
|
749
|
+
\`\`\`javascript
|
|
750
|
+
link_recipe({
|
|
751
|
+
hash: "abc123xyz",
|
|
752
|
+
alias: "code-reviewer" // Use hyphens, not underscores!
|
|
753
|
+
})
|
|
754
|
+
\`\`\`
|
|
755
|
+
|
|
756
|
+
**CRITICAL Alias Rules:**
|
|
757
|
+
- Use HYPHENS: \`my-recipe\` ✓
|
|
758
|
+
- NO underscores: \`my_recipe\` ✗ (causes 422 error)
|
|
759
|
+
- Lowercase, alphanumeric + hyphens only
|
|
760
|
+
|
|
761
|
+
### Step 7: Run via CLI
|
|
762
|
+
\`\`\`bash
|
|
763
|
+
# Run with alias
|
|
764
|
+
npx flowdot recipes run code-reviewer --input '{"request":"Review src/app.js"}'
|
|
765
|
+
|
|
766
|
+
# Or with hash
|
|
767
|
+
npx flowdot recipes run abc123xyz --input '{"request":"Review src/app.js"}'
|
|
768
|
+
\`\`\`
|
|
769
|
+
|
|
770
|
+
## Complete Example
|
|
771
|
+
|
|
772
|
+
\`\`\`javascript
|
|
773
|
+
// 1. Create recipe
|
|
774
|
+
const recipe = await create_recipe({
|
|
775
|
+
name: "code-reviewer",
|
|
776
|
+
description: "Reviews code and suggests improvements"
|
|
777
|
+
});
|
|
778
|
+
const hash = recipe.hash;
|
|
779
|
+
|
|
780
|
+
// 2. Define stores
|
|
781
|
+
await add_recipe_store({ hash, key: "request", is_input: true });
|
|
782
|
+
await add_recipe_store({ hash, key: "review", is_output: true });
|
|
783
|
+
await add_recipe_store({ hash, key: "files" });
|
|
784
|
+
|
|
785
|
+
// 3. Add agent step
|
|
786
|
+
const step1 = await add_recipe_step({
|
|
787
|
+
hash,
|
|
788
|
+
name: "find-files",
|
|
789
|
+
type: "agent",
|
|
790
|
+
config: {
|
|
791
|
+
user_prompt: "Find files to review: {{inputs.request}}",
|
|
792
|
+
tools: ["search"],
|
|
793
|
+
output_store: "files"
|
|
794
|
+
}
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
// 4. Add review step
|
|
798
|
+
const step2 = await add_recipe_step({
|
|
799
|
+
hash,
|
|
800
|
+
name: "review-code",
|
|
801
|
+
type: "agent",
|
|
802
|
+
config: {
|
|
803
|
+
user_prompt: "Review these files: {{files}}",
|
|
804
|
+
tools: ["read", "analyze"],
|
|
805
|
+
output_store: "review"
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
// 5. Connect steps
|
|
810
|
+
await update_recipe_step({ hash, step_id: step1.id, next: step2.id });
|
|
811
|
+
|
|
812
|
+
// 6. Set entry point
|
|
813
|
+
await update_recipe({ hash, entry_step_id: step1.id });
|
|
814
|
+
|
|
815
|
+
// 7. Link for CLI
|
|
816
|
+
await link_recipe({ hash, alias: "code-reviewer" });
|
|
817
|
+
\`\`\`
|
|
818
|
+
|
|
819
|
+
## Model Tiers
|
|
820
|
+
|
|
821
|
+
Recipes support different model tiers per step. Pick the cheapest tier that works:
|
|
822
|
+
|
|
823
|
+
| Tier | Use Case | Example Models |
|
|
824
|
+
|------|----------|----------------|
|
|
825
|
+
| \`lite\` | Simple extraction, formatting, classification | gemini-2.5-flash-lite |
|
|
826
|
+
| \`capable\` | General reasoning, tool use, most agent steps | gpt-4o-mini, gemini-2.5-flash |
|
|
827
|
+
| \`complex\` | Hard reasoning, final synthesis, judgment calls | claude-3.5-sonnet, gpt-4o |
|
|
828
|
+
|
|
829
|
+
Set per agent step in the config:
|
|
830
|
+
|
|
831
|
+
\`\`\`javascript
|
|
832
|
+
add_recipe_step({
|
|
833
|
+
hash: "abc123xyz",
|
|
834
|
+
name: "synthesize",
|
|
835
|
+
type: "agent",
|
|
836
|
+
config: {
|
|
837
|
+
model_tier: "complex", // Use complex only for the final synthesis
|
|
838
|
+
user_prompt: "...",
|
|
839
|
+
output_store: "answer"
|
|
840
|
+
}
|
|
841
|
+
})
|
|
842
|
+
\`\`\`
|
|
843
|
+
|
|
844
|
+
**Cost optimization rule of thumb:** Use \`lite\` for any deterministic extraction, \`capable\` for tool-using agents (the bulk of most recipes), and \`complex\` only when judgment quality matters and \`capable\` isn't enough. A typical research recipe runs 80% of its steps on \`lite\` or \`capable\`, with one \`complex\` synthesizer at the end.
|
|
845
|
+
|
|
846
|
+
## Designing for Small Models
|
|
847
|
+
|
|
848
|
+
Recipes should work on \`lite\` and \`capable\` tiers, not just \`complex\`. Small models are fast and cheap but they need **explicit, prescriptive prompts**. Vague prompts that work on \`complex\` will silently fail on \`lite\`.
|
|
849
|
+
|
|
850
|
+
**The problem:**
|
|
851
|
+
|
|
852
|
+
\`\`\`
|
|
853
|
+
Use the search tool to find relevant files.
|
|
854
|
+
\`\`\`
|
|
855
|
+
|
|
856
|
+
A small model with this prompt doesn't know what to search for, picks bad keywords, runs one search, gets nothing, and gives up.
|
|
857
|
+
|
|
858
|
+
**The fix: prescriptive prompt lecturing.** Tell the model exactly what to do, with concrete examples:
|
|
859
|
+
|
|
860
|
+
\`\`\`
|
|
861
|
+
### Step 1: Search for Related Files
|
|
862
|
+
Use the 'search' tool with keywords from the user's request. Examples:
|
|
863
|
+
- For "how does authentication work" → search for: auth, login, token, session
|
|
864
|
+
- For "how does routing work" → search for: router, route, endpoint, handler
|
|
865
|
+
- For "how does the database work" → search for: database, query, model, schema
|
|
866
|
+
|
|
867
|
+
Run at least 3 different searches with different keywords.
|
|
868
|
+
\`\`\`
|
|
869
|
+
|
|
870
|
+
This is the single most important skill for writing recipes that scale across model tiers.
|
|
871
|
+
|
|
872
|
+
## Prompt Lecturing Principles
|
|
873
|
+
|
|
874
|
+
When writing agent step prompts, apply these five principles:
|
|
875
|
+
|
|
876
|
+
### 1. Give Concrete Examples
|
|
877
|
+
|
|
878
|
+
**Bad:**
|
|
879
|
+
\`\`\`
|
|
880
|
+
Search for files related to the request.
|
|
881
|
+
\`\`\`
|
|
882
|
+
|
|
883
|
+
**Good:**
|
|
884
|
+
\`\`\`
|
|
885
|
+
Search for files using these example patterns:
|
|
886
|
+
- "handleLogin" - function names
|
|
887
|
+
- "AuthService" - class names
|
|
888
|
+
- "Bearer" - specific strings in code
|
|
889
|
+
\`\`\`
|
|
890
|
+
|
|
891
|
+
### 2. Quantify Requirements
|
|
892
|
+
|
|
893
|
+
**Bad:**
|
|
894
|
+
\`\`\`
|
|
895
|
+
Use tools before responding.
|
|
896
|
+
\`\`\`
|
|
897
|
+
|
|
898
|
+
**Good:**
|
|
899
|
+
\`\`\`
|
|
900
|
+
You MUST:
|
|
901
|
+
1. Run at least 3 different searches
|
|
902
|
+
2. Read at least 2 files
|
|
903
|
+
3. Only then generate your response
|
|
904
|
+
\`\`\`
|
|
905
|
+
|
|
906
|
+
### 3. Provide Fallback Paths
|
|
907
|
+
|
|
908
|
+
**Bad:**
|
|
909
|
+
\`\`\`
|
|
910
|
+
Search for the relevant code.
|
|
911
|
+
\`\`\`
|
|
912
|
+
|
|
913
|
+
**Good:**
|
|
914
|
+
\`\`\`
|
|
915
|
+
Search for relevant code. If your first search returns no results:
|
|
916
|
+
- Try alternative keywords
|
|
917
|
+
- Search for broader terms
|
|
918
|
+
- Look for related concepts
|
|
919
|
+
\`\`\`
|
|
920
|
+
|
|
921
|
+
### 4. Specify Exact Output Format
|
|
922
|
+
|
|
923
|
+
**Bad:**
|
|
924
|
+
\`\`\`
|
|
925
|
+
Return your findings as JSON.
|
|
926
|
+
\`\`\`
|
|
927
|
+
|
|
928
|
+
**Good:**
|
|
929
|
+
\`\`\`
|
|
930
|
+
Your final response must be ONLY a valid JSON array. No markdown, no explanation:
|
|
931
|
+
["Question about file 1?", "Question about file 2?", "Question 3?"]
|
|
932
|
+
|
|
933
|
+
Example correct output:
|
|
934
|
+
["How does AuthService.authenticate() validate tokens?", "What middleware checks sessions?"]
|
|
935
|
+
\`\`\`
|
|
936
|
+
|
|
937
|
+
### 5. Prevent Common Failures
|
|
938
|
+
|
|
939
|
+
**Bad:**
|
|
940
|
+
\`\`\`
|
|
941
|
+
Find information about authentication.
|
|
942
|
+
\`\`\`
|
|
943
|
+
|
|
944
|
+
**Good:**
|
|
945
|
+
\`\`\`
|
|
946
|
+
You are searching LOCAL FILES in a codebase.
|
|
947
|
+
DO NOT use web search.
|
|
948
|
+
DO NOT make up file paths.
|
|
949
|
+
DO NOT cite files you haven't actually read with the 'read' tool.
|
|
950
|
+
\`\`\`
|
|
951
|
+
|
|
952
|
+
**The meta-rule:** Show, don't tell. A concrete example is worth ten sentences of description.
|
|
953
|
+
|
|
954
|
+
## Gate Steps via COMMS (Remote Approvals)
|
|
955
|
+
|
|
956
|
+
Gate steps don't only pause for terminal approval — they can route the approval request through a user's COMMS channel (Telegram, Discord) so they can approve from anywhere. The user clicks a button on their phone and the recipe continues.
|
|
957
|
+
|
|
958
|
+
This is what enables long-running recipes that span hours. The user kicks off the recipe, walks away, gets a Telegram notification at the gate step, taps a button, and the recipe continues without them returning to their terminal.
|
|
959
|
+
|
|
960
|
+
The same \`input_options\` schema works for COMMS as for terminal gates — preset buttons, custom buttons, \`allow_comment\`, and \`comment_required\` all behave identically. The only difference is the *channel* through which the approval is collected, and that's controlled by the user's COMMS configuration, not by the recipe definition.
|
|
961
|
+
|
|
962
|
+
**For recipe designers:** You don't need to write COMMS-specific code. Just author the gate step as you normally would. If the user has a COMMS channel configured and the recipe is run with COMMS routing enabled, the gate request automatically flows through that channel. The recipe is portable across terminal-only and remote-controlled execution without any change.
|
|
963
|
+
|
|
964
|
+
## Managing Recipes
|
|
965
|
+
|
|
966
|
+
\`\`\`javascript
|
|
967
|
+
// List recipes
|
|
968
|
+
list_recipes()
|
|
969
|
+
|
|
970
|
+
// Get recipe details
|
|
971
|
+
get_recipe({ hash: "abc123xyz" })
|
|
972
|
+
|
|
973
|
+
// Get full definition (YAML/JSON)
|
|
974
|
+
get_recipe_definition({ hash: "abc123xyz", format: "yaml" })
|
|
975
|
+
|
|
976
|
+
// List steps
|
|
977
|
+
list_recipe_steps({ hash: "abc123xyz" })
|
|
978
|
+
|
|
979
|
+
// List stores
|
|
980
|
+
list_recipe_stores({ hash: "abc123xyz" })
|
|
981
|
+
|
|
982
|
+
// Browse public recipes
|
|
983
|
+
browse_recipes({ search: "code", sort: "popular" })
|
|
984
|
+
|
|
985
|
+
// Fork a public recipe
|
|
986
|
+
fork_recipe({ hash: "public-recipe-hash", name: "My Fork" })
|
|
987
|
+
|
|
988
|
+
// Delete recipe
|
|
989
|
+
delete_recipe({ hash: "abc123xyz", confirm: true })
|
|
990
|
+
\`\`\`
|
|
991
|
+
|
|
992
|
+
## Debugging Recipes
|
|
993
|
+
|
|
994
|
+
When a recipe doesn't work, check these in order:
|
|
995
|
+
|
|
996
|
+
### 1. Inspect the recipe definition
|
|
997
|
+
|
|
998
|
+
Use \`get_recipe_definition({ hash, format: "yaml" })\` to dump the entire recipe as YAML and review it. This is the fastest way to spot misconfigured stores, missing connections, or wrong step types.
|
|
999
|
+
|
|
1000
|
+
### 2. List the steps and stores explicitly
|
|
1001
|
+
|
|
1002
|
+
\`\`\`javascript
|
|
1003
|
+
list_recipe_steps({ hash: "abc123xyz" })
|
|
1004
|
+
list_recipe_stores({ hash: "abc123xyz" })
|
|
1005
|
+
\`\`\`
|
|
1006
|
+
|
|
1007
|
+
Confirm:
|
|
1008
|
+
- The entry step ID matches the first step you want to run
|
|
1009
|
+
- Every step's \`next\` points to a real step ID
|
|
1010
|
+
- Every \`output_store\` references a real store key
|
|
1011
|
+
- The primary input store is named \`request\`
|
|
1012
|
+
|
|
1013
|
+
### 3. Check for the most common mistakes
|
|
1014
|
+
|
|
1015
|
+
| Symptom | Cause |
|
|
1016
|
+
|---------|-------|
|
|
1017
|
+
| Recipe won't start | \`entry_step_id\` not set on the recipe |
|
|
1018
|
+
| 422 error linking alias | Alias contains underscores instead of hyphens |
|
|
1019
|
+
| Agent ignores prompt | Used \`prompt\` instead of \`user_prompt\` in agent step config |
|
|
1020
|
+
| Loop produces empty results | \`output_store\` not set on the loop step config |
|
|
1021
|
+
| Step output not visible to next step | The step's \`output_store\` doesn't match what the next step interpolates |
|
|
1022
|
+
| Branch step always falls through to default | \`expression\` syntax wrong — must be a JS-style boolean expression |
|
|
1023
|
+
| Small model gives up immediately | Prompt isn't prescriptive enough — apply prompt lecturing principles |
|
|
1024
|
+
| MCP tool not found at runtime | User's CLI environment doesn't have that MCP server configured — document required servers in the recipe description |
|
|
1025
|
+
| \`mcp__*\` wildcard expands to nothing | No MCP servers registered in the CLI, or the named server isn't running |
|
|
1026
|
+
|
|
1027
|
+
### 4. Persistent execution state (CLI-side)
|
|
1028
|
+
|
|
1029
|
+
When a recipe runs on the FlowDot CLI, every execution persists state to disk so you can post-mortem failures. Each execution gets its own folder with:
|
|
1030
|
+
|
|
1031
|
+
- **state.json** — overall execution state
|
|
1032
|
+
- **stores.json** — store values at each step
|
|
1033
|
+
- **logs/** — per-step logs
|
|
1034
|
+
|
|
1035
|
+
The CLI also supports a \`DEBUG=RECIPE\` environment variable for verbose recipe-runtime tracing. Both of these are CLI-side artifacts — MCP-driven debugging works through \`get_recipe_definition\`, \`list_recipe_steps\`, and \`list_recipe_stores\` instead.
|
|
1036
|
+
|
|
1037
|
+
## Best Practices
|
|
1038
|
+
|
|
1039
|
+
1. **Name primary input \`request\`** - CLI convention
|
|
1040
|
+
2. **Use \`user_prompt\` not \`prompt\`** - Runtime requirement
|
|
1041
|
+
3. **Set entry_step_id** - Recipe won't run without it
|
|
1042
|
+
4. **Use hyphens in aliases** - Not underscores
|
|
1043
|
+
5. **Save all IDs** - You need hashes and step IDs for updates
|
|
1044
|
+
6. **Test incrementally** - Build one step at a time
|
|
1045
|
+
7. **Handle errors** - Use \`on_error\` for critical steps
|
|
1046
|
+
8. **Reach beyond the built-ins** - If the task needs browser automation, trading APIs, or any external capability, check whether the user has an MCP server or toolkit for it and reference its tools directly in the step's \`tools\` array (e.g. \`mcp__electron-qa__*\`) rather than trying to shoehorn it into \`execute-command\`
|
|
1047
|
+
|
|
1048
|
+
## Troubleshooting
|
|
1049
|
+
|
|
1050
|
+
### Recipe Won't Execute
|
|
1051
|
+
- Check entry_step_id is set: \`update_recipe\`
|
|
1052
|
+
- Verify alias is linked: \`link_recipe\`
|
|
1053
|
+
- Ensure stores are defined (especially \`request\`)
|
|
1054
|
+
|
|
1055
|
+
### Steps Not Connecting
|
|
1056
|
+
- Verify step IDs are correct
|
|
1057
|
+
- Use \`list_recipe_steps\` to see all step IDs
|
|
1058
|
+
- Check \`next\` and \`on_error\` are valid step IDs
|
|
1059
|
+
|
|
1060
|
+
### Agent Steps Failing
|
|
1061
|
+
- Use \`user_prompt\` not \`prompt\`
|
|
1062
|
+
- Verify tool names are correct
|
|
1063
|
+
- Check interpolation syntax: \`{{store_key}}\`
|
|
1064
|
+
|
|
1065
|
+
## Recipe Design Checklist
|
|
1066
|
+
|
|
1067
|
+
Before declaring a recipe done, verify:
|
|
1068
|
+
|
|
1069
|
+
- [ ] Architecture pattern chosen up front (Orchestrator+Workers / Sequential / Parallel Fan-Out)
|
|
1070
|
+
- [ ] Primary input store is named \`request\`
|
|
1071
|
+
- [ ] Output stores marked with \`is_output: true\`
|
|
1072
|
+
- [ ] All intermediate stores defined with appropriate types
|
|
1073
|
+
- [ ] Every agent step has \`user_prompt\` (not \`prompt\`)
|
|
1074
|
+
- [ ] Every agent step has \`output_store\` configured
|
|
1075
|
+
- [ ] Every loop step has \`loop_variable\` and \`output_store\`
|
|
1076
|
+
- [ ] All steps connected via \`next\`
|
|
1077
|
+
- [ ] \`on_error\` set on critical steps
|
|
1078
|
+
- [ ] \`entry_step_id\` set on the recipe
|
|
1079
|
+
- [ ] Alias linked using **hyphens**, not underscores
|
|
1080
|
+
- [ ] Each step uses the cheapest model tier that works (\`lite\` > \`capable\` > \`complex\`)
|
|
1081
|
+
- [ ] Prompts include concrete examples (Principle 1)
|
|
1082
|
+
- [ ] Prompts quantify requirements (Principle 2)
|
|
1083
|
+
- [ ] Prompts provide fallback paths (Principle 3)
|
|
1084
|
+
- [ ] Output format explicitly specified with example (Principle 4)
|
|
1085
|
+
- [ ] Common failure modes called out as DO NOT instructions (Principle 5)
|
|
1086
|
+
- [ ] Any external capability (browser, trading, email, etc.) is wired via an \`mcp__*\` or \`toolkit__*\` tool reference rather than hacked around with \`execute-command\`
|
|
1087
|
+
- [ ] Required MCP servers / toolkits listed in the recipe description so users know what to configure
|
|
1088
|
+
|
|
1089
|
+
## Related Resources
|
|
1090
|
+
|
|
1091
|
+
- **Workflows:** \`learn://workflows\` - Visual automation workflows
|
|
1092
|
+
- **Custom Nodes:** \`learn://custom-nodes\` - Extend agent capabilities
|
|
1093
|
+
- **Toolkits:** \`learn://toolkits\` - MCP toolkit integration
|
|
674
1094
|
`,
|
|
675
1095
|
},
|
|
676
1096
|
'learn://custom-nodes': {
|
|
677
1097
|
name: 'Custom Nodes Complete Guide',
|
|
678
1098
|
description: 'Complete guide to creating and managing custom nodes in FlowDot',
|
|
679
1099
|
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
|
-
##
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
//
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
return {
|
|
770
|
-
Summary:
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
\`\`\`
|
|
774
|
-
|
|
775
|
-
**
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
\`\`\`
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
//
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
})
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
//
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1100
|
+
content: `# FlowDot Custom Nodes - Complete Guide
|
|
1101
|
+
|
|
1102
|
+
## What Are Custom Nodes?
|
|
1103
|
+
|
|
1104
|
+
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.
|
|
1105
|
+
|
|
1106
|
+
## Key Concepts
|
|
1107
|
+
|
|
1108
|
+
### Inputs
|
|
1109
|
+
Data the node receives:
|
|
1110
|
+
- Define name, data type, and description
|
|
1111
|
+
- Access via \`inputs.InputName\` in script
|
|
1112
|
+
- **Valid types:** text, number, boolean, json, array, any
|
|
1113
|
+
|
|
1114
|
+
### Outputs
|
|
1115
|
+
Data the node produces:
|
|
1116
|
+
- Define name, data type, and description
|
|
1117
|
+
- Return via \`return { OutputName: value }\`
|
|
1118
|
+
- **Must match exactly** (case-sensitive)
|
|
1119
|
+
|
|
1120
|
+
### Properties
|
|
1121
|
+
Configuration values:
|
|
1122
|
+
- Set by user in node UI
|
|
1123
|
+
- Access via \`properties.propertyKey\`
|
|
1124
|
+
- Examples: API URLs, prompts, thresholds
|
|
1125
|
+
|
|
1126
|
+
### Script
|
|
1127
|
+
JavaScript code that processes inputs:
|
|
1128
|
+
- **Must define:** \`function processData(inputs, properties, llm)\`
|
|
1129
|
+
- **Must return:** Object with output names as keys
|
|
1130
|
+
- **Sandboxed:** No imports, eval, or file system access
|
|
1131
|
+
|
|
1132
|
+
### LLM Capability (Optional)
|
|
1133
|
+
Enable AI features:
|
|
1134
|
+
- Users see Quick Select buttons (FlowDot, Simple, Capable, Complex)
|
|
1135
|
+
- Script can call \`llm.call()\` to make LLM requests
|
|
1136
|
+
- Useful for AI-powered processing
|
|
1137
|
+
|
|
1138
|
+
## Inputs vs Properties: When to Use Which
|
|
1139
|
+
|
|
1140
|
+
Both inputs and properties feed data into your node, but they're meant for different things. Choosing wrong is the most common design mistake in custom nodes.
|
|
1141
|
+
|
|
1142
|
+
| Use **inputs** when | Use **properties** when |
|
|
1143
|
+
|---------------------|--------------------------|
|
|
1144
|
+
| The value comes from another node at runtime | The value is configured once when the node is added to a workflow |
|
|
1145
|
+
| The value changes per execution | The value is the same every execution |
|
|
1146
|
+
| The value is dynamic (e.g., user query, file contents, API response) | The value is static (e.g., API URL, prompt template, threshold) |
|
|
1147
|
+
| You want the value to flow through the workflow graph | You want the value to be invisible at the graph level |
|
|
1148
|
+
|
|
1149
|
+
**Examples:**
|
|
1150
|
+
- A summarizer node: \`Text\` is an **input** (changes per call), \`maxLength\` is a **property** (configured once)
|
|
1151
|
+
- An HTTP wrapper node: \`requestBody\` is an **input** (built upstream), \`apiBaseUrl\` is a **property** (set per workflow)
|
|
1152
|
+
- A classifier node: \`text\` is an **input**, \`categories\` is a **property** (the fixed set of labels)
|
|
1153
|
+
|
|
1154
|
+
**Rule of thumb:** If you'd connect an arrow to it from another node, it's an input. If a user would type it into a config form, it's a property.
|
|
1155
|
+
|
|
1156
|
+
## Creating a Custom Node
|
|
1157
|
+
|
|
1158
|
+
### Step 1: Get Template (Optional)
|
|
1159
|
+
\`\`\`javascript
|
|
1160
|
+
get_custom_node_template({
|
|
1161
|
+
inputs: [
|
|
1162
|
+
{ name: "Text", dataType: "text" },
|
|
1163
|
+
{ name: "MaxLength", dataType: "number" }
|
|
1164
|
+
],
|
|
1165
|
+
outputs: [
|
|
1166
|
+
{ name: "Summary", dataType: "text" }
|
|
1167
|
+
],
|
|
1168
|
+
llm_enabled: true
|
|
1169
|
+
})
|
|
1170
|
+
// Returns template code you can customize
|
|
1171
|
+
\`\`\`
|
|
1172
|
+
|
|
1173
|
+
### Step 2: Write Your Script
|
|
1174
|
+
|
|
1175
|
+
**REQUIRED FORMAT:**
|
|
1176
|
+
\`\`\`javascript
|
|
1177
|
+
function processData(inputs, properties, llm) {
|
|
1178
|
+
// Access inputs by exact names
|
|
1179
|
+
const text = inputs.Text || '';
|
|
1180
|
+
const maxLength = inputs.MaxLength || 100;
|
|
1181
|
+
|
|
1182
|
+
// Access properties
|
|
1183
|
+
const apiUrl = properties.apiUrl || 'https://api.example.com';
|
|
1184
|
+
|
|
1185
|
+
// Your logic here
|
|
1186
|
+
const summary = text.substring(0, maxLength);
|
|
1187
|
+
|
|
1188
|
+
// Return object with keys matching output names EXACTLY
|
|
1189
|
+
return {
|
|
1190
|
+
Summary: summary
|
|
1191
|
+
};
|
|
1192
|
+
}
|
|
1193
|
+
\`\`\`
|
|
1194
|
+
|
|
1195
|
+
**With LLM:**
|
|
1196
|
+
\`\`\`javascript
|
|
1197
|
+
function processData(inputs, properties, llm) {
|
|
1198
|
+
const text = inputs.Text || '';
|
|
1199
|
+
|
|
1200
|
+
// Call LLM
|
|
1201
|
+
const result = llm.call({
|
|
1202
|
+
prompt: \`Summarize: \${text}\`,
|
|
1203
|
+
temperature: 0.7,
|
|
1204
|
+
maxTokens: 500
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
return {
|
|
1208
|
+
Summary: result.success ? result.response : result.error
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
\`\`\`
|
|
1212
|
+
|
|
1213
|
+
**Important Rules:**
|
|
1214
|
+
- ✅ processData function is REQUIRED
|
|
1215
|
+
- ✅ Input/output names are case-sensitive
|
|
1216
|
+
- ✅ Return keys must match output names exactly
|
|
1217
|
+
- ❌ No top-level return statements
|
|
1218
|
+
- ❌ No require/import, eval, process, global
|
|
1219
|
+
- ❌ No file system access
|
|
1220
|
+
- ✅ Available: console, JSON, Math, String, Array methods
|
|
1221
|
+
|
|
1222
|
+
## What the Script Validator Enforces
|
|
1223
|
+
|
|
1224
|
+
Before your custom node is saved, FlowDot's MCP server runs your script through an **AST-based validator** (using the \`acorn\` JavaScript parser) — not regex. This means the validator can catch things that pattern matching would miss. Knowing what's checked helps you avoid silent rejection.
|
|
1225
|
+
|
|
1226
|
+
**Hard errors (script will be rejected):**
|
|
1227
|
+
- **Syntax errors** — must be valid ES2020 JavaScript
|
|
1228
|
+
- **Missing \`processData\` function** — the validator walks the AST looking for a top-level function declaration with that exact name
|
|
1229
|
+
- **No return statement inside \`processData\`** — function with no return is rejected
|
|
1230
|
+
- **Top-level return statements** — rejected (returns must be inside the function)
|
|
1231
|
+
- **Output key mismatches** — every output you declared must appear as a key in the function's return statement; extras are also flagged
|
|
1232
|
+
- **Banned globals** — any reference to \`eval\`, \`Function\`, \`require\`, \`import\`, \`process\`, \`global\`, \`globalThis\`, \`window\`, \`document\`, \`fetch\`, \`XMLHttpRequest\`, or \`WebSocket\` is rejected
|
|
1233
|
+
|
|
1234
|
+
**Soft warnings (script saves but you'll see warnings):**
|
|
1235
|
+
- Unused inputs (declared in your inputs array but never read in the script)
|
|
1236
|
+
- Unhandled error paths in async-style code
|
|
1237
|
+
- Complex control flow that might be hard to maintain
|
|
1238
|
+
|
|
1239
|
+
**Why this matters:** The validator catches mismatches between your declared schema (the inputs/outputs arrays you pass to \`create_custom_node\`) and your actual code. If you declare an output named \`Summary\` but your code returns \`{ summary: ... }\` (lowercase), the validator will reject the script with a clear error pointing to the mismatch — you don't have to wait until runtime to find out.
|
|
1240
|
+
|
|
1241
|
+
**Practical workflow:**
|
|
1242
|
+
1. Get a template with \`get_custom_node_template\` — this generates code where inputs and outputs already match your schema
|
|
1243
|
+
2. Modify the template body, but keep the input/output access patterns
|
|
1244
|
+
3. Submit with \`create_custom_node\` — if the validator rejects, the error message tells you exactly what to fix
|
|
1245
|
+
|
|
1246
|
+
### Step 3: Create Node
|
|
1247
|
+
\`\`\`javascript
|
|
1248
|
+
create_custom_node({
|
|
1249
|
+
name: "text-summarizer",
|
|
1250
|
+
title: "Text Summarizer",
|
|
1251
|
+
description: "Summarizes text to a specified length",
|
|
1252
|
+
inputs: [
|
|
1253
|
+
{
|
|
1254
|
+
name: "Text",
|
|
1255
|
+
dataType: "text",
|
|
1256
|
+
description: "The text to summarize"
|
|
1257
|
+
},
|
|
1258
|
+
{
|
|
1259
|
+
name: "MaxLength",
|
|
1260
|
+
dataType: "number",
|
|
1261
|
+
description: "Maximum summary length"
|
|
1262
|
+
}
|
|
1263
|
+
],
|
|
1264
|
+
outputs: [
|
|
1265
|
+
{
|
|
1266
|
+
name: "Summary",
|
|
1267
|
+
dataType: "text",
|
|
1268
|
+
description: "The summarized text"
|
|
1269
|
+
}
|
|
1270
|
+
],
|
|
1271
|
+
script_code: "function processData(inputs, properties, llm) { ... }",
|
|
1272
|
+
llm_enabled: false,
|
|
1273
|
+
execution_timeout: 5000,
|
|
1274
|
+
memory_limit: 128
|
|
1275
|
+
})
|
|
1276
|
+
// Returns: { hash: "node-abc123", ... }
|
|
1277
|
+
\`\`\`
|
|
1278
|
+
|
|
1279
|
+
### Step 4: Test Your Node
|
|
1280
|
+
\`\`\`javascript
|
|
1281
|
+
// Add to a workflow
|
|
1282
|
+
add_node({
|
|
1283
|
+
workflow_id: "workflow-123",
|
|
1284
|
+
node_type: "custom_node_abc123", // custom_node_{hash}
|
|
1285
|
+
position: { x: 100, y: 100 }
|
|
1286
|
+
})
|
|
1287
|
+
\`\`\`
|
|
1288
|
+
|
|
1289
|
+
## LLM-Enabled Nodes
|
|
1290
|
+
|
|
1291
|
+
Enable AI capabilities in your custom nodes:
|
|
1292
|
+
|
|
1293
|
+
\`\`\`javascript
|
|
1294
|
+
create_custom_node({
|
|
1295
|
+
name: "ai-analyzer",
|
|
1296
|
+
title: "AI Analyzer",
|
|
1297
|
+
description: "Analyzes data with AI",
|
|
1298
|
+
inputs: [{ name: "Data", dataType: "text" }],
|
|
1299
|
+
outputs: [{ name: "Analysis", dataType: "text" }],
|
|
1300
|
+
llm_enabled: true, // Enable LLM
|
|
1301
|
+
script_code: \`
|
|
1302
|
+
function processData(inputs, properties, llm) {
|
|
1303
|
+
const result = llm.call({
|
|
1304
|
+
prompt: "Analyze: " + inputs.Data,
|
|
1305
|
+
systemPrompt: "You are an expert analyst.",
|
|
1306
|
+
temperature: 0.7,
|
|
1307
|
+
maxTokens: 1000
|
|
1308
|
+
});
|
|
1309
|
+
|
|
1310
|
+
return {
|
|
1311
|
+
Analysis: result.success ? result.response : "Error: " + result.error
|
|
1312
|
+
};
|
|
1313
|
+
}
|
|
1314
|
+
\`
|
|
1315
|
+
})
|
|
1316
|
+
\`\`\`
|
|
1317
|
+
|
|
1318
|
+
**LLM Response Structure:**
|
|
1319
|
+
\`\`\`javascript
|
|
1320
|
+
{
|
|
1321
|
+
success: boolean, // true if call succeeded
|
|
1322
|
+
response: string, // The LLM's response text
|
|
1323
|
+
error: string | null, // Error message if failed
|
|
1324
|
+
provider: string, // Provider used (e.g., "openai")
|
|
1325
|
+
model: string, // Model used (e.g., "gpt-4")
|
|
1326
|
+
tokens: { prompt, response, total }
|
|
1327
|
+
}
|
|
1328
|
+
\`\`\`
|
|
1329
|
+
|
|
1330
|
+
### LLM Call Best Practices
|
|
1331
|
+
|
|
1332
|
+
The \`llm.call()\` API is simple but the parameters matter a lot for cost, speed, and reliability:
|
|
1333
|
+
|
|
1334
|
+
| Parameter | What It Does | Typical Values |
|
|
1335
|
+
|-----------|--------------|----------------|
|
|
1336
|
+
| \`prompt\` | The user message | Your actual request |
|
|
1337
|
+
| \`systemPrompt\` | Persistent instructions for the model | "You are a JSON formatter. Always return valid JSON." |
|
|
1338
|
+
| \`temperature\` | Randomness (0 = deterministic, 1 = creative) | **0** for extraction/classification, **0.3** for structured generation, **0.7** for creative writing |
|
|
1339
|
+
| \`maxTokens\` | Cap on response length | Match your actual need — bigger costs more |
|
|
1340
|
+
|
|
1341
|
+
**Always check \`result.success\` before using \`result.response\`.** LLM calls fail more often than you expect — rate limits, network blips, content filters. If you blindly use \`result.response\`, you'll inject \`undefined\` into your output and downstream nodes will break.
|
|
1342
|
+
|
|
1343
|
+
**Cost discipline:**
|
|
1344
|
+
- Custom nodes that call LLMs run *every time the workflow runs*. A node called inside a loop multiplies cost by the loop size.
|
|
1345
|
+
- Use the lowest temperature that works. Higher temperature isn't free — it correlates with longer, more verbose responses.
|
|
1346
|
+
- Set \`maxTokens\` aggressively. The model will stop when it hits the cap, but it won't produce 10x the tokens you asked for.
|
|
1347
|
+
- For deterministic tasks (extraction, classification, formatting), \`temperature: 0\` + low \`maxTokens\` is the right shape.
|
|
1348
|
+
|
|
1349
|
+
**Pattern: graceful LLM fallback**
|
|
1350
|
+
\`\`\`javascript
|
|
1351
|
+
function processData(inputs, properties, llm) {
|
|
1352
|
+
const text = inputs.Text || '';
|
|
1353
|
+
if (!text) return { Result: '' };
|
|
1354
|
+
|
|
1355
|
+
const result = llm.call({
|
|
1356
|
+
prompt: \`Extract the main topic from: \${text}\`,
|
|
1357
|
+
systemPrompt: "Reply with ONLY the topic as a single short phrase.",
|
|
1358
|
+
temperature: 0,
|
|
1359
|
+
maxTokens: 20
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
return {
|
|
1363
|
+
Result: result.success ? result.response.trim() : '[LLM failed: ' + result.error + ']'
|
|
1364
|
+
};
|
|
1365
|
+
}
|
|
1366
|
+
\`\`\`
|
|
1367
|
+
|
|
1368
|
+
The fallback string lets downstream nodes detect failure without crashing.
|
|
1369
|
+
|
|
1370
|
+
## Managing Custom Nodes
|
|
1371
|
+
|
|
1372
|
+
\`\`\`javascript
|
|
1373
|
+
// List your nodes
|
|
1374
|
+
list_custom_nodes({ search: "summarizer" })
|
|
1375
|
+
|
|
1376
|
+
// Search public nodes
|
|
1377
|
+
search_public_custom_nodes({
|
|
1378
|
+
query: "text processing",
|
|
1379
|
+
verified_only: true
|
|
1380
|
+
})
|
|
1381
|
+
|
|
1382
|
+
// Get node details
|
|
1383
|
+
get_custom_node({ node_id: "node-abc123" })
|
|
1384
|
+
|
|
1385
|
+
// Update node
|
|
1386
|
+
update_custom_node({
|
|
1387
|
+
node_id: "node-abc123",
|
|
1388
|
+
description: "New description",
|
|
1389
|
+
script_code: "function processData(...) { ... }"
|
|
1390
|
+
})
|
|
1391
|
+
|
|
1392
|
+
// Delete node
|
|
1393
|
+
delete_custom_node({ node_id: "node-abc123" })
|
|
1394
|
+
|
|
1395
|
+
// Copy public node to your library
|
|
1396
|
+
copy_custom_node({
|
|
1397
|
+
node_id: "public-node-xyz",
|
|
1398
|
+
name: "my-custom-analyzer"
|
|
1399
|
+
})
|
|
1400
|
+
\`\`\`
|
|
1401
|
+
|
|
1402
|
+
## Sharing Custom Nodes
|
|
1403
|
+
|
|
1404
|
+
\`\`\`javascript
|
|
1405
|
+
// Make public
|
|
1406
|
+
toggle_custom_node_visibility({
|
|
1407
|
+
node_id: "node-abc123",
|
|
1408
|
+
visibility: "public"
|
|
1409
|
+
})
|
|
1410
|
+
|
|
1411
|
+
// Make private
|
|
1412
|
+
toggle_custom_node_visibility({
|
|
1413
|
+
node_id: "node-abc123",
|
|
1414
|
+
visibility: "private"
|
|
1415
|
+
})
|
|
1416
|
+
|
|
1417
|
+
// Unlisted (accessible via link only)
|
|
1418
|
+
toggle_custom_node_visibility({
|
|
1419
|
+
node_id: "node-abc123",
|
|
1420
|
+
visibility: "unlisted"
|
|
1421
|
+
})
|
|
1422
|
+
\`\`\`
|
|
1423
|
+
|
|
1424
|
+
## Common Patterns
|
|
1425
|
+
|
|
1426
|
+
### Pattern 1: Data Transformation
|
|
1427
|
+
\`\`\`javascript
|
|
1428
|
+
function processData(inputs, properties, llm) {
|
|
1429
|
+
const data = inputs.Data || {};
|
|
1430
|
+
|
|
1431
|
+
// Transform
|
|
1432
|
+
const transformed = Object.keys(data).reduce((acc, key) => {
|
|
1433
|
+
acc[key.toUpperCase()] = data[key];
|
|
1434
|
+
return acc;
|
|
1435
|
+
}, {});
|
|
1436
|
+
|
|
1437
|
+
return { Transformed: transformed };
|
|
1438
|
+
}
|
|
1439
|
+
\`\`\`
|
|
1440
|
+
|
|
1441
|
+
### Pattern 2: API Integration
|
|
1442
|
+
\`\`\`javascript
|
|
1443
|
+
function processData(inputs, properties, llm) {
|
|
1444
|
+
const query = inputs.Query || '';
|
|
1445
|
+
const apiKey = properties.apiKey || '';
|
|
1446
|
+
|
|
1447
|
+
// Note: No fetch() in sandbox - use HTTPRequest node instead
|
|
1448
|
+
// This pattern shows data preparation
|
|
1449
|
+
|
|
1450
|
+
const requestData = {
|
|
1451
|
+
query: query,
|
|
1452
|
+
apiKey: apiKey
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
return { RequestData: requestData };
|
|
1456
|
+
}
|
|
1457
|
+
\`\`\`
|
|
1458
|
+
|
|
1459
|
+
### Pattern 3: Conditional Logic
|
|
1460
|
+
\`\`\`javascript
|
|
1461
|
+
function processData(inputs, properties, llm) {
|
|
1462
|
+
const value = inputs.Value || 0;
|
|
1463
|
+
const threshold = properties.threshold || 50;
|
|
1464
|
+
|
|
1465
|
+
let category;
|
|
1466
|
+
if (value > threshold * 2) {
|
|
1467
|
+
category = 'high';
|
|
1468
|
+
} else if (value > threshold) {
|
|
1469
|
+
category = 'medium';
|
|
1470
|
+
} else {
|
|
1471
|
+
category = 'low';
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
return { Category: category };
|
|
1475
|
+
}
|
|
1476
|
+
\`\`\`
|
|
1477
|
+
|
|
1478
|
+
### Pattern 4: Array Processing
|
|
1479
|
+
\`\`\`javascript
|
|
1480
|
+
function processData(inputs, properties, llm) {
|
|
1481
|
+
const items = inputs.Items || [];
|
|
1482
|
+
|
|
1483
|
+
const filtered = items.filter(item => item.active);
|
|
1484
|
+
const mapped = filtered.map(item => ({
|
|
1485
|
+
id: item.id,
|
|
1486
|
+
name: item.name.toUpperCase()
|
|
1487
|
+
}));
|
|
1488
|
+
|
|
1489
|
+
return { ProcessedItems: mapped };
|
|
1490
|
+
}
|
|
1491
|
+
\`\`\`
|
|
1492
|
+
|
|
1493
|
+
## Best Practices
|
|
1494
|
+
|
|
1495
|
+
1. **Validate inputs:** Always provide defaults
|
|
1496
|
+
2. **Clear naming:** Use descriptive input/output names
|
|
1497
|
+
3. **Handle errors:** Try-catch for risky operations
|
|
1498
|
+
4. **Test thoroughly:** Test with edge cases
|
|
1499
|
+
5. **Document:** Add clear descriptions
|
|
1500
|
+
6. **Keep it simple:** One clear purpose per node
|
|
1501
|
+
7. **Use LLM wisely:** Only when AI adds value
|
|
1502
|
+
|
|
1503
|
+
## Troubleshooting
|
|
1504
|
+
|
|
1505
|
+
### Script Validation Errors
|
|
1506
|
+
- Check function name: must be \`processData\`
|
|
1507
|
+
- Verify return object keys match output names exactly
|
|
1508
|
+
- Remove any top-level code outside function
|
|
1509
|
+
- No imports or require statements
|
|
1510
|
+
|
|
1511
|
+
### Runtime Errors
|
|
1512
|
+
- Check input names match exactly (case-sensitive)
|
|
1513
|
+
- Verify all inputs have defaults: \`inputs.X || defaultValue\`
|
|
1514
|
+
- Console.log for debugging: \`console.log("Debug:", value)\`
|
|
1515
|
+
|
|
1516
|
+
### LLM Calls Failing
|
|
1517
|
+
- Ensure \`llm_enabled: true\` when creating node
|
|
1518
|
+
- Check LLM response: \`result.success\` before using
|
|
1519
|
+
- Handle errors: \`result.error\` when \`success\` is false
|
|
1520
|
+
|
|
1521
|
+
## Related Resources
|
|
1522
|
+
|
|
1523
|
+
- **Workflows:** \`learn://workflows\` - Use custom nodes in workflows
|
|
1524
|
+
- **Templates:** Use \`get_custom_node_template\` for starter code
|
|
1525
|
+
- **Public Nodes:** Browse with \`search_public_custom_nodes\`
|
|
1024
1526
|
`,
|
|
1025
1527
|
},
|
|
1026
1528
|
'learn://apps': {
|
|
1027
1529
|
name: 'Apps Complete Guide',
|
|
1028
1530
|
description: 'Complete guide to building multi-file React applications with FlowDot',
|
|
1029
1531
|
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
|
-
###
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
\`\`\`javascript
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
//
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
\`\`\`
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1430
|
-
\`\`\`
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1532
|
+
content: `# FlowDot Apps - Complete Guide
|
|
1533
|
+
|
|
1534
|
+
## What Are Apps?
|
|
1535
|
+
|
|
1536
|
+
Apps are **React frontend applications** that run in a sandboxed browser environment. They can invoke FlowDot workflows as backends to create full-stack applications.
|
|
1537
|
+
|
|
1538
|
+
**Use cases:**
|
|
1539
|
+
- Custom UIs for workflows
|
|
1540
|
+
- Dashboards and data visualization
|
|
1541
|
+
- Interactive forms and wizards
|
|
1542
|
+
- Chat interfaces with workflow backends
|
|
1543
|
+
- Data exploration tools
|
|
1544
|
+
|
|
1545
|
+
## Key Concepts
|
|
1546
|
+
|
|
1547
|
+
### Execution Environment
|
|
1548
|
+
Apps run in a sandboxed iframe with:
|
|
1549
|
+
- **React 18** (global - no imports needed)
|
|
1550
|
+
- **Tailwind CSS** (full utility classes)
|
|
1551
|
+
- **FlowDot color tokens:** primary-50 to primary-900, secondary-50 to secondary-900
|
|
1552
|
+
- **invokeWorkflow()** function to call linked workflows
|
|
1553
|
+
|
|
1554
|
+
### Multi-File Structure
|
|
1555
|
+
All apps are multi-file by default:
|
|
1556
|
+
- **Entry file:** Main component (App.jsx)
|
|
1557
|
+
- **Components:** Reusable UI components
|
|
1558
|
+
- **Utilities:** Helper functions
|
|
1559
|
+
- **Hooks:** Custom React hooks
|
|
1560
|
+
- **Styles:** CSS files
|
|
1561
|
+
|
|
1562
|
+
### Bundler & Cross-File Imports
|
|
1563
|
+
|
|
1564
|
+
When the app runs, all files are bundled together by an in-browser ESBuild WASM bundler. This is what makes multi-file apps possible inside a sandboxed iframe.
|
|
1565
|
+
|
|
1566
|
+
**Cross-file imports use ESM-style \`import\`/\`export\` syntax** — and these *are* allowed (unlike React imports, which are not, because React is injected as a global). The bundler resolves them at build time.
|
|
1567
|
+
|
|
1568
|
+
\`\`\`javascript
|
|
1569
|
+
// File: components/DataCard.jsx
|
|
1570
|
+
function DataCard({ title, value }) {
|
|
1571
|
+
return <div className="p-4 bg-white">{title}: {value}</div>;
|
|
1572
|
+
}
|
|
1573
|
+
export default DataCard;
|
|
1574
|
+
|
|
1575
|
+
// File: utils/format.js
|
|
1576
|
+
export function formatNumber(n) {
|
|
1577
|
+
return new Intl.NumberFormat().format(n);
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
// File: App.jsx (entry)
|
|
1581
|
+
import DataCard from './components/DataCard.jsx';
|
|
1582
|
+
import { formatNumber } from './utils/format.js';
|
|
1583
|
+
|
|
1584
|
+
function App() {
|
|
1585
|
+
return <DataCard title="Total" value={formatNumber(1234567)} />;
|
|
1586
|
+
}
|
|
1587
|
+
export default App;
|
|
1588
|
+
\`\`\`
|
|
1589
|
+
|
|
1590
|
+
**Path resolution rules:**
|
|
1591
|
+
- Paths are relative to the importing file
|
|
1592
|
+
- Always include the file extension (\`.jsx\`, \`.js\`, etc.)
|
|
1593
|
+
- Files are referenced by the \`path\` field you used in \`create_app_file\`
|
|
1594
|
+
- The entry file is set with \`set_app_entry_file\` (defaults to \`App.jsx\`)
|
|
1595
|
+
|
|
1596
|
+
**The two import rules to remember:**
|
|
1597
|
+
- ✅ **Cross-file imports (your own files):** Use ESM \`import\`/\`export\` — these are bundled
|
|
1598
|
+
- ❌ **External library imports:** Most are NOT allowed (see "Allowed Libraries" below) — and React specifically is a *global*, not an import
|
|
1599
|
+
|
|
1600
|
+
### Allowed Libraries
|
|
1601
|
+
|
|
1602
|
+
Beyond React (which is global), the bundler has a whitelist of external libraries that *are* importable. Use \`get_app_template\` to see examples for each:
|
|
1603
|
+
|
|
1604
|
+
| Library | Purpose | Import |
|
|
1605
|
+
|---------|---------|--------|
|
|
1606
|
+
| **Lucide React** | Icon library (~1000 icons) | \`import { Search, X, Check } from 'lucide-react';\` |
|
|
1607
|
+
| **Recharts** | Charting library | \`import { LineChart, Line, XAxis } from 'recharts';\` |
|
|
1608
|
+
| **Framer Motion** | Animation primitives | \`import { motion } from 'framer-motion';\` |
|
|
1609
|
+
| **clsx / classnames** | Conditional className helper | \`import clsx from 'clsx';\` |
|
|
1610
|
+
| **date-fns** | Date manipulation | \`import { format } from 'date-fns';\` |
|
|
1611
|
+
|
|
1612
|
+
Anything else — including \`fetch\`, \`axios\`, Node built-ins, or arbitrary npm packages — will fail at bundle time. **If you need network access, call a workflow via \`invokeWorkflow()\` instead** — workflows run on the server side and can hit external APIs freely.
|
|
1613
|
+
|
|
1614
|
+
If you're not sure whether a library is allowed, the bundler error message will tell you. The error is loud and immediate, not silent.
|
|
1615
|
+
|
|
1616
|
+
### Display Modes
|
|
1617
|
+
- **windowed:** Standard view with FlowDot header (default)
|
|
1618
|
+
- **fullscreen:** Full viewport, minimal control bar
|
|
1619
|
+
- **embedded:** No FlowDot UI, for iframe embedding
|
|
1620
|
+
|
|
1621
|
+
## CRITICAL CODE RULES
|
|
1622
|
+
|
|
1623
|
+
**These rules are MANDATORY due to sandbox constraints:**
|
|
1624
|
+
|
|
1625
|
+
1. **NO IMPORTS** - React is global
|
|
1626
|
+
\`\`\`javascript
|
|
1627
|
+
// ❌ WRONG
|
|
1628
|
+
import React from 'react';
|
|
1629
|
+
import { useState } from 'react';
|
|
1630
|
+
|
|
1631
|
+
// ✅ CORRECT
|
|
1632
|
+
function MyApp() {
|
|
1633
|
+
const [state, setState] = React.useState(null);
|
|
1634
|
+
}
|
|
1635
|
+
\`\`\`
|
|
1636
|
+
|
|
1637
|
+
2. **MUST export default**
|
|
1638
|
+
\`\`\`javascript
|
|
1639
|
+
function MyApp() {
|
|
1640
|
+
// component code
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
export default MyApp; // REQUIRED!
|
|
1644
|
+
\`\`\`
|
|
1645
|
+
|
|
1646
|
+
3. **Function must be named**
|
|
1647
|
+
\`\`\`javascript
|
|
1648
|
+
// ❌ WRONG
|
|
1649
|
+
export default function() { ... }
|
|
1650
|
+
|
|
1651
|
+
// ✅ CORRECT
|
|
1652
|
+
function MyApp() { ... }
|
|
1653
|
+
export default MyApp;
|
|
1654
|
+
\`\`\`
|
|
1655
|
+
|
|
1656
|
+
4. **Use Tailwind CSS only**
|
|
1657
|
+
\`\`\`javascript
|
|
1658
|
+
// ❌ WRONG (no inline styles)
|
|
1659
|
+
<div style={{ color: 'red' }}>Text</div>
|
|
1660
|
+
|
|
1661
|
+
// ✅ CORRECT
|
|
1662
|
+
<div className="text-red-500">Text</div>
|
|
1663
|
+
\`\`\`
|
|
1664
|
+
|
|
1665
|
+
5. **NO FORM ELEMENTS**
|
|
1666
|
+
\`\`\`javascript
|
|
1667
|
+
// ❌ WRONG (sandbox blocks forms)
|
|
1668
|
+
<form onSubmit={handleSubmit}>
|
|
1669
|
+
<button type="submit">Submit</button>
|
|
1670
|
+
</form>
|
|
1671
|
+
|
|
1672
|
+
// ✅ CORRECT
|
|
1673
|
+
<div>
|
|
1674
|
+
<input onKeyDown={(e) => e.key === 'Enter' && handleClick()} />
|
|
1675
|
+
<button type="button" onClick={handleClick}>Submit</button>
|
|
1676
|
+
</div>
|
|
1677
|
+
\`\`\`
|
|
1678
|
+
|
|
1679
|
+
6. **ALL BUTTONS need type="button"**
|
|
1680
|
+
\`\`\`javascript
|
|
1681
|
+
<button type="button" onClick={handleClick}>Click Me</button>
|
|
1682
|
+
\`\`\`
|
|
1683
|
+
|
|
1684
|
+
## Why the Sandbox Restrictions Exist
|
|
1685
|
+
|
|
1686
|
+
The "no imports, no forms, no fetch" rules aren't arbitrary — they exist because every FlowDot app runs inside an **iframe sandbox** that intentionally restricts what client-side code can do.
|
|
1687
|
+
|
|
1688
|
+
| Restriction | Reason |
|
|
1689
|
+
|-------------|--------|
|
|
1690
|
+
| **No \`<form>\` tags** | The sandbox blocks form submissions because they would trigger a full-page navigation that escapes the iframe. Use buttons + state instead. |
|
|
1691
|
+
| **No \`fetch\` / \`XMLHttpRequest\`** | The sandbox has no network access. This prevents apps from leaking user data, calling third-party APIs without consent, or being used as exfiltration vectors. **All network calls go through \`invokeWorkflow()\`**, which runs server-side under the user's permissions and is auditable. |
|
|
1692
|
+
| **No Node built-ins** | This is a browser, not Node — but more importantly, things like \`fs\`, \`child_process\`, or \`os\` would be a security disaster even if they worked. |
|
|
1693
|
+
| **No imports for React itself** | React is injected as a global so the bundle stays small and consistent. Letting users import their own React would let them load unchecked versions. |
|
|
1694
|
+
| **All buttons need \`type="button"\`** | Default \`<button>\` type is \`submit\`, which inside any form-like context tries to navigate. Setting \`type="button"\` is harmless and prevents the sandbox from blocking the click. |
|
|
1695
|
+
|
|
1696
|
+
**Mental model:** An app is a *renderer*. It draws UI, manages local state, and calls workflows. It does not talk to the network or the host browser directly. If you want to do something an app can't do, the answer is almost always "make a workflow that does it and invoke it from the app."
|
|
1697
|
+
|
|
1698
|
+
## Creating an App
|
|
1699
|
+
|
|
1700
|
+
### Step 1: Create App
|
|
1701
|
+
\`\`\`javascript
|
|
1702
|
+
create_app({
|
|
1703
|
+
name: "my-dashboard",
|
|
1704
|
+
description: "Interactive dashboard with workflow backend",
|
|
1705
|
+
category: "productivity",
|
|
1706
|
+
tags: ["dashboard", "analytics"]
|
|
1707
|
+
})
|
|
1708
|
+
// Returns: { id: "app-abc123", ... }
|
|
1709
|
+
\`\`\`
|
|
1710
|
+
|
|
1711
|
+
This creates an app with a default Hello World App.jsx entry file.
|
|
1712
|
+
|
|
1713
|
+
### Step 2: Update Entry File
|
|
1714
|
+
\`\`\`javascript
|
|
1715
|
+
update_app_file({
|
|
1716
|
+
app_id: "app-abc123",
|
|
1717
|
+
file_path: "App.jsx",
|
|
1718
|
+
content: \`
|
|
1719
|
+
function MyDashboard() {
|
|
1720
|
+
const [data, setData] = React.useState(null);
|
|
1721
|
+
const [loading, setLoading] = React.useState(false);
|
|
1722
|
+
|
|
1723
|
+
const handleFetch = async () => {
|
|
1724
|
+
setLoading(true);
|
|
1725
|
+
try {
|
|
1726
|
+
const result = await invokeWorkflow('workflow-hash', {
|
|
1727
|
+
input: 'fetch data'
|
|
1728
|
+
});
|
|
1729
|
+
const output = getNodeOutput(result, 'Output Node');
|
|
1730
|
+
setData(output);
|
|
1731
|
+
} catch (error) {
|
|
1732
|
+
console.error('Error:', error);
|
|
1733
|
+
} finally {
|
|
1734
|
+
setLoading(false);
|
|
1735
|
+
}
|
|
1736
|
+
};
|
|
1737
|
+
|
|
1738
|
+
return (
|
|
1739
|
+
<div className="min-h-screen bg-gray-50 p-8">
|
|
1740
|
+
<h1 className="text-3xl font-bold text-gray-900 mb-4">
|
|
1741
|
+
My Dashboard
|
|
1742
|
+
</h1>
|
|
1743
|
+
|
|
1744
|
+
<button
|
|
1745
|
+
type="button"
|
|
1746
|
+
onClick={handleFetch}
|
|
1747
|
+
disabled={loading}
|
|
1748
|
+
className="px-4 py-2 bg-primary-600 text-white rounded hover:bg-primary-700 disabled:opacity-50"
|
|
1749
|
+
>
|
|
1750
|
+
{loading ? 'Loading...' : 'Fetch Data'}
|
|
1751
|
+
</button>
|
|
1752
|
+
|
|
1753
|
+
{data && (
|
|
1754
|
+
<div className="mt-4 p-4 bg-white rounded shadow">
|
|
1755
|
+
<pre>{JSON.stringify(data, null, 2)}</pre>
|
|
1756
|
+
</div>
|
|
1757
|
+
)}
|
|
1758
|
+
</div>
|
|
1759
|
+
);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
export default MyDashboard;
|
|
1763
|
+
\`
|
|
1764
|
+
})
|
|
1765
|
+
\`\`\`
|
|
1766
|
+
|
|
1767
|
+
### Step 3: Add Additional Files
|
|
1768
|
+
\`\`\`javascript
|
|
1769
|
+
// Create a component
|
|
1770
|
+
create_app_file({
|
|
1771
|
+
app_id: "app-abc123",
|
|
1772
|
+
path: "components/DataCard.jsx",
|
|
1773
|
+
type: "component",
|
|
1774
|
+
content: \`
|
|
1775
|
+
function DataCard({ title, value }) {
|
|
1776
|
+
return (
|
|
1777
|
+
<div className="p-6 bg-white rounded-lg shadow">
|
|
1778
|
+
<h3 className="text-lg font-semibold text-gray-700">{title}</h3>
|
|
1779
|
+
<p className="text-3xl font-bold text-primary-600">{value}</p>
|
|
1780
|
+
</div>
|
|
1781
|
+
);
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
export default DataCard;
|
|
1785
|
+
\`
|
|
1786
|
+
})
|
|
1787
|
+
|
|
1788
|
+
// Create a utility
|
|
1789
|
+
create_app_file({
|
|
1790
|
+
app_id: "app-abc123",
|
|
1791
|
+
path: "utils/helpers.js",
|
|
1792
|
+
type: "utility",
|
|
1793
|
+
content: \`
|
|
1794
|
+
export function formatNumber(num) {
|
|
1795
|
+
return new Intl.NumberFormat().format(num);
|
|
1796
|
+
}
|
|
1797
|
+
|
|
1798
|
+
export function formatDate(date) {
|
|
1799
|
+
return new Date(date).toLocaleDateString();
|
|
1800
|
+
}
|
|
1801
|
+
\`
|
|
1802
|
+
})
|
|
1803
|
+
\`\`\`
|
|
1804
|
+
|
|
1805
|
+
### Step 4: Link Workflow
|
|
1806
|
+
\`\`\`javascript
|
|
1807
|
+
link_app_workflow({
|
|
1808
|
+
app_id: "app-abc123",
|
|
1809
|
+
workflow_hash: "workflow-xyz",
|
|
1810
|
+
alias: "dataFetcher"
|
|
1811
|
+
})
|
|
1812
|
+
\`\`\`
|
|
1813
|
+
|
|
1814
|
+
## Workflow Integration
|
|
1815
|
+
|
|
1816
|
+
### Invoking Workflows
|
|
1817
|
+
\`\`\`javascript
|
|
1818
|
+
const result = await invokeWorkflow('workflow-hash', {
|
|
1819
|
+
inputName: 'value'
|
|
1820
|
+
});
|
|
1821
|
+
\`\`\`
|
|
1822
|
+
|
|
1823
|
+
### Workflow Response Structure
|
|
1824
|
+
\`\`\`javascript
|
|
1825
|
+
{
|
|
1826
|
+
success: boolean,
|
|
1827
|
+
data: {
|
|
1828
|
+
"[nodeId]": {
|
|
1829
|
+
nodeId: "uuid",
|
|
1830
|
+
nodeTitle: "My Output Node",
|
|
1831
|
+
nodeType: "text_output",
|
|
1832
|
+
outputs: {
|
|
1833
|
+
"Consolidated Text": {
|
|
1834
|
+
value: "the actual data",
|
|
1835
|
+
metadata: {...}
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
\`\`\`
|
|
1842
|
+
|
|
1843
|
+
### Extract Output Helper
|
|
1844
|
+
**CRITICAL:** Use this helper to safely extract workflow outputs:
|
|
1845
|
+
|
|
1846
|
+
\`\`\`javascript
|
|
1847
|
+
const getNodeOutput = (result, nodeTitle, socketName = 'Consolidated Text') => {
|
|
1848
|
+
if (!result?.data) return null;
|
|
1849
|
+
const node = Object.values(result.data).find(n => n.nodeTitle === nodeTitle);
|
|
1850
|
+
return node?.outputs?.[socketName]?.value;
|
|
1851
|
+
};
|
|
1852
|
+
|
|
1853
|
+
// Usage
|
|
1854
|
+
const result = await invokeWorkflow('hash', { input });
|
|
1855
|
+
const data = getNodeOutput(result, 'Output Node');
|
|
1856
|
+
if (data) {
|
|
1857
|
+
// use data
|
|
1858
|
+
}
|
|
1859
|
+
\`\`\`
|
|
1860
|
+
|
|
1861
|
+
## Managing Apps
|
|
1862
|
+
|
|
1863
|
+
\`\`\`javascript
|
|
1864
|
+
// List your apps
|
|
1865
|
+
list_apps({ search: "dashboard" })
|
|
1866
|
+
|
|
1867
|
+
// Search public apps
|
|
1868
|
+
search_apps({ query: "analytics", sort: "trending" })
|
|
1869
|
+
|
|
1870
|
+
// Get app details
|
|
1871
|
+
get_app({ app_id: "app-abc123" })
|
|
1872
|
+
|
|
1873
|
+
// List files in app
|
|
1874
|
+
list_app_files({ app_id: "app-abc123" })
|
|
1875
|
+
|
|
1876
|
+
// Get specific file
|
|
1877
|
+
get_app_file({ app_id: "app-abc123", file_path: "App.jsx" })
|
|
1878
|
+
|
|
1879
|
+
// Update app metadata
|
|
1880
|
+
update_app({
|
|
1881
|
+
app_id: "app-abc123",
|
|
1882
|
+
name: "New Name",
|
|
1883
|
+
description: "New description",
|
|
1884
|
+
config: { displayMode: "fullscreen" }
|
|
1885
|
+
})
|
|
1886
|
+
|
|
1887
|
+
// Delete file
|
|
1888
|
+
delete_app_file({ app_id: "app-abc123", file_path: "old-file.jsx" })
|
|
1889
|
+
|
|
1890
|
+
// Rename/move file
|
|
1891
|
+
rename_app_file({
|
|
1892
|
+
app_id: "app-abc123",
|
|
1893
|
+
file_path: "Button.jsx",
|
|
1894
|
+
new_path: "components/Button.jsx"
|
|
1895
|
+
})
|
|
1896
|
+
\`\`\`
|
|
1897
|
+
|
|
1898
|
+
## Code Editing Tools
|
|
1899
|
+
|
|
1900
|
+
### Surgical Edits (Find/Replace)
|
|
1901
|
+
\`\`\`javascript
|
|
1902
|
+
edit_app_code({
|
|
1903
|
+
app_id: "app-abc123",
|
|
1904
|
+
old_string: "const [count, setCount] = React.useState(0);",
|
|
1905
|
+
new_string: "const [count, setCount] = React.useState(10);"
|
|
1906
|
+
})
|
|
1907
|
+
\`\`\`
|
|
1908
|
+
|
|
1909
|
+
### Insert Code
|
|
1910
|
+
\`\`\`javascript
|
|
1911
|
+
insert_app_code({
|
|
1912
|
+
app_id: "app-abc123",
|
|
1913
|
+
after_pattern: "const [data, setData] = React.useState(null);",
|
|
1914
|
+
content: "\\n const [error, setError] = React.useState(null);"
|
|
1915
|
+
})
|
|
1916
|
+
\`\`\`
|
|
1917
|
+
|
|
1918
|
+
### Append Code
|
|
1919
|
+
\`\`\`javascript
|
|
1920
|
+
append_app_code({
|
|
1921
|
+
app_id: "app-abc123",
|
|
1922
|
+
content: "\\n\\n// Helper functions\\nfunction formatData(d) { return d; }"
|
|
1923
|
+
})
|
|
1924
|
+
\`\`\`
|
|
1925
|
+
|
|
1926
|
+
### Prepend Code
|
|
1927
|
+
\`\`\`javascript
|
|
1928
|
+
prepend_app_code({
|
|
1929
|
+
app_id: "app-abc123",
|
|
1930
|
+
content: "// Dashboard Configuration\\nconst API_URL = 'https://api.example.com';\\n\\n"
|
|
1931
|
+
})
|
|
1932
|
+
\`\`\`
|
|
1933
|
+
|
|
1934
|
+
## Templates
|
|
1935
|
+
|
|
1936
|
+
Get starter code for common patterns:
|
|
1937
|
+
|
|
1938
|
+
\`\`\`javascript
|
|
1939
|
+
get_app_template({ template: "basic" })
|
|
1940
|
+
get_app_template({ template: "chat" })
|
|
1941
|
+
get_app_template({ template: "dashboard" })
|
|
1942
|
+
get_app_template({ template: "form-builder" })
|
|
1943
|
+
get_app_template({ template: "data-viewer" })
|
|
1944
|
+
get_app_template({ template: "all" }) // See all templates
|
|
1945
|
+
\`\`\`
|
|
1946
|
+
|
|
1947
|
+
## Publishing Apps
|
|
1948
|
+
|
|
1949
|
+
\`\`\`javascript
|
|
1950
|
+
// Publish to marketplace
|
|
1951
|
+
publish_app({ app_id: "app-abc123" })
|
|
1952
|
+
|
|
1953
|
+
// Unpublish (make private)
|
|
1954
|
+
unpublish_app({ app_id: "app-abc123" })
|
|
1955
|
+
|
|
1956
|
+
// Clone public app
|
|
1957
|
+
clone_app({
|
|
1958
|
+
app_id: "public-app-xyz",
|
|
1959
|
+
name: "My Custom Dashboard"
|
|
1960
|
+
})
|
|
1961
|
+
\`\`\`
|
|
1962
|
+
|
|
1963
|
+
## Best Practices
|
|
1964
|
+
|
|
1965
|
+
1. **Start with templates:** Use \`get_app_template\`
|
|
1966
|
+
2. **One component per file:** Keep files focused
|
|
1967
|
+
3. **Use getNodeOutput helper:** Always for workflow results
|
|
1968
|
+
4. **Type="button" everywhere:** Prevent form behavior
|
|
1969
|
+
5. **Tailwind only:** No inline styles
|
|
1970
|
+
6. **Test incrementally:** Build piece by piece
|
|
1971
|
+
7. **Link workflows first:** Before invoking them
|
|
1972
|
+
|
|
1973
|
+
## Common Patterns
|
|
1974
|
+
|
|
1975
|
+
### Loading States
|
|
1976
|
+
\`\`\`javascript
|
|
1977
|
+
const [loading, setLoading] = React.useState(false);
|
|
1978
|
+
|
|
1979
|
+
const handleAction = async () => {
|
|
1980
|
+
setLoading(true);
|
|
1981
|
+
try {
|
|
1982
|
+
const result = await invokeWorkflow('hash', { input });
|
|
1983
|
+
// handle result
|
|
1984
|
+
} finally {
|
|
1985
|
+
setLoading(false);
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
\`\`\`
|
|
1989
|
+
|
|
1990
|
+
### Error Handling
|
|
1991
|
+
\`\`\`javascript
|
|
1992
|
+
const [error, setError] = React.useState(null);
|
|
1993
|
+
|
|
1994
|
+
try {
|
|
1995
|
+
const result = await invokeWorkflow('hash', { input });
|
|
1996
|
+
setError(null);
|
|
1997
|
+
} catch (err) {
|
|
1998
|
+
setError(err.message);
|
|
1999
|
+
}
|
|
2000
|
+
\`\`\`
|
|
2001
|
+
|
|
2002
|
+
### Enter Key Submission
|
|
2003
|
+
\`\`\`javascript
|
|
2004
|
+
<input
|
|
2005
|
+
onKeyDown={(e) => {
|
|
2006
|
+
if (e.key === 'Enter') {
|
|
2007
|
+
handleSubmit();
|
|
2008
|
+
}
|
|
2009
|
+
}}
|
|
2010
|
+
/>
|
|
2011
|
+
\`\`\`
|
|
2012
|
+
|
|
2013
|
+
## Troubleshooting
|
|
2014
|
+
|
|
2015
|
+
### Import Errors
|
|
2016
|
+
- Remove all import statements
|
|
2017
|
+
- Use React.useState, React.useEffect, etc.
|
|
2018
|
+
|
|
2019
|
+
### Form Not Working
|
|
2020
|
+
- Remove <form> tags
|
|
2021
|
+
- Add type="button" to all buttons
|
|
2022
|
+
- Use onKeyDown for Enter key
|
|
2023
|
+
|
|
2024
|
+
### Workflow Not Responding
|
|
2025
|
+
- Check workflow is linked: \`link_app_workflow\`
|
|
2026
|
+
- Use getNodeOutput helper to extract data
|
|
2027
|
+
- Verify node titles match exactly
|
|
2028
|
+
|
|
2029
|
+
## Related Resources
|
|
2030
|
+
|
|
2031
|
+
- **Workflows:** \`learn://workflows\` - Create workflow backends
|
|
2032
|
+
- **Templates:** \`get_app_template\` - Starter code patterns
|
|
1463
2033
|
`,
|
|
1464
2034
|
},
|
|
1465
2035
|
'learn://toolkits': {
|
|
1466
2036
|
name: 'Agent Toolkits Complete Guide',
|
|
1467
2037
|
description: 'Complete guide to creating and managing MCP agent toolkits',
|
|
1468
2038
|
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
|
-
##
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
\`\`\`javascript
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
})
|
|
1691
|
-
\`\`\`
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
}
|
|
1743
|
-
\`\`\`
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
\`\`\`javascript
|
|
1771
|
-
//
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
-
|
|
1821
|
-
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
2039
|
+
content: `# FlowDot Agent Toolkits - Complete Guide
|
|
2040
|
+
|
|
2041
|
+
## What Are Agent Toolkits?
|
|
2042
|
+
|
|
2043
|
+
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.
|
|
2044
|
+
|
|
2045
|
+
**Think of it as:** Creating mini MCP servers that can be installed and used by agents.
|
|
2046
|
+
|
|
2047
|
+
## Key Concepts
|
|
2048
|
+
|
|
2049
|
+
### Toolkits
|
|
2050
|
+
Collections of related tools:
|
|
2051
|
+
- **Name:** Unique identifier (e.g., "spotify-api")
|
|
2052
|
+
- **Tools:** Array of HTTP or Workflow-based tools
|
|
2053
|
+
- **Credentials:** Shared API keys, OAuth tokens, etc.
|
|
2054
|
+
- **Visibility:** public, private, or unlisted
|
|
2055
|
+
|
|
2056
|
+
### Tools
|
|
2057
|
+
Individual capabilities within a toolkit:
|
|
2058
|
+
- **HTTP tools:** Make REST API calls
|
|
2059
|
+
- **Workflow tools:** Execute FlowDot workflows
|
|
2060
|
+
- **Input schema:** Define required parameters
|
|
2061
|
+
- **Output schema:** Define expected responses
|
|
2062
|
+
|
|
2063
|
+
### Credentials
|
|
2064
|
+
Authentication requirements:
|
|
2065
|
+
- **api_key:** Standard API keys
|
|
2066
|
+
- **oauth:** OAuth 2.0 tokens (auto-refreshable)
|
|
2067
|
+
- **bearer:** Bearer tokens
|
|
2068
|
+
- **basic:** Basic auth
|
|
2069
|
+
- **custom:** Custom authentication
|
|
2070
|
+
|
|
2071
|
+
### Installation
|
|
2072
|
+
Users install toolkits to their account:
|
|
2073
|
+
- Map toolkit credentials to their stored API keys
|
|
2074
|
+
- Enable/disable as needed
|
|
2075
|
+
- Invoke tools with credentials applied
|
|
2076
|
+
|
|
2077
|
+
## Creating a Toolkit
|
|
2078
|
+
|
|
2079
|
+
### Step 1: Create Toolkit
|
|
2080
|
+
\`\`\`javascript
|
|
2081
|
+
create_agent_toolkit({
|
|
2082
|
+
name: "spotify-api",
|
|
2083
|
+
title: "Spotify API",
|
|
2084
|
+
description: "Access Spotify music data and playback",
|
|
2085
|
+
category: "api-integration",
|
|
2086
|
+
tags: ["music", "api", "streaming"],
|
|
2087
|
+
credential_requirements: [
|
|
2088
|
+
{
|
|
2089
|
+
key_name: "SPOTIFY_CLIENT_ID",
|
|
2090
|
+
label: "Spotify Client ID",
|
|
2091
|
+
credential_type: "api_key",
|
|
2092
|
+
is_required: true,
|
|
2093
|
+
description: "Your Spotify app client ID"
|
|
2094
|
+
},
|
|
2095
|
+
{
|
|
2096
|
+
key_name: "SPOTIFY_CLIENT_SECRET",
|
|
2097
|
+
label: "Spotify Client Secret",
|
|
2098
|
+
credential_type: "api_key",
|
|
2099
|
+
is_required: true,
|
|
2100
|
+
description: "Your Spotify app client secret"
|
|
2101
|
+
}
|
|
2102
|
+
]
|
|
2103
|
+
})
|
|
2104
|
+
// Returns: { id: "toolkit-abc123", ... }
|
|
2105
|
+
\`\`\`
|
|
2106
|
+
|
|
2107
|
+
### Step 2: Add HTTP Tool
|
|
2108
|
+
\`\`\`javascript
|
|
2109
|
+
create_toolkit_tool({
|
|
2110
|
+
toolkit_id: "toolkit-abc123",
|
|
2111
|
+
name: "search-tracks",
|
|
2112
|
+
title: "Search Tracks",
|
|
2113
|
+
description: "Search for tracks on Spotify",
|
|
2114
|
+
tool_type: "http",
|
|
2115
|
+
endpoint_config: {
|
|
2116
|
+
method: "GET",
|
|
2117
|
+
url: "https://api.spotify.com/v1/search"
|
|
2118
|
+
},
|
|
2119
|
+
input_schema: {
|
|
2120
|
+
type: "object",
|
|
2121
|
+
properties: {
|
|
2122
|
+
query: {
|
|
2123
|
+
type: "string",
|
|
2124
|
+
description: "Search query"
|
|
2125
|
+
},
|
|
2126
|
+
type: {
|
|
2127
|
+
type: "string",
|
|
2128
|
+
enum: ["track", "album", "artist"],
|
|
2129
|
+
description: "Type of content to search"
|
|
2130
|
+
},
|
|
2131
|
+
limit: {
|
|
2132
|
+
type: "number",
|
|
2133
|
+
description: "Number of results (1-50)"
|
|
2134
|
+
}
|
|
2135
|
+
},
|
|
2136
|
+
required: ["query", "type"]
|
|
2137
|
+
},
|
|
2138
|
+
credential_keys: ["SPOTIFY_CLIENT_ID", "SPOTIFY_CLIENT_SECRET"]
|
|
2139
|
+
})
|
|
2140
|
+
\`\`\`
|
|
2141
|
+
|
|
2142
|
+
### Step 3: Add Workflow Tool
|
|
2143
|
+
\`\`\`javascript
|
|
2144
|
+
create_toolkit_tool({
|
|
2145
|
+
toolkit_id: "toolkit-abc123",
|
|
2146
|
+
name: "analyze-playlist",
|
|
2147
|
+
title: "Analyze Playlist",
|
|
2148
|
+
description: "Analyze a Spotify playlist with AI",
|
|
2149
|
+
tool_type: "workflow",
|
|
2150
|
+
workflow_hash: "workflow-xyz",
|
|
2151
|
+
input_schema: {
|
|
2152
|
+
type: "object",
|
|
2153
|
+
properties: {
|
|
2154
|
+
playlist_id: {
|
|
2155
|
+
type: "string",
|
|
2156
|
+
description: "Spotify playlist ID"
|
|
2157
|
+
}
|
|
2158
|
+
},
|
|
2159
|
+
required: ["playlist_id"]
|
|
2160
|
+
}
|
|
2161
|
+
})
|
|
2162
|
+
\`\`\`
|
|
2163
|
+
|
|
2164
|
+
## Designing HTTP Tool Schemas
|
|
2165
|
+
|
|
2166
|
+
The \`input_schema\` you give a tool is **the only thing the calling AI agent sees** when it decides whether and how to invoke your tool. A bad schema produces bad tool calls. A great schema turns a tool into something the agent can use confidently on the first try.
|
|
2167
|
+
|
|
2168
|
+
### Schema design rules
|
|
2169
|
+
|
|
2170
|
+
1. **Use rich \`description\` fields, not just types.** Every property should have a description that tells the agent *when* to use it and *what shape* the value should take. Type alone is not enough.
|
|
2171
|
+
|
|
2172
|
+
\`\`\`javascript
|
|
2173
|
+
// ❌ Weak
|
|
2174
|
+
query: { type: "string" }
|
|
2175
|
+
|
|
2176
|
+
// ✅ Strong
|
|
2177
|
+
query: {
|
|
2178
|
+
type: "string",
|
|
2179
|
+
description: "Search query as a single phrase. Spaces are allowed. Keep under 100 chars. Examples: 'jazz piano 1960s', 'Miles Davis Kind of Blue'."
|
|
2180
|
+
}
|
|
2181
|
+
\`\`\`
|
|
2182
|
+
|
|
2183
|
+
2. **Use \`enum\` for closed sets.** If a parameter only accepts certain values, list them. This eliminates a whole class of guessing errors.
|
|
2184
|
+
|
|
2185
|
+
\`\`\`javascript
|
|
2186
|
+
type: {
|
|
2187
|
+
type: "string",
|
|
2188
|
+
enum: ["track", "album", "artist", "playlist"],
|
|
2189
|
+
description: "What kind of Spotify entity to search for"
|
|
2190
|
+
}
|
|
2191
|
+
\`\`\`
|
|
2192
|
+
|
|
2193
|
+
3. **Mark required fields explicitly.** The \`required\` array at the schema root tells the agent which fields it cannot omit. Anything not in \`required\` is optional and the agent will know it can skip it.
|
|
2194
|
+
|
|
2195
|
+
4. **Constrain numeric ranges.** If a parameter has min/max bounds, encode them with \`minimum\` and \`maximum\`. The agent will respect them.
|
|
2196
|
+
|
|
2197
|
+
\`\`\`javascript
|
|
2198
|
+
limit: {
|
|
2199
|
+
type: "number",
|
|
2200
|
+
minimum: 1,
|
|
2201
|
+
maximum: 50,
|
|
2202
|
+
description: "Number of results to return (default 20, max 50)"
|
|
2203
|
+
}
|
|
2204
|
+
\`\`\`
|
|
2205
|
+
|
|
2206
|
+
5. **Describe what the tool *does* in the tool description, not just what it is.** "Searches Spotify" is weak. "Searches Spotify's catalog by free-text query and returns matching tracks, albums, or artists with metadata. Use this when the user asks about a song, album, or artist by name." is strong.
|
|
2207
|
+
|
|
2208
|
+
6. **Avoid \`additionalProperties: true\` unless you mean it.** If you allow arbitrary extra fields, agents will start passing fields that don't exist on your endpoint and your API will reject them silently.
|
|
2209
|
+
|
|
2210
|
+
### Output schemas matter too
|
|
2211
|
+
|
|
2212
|
+
If your tool defines an \`output_schema\`, the agent uses it to know what shape the response will have — which means it can chain tools together more confidently. A tool that returns \`{ tracks: [...] }\` should declare that. The next tool can then expect \`tracks\` as input without having to inspect the actual response first.
|
|
2213
|
+
|
|
2214
|
+
### The mental test
|
|
2215
|
+
|
|
2216
|
+
Before saving a tool, ask yourself: *if I gave this schema to a stranger who has never seen the underlying API, could they make a successful call on the first try using only the schema and descriptions?* If not, the schema needs more detail.
|
|
2217
|
+
|
|
2218
|
+
## OAuth Configuration
|
|
2219
|
+
|
|
2220
|
+
For APIs requiring OAuth 2.0:
|
|
2221
|
+
|
|
2222
|
+
\`\`\`javascript
|
|
2223
|
+
create_agent_toolkit({
|
|
2224
|
+
name: "schwab-trading",
|
|
2225
|
+
title: "Schwab Trading API",
|
|
2226
|
+
description: "Access Schwab brokerage data",
|
|
2227
|
+
credential_requirements: [
|
|
2228
|
+
{
|
|
2229
|
+
key_name: "SCHWAB_APP_KEY",
|
|
2230
|
+
label: "Schwab App Key (Client ID)",
|
|
2231
|
+
credential_type: "api_key",
|
|
2232
|
+
is_required: true,
|
|
2233
|
+
description: "Your Schwab Developer App Key"
|
|
2234
|
+
},
|
|
2235
|
+
{
|
|
2236
|
+
key_name: "SCHWAB_APP_SECRET",
|
|
2237
|
+
label: "Schwab App Secret (Client Secret)",
|
|
2238
|
+
credential_type: "api_key",
|
|
2239
|
+
is_required: true,
|
|
2240
|
+
description: "Your Schwab Developer App Secret"
|
|
2241
|
+
},
|
|
2242
|
+
{
|
|
2243
|
+
key_name: "SCHWAB_ACCESS_TOKEN",
|
|
2244
|
+
label: "Schwab Access Token",
|
|
2245
|
+
credential_type: "oauth",
|
|
2246
|
+
is_required: true,
|
|
2247
|
+
description: "OAuth access token (auto-refreshed via Reconnect)",
|
|
2248
|
+
oauth_config: {
|
|
2249
|
+
authorization_url: "https://api.schwabapi.com/v1/oauth/authorize",
|
|
2250
|
+
token_endpoint: "https://api.schwabapi.com/v1/oauth/token",
|
|
2251
|
+
scopes: ["api"],
|
|
2252
|
+
client_id_credential_key: "SCHWAB_APP_KEY",
|
|
2253
|
+
client_secret_credential_key: "SCHWAB_APP_SECRET",
|
|
2254
|
+
pkce_enabled: true,
|
|
2255
|
+
auth_error_codes: [401, 403],
|
|
2256
|
+
auth_error_patterns: ["invalid_token", "expired_token"]
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
]
|
|
2260
|
+
})
|
|
2261
|
+
\`\`\`
|
|
2262
|
+
|
|
2263
|
+
**OAuth Config Fields:**
|
|
2264
|
+
- **authorization_url:** OAuth authorization endpoint
|
|
2265
|
+
- **token_endpoint:** Token exchange endpoint
|
|
2266
|
+
- **scopes:** Array of OAuth scopes
|
|
2267
|
+
- **client_id_credential_key:** Key name of credential with client ID
|
|
2268
|
+
- **client_secret_credential_key:** Key name of credential with client secret
|
|
2269
|
+
- **pkce_enabled:** Enable PKCE (recommended)
|
|
2270
|
+
- **auth_error_codes:** HTTP codes indicating auth failure
|
|
2271
|
+
- **auth_error_patterns:** Error message patterns for auth failure
|
|
2272
|
+
|
|
2273
|
+
### When to Use Each Credential Type
|
|
2274
|
+
|
|
2275
|
+
| Credential type | Use when | Notes |
|
|
2276
|
+
|---|---|---|
|
|
2277
|
+
| **api_key** | The API uses a long-lived static key in a header (e.g., \`X-API-Key\`, \`Authorization: ApiKey ...\`) | Simplest. No refresh needed. Good for OpenAI, Anthropic, most data APIs. |
|
|
2278
|
+
| **bearer** | The API uses a static \`Authorization: Bearer <token>\` header without OAuth | Like api_key but with the standard \`Bearer\` prefix. |
|
|
2279
|
+
| **basic** | The API uses HTTP Basic Auth (\`Authorization: Basic base64(user:pass)\`) | Rare for modern APIs but still common in legacy services. |
|
|
2280
|
+
| **oauth** | The API requires user-delegated access via OAuth 2.0 | Use this for any API where the user logs in to authorize access (Google, Schwab, Notion, GitHub user data, etc.). Tokens auto-refresh. |
|
|
2281
|
+
| **custom** | The API has a non-standard auth flow (signed requests, mutual TLS, request signing) | Last resort. You'll likely need to wrap the API in a workflow tool instead and let the workflow handle the signing. |
|
|
2282
|
+
|
|
2283
|
+
### Why PKCE Matters
|
|
2284
|
+
|
|
2285
|
+
PKCE (Proof Key for Code Exchange) is an OAuth 2.0 extension that prevents authorization code interception. Set \`pkce_enabled: true\` whenever the OAuth provider supports it — most modern providers (Google, Schwab, Notion, GitHub) do. PKCE adds no friction for the user; it just makes the flow safer.
|
|
2286
|
+
|
|
2287
|
+
The only reason to leave PKCE off is if the provider explicitly doesn't support it and rejects the extra parameters.
|
|
2288
|
+
|
|
2289
|
+
### How Token Refresh Actually Works
|
|
2290
|
+
|
|
2291
|
+
When a tool call returns one of the \`auth_error_codes\` (typically 401 or 403), or when the response body matches one of the \`auth_error_patterns\` (e.g., \`"invalid_token"\`, \`"expired_token"\`), FlowDot automatically:
|
|
2292
|
+
|
|
2293
|
+
1. Uses the stored refresh token to call \`token_endpoint\`
|
|
2294
|
+
2. Receives a new access token + (usually) a new refresh token
|
|
2295
|
+
3. Stores both back in the user's encrypted credential vault
|
|
2296
|
+
4. Retries the original tool call with the new access token
|
|
2297
|
+
|
|
2298
|
+
The agent never has to handle this. From the agent's perspective, the call just succeeds.
|
|
2299
|
+
|
|
2300
|
+
**Things you must get right for refresh to work:**
|
|
2301
|
+
- \`auth_error_codes\` must include every status code the API returns on token expiry (some APIs use 401, some use 403, some use both)
|
|
2302
|
+
- \`auth_error_patterns\` should include the actual error string the API returns — check the API docs for the exact wording
|
|
2303
|
+
- \`client_id_credential_key\` and \`client_secret_credential_key\` must point to credentials that are actually populated at refresh time (not just at install time)
|
|
2304
|
+
|
|
2305
|
+
**If refresh is failing:** the most common cause is that the API returns a 200 OK with an error body instead of a 4xx code. In that case, you need to add the error pattern to \`auth_error_patterns\` so FlowDot can detect it from the body.
|
|
2306
|
+
|
|
2307
|
+
## Installing & Using Toolkits
|
|
2308
|
+
|
|
2309
|
+
### Install Toolkit
|
|
2310
|
+
\`\`\`javascript
|
|
2311
|
+
install_toolkit({ toolkit_id: "toolkit-abc123" })
|
|
2312
|
+
// Returns: { installation_id: "install-xyz", ... }
|
|
2313
|
+
\`\`\`
|
|
2314
|
+
|
|
2315
|
+
### Configure Credentials
|
|
2316
|
+
\`\`\`javascript
|
|
2317
|
+
update_toolkit_installation({
|
|
2318
|
+
installation_id: "install-xyz",
|
|
2319
|
+
credential_mapping: {
|
|
2320
|
+
"SPOTIFY_CLIENT_ID": "my-spotify-client-id",
|
|
2321
|
+
"SPOTIFY_CLIENT_SECRET": "my-spotify-secret"
|
|
2322
|
+
}
|
|
2323
|
+
})
|
|
2324
|
+
\`\`\`
|
|
2325
|
+
|
|
2326
|
+
### Invoke Tool
|
|
2327
|
+
\`\`\`javascript
|
|
2328
|
+
invoke_toolkit_tool({
|
|
2329
|
+
installation_id: "install-xyz",
|
|
2330
|
+
tool_name: "search-tracks",
|
|
2331
|
+
inputs: {
|
|
2332
|
+
query: "Miles Davis",
|
|
2333
|
+
type: "track",
|
|
2334
|
+
limit: 10
|
|
2335
|
+
}
|
|
2336
|
+
})
|
|
2337
|
+
\`\`\`
|
|
2338
|
+
|
|
2339
|
+
### Dynamic Credentials (OAuth)
|
|
2340
|
+
\`\`\`javascript
|
|
2341
|
+
// Pass fresh tokens from a token refresh call
|
|
2342
|
+
invoke_toolkit_tool({
|
|
2343
|
+
installation_id: "install-xyz",
|
|
2344
|
+
tool_name: "get-account",
|
|
2345
|
+
credential_overrides: {
|
|
2346
|
+
"SCHWAB_ACCESS_TOKEN": freshTokenValue
|
|
2347
|
+
}
|
|
2348
|
+
})
|
|
2349
|
+
\`\`\`
|
|
2350
|
+
|
|
2351
|
+
## Managing Toolkits
|
|
2352
|
+
|
|
2353
|
+
\`\`\`javascript
|
|
2354
|
+
// List your toolkits
|
|
2355
|
+
mcp__flowdot__list_agent_toolkits({ search: "api" })
|
|
2356
|
+
|
|
2357
|
+
// Search public toolkits
|
|
2358
|
+
mcp__flowdot__search_agent_toolkits({
|
|
2359
|
+
query: "spotify",
|
|
2360
|
+
verified_only: true
|
|
2361
|
+
})
|
|
2362
|
+
|
|
2363
|
+
// Get toolkit details
|
|
2364
|
+
mcp__flowdot__get_agent_toolkit({ toolkit_id: "toolkit-abc123" })
|
|
2365
|
+
|
|
2366
|
+
// List tools in toolkit
|
|
2367
|
+
mcp__flowdot__list_toolkit_tools({ toolkit_id: "toolkit-abc123" })
|
|
2368
|
+
|
|
2369
|
+
// Update toolkit
|
|
2370
|
+
mcp__flowdot__update_agent_toolkit({
|
|
2371
|
+
toolkit_id: "toolkit-abc123",
|
|
2372
|
+
description: "Updated description"
|
|
2373
|
+
})
|
|
2374
|
+
|
|
2375
|
+
// Delete toolkit
|
|
2376
|
+
mcp__flowdot__delete_agent_toolkit({ toolkit_id: "toolkit-abc123" })
|
|
2377
|
+
\`\`\`
|
|
2378
|
+
|
|
2379
|
+
## Managing Tools
|
|
2380
|
+
|
|
2381
|
+
\`\`\`javascript
|
|
2382
|
+
// Get tool details
|
|
2383
|
+
mcp__flowdot__get_toolkit_tool({
|
|
2384
|
+
toolkit_id: "toolkit-abc123",
|
|
2385
|
+
tool_id: "tool-xyz"
|
|
2386
|
+
})
|
|
2387
|
+
|
|
2388
|
+
// Update tool
|
|
2389
|
+
mcp__flowdot__update_toolkit_tool({
|
|
2390
|
+
toolkit_id: "toolkit-abc123",
|
|
2391
|
+
tool_id: "tool-xyz",
|
|
2392
|
+
description: "Updated description",
|
|
2393
|
+
input_schema: { /* new schema */ }
|
|
2394
|
+
})
|
|
2395
|
+
|
|
2396
|
+
// Delete tool
|
|
2397
|
+
mcp__flowdot__delete_toolkit_tool({
|
|
2398
|
+
toolkit_id: "toolkit-abc123",
|
|
2399
|
+
tool_id: "tool-xyz"
|
|
2400
|
+
})
|
|
2401
|
+
\`\`\`
|
|
2402
|
+
|
|
2403
|
+
## Managing Installations
|
|
2404
|
+
|
|
2405
|
+
\`\`\`javascript
|
|
2406
|
+
// List installed toolkits
|
|
2407
|
+
mcp__flowdot__list_installed_toolkits()
|
|
2408
|
+
|
|
2409
|
+
// Check credentials
|
|
2410
|
+
mcp__flowdot__check_toolkit_credentials({
|
|
2411
|
+
installation_id: "install-xyz"
|
|
2412
|
+
})
|
|
2413
|
+
|
|
2414
|
+
// Enable/disable installation
|
|
2415
|
+
mcp__flowdot__toggle_toolkit_active({
|
|
2416
|
+
installation_id: "install-xyz",
|
|
2417
|
+
is_active: false
|
|
2418
|
+
})
|
|
2419
|
+
|
|
2420
|
+
// Uninstall
|
|
2421
|
+
mcp__flowdot__uninstall_toolkit({
|
|
2422
|
+
installation_id: "install-xyz"
|
|
2423
|
+
})
|
|
2424
|
+
\`\`\`
|
|
2425
|
+
|
|
2426
|
+
## Sharing Toolkits
|
|
2427
|
+
|
|
2428
|
+
\`\`\`javascript
|
|
2429
|
+
// Make public
|
|
2430
|
+
mcp__flowdot__toggle_toolkit_visibility({
|
|
2431
|
+
toolkit_id: "toolkit-abc123",
|
|
2432
|
+
visibility: "public"
|
|
2433
|
+
})
|
|
2434
|
+
|
|
2435
|
+
// Vote on toolkit
|
|
2436
|
+
mcp__flowdot__vote_toolkit({
|
|
2437
|
+
toolkit_id: "toolkit-abc123",
|
|
2438
|
+
vote: "up"
|
|
2439
|
+
})
|
|
2440
|
+
|
|
2441
|
+
// Favorite toolkit
|
|
2442
|
+
mcp__flowdot__favorite_toolkit({
|
|
2443
|
+
toolkit_id: "toolkit-abc123",
|
|
2444
|
+
favorite: true
|
|
2445
|
+
})
|
|
2446
|
+
|
|
2447
|
+
// Add comment
|
|
2448
|
+
mcp__flowdot__add_toolkit_comment({
|
|
2449
|
+
toolkit_id: "toolkit-abc123",
|
|
2450
|
+
content: "Great toolkit for music APIs!"
|
|
2451
|
+
})
|
|
2452
|
+
\`\`\`
|
|
2453
|
+
|
|
2454
|
+
## Best Practices
|
|
2455
|
+
|
|
2456
|
+
1. **Group related tools:** Keep toolkits focused on one domain
|
|
2457
|
+
2. **Clear credential names:** Use descriptive key names
|
|
2458
|
+
3. **OAuth when possible:** Enables auto-refresh
|
|
2459
|
+
4. **Document thoroughly:** Add descriptions to everything
|
|
2460
|
+
5. **Test credentials:** Verify all required credentials work
|
|
2461
|
+
6. **Version carefully:** Breaking changes need new toolkit
|
|
2462
|
+
7. **Security first:** Never expose credentials in tool configs
|
|
2463
|
+
|
|
2464
|
+
## Common Patterns
|
|
2465
|
+
|
|
2466
|
+
### Pattern 1: RESTful API Toolkit
|
|
2467
|
+
- Multiple HTTP tools for different endpoints
|
|
2468
|
+
- Shared API key credentials
|
|
2469
|
+
- Input schemas matching API parameters
|
|
2470
|
+
|
|
2471
|
+
### Pattern 2: AI-Powered Toolkit
|
|
2472
|
+
- Workflow tools calling LLM workflows
|
|
2473
|
+
- Pre-configured prompts and processing
|
|
2474
|
+
- Abstracted complexity
|
|
2475
|
+
|
|
2476
|
+
### Pattern 3: Hybrid Toolkit
|
|
2477
|
+
- HTTP tools for data fetching
|
|
2478
|
+
- Workflow tools for processing
|
|
2479
|
+
- Combined capabilities
|
|
2480
|
+
|
|
2481
|
+
## Troubleshooting
|
|
2482
|
+
|
|
2483
|
+
### Credentials Not Working
|
|
2484
|
+
- Check \`check_toolkit_credentials\`
|
|
2485
|
+
- Verify credential mapping is correct
|
|
2486
|
+
- Ensure API keys are valid
|
|
2487
|
+
|
|
2488
|
+
### OAuth Tokens Expiring
|
|
2489
|
+
- Verify oauth_config is complete
|
|
2490
|
+
- Check auth_error_codes and patterns
|
|
2491
|
+
- Ensure client credentials are correct
|
|
2492
|
+
|
|
2493
|
+
### Tool Invocation Failing
|
|
2494
|
+
- Verify input schema requirements
|
|
2495
|
+
- Check endpoint configuration
|
|
2496
|
+
- Test with credential_overrides
|
|
2497
|
+
|
|
2498
|
+
## Related Resources
|
|
2499
|
+
|
|
2500
|
+
- **Workflows:** \`learn://workflows\` - Build workflow-based tools
|
|
2501
|
+
- **Custom Nodes:** \`learn://custom-nodes\` - Extend processing capabilities
|
|
1844
2502
|
`,
|
|
1845
2503
|
},
|
|
1846
2504
|
'learn://knowledge-base': {
|
|
1847
2505
|
name: 'Knowledge Base Complete Guide',
|
|
1848
2506
|
description: 'Complete guide to using the FlowDot knowledge base with RAG',
|
|
1849
2507
|
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
|
-
##
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
category_id: 123
|
|
1946
|
-
})
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
}
|
|
1953
|
-
\`\`\`
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
###
|
|
1968
|
-
\`\`\`javascript
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
\`\`\`
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
\`\`\`
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
\`\`\`
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
\`\`\`
|
|
2027
|
-
|
|
2028
|
-
###
|
|
2029
|
-
\`\`\`javascript
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
##
|
|
2142
|
-
|
|
2143
|
-
###
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2508
|
+
content: `# FlowDot Knowledge Base - Complete Guide
|
|
2509
|
+
|
|
2510
|
+
## What Is the Knowledge Base?
|
|
2511
|
+
|
|
2512
|
+
The Knowledge Base is a **document storage and RAG (Retrieval-Augmented Generation) system** that lets you:
|
|
2513
|
+
- Upload documents (PDF, DOCX, TXT, Markdown, CSV, JSON)
|
|
2514
|
+
- Organize with categories and teams
|
|
2515
|
+
- Search with semantic + keyword search
|
|
2516
|
+
- Use in workflows and agents for context
|
|
2517
|
+
|
|
2518
|
+
## Key Concepts
|
|
2519
|
+
|
|
2520
|
+
### Documents
|
|
2521
|
+
Files uploaded to your knowledge base:
|
|
2522
|
+
- **Max size:** 50MB per file
|
|
2523
|
+
- **Formats:** PDF, DOCX, TXT, MD, CSV, JSON
|
|
2524
|
+
- **Processing:** Auto-chunked and embedded
|
|
2525
|
+
- **Status:** pending → processing → ready or failed
|
|
2526
|
+
|
|
2527
|
+
### Categories
|
|
2528
|
+
Organization for documents:
|
|
2529
|
+
- Create categories to group related docs
|
|
2530
|
+
- Color-coded for visual organization
|
|
2531
|
+
- Can be personal or team-based
|
|
2532
|
+
|
|
2533
|
+
### Teams
|
|
2534
|
+
Shared knowledge bases:
|
|
2535
|
+
- Share documents with team members
|
|
2536
|
+
- Team-specific categories
|
|
2537
|
+
- Access control per team
|
|
2538
|
+
|
|
2539
|
+
### Chunking
|
|
2540
|
+
Documents are split into chunks:
|
|
2541
|
+
- Each chunk is embedded for semantic search
|
|
2542
|
+
- Optimized chunk size for context
|
|
2543
|
+
- Preserves document structure
|
|
2544
|
+
|
|
2545
|
+
### RAG Search
|
|
2546
|
+
Retrieval-Augmented Generation:
|
|
2547
|
+
- Semantic search (meaning-based)
|
|
2548
|
+
- Keyword search (exact matches)
|
|
2549
|
+
- Returns ranked chunks with sources
|
|
2550
|
+
- Use results to ground AI responses
|
|
2551
|
+
|
|
2552
|
+
## Writing Documents for Good Retrieval
|
|
2553
|
+
|
|
2554
|
+
The shape of your documents directly determines whether RAG queries return useful results. A 100-page PDF with no headings is harder to retrieve from than the same content split into well-structured sections, even though both contain the same information.
|
|
2555
|
+
|
|
2556
|
+
### Why structure matters
|
|
2557
|
+
|
|
2558
|
+
When a document is uploaded, FlowDot splits it into chunks (typically a few hundred tokens each), generates an embedding for every chunk, and stores them. At query time, the user's question is embedded and the system returns the chunks with the most similar embeddings.
|
|
2559
|
+
|
|
2560
|
+
This means **the chunk is the unit of retrieval**, not the document. A well-formed chunk contains a single coherent idea that makes sense on its own, with enough surrounding context that an LLM reading it (without seeing the rest of the document) can still understand what it's about.
|
|
2561
|
+
|
|
2562
|
+
### Document preparation rules
|
|
2563
|
+
|
|
2564
|
+
1. **Use headings.** Markdown \`#\` / \`##\` headers, or PDF section headings, give the chunker natural break points and the embedder useful context. A document with no headings becomes a soup that's hard to retrieve from.
|
|
2565
|
+
|
|
2566
|
+
2. **One topic per section.** If a section covers three different concepts, the chunks will mix them and embeddings will be averaged. Split into three sections instead.
|
|
2567
|
+
|
|
2568
|
+
3. **Front-load the topic in each section.** The first sentence of a section should name what the section is about. Embeddings weight earlier tokens more, so "OAuth Configuration: To set up OAuth..." retrieves better than "To set up OAuth, which we use because..."
|
|
2569
|
+
|
|
2570
|
+
4. **Avoid pronoun chains across paragraphs.** A chunk that says "It uses the same approach as before" without explaining what "it" is becomes useless when retrieved out of context. Repeat nouns when in doubt.
|
|
2571
|
+
|
|
2572
|
+
5. **Inline definitions.** If a section uses a term defined elsewhere, briefly redefine it. Each chunk should be self-contained.
|
|
2573
|
+
|
|
2574
|
+
6. **Prefer Markdown over PDF when you have the choice.** Markdown round-trips through chunking with no formatting loss. PDFs lose tables, footnotes, and sometimes paragraph boundaries.
|
|
2575
|
+
|
|
2576
|
+
7. **Split very long documents.** A 200-page manual is better as ten 20-page documents organized in a category than as one giant file. Each upload is a discrete retrievable unit.
|
|
2577
|
+
|
|
2578
|
+
### What to avoid
|
|
2579
|
+
|
|
2580
|
+
- **Wall-of-text without breaks** — chunker has nothing to grab onto
|
|
2581
|
+
- **Tables of contents and index pages** — these get embedded as if they were content and pollute results
|
|
2582
|
+
- **Heavily templated repeated content** (e.g., 50 product pages with identical headers) — embeddings collapse and the system can't distinguish between them
|
|
2583
|
+
- **Image-only PDFs** — there's no text to chunk; OCR if needed before uploading
|
|
2584
|
+
|
|
2585
|
+
## Uploading Documents
|
|
2586
|
+
|
|
2587
|
+
### Upload Text Content
|
|
2588
|
+
\`\`\`javascript
|
|
2589
|
+
upload_text_document({
|
|
2590
|
+
title: "Project Overview",
|
|
2591
|
+
content: "This is the content of my document...",
|
|
2592
|
+
mime_type: "text/markdown",
|
|
2593
|
+
category_id: 123 // optional
|
|
2594
|
+
})
|
|
2595
|
+
// Returns: { id: 456, status: "processing", ... }
|
|
2596
|
+
\`\`\`
|
|
2597
|
+
|
|
2598
|
+
### Upload from URL
|
|
2599
|
+
\`\`\`javascript
|
|
2600
|
+
upload_document_from_url({
|
|
2601
|
+
url: "https://example.com/whitepaper.pdf",
|
|
2602
|
+
title: "Company Whitepaper",
|
|
2603
|
+
category_id: 123 // optional
|
|
2604
|
+
})
|
|
2605
|
+
\`\`\`
|
|
2606
|
+
|
|
2607
|
+
### Check Processing Status
|
|
2608
|
+
\`\`\`javascript
|
|
2609
|
+
get_knowledge_document({ document_id: 456 })
|
|
2610
|
+
// Returns: { status: "ready", chunk_count: 42, ... }
|
|
2611
|
+
\`\`\`
|
|
2612
|
+
|
|
2613
|
+
## Organizing Documents
|
|
2614
|
+
|
|
2615
|
+
### Create Categories
|
|
2616
|
+
\`\`\`javascript
|
|
2617
|
+
create_knowledge_category({
|
|
2618
|
+
name: "Product Documentation",
|
|
2619
|
+
description: "Official product docs and guides",
|
|
2620
|
+
color: "#3B82F6"
|
|
2621
|
+
})
|
|
2622
|
+
// Returns: { id: 123, ... }
|
|
2623
|
+
\`\`\`
|
|
2624
|
+
|
|
2625
|
+
### List Categories
|
|
2626
|
+
\`\`\`javascript
|
|
2627
|
+
list_knowledge_categories()
|
|
2628
|
+
list_knowledge_categories({ team_id: 5 }) // Team-specific
|
|
2629
|
+
list_knowledge_categories({ personal: true }) // Personal only
|
|
2630
|
+
\`\`\`
|
|
2631
|
+
|
|
2632
|
+
### Move Document to Category
|
|
2633
|
+
\`\`\`javascript
|
|
2634
|
+
move_document_to_category({
|
|
2635
|
+
document_id: 456,
|
|
2636
|
+
category_id: 123
|
|
2637
|
+
})
|
|
2638
|
+
|
|
2639
|
+
// Or remove from category
|
|
2640
|
+
move_document_to_category({
|
|
2641
|
+
document_id: 456,
|
|
2642
|
+
category_id: null
|
|
2643
|
+
})
|
|
2644
|
+
\`\`\`
|
|
2645
|
+
|
|
2646
|
+
### Update Category
|
|
2647
|
+
\`\`\`javascript
|
|
2648
|
+
update_knowledge_category({
|
|
2649
|
+
category_id: 123,
|
|
2650
|
+
name: "Updated Name",
|
|
2651
|
+
description: "New description",
|
|
2652
|
+
color: "#EF4444"
|
|
2653
|
+
})
|
|
2654
|
+
\`\`\`
|
|
2655
|
+
|
|
2656
|
+
## Searching the Knowledge Base
|
|
2657
|
+
|
|
2658
|
+
### Basic Search
|
|
2659
|
+
\`\`\`javascript
|
|
2660
|
+
query_knowledge_base({
|
|
2661
|
+
query: "How do I configure OAuth authentication?",
|
|
2662
|
+
top_k: 5
|
|
2663
|
+
})
|
|
2664
|
+
// Returns: Array of matching chunks with sources
|
|
2665
|
+
\`\`\`
|
|
2666
|
+
|
|
2667
|
+
### Search Specific Category
|
|
2668
|
+
\`\`\`javascript
|
|
2669
|
+
query_knowledge_base({
|
|
2670
|
+
query: "deployment procedures",
|
|
2671
|
+
category_id: 123,
|
|
2672
|
+
top_k: 10
|
|
2673
|
+
})
|
|
2674
|
+
\`\`\`
|
|
2675
|
+
|
|
2676
|
+
### Search Team Documents
|
|
2677
|
+
\`\`\`javascript
|
|
2678
|
+
query_knowledge_base({
|
|
2679
|
+
query: "security policies",
|
|
2680
|
+
team_id: 5,
|
|
2681
|
+
include_personal: false,
|
|
2682
|
+
top_k: 5
|
|
2683
|
+
})
|
|
2684
|
+
\`\`\`
|
|
2685
|
+
|
|
2686
|
+
### Search Response Structure
|
|
2687
|
+
\`\`\`javascript
|
|
2688
|
+
[
|
|
2689
|
+
{
|
|
2690
|
+
chunk_text: "...relevant text...",
|
|
2691
|
+
document_title: "Security Guidelines",
|
|
2692
|
+
document_id: 456,
|
|
2693
|
+
score: 0.89,
|
|
2694
|
+
metadata: {
|
|
2695
|
+
page: 3,
|
|
2696
|
+
section: "OAuth Configuration"
|
|
2697
|
+
}
|
|
2698
|
+
},
|
|
2699
|
+
// ... more results
|
|
2700
|
+
]
|
|
2701
|
+
\`\`\`
|
|
2702
|
+
|
|
2703
|
+
## Querying Effectively
|
|
2704
|
+
|
|
2705
|
+
The query you pass to \`query_knowledge_base\` is itself embedded and compared against chunk embeddings. The shape of the query matters as much as the shape of the documents.
|
|
2706
|
+
|
|
2707
|
+
### Query design principles
|
|
2708
|
+
|
|
2709
|
+
1. **Phrase queries as questions or statements, not keyword soup.** Semantic search works on meaning, not term frequency. \`"How do I configure OAuth for the Schwab API"\` will retrieve better than \`"oauth schwab config"\`.
|
|
2710
|
+
|
|
2711
|
+
2. **Be specific about what you want.** A query like \`"authentication"\` will match every chunk that mentions auth — too broad to be useful. \`"How do I refresh an OAuth access token when it expires?"\` will retrieve the exact section you need.
|
|
2712
|
+
|
|
2713
|
+
3. **Match the document's vocabulary when possible.** If the docs say "API key" but the user asks about "credentials," use \`"API key for the X service"\` rather than \`"X credentials"\`. Embeddings handle synonyms decently but exact term matches still help.
|
|
2714
|
+
|
|
2715
|
+
4. **Ask one question per query.** \`"How do I install the toolkit and configure OAuth and refresh tokens"\` will return mediocre results for all three. Ask each separately and combine the results in your prompt.
|
|
2716
|
+
|
|
2717
|
+
### Tuning \`top_k\`
|
|
2718
|
+
|
|
2719
|
+
\`top_k\` controls how many chunks to return. Defaults to 5-10:
|
|
2720
|
+
- **3-5**: Use when you need a focused, high-precision answer
|
|
2721
|
+
- **10**: Use for general research questions where you want broader coverage
|
|
2722
|
+
- **20+**: Use when you're going to feed everything into a synthesis step and need comprehensive coverage. Be aware this dilutes signal — the bottom of the list is often noise.
|
|
2723
|
+
|
|
2724
|
+
### Filtering by category or team
|
|
2725
|
+
|
|
2726
|
+
Pass \`category_id\` or \`team_id\` to narrow the search scope. This dramatically improves quality when you know the answer is in a specific area:
|
|
2727
|
+
|
|
2728
|
+
\`\`\`javascript
|
|
2729
|
+
// Search only in product docs
|
|
2730
|
+
query_knowledge_base({
|
|
2731
|
+
query: "How do I deploy to production?",
|
|
2732
|
+
category_id: 123, // "Product Documentation" category
|
|
2733
|
+
top_k: 5
|
|
2734
|
+
})
|
|
2735
|
+
\`\`\`
|
|
2736
|
+
|
|
2737
|
+
This is faster *and* more accurate than searching everything — the embedding model can't distinguish between "production deployment" in your product docs and "production deployment" in a competitor analysis from a different category.
|
|
2738
|
+
|
|
2739
|
+
### Using results as LLM context (the actual RAG pattern)
|
|
2740
|
+
|
|
2741
|
+
The point of \`query_knowledge_base\` is rarely to show results to a user directly — it's to feed them as context into an LLM call. The standard pattern:
|
|
2742
|
+
|
|
2743
|
+
\`\`\`javascript
|
|
2744
|
+
// 1. Retrieve
|
|
2745
|
+
const results = await query_knowledge_base({
|
|
2746
|
+
query: "{{inputs.user_question}}",
|
|
2747
|
+
top_k: 5
|
|
2748
|
+
});
|
|
2749
|
+
|
|
2750
|
+
// 2. Format as context
|
|
2751
|
+
const context = results.map(r =>
|
|
2752
|
+
\`[Source: \${r.document_title}, score: \${r.score}]\\n\${r.chunk_text}\`
|
|
2753
|
+
).join('\\n\\n---\\n\\n');
|
|
2754
|
+
|
|
2755
|
+
// 3. Pass to LLM with explicit instructions
|
|
2756
|
+
// (typically done in an agent step's user_prompt)
|
|
2757
|
+
const prompt = \`
|
|
2758
|
+
Answer the user's question using ONLY the provided context.
|
|
2759
|
+
If the context doesn't contain the answer, say so.
|
|
2760
|
+
ALWAYS cite the source document title.
|
|
2761
|
+
|
|
2762
|
+
CONTEXT:
|
|
2763
|
+
\${context}
|
|
2764
|
+
|
|
2765
|
+
QUESTION: {{inputs.user_question}}
|
|
2766
|
+
\`;
|
|
2767
|
+
\`\`\`
|
|
2768
|
+
|
|
2769
|
+
**The "ALWAYS cite" instruction is critical.** Without it, LLMs will silently mix retrieved facts with their training data, and you'll have no way to verify what came from where.
|
|
2770
|
+
|
|
2771
|
+
**The "ONLY the provided context" instruction is also critical.** Without it, the LLM will fall back to its training data for things the docs don't mention, which defeats the purpose of having a knowledge base.
|
|
2772
|
+
|
|
2773
|
+
## Managing Documents
|
|
2774
|
+
|
|
2775
|
+
### List Documents
|
|
2776
|
+
\`\`\`javascript
|
|
2777
|
+
list_knowledge_documents()
|
|
2778
|
+
list_knowledge_documents({ category_id: 123 })
|
|
2779
|
+
list_knowledge_documents({ status: "ready" })
|
|
2780
|
+
list_knowledge_documents({ team_id: 5 })
|
|
2781
|
+
\`\`\`
|
|
2782
|
+
|
|
2783
|
+
### Get Document Details
|
|
2784
|
+
\`\`\`javascript
|
|
2785
|
+
get_knowledge_document({ document_id: 456 })
|
|
2786
|
+
// Returns: Full metadata, chunks, processing status
|
|
2787
|
+
\`\`\`
|
|
2788
|
+
|
|
2789
|
+
### Reprocess Failed Document
|
|
2790
|
+
\`\`\`javascript
|
|
2791
|
+
reprocess_document({ document_id: 456 })
|
|
2792
|
+
\`\`\`
|
|
2793
|
+
|
|
2794
|
+
### Delete Document
|
|
2795
|
+
\`\`\`javascript
|
|
2796
|
+
delete_knowledge_document({ document_id: 456 })
|
|
2797
|
+
\`\`\`
|
|
2798
|
+
|
|
2799
|
+
## Team Features
|
|
2800
|
+
|
|
2801
|
+
### List Your Teams
|
|
2802
|
+
\`\`\`javascript
|
|
2803
|
+
list_user_teams()
|
|
2804
|
+
// Returns: Teams you belong to with roles
|
|
2805
|
+
\`\`\`
|
|
2806
|
+
|
|
2807
|
+
### Upload to Team
|
|
2808
|
+
\`\`\`javascript
|
|
2809
|
+
upload_text_document({
|
|
2810
|
+
title: "Team Playbook",
|
|
2811
|
+
content: "...",
|
|
2812
|
+
team_id: 5
|
|
2813
|
+
})
|
|
2814
|
+
\`\`\`
|
|
2815
|
+
|
|
2816
|
+
### Transfer Document Ownership
|
|
2817
|
+
\`\`\`javascript
|
|
2818
|
+
// Personal → Team
|
|
2819
|
+
transfer_document_ownership({
|
|
2820
|
+
document_id: 456,
|
|
2821
|
+
team_id: 5,
|
|
2822
|
+
category_id: 789 // optional team category
|
|
2823
|
+
})
|
|
2824
|
+
|
|
2825
|
+
// Team → Personal
|
|
2826
|
+
transfer_document_ownership({
|
|
2827
|
+
document_id: 456,
|
|
2828
|
+
team_id: null // or omit
|
|
2829
|
+
})
|
|
2830
|
+
\`\`\`
|
|
2831
|
+
|
|
2832
|
+
## Storage Management
|
|
2833
|
+
|
|
2834
|
+
### Check Storage Usage
|
|
2835
|
+
\`\`\`javascript
|
|
2836
|
+
get_knowledge_storage()
|
|
2837
|
+
// Returns: {
|
|
2838
|
+
// used_bytes: 12345678,
|
|
2839
|
+
// limit_bytes: 1073741824,
|
|
2840
|
+
// document_count: 42,
|
|
2841
|
+
// percentage_used: 1.15
|
|
2842
|
+
// }
|
|
2843
|
+
\`\`\`
|
|
2844
|
+
|
|
2845
|
+
## Using in Workflows & Agents
|
|
2846
|
+
|
|
2847
|
+
### In Agent Steps
|
|
2848
|
+
\`\`\`javascript
|
|
2849
|
+
add_recipe_step({
|
|
2850
|
+
hash: "recipe-xyz",
|
|
2851
|
+
name: "research",
|
|
2852
|
+
type: "agent",
|
|
2853
|
+
config: {
|
|
2854
|
+
user_prompt: \`
|
|
2855
|
+
Search knowledge base for: {{inputs.request}}
|
|
2856
|
+
|
|
2857
|
+
Use query_knowledge_base tool to find relevant information.
|
|
2858
|
+
Summarize the findings.
|
|
2859
|
+
\`,
|
|
2860
|
+
tools: ["query_knowledge_base", "search"],
|
|
2861
|
+
output_store: "research_result"
|
|
2862
|
+
}
|
|
2863
|
+
})
|
|
2864
|
+
\`\`\`
|
|
2865
|
+
|
|
2866
|
+
### In Workflows
|
|
2867
|
+
Use a custom node or LLM node that:
|
|
2868
|
+
1. Calls \`query_knowledge_base\`
|
|
2869
|
+
2. Retrieves relevant chunks
|
|
2870
|
+
3. Uses chunks as context for generation
|
|
2871
|
+
|
|
2872
|
+
## Best Practices
|
|
2873
|
+
|
|
2874
|
+
1. **Categorize from the start:** Easier to find later
|
|
2875
|
+
2. **Descriptive titles:** Help with search ranking
|
|
2876
|
+
3. **Check status:** Wait for "ready" before using
|
|
2877
|
+
4. **Team vs personal:** Decide visibility upfront
|
|
2878
|
+
5. **Regular cleanup:** Delete outdated docs
|
|
2879
|
+
6. **Optimize queries:** Specific questions work best
|
|
2880
|
+
7. **Use top_k wisely:** 5-10 results usually enough
|
|
2881
|
+
|
|
2882
|
+
## Common Patterns
|
|
2883
|
+
|
|
2884
|
+
### Pattern 1: Documentation Assistant
|
|
2885
|
+
1. Upload product documentation
|
|
2886
|
+
2. Create "docs" category
|
|
2887
|
+
3. Agent searches knowledge base
|
|
2888
|
+
4. Returns specific answers with sources
|
|
2889
|
+
|
|
2890
|
+
### Pattern 2: Team Knowledge
|
|
2891
|
+
1. Upload team playbooks, procedures
|
|
2892
|
+
2. Share via team knowledge base
|
|
2893
|
+
3. Team members query for guidance
|
|
2894
|
+
4. Consistent information across team
|
|
2895
|
+
|
|
2896
|
+
### Pattern 3: Research Assistant
|
|
2897
|
+
1. Upload research papers, articles
|
|
2898
|
+
2. Categorize by topic
|
|
2899
|
+
3. Agent finds relevant passages
|
|
2900
|
+
4. Synthesizes insights from multiple sources
|
|
2901
|
+
|
|
2902
|
+
## Troubleshooting
|
|
2903
|
+
|
|
2904
|
+
### Document Stuck in "processing"
|
|
2905
|
+
- Wait a few minutes (large docs take time)
|
|
2906
|
+
- Check \`get_knowledge_document\` for status
|
|
2907
|
+
- If stuck >10min, try \`reprocess_document\`
|
|
2908
|
+
|
|
2909
|
+
### Search Returns No Results
|
|
2910
|
+
- Verify document status is "ready"
|
|
2911
|
+
- Try more general query terms
|
|
2912
|
+
- Check document is in expected category
|
|
2913
|
+
- Ensure team/personal filters are correct
|
|
2914
|
+
|
|
2915
|
+
### Upload Fails
|
|
2916
|
+
- Check file size (<50MB)
|
|
2917
|
+
- Verify file format is supported
|
|
2918
|
+
- Try simpler filename (no special chars)
|
|
2919
|
+
|
|
2920
|
+
## Related Resources
|
|
2921
|
+
|
|
2922
|
+
- **Recipes:** \`learn://recipes\` - Use knowledge base in agent workflows
|
|
2923
|
+
- **Custom Nodes:** \`learn://custom-nodes\` - Build RAG-powered nodes
|
|
2924
|
+
- **Workflows:** \`learn://workflows\` - Integrate knowledge base lookups
|
|
2164
2925
|
`,
|
|
2165
2926
|
},
|
|
2166
2927
|
};
|
|
2167
|
-
/**
|
|
2168
|
-
* Register learning resources with the MCP server.
|
|
2169
|
-
*/
|
|
2170
2928
|
export function registerResources(server) {
|
|
2171
|
-
// Handle list resources request
|
|
2172
2929
|
server.setRequestHandler(ListResourcesRequestSchema, async () => {
|
|
2173
2930
|
return {
|
|
2174
2931
|
resources: Object.entries(LEARN_RESOURCES).map(([uri, resource]) => ({
|
|
@@ -2179,7 +2936,6 @@ export function registerResources(server) {
|
|
|
2179
2936
|
})),
|
|
2180
2937
|
};
|
|
2181
2938
|
});
|
|
2182
|
-
// Handle read resource request
|
|
2183
2939
|
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
2184
2940
|
const uri = request.params.uri;
|
|
2185
2941
|
const resource = LEARN_RESOURCES[uri];
|
|
@@ -2201,4 +2957,3 @@ export function registerResources(server) {
|
|
|
2201
2957
|
console.error(` • ${uri}`);
|
|
2202
2958
|
});
|
|
2203
2959
|
}
|
|
2204
|
-
//# sourceMappingURL=index.js.map
|