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