@oh-my-pi/pi-coding-agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (337) hide show
  1. package/CHANGELOG.md +1629 -0
  2. package/README.md +1041 -0
  3. package/docs/compaction.md +403 -0
  4. package/docs/config-usage.md +113 -0
  5. package/docs/custom-tools.md +541 -0
  6. package/docs/extension-loading.md +1004 -0
  7. package/docs/hooks.md +867 -0
  8. package/docs/rpc.md +1040 -0
  9. package/docs/sdk.md +994 -0
  10. package/docs/session-tree-plan.md +441 -0
  11. package/docs/session.md +240 -0
  12. package/docs/skills.md +290 -0
  13. package/docs/theme.md +670 -0
  14. package/docs/tree.md +197 -0
  15. package/docs/tui.md +341 -0
  16. package/examples/README.md +21 -0
  17. package/examples/custom-tools/README.md +124 -0
  18. package/examples/custom-tools/hello/index.ts +20 -0
  19. package/examples/custom-tools/question/index.ts +84 -0
  20. package/examples/custom-tools/subagent/README.md +172 -0
  21. package/examples/custom-tools/subagent/agents/planner.md +37 -0
  22. package/examples/custom-tools/subagent/agents/scout.md +50 -0
  23. package/examples/custom-tools/subagent/agents/worker.md +24 -0
  24. package/examples/custom-tools/subagent/agents.ts +156 -0
  25. package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
  26. package/examples/custom-tools/subagent/commands/implement.md +10 -0
  27. package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
  28. package/examples/custom-tools/subagent/index.ts +1002 -0
  29. package/examples/custom-tools/todo/index.ts +212 -0
  30. package/examples/hooks/README.md +56 -0
  31. package/examples/hooks/auto-commit-on-exit.ts +49 -0
  32. package/examples/hooks/confirm-destructive.ts +59 -0
  33. package/examples/hooks/custom-compaction.ts +116 -0
  34. package/examples/hooks/dirty-repo-guard.ts +52 -0
  35. package/examples/hooks/file-trigger.ts +41 -0
  36. package/examples/hooks/git-checkpoint.ts +53 -0
  37. package/examples/hooks/handoff.ts +150 -0
  38. package/examples/hooks/permission-gate.ts +34 -0
  39. package/examples/hooks/protected-paths.ts +30 -0
  40. package/examples/hooks/qna.ts +119 -0
  41. package/examples/hooks/snake.ts +343 -0
  42. package/examples/hooks/status-line.ts +40 -0
  43. package/examples/sdk/01-minimal.ts +22 -0
  44. package/examples/sdk/02-custom-model.ts +49 -0
  45. package/examples/sdk/03-custom-prompt.ts +44 -0
  46. package/examples/sdk/04-skills.ts +44 -0
  47. package/examples/sdk/05-tools.ts +90 -0
  48. package/examples/sdk/06-hooks.ts +61 -0
  49. package/examples/sdk/07-context-files.ts +36 -0
  50. package/examples/sdk/08-slash-commands.ts +42 -0
  51. package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
  52. package/examples/sdk/10-settings.ts +38 -0
  53. package/examples/sdk/11-sessions.ts +48 -0
  54. package/examples/sdk/12-full-control.ts +95 -0
  55. package/examples/sdk/README.md +154 -0
  56. package/package.json +89 -0
  57. package/src/bun-imports.d.ts +16 -0
  58. package/src/capability/context-file.ts +40 -0
  59. package/src/capability/extension.ts +48 -0
  60. package/src/capability/hook.ts +40 -0
  61. package/src/capability/index.ts +616 -0
  62. package/src/capability/instruction.ts +37 -0
  63. package/src/capability/mcp.ts +52 -0
  64. package/src/capability/prompt.ts +35 -0
  65. package/src/capability/rule.ts +56 -0
  66. package/src/capability/settings.ts +35 -0
  67. package/src/capability/skill.ts +49 -0
  68. package/src/capability/slash-command.ts +40 -0
  69. package/src/capability/system-prompt.ts +35 -0
  70. package/src/capability/tool.ts +38 -0
  71. package/src/capability/types.ts +166 -0
  72. package/src/cli/args.ts +259 -0
  73. package/src/cli/file-processor.ts +121 -0
  74. package/src/cli/list-models.ts +104 -0
  75. package/src/cli/plugin-cli.ts +661 -0
  76. package/src/cli/session-picker.ts +41 -0
  77. package/src/cli/update-cli.ts +274 -0
  78. package/src/cli.ts +10 -0
  79. package/src/config.ts +391 -0
  80. package/src/core/agent-session.ts +2178 -0
  81. package/src/core/auth-storage.ts +258 -0
  82. package/src/core/bash-executor.ts +197 -0
  83. package/src/core/compaction/branch-summarization.ts +315 -0
  84. package/src/core/compaction/compaction.ts +664 -0
  85. package/src/core/compaction/index.ts +7 -0
  86. package/src/core/compaction/utils.ts +153 -0
  87. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  88. package/src/core/custom-commands/index.ts +15 -0
  89. package/src/core/custom-commands/loader.ts +226 -0
  90. package/src/core/custom-commands/types.ts +112 -0
  91. package/src/core/custom-tools/index.ts +22 -0
  92. package/src/core/custom-tools/loader.ts +248 -0
  93. package/src/core/custom-tools/types.ts +185 -0
  94. package/src/core/custom-tools/wrapper.ts +29 -0
  95. package/src/core/exec.ts +139 -0
  96. package/src/core/export-html/index.ts +159 -0
  97. package/src/core/export-html/template.css +774 -0
  98. package/src/core/export-html/template.generated.ts +2 -0
  99. package/src/core/export-html/template.html +45 -0
  100. package/src/core/export-html/template.js +1185 -0
  101. package/src/core/export-html/template.macro.ts +24 -0
  102. package/src/core/file-mentions.ts +54 -0
  103. package/src/core/hooks/index.ts +16 -0
  104. package/src/core/hooks/loader.ts +288 -0
  105. package/src/core/hooks/runner.ts +434 -0
  106. package/src/core/hooks/tool-wrapper.ts +98 -0
  107. package/src/core/hooks/types.ts +770 -0
  108. package/src/core/index.ts +53 -0
  109. package/src/core/logger.ts +112 -0
  110. package/src/core/mcp/client.ts +185 -0
  111. package/src/core/mcp/config.ts +248 -0
  112. package/src/core/mcp/index.ts +45 -0
  113. package/src/core/mcp/loader.ts +99 -0
  114. package/src/core/mcp/manager.ts +235 -0
  115. package/src/core/mcp/tool-bridge.ts +156 -0
  116. package/src/core/mcp/transports/http.ts +316 -0
  117. package/src/core/mcp/transports/index.ts +6 -0
  118. package/src/core/mcp/transports/stdio.ts +252 -0
  119. package/src/core/mcp/types.ts +228 -0
  120. package/src/core/messages.ts +211 -0
  121. package/src/core/model-registry.ts +334 -0
  122. package/src/core/model-resolver.ts +494 -0
  123. package/src/core/plugins/doctor.ts +67 -0
  124. package/src/core/plugins/index.ts +38 -0
  125. package/src/core/plugins/installer.ts +189 -0
  126. package/src/core/plugins/loader.ts +339 -0
  127. package/src/core/plugins/manager.ts +672 -0
  128. package/src/core/plugins/parser.ts +105 -0
  129. package/src/core/plugins/paths.ts +37 -0
  130. package/src/core/plugins/types.ts +190 -0
  131. package/src/core/sdk.ts +900 -0
  132. package/src/core/session-manager.ts +1837 -0
  133. package/src/core/settings-manager.ts +860 -0
  134. package/src/core/skills.ts +352 -0
  135. package/src/core/slash-commands.ts +132 -0
  136. package/src/core/system-prompt.ts +442 -0
  137. package/src/core/timings.ts +25 -0
  138. package/src/core/title-generator.ts +110 -0
  139. package/src/core/tools/ask.ts +193 -0
  140. package/src/core/tools/bash-interceptor.ts +120 -0
  141. package/src/core/tools/bash.ts +91 -0
  142. package/src/core/tools/context.ts +32 -0
  143. package/src/core/tools/edit-diff.ts +487 -0
  144. package/src/core/tools/edit.ts +140 -0
  145. package/src/core/tools/exa/company.ts +59 -0
  146. package/src/core/tools/exa/index.ts +63 -0
  147. package/src/core/tools/exa/linkedin.ts +59 -0
  148. package/src/core/tools/exa/mcp-client.ts +368 -0
  149. package/src/core/tools/exa/render.ts +200 -0
  150. package/src/core/tools/exa/researcher.ts +90 -0
  151. package/src/core/tools/exa/search.ts +338 -0
  152. package/src/core/tools/exa/types.ts +167 -0
  153. package/src/core/tools/exa/websets.ts +248 -0
  154. package/src/core/tools/find.ts +244 -0
  155. package/src/core/tools/grep.ts +584 -0
  156. package/src/core/tools/index.ts +283 -0
  157. package/src/core/tools/ls.ts +142 -0
  158. package/src/core/tools/lsp/client.ts +767 -0
  159. package/src/core/tools/lsp/clients/biome-client.ts +207 -0
  160. package/src/core/tools/lsp/clients/index.ts +49 -0
  161. package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
  162. package/src/core/tools/lsp/config.ts +845 -0
  163. package/src/core/tools/lsp/edits.ts +110 -0
  164. package/src/core/tools/lsp/index.ts +1364 -0
  165. package/src/core/tools/lsp/render.ts +560 -0
  166. package/src/core/tools/lsp/rust-analyzer.ts +145 -0
  167. package/src/core/tools/lsp/types.ts +495 -0
  168. package/src/core/tools/lsp/utils.ts +526 -0
  169. package/src/core/tools/notebook.ts +182 -0
  170. package/src/core/tools/output.ts +198 -0
  171. package/src/core/tools/path-utils.ts +61 -0
  172. package/src/core/tools/read.ts +507 -0
  173. package/src/core/tools/renderers.ts +820 -0
  174. package/src/core/tools/review.ts +275 -0
  175. package/src/core/tools/rulebook.ts +124 -0
  176. package/src/core/tools/task/agents.ts +158 -0
  177. package/src/core/tools/task/artifacts.ts +114 -0
  178. package/src/core/tools/task/commands.ts +157 -0
  179. package/src/core/tools/task/discovery.ts +217 -0
  180. package/src/core/tools/task/executor.ts +531 -0
  181. package/src/core/tools/task/index.ts +548 -0
  182. package/src/core/tools/task/model-resolver.ts +176 -0
  183. package/src/core/tools/task/parallel.ts +38 -0
  184. package/src/core/tools/task/render.ts +502 -0
  185. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  186. package/src/core/tools/task/types.ts +142 -0
  187. package/src/core/tools/truncate.ts +265 -0
  188. package/src/core/tools/web-fetch.ts +2511 -0
  189. package/src/core/tools/web-search/auth.ts +199 -0
  190. package/src/core/tools/web-search/index.ts +583 -0
  191. package/src/core/tools/web-search/providers/anthropic.ts +198 -0
  192. package/src/core/tools/web-search/providers/exa.ts +196 -0
  193. package/src/core/tools/web-search/providers/perplexity.ts +195 -0
  194. package/src/core/tools/web-search/render.ts +372 -0
  195. package/src/core/tools/web-search/types.ts +180 -0
  196. package/src/core/tools/write.ts +63 -0
  197. package/src/core/ttsr.ts +211 -0
  198. package/src/core/utils.ts +187 -0
  199. package/src/discovery/agents-md.ts +75 -0
  200. package/src/discovery/builtin.ts +647 -0
  201. package/src/discovery/claude.ts +623 -0
  202. package/src/discovery/cline.ts +104 -0
  203. package/src/discovery/codex.ts +571 -0
  204. package/src/discovery/cursor.ts +266 -0
  205. package/src/discovery/gemini.ts +368 -0
  206. package/src/discovery/github.ts +120 -0
  207. package/src/discovery/helpers.test.ts +127 -0
  208. package/src/discovery/helpers.ts +249 -0
  209. package/src/discovery/index.ts +84 -0
  210. package/src/discovery/mcp-json.ts +127 -0
  211. package/src/discovery/vscode.ts +99 -0
  212. package/src/discovery/windsurf.ts +219 -0
  213. package/src/index.ts +192 -0
  214. package/src/main.ts +507 -0
  215. package/src/migrations.ts +156 -0
  216. package/src/modes/cleanup.ts +23 -0
  217. package/src/modes/index.ts +48 -0
  218. package/src/modes/interactive/components/armin.ts +382 -0
  219. package/src/modes/interactive/components/assistant-message.ts +86 -0
  220. package/src/modes/interactive/components/bash-execution.ts +199 -0
  221. package/src/modes/interactive/components/bordered-loader.ts +41 -0
  222. package/src/modes/interactive/components/branch-summary-message.ts +42 -0
  223. package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
  224. package/src/modes/interactive/components/custom-editor.ts +122 -0
  225. package/src/modes/interactive/components/diff.ts +147 -0
  226. package/src/modes/interactive/components/dynamic-border.ts +25 -0
  227. package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
  228. package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
  229. package/src/modes/interactive/components/extensions/index.ts +9 -0
  230. package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
  231. package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
  232. package/src/modes/interactive/components/extensions/types.ts +191 -0
  233. package/src/modes/interactive/components/hook-editor.ts +117 -0
  234. package/src/modes/interactive/components/hook-input.ts +64 -0
  235. package/src/modes/interactive/components/hook-message.ts +96 -0
  236. package/src/modes/interactive/components/hook-selector.ts +91 -0
  237. package/src/modes/interactive/components/model-selector.ts +560 -0
  238. package/src/modes/interactive/components/oauth-selector.ts +136 -0
  239. package/src/modes/interactive/components/plugin-settings.ts +481 -0
  240. package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
  241. package/src/modes/interactive/components/session-selector.ts +220 -0
  242. package/src/modes/interactive/components/settings-defs.ts +597 -0
  243. package/src/modes/interactive/components/settings-selector.ts +545 -0
  244. package/src/modes/interactive/components/show-images-selector.ts +45 -0
  245. package/src/modes/interactive/components/status-line/index.ts +4 -0
  246. package/src/modes/interactive/components/status-line/presets.ts +94 -0
  247. package/src/modes/interactive/components/status-line/segments.ts +350 -0
  248. package/src/modes/interactive/components/status-line/separators.ts +55 -0
  249. package/src/modes/interactive/components/status-line/types.ts +81 -0
  250. package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
  251. package/src/modes/interactive/components/status-line.ts +384 -0
  252. package/src/modes/interactive/components/theme-selector.ts +62 -0
  253. package/src/modes/interactive/components/thinking-selector.ts +64 -0
  254. package/src/modes/interactive/components/tool-execution.ts +946 -0
  255. package/src/modes/interactive/components/tree-selector.ts +877 -0
  256. package/src/modes/interactive/components/ttsr-notification.ts +82 -0
  257. package/src/modes/interactive/components/user-message-selector.ts +159 -0
  258. package/src/modes/interactive/components/user-message.ts +18 -0
  259. package/src/modes/interactive/components/visual-truncate.ts +50 -0
  260. package/src/modes/interactive/components/welcome.ts +228 -0
  261. package/src/modes/interactive/interactive-mode.ts +2669 -0
  262. package/src/modes/interactive/theme/dark.json +102 -0
  263. package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
  264. package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
  265. package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
  266. package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
  267. package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
  268. package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
  269. package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
  270. package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
  271. package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
  272. package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
  273. package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
  274. package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
  275. package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
  276. package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
  277. package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
  278. package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
  279. package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
  280. package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
  281. package/src/modes/interactive/theme/defaults/index.ts +67 -0
  282. package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
  283. package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
  284. package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
  285. package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
  286. package/src/modes/interactive/theme/defaults/light-github.json +114 -0
  287. package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
  288. package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
  289. package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
  290. package/src/modes/interactive/theme/defaults/light-one.json +105 -0
  291. package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
  292. package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
  293. package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
  294. package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
  295. package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
  296. package/src/modes/interactive/theme/light.json +99 -0
  297. package/src/modes/interactive/theme/theme-schema.json +424 -0
  298. package/src/modes/interactive/theme/theme.ts +2211 -0
  299. package/src/modes/print-mode.ts +163 -0
  300. package/src/modes/rpc/rpc-client.ts +527 -0
  301. package/src/modes/rpc/rpc-mode.ts +494 -0
  302. package/src/modes/rpc/rpc-types.ts +203 -0
  303. package/src/prompts/architect-plan.md +10 -0
  304. package/src/prompts/branch-summary-preamble.md +3 -0
  305. package/src/prompts/branch-summary.md +28 -0
  306. package/src/prompts/browser.md +71 -0
  307. package/src/prompts/compaction-summary.md +34 -0
  308. package/src/prompts/compaction-turn-prefix.md +16 -0
  309. package/src/prompts/compaction-update-summary.md +41 -0
  310. package/src/prompts/explore.md +82 -0
  311. package/src/prompts/implement-with-critic.md +11 -0
  312. package/src/prompts/implement.md +11 -0
  313. package/src/prompts/init.md +30 -0
  314. package/src/prompts/plan.md +54 -0
  315. package/src/prompts/reviewer.md +81 -0
  316. package/src/prompts/summarization-system.md +3 -0
  317. package/src/prompts/system-prompt.md +27 -0
  318. package/src/prompts/task.md +56 -0
  319. package/src/prompts/title-system.md +8 -0
  320. package/src/prompts/tools/ask.md +24 -0
  321. package/src/prompts/tools/bash.md +23 -0
  322. package/src/prompts/tools/edit.md +9 -0
  323. package/src/prompts/tools/find.md +6 -0
  324. package/src/prompts/tools/grep.md +12 -0
  325. package/src/prompts/tools/lsp.md +14 -0
  326. package/src/prompts/tools/output.md +23 -0
  327. package/src/prompts/tools/read.md +25 -0
  328. package/src/prompts/tools/web-fetch.md +8 -0
  329. package/src/prompts/tools/web-search.md +10 -0
  330. package/src/prompts/tools/write.md +10 -0
  331. package/src/utils/changelog.ts +99 -0
  332. package/src/utils/clipboard.ts +265 -0
  333. package/src/utils/fuzzy.ts +108 -0
  334. package/src/utils/mime.ts +30 -0
  335. package/src/utils/shell-snapshot.ts +218 -0
  336. package/src/utils/shell.ts +364 -0
  337. package/src/utils/tools-manager.ts +265 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * MCP tools loader.
3
+ *
4
+ * Integrates MCP tool discovery with the custom tools system.
5
+ */
6
+
7
+ import type { LoadedCustomTool } from "../custom-tools/types";
8
+ import { type MCPLoadResult, MCPManager } from "./manager";
9
+ import { parseMCPToolName } from "./tool-bridge";
10
+
11
+ /** Result from loading MCP tools */
12
+ export interface MCPToolsLoadResult {
13
+ /** MCP manager (for lifecycle management) */
14
+ manager: MCPManager;
15
+ /** Loaded tools as LoadedCustomTool format */
16
+ tools: LoadedCustomTool[];
17
+ /** Errors keyed by server name */
18
+ errors: Array<{ path: string; error: string }>;
19
+ /** Connected server names */
20
+ connectedServers: string[];
21
+ /** Extracted Exa API keys from filtered MCP servers */
22
+ exaApiKeys: string[];
23
+ }
24
+
25
+ /** Options for loading MCP tools */
26
+ export interface MCPToolsLoadOptions {
27
+ /** Called when starting to connect to servers */
28
+ onConnecting?: (serverNames: string[]) => void;
29
+ /** Whether to load project-level config (default: true) */
30
+ enableProjectConfig?: boolean;
31
+ /** Whether to filter out Exa MCP servers (default: true) */
32
+ filterExa?: boolean;
33
+ }
34
+
35
+ /**
36
+ * Discover and load MCP tools from .mcp.json files.
37
+ *
38
+ * @param cwd Working directory (project root)
39
+ * @param options Load options including progress callbacks
40
+ * @returns MCP tools in LoadedCustomTool format for integration
41
+ */
42
+ export async function discoverAndLoadMCPTools(cwd: string, options?: MCPToolsLoadOptions): Promise<MCPToolsLoadResult> {
43
+ const manager = new MCPManager(cwd);
44
+
45
+ let result: MCPLoadResult;
46
+ try {
47
+ result = await manager.discoverAndConnect({
48
+ onConnecting: options?.onConnecting,
49
+ enableProjectConfig: options?.enableProjectConfig,
50
+ filterExa: options?.filterExa,
51
+ });
52
+ } catch (error) {
53
+ // If discovery fails entirely, return empty result
54
+ const message = error instanceof Error ? error.message : String(error);
55
+ return {
56
+ manager,
57
+ tools: [],
58
+ errors: [{ path: ".mcp.json", error: message }],
59
+ connectedServers: [],
60
+ exaApiKeys: [],
61
+ };
62
+ }
63
+
64
+ // Convert MCP tools to LoadedCustomTool format
65
+ const loadedTools: LoadedCustomTool[] = result.tools.map((tool) => {
66
+ // Parse the MCP tool name to get server name
67
+ const parsed = parseMCPToolName(tool.name);
68
+ const serverName = parsed?.serverName;
69
+
70
+ // Get provider info from manager's connection if available
71
+ const connection = serverName ? manager.getConnection(serverName) : undefined;
72
+ const provider = connection?._source?.provider;
73
+
74
+ // Format path with provider info if available
75
+ // Format: "mcp:serverName via providerName" (e.g., "mcp:agentx via Claude Code")
76
+ const path =
77
+ provider && serverName ? `mcp:${serverName} via ${connection._source!.providerName}` : `mcp:${tool.name}`;
78
+
79
+ return {
80
+ path,
81
+ resolvedPath: `mcp:${tool.name}`,
82
+ tool: tool as any, // MCPToolDetails is compatible with CustomTool<TSchema, any>
83
+ };
84
+ });
85
+
86
+ // Convert error map to array format
87
+ const errors: Array<{ path: string; error: string }> = [];
88
+ for (const [serverName, errorMsg] of result.errors) {
89
+ errors.push({ path: `mcp:${serverName}`, error: errorMsg });
90
+ }
91
+
92
+ return {
93
+ manager,
94
+ tools: loadedTools,
95
+ errors,
96
+ connectedServers: result.connectedServers,
97
+ exaApiKeys: result.exaApiKeys,
98
+ };
99
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * MCP Server Manager.
3
+ *
4
+ * Discovers, connects to, and manages MCP servers.
5
+ * Handles tool loading and lifecycle.
6
+ */
7
+
8
+ import type { TSchema } from "@sinclair/typebox";
9
+ import type { CustomTool } from "../custom-tools/types";
10
+ import { connectToServer, disconnectServer, listTools } from "./client";
11
+ import { loadAllMCPConfigs, validateServerConfig } from "./config";
12
+ import type { MCPToolDetails } from "./tool-bridge";
13
+ import { createMCPTools } from "./tool-bridge";
14
+ import type { MCPServerConfig, MCPServerConnection } from "./types";
15
+
16
+ /** Result of loading MCP tools */
17
+ export interface MCPLoadResult {
18
+ /** Loaded tools as CustomTool instances */
19
+ tools: CustomTool<TSchema, MCPToolDetails>[];
20
+ /** Connection errors by server name */
21
+ errors: Map<string, string>;
22
+ /** Connected server names */
23
+ connectedServers: string[];
24
+ /** Extracted Exa API keys from filtered MCP servers */
25
+ exaApiKeys: string[];
26
+ }
27
+
28
+ /** Options for discovering and connecting to MCP servers */
29
+ export interface MCPDiscoverOptions {
30
+ /** Whether to load project-level config (default: true) */
31
+ enableProjectConfig?: boolean;
32
+ /** Whether to filter out Exa MCP servers (default: true) */
33
+ filterExa?: boolean;
34
+ /** Called when starting to connect to servers */
35
+ onConnecting?: (serverNames: string[]) => void;
36
+ }
37
+
38
+ /**
39
+ * MCP Server Manager.
40
+ *
41
+ * Manages connections to MCP servers and provides tools to the agent.
42
+ */
43
+ export class MCPManager {
44
+ private connections = new Map<string, MCPServerConnection>();
45
+ private tools: CustomTool<TSchema, MCPToolDetails>[] = [];
46
+
47
+ constructor(private cwd: string) {}
48
+
49
+ /**
50
+ * Discover and connect to all MCP servers from .mcp.json files.
51
+ * Returns tools and any connection errors.
52
+ */
53
+ async discoverAndConnect(options?: MCPDiscoverOptions): Promise<MCPLoadResult> {
54
+ const { configs, exaApiKeys, sources } = await loadAllMCPConfigs(this.cwd, {
55
+ enableProjectConfig: options?.enableProjectConfig,
56
+ filterExa: options?.filterExa,
57
+ });
58
+ const result = await this.connectServers(configs, sources, options?.onConnecting);
59
+ result.exaApiKeys = exaApiKeys;
60
+ return result;
61
+ }
62
+
63
+ /**
64
+ * Connect to specific MCP servers.
65
+ * Connections are made in parallel for faster startup.
66
+ */
67
+ async connectServers(
68
+ configs: Record<string, MCPServerConfig>,
69
+ sources: Record<string, import("../../capability/types").SourceMeta>,
70
+ onConnecting?: (serverNames: string[]) => void,
71
+ ): Promise<MCPLoadResult> {
72
+ const errors = new Map<string, string>();
73
+ const connectedServers: string[] = [];
74
+ const allTools: CustomTool<TSchema, MCPToolDetails>[] = [];
75
+
76
+ // Prepare connection tasks
77
+ const connectionTasks: Array<{
78
+ name: string;
79
+ config: MCPServerConfig;
80
+ validationErrors: string[];
81
+ }> = [];
82
+
83
+ for (const [name, config] of Object.entries(configs)) {
84
+ // Skip if already connected
85
+ if (this.connections.has(name)) {
86
+ connectedServers.push(name);
87
+ continue;
88
+ }
89
+
90
+ // Validate config
91
+ const validationErrors = validateServerConfig(name, config);
92
+ if (validationErrors.length > 0) {
93
+ errors.set(name, validationErrors.join("; "));
94
+ continue;
95
+ }
96
+
97
+ connectionTasks.push({ name, config, validationErrors });
98
+ }
99
+
100
+ // Notify about servers we're connecting to
101
+ if (connectionTasks.length > 0 && onConnecting) {
102
+ onConnecting(connectionTasks.map((t) => t.name));
103
+ }
104
+
105
+ // Connect to all servers in parallel
106
+ const results = await Promise.allSettled(
107
+ connectionTasks.map(async ({ name, config }) => {
108
+ const connection = await connectToServer(name, config);
109
+ // Attach source metadata to connection
110
+ if (sources[name]) {
111
+ connection._source = sources[name];
112
+ }
113
+ const serverTools = await listTools(connection);
114
+ return { name, connection, serverTools };
115
+ }),
116
+ );
117
+
118
+ // Process results
119
+ for (let i = 0; i < results.length; i++) {
120
+ const result = results[i];
121
+ const { name } = connectionTasks[i];
122
+
123
+ if (result.status === "fulfilled") {
124
+ const { connection, serverTools } = result.value;
125
+ this.connections.set(name, connection);
126
+ connectedServers.push(name);
127
+
128
+ const customTools = createMCPTools(connection, serverTools);
129
+ allTools.push(...customTools);
130
+ } else {
131
+ const message = result.reason instanceof Error ? result.reason.message : String(result.reason);
132
+ errors.set(name, message);
133
+ }
134
+ }
135
+
136
+ // Update cached tools
137
+ this.tools = allTools;
138
+
139
+ return {
140
+ tools: allTools,
141
+ errors,
142
+ connectedServers,
143
+ exaApiKeys: [], // Will be populated by discoverAndConnect
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Get all loaded tools.
149
+ */
150
+ getTools(): CustomTool<TSchema, MCPToolDetails>[] {
151
+ return this.tools;
152
+ }
153
+
154
+ /**
155
+ * Get a specific connection.
156
+ */
157
+ getConnection(name: string): MCPServerConnection | undefined {
158
+ return this.connections.get(name);
159
+ }
160
+
161
+ /**
162
+ * Get all connected server names.
163
+ */
164
+ getConnectedServers(): string[] {
165
+ return Array.from(this.connections.keys());
166
+ }
167
+
168
+ /**
169
+ * Disconnect from a specific server.
170
+ */
171
+ async disconnectServer(name: string): Promise<void> {
172
+ const connection = this.connections.get(name);
173
+ if (!connection) return;
174
+
175
+ await disconnectServer(connection);
176
+ this.connections.delete(name);
177
+
178
+ // Remove tools from this server
179
+ this.tools = this.tools.filter((t) => !t.name.startsWith(`mcp_${name}_`));
180
+ }
181
+
182
+ /**
183
+ * Disconnect from all servers.
184
+ */
185
+ async disconnectAll(): Promise<void> {
186
+ const promises = Array.from(this.connections.values()).map((conn) => disconnectServer(conn));
187
+ await Promise.allSettled(promises);
188
+
189
+ this.connections.clear();
190
+ this.tools = [];
191
+ }
192
+
193
+ /**
194
+ * Refresh tools from a specific server.
195
+ */
196
+ async refreshServerTools(name: string): Promise<void> {
197
+ const connection = this.connections.get(name);
198
+ if (!connection) return;
199
+
200
+ // Clear cached tools
201
+ connection.tools = undefined;
202
+
203
+ // Reload tools
204
+ const serverTools = await listTools(connection);
205
+ const customTools = createMCPTools(connection, serverTools);
206
+
207
+ // Replace tools from this server
208
+ this.tools = this.tools.filter((t) => !t.name.startsWith(`mcp_${name}_`));
209
+ this.tools.push(...customTools);
210
+ }
211
+
212
+ /**
213
+ * Refresh tools from all servers.
214
+ */
215
+ async refreshAllTools(): Promise<void> {
216
+ const promises = Array.from(this.connections.keys()).map((name) => this.refreshServerTools(name));
217
+ await Promise.allSettled(promises);
218
+ }
219
+ }
220
+
221
+ /**
222
+ * Create an MCP manager and discover servers.
223
+ * Convenience function for quick setup.
224
+ */
225
+ export async function createMCPManager(
226
+ cwd: string,
227
+ options?: MCPDiscoverOptions,
228
+ ): Promise<{
229
+ manager: MCPManager;
230
+ result: MCPLoadResult;
231
+ }> {
232
+ const manager = new MCPManager(cwd);
233
+ const result = await manager.discoverAndConnect(options);
234
+ return { manager, result };
235
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * MCP to CustomTool bridge.
3
+ *
4
+ * Converts MCP tool definitions to CustomTool format for the agent.
5
+ */
6
+
7
+ import type { TSchema } from "@sinclair/typebox";
8
+ import type { CustomTool, CustomToolResult } from "../custom-tools/types";
9
+ import { callTool } from "./client";
10
+ import type { MCPContent, MCPServerConnection, MCPToolDefinition } from "./types";
11
+
12
+ /** Details included in MCP tool results for rendering */
13
+ export interface MCPToolDetails {
14
+ /** Server name */
15
+ serverName: string;
16
+ /** Original MCP tool name */
17
+ mcpToolName: string;
18
+ /** Whether the call resulted in an error */
19
+ isError?: boolean;
20
+ /** Raw content from MCP response */
21
+ rawContent?: MCPContent[];
22
+ /** Provider ID (e.g., "claude", "mcp-json") */
23
+ provider?: string;
24
+ /** Provider display name (e.g., "Claude Code", "MCP Config") */
25
+ providerName?: string;
26
+ }
27
+
28
+ /**
29
+ * Convert JSON Schema from MCP to TypeBox-compatible schema.
30
+ * MCP uses standard JSON Schema, TypeBox uses a compatible subset.
31
+ */
32
+ function convertSchema(mcpSchema: MCPToolDefinition["inputSchema"]): TSchema {
33
+ // MCP schemas are JSON Schema objects, TypeBox can use them directly
34
+ // as long as we ensure the structure is correct
35
+ return mcpSchema as unknown as TSchema;
36
+ }
37
+
38
+ /**
39
+ * Format MCP content for LLM consumption.
40
+ */
41
+ function formatMCPContent(content: MCPContent[]): string {
42
+ const parts: string[] = [];
43
+
44
+ for (const item of content) {
45
+ switch (item.type) {
46
+ case "text":
47
+ parts.push(item.text);
48
+ break;
49
+ case "image":
50
+ parts.push(`[Image: ${item.mimeType}]`);
51
+ break;
52
+ case "resource":
53
+ if (item.resource.text) {
54
+ parts.push(`[Resource: ${item.resource.uri}]\n${item.resource.text}`);
55
+ } else {
56
+ parts.push(`[Resource: ${item.resource.uri}]`);
57
+ }
58
+ break;
59
+ }
60
+ }
61
+
62
+ return parts.join("\n\n");
63
+ }
64
+
65
+ /**
66
+ * Create a unique tool name for an MCP tool.
67
+ * Prefixes with server name to avoid conflicts.
68
+ */
69
+ export function createMCPToolName(serverName: string, toolName: string): string {
70
+ // Use underscore separator since tool names can't have special chars
71
+ return `mcp_${serverName}_${toolName}`;
72
+ }
73
+
74
+ /**
75
+ * Parse an MCP tool name back to server and tool components.
76
+ */
77
+ export function parseMCPToolName(name: string): { serverName: string; toolName: string } | null {
78
+ if (!name.startsWith("mcp_")) return null;
79
+
80
+ const rest = name.slice(4);
81
+ const underscoreIdx = rest.lastIndexOf("_");
82
+ if (underscoreIdx === -1) return null;
83
+
84
+ return {
85
+ serverName: rest.slice(0, underscoreIdx),
86
+ toolName: rest.slice(underscoreIdx + 1),
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Convert an MCP tool definition to a CustomTool.
92
+ */
93
+ export function createMCPTool(
94
+ connection: MCPServerConnection,
95
+ tool: MCPToolDefinition,
96
+ ): CustomTool<TSchema, MCPToolDetails> {
97
+ const name = createMCPToolName(connection.name, tool.name);
98
+ const schema = convertSchema(tool.inputSchema);
99
+
100
+ return {
101
+ name,
102
+ label: `${connection.name}/${tool.name}`,
103
+ description: tool.description ?? `MCP tool from ${connection.name}`,
104
+ parameters: schema,
105
+
106
+ async execute(_toolCallId, params, _onUpdate, _ctx, _signal): Promise<CustomToolResult<MCPToolDetails>> {
107
+ try {
108
+ const result = await callTool(connection, tool.name, params as Record<string, unknown>);
109
+
110
+ const text = formatMCPContent(result.content);
111
+ const details: MCPToolDetails = {
112
+ serverName: connection.name,
113
+ mcpToolName: tool.name,
114
+ isError: result.isError,
115
+ rawContent: result.content,
116
+ provider: connection._source?.provider,
117
+ providerName: connection._source?.providerName,
118
+ };
119
+
120
+ if (result.isError) {
121
+ return {
122
+ content: [{ type: "text", text: `Error: ${text}` }],
123
+ details,
124
+ };
125
+ }
126
+
127
+ return {
128
+ content: [{ type: "text", text }],
129
+ details,
130
+ };
131
+ } catch (error) {
132
+ const message = error instanceof Error ? error.message : String(error);
133
+ return {
134
+ content: [{ type: "text", text: `MCP error: ${message}` }],
135
+ details: {
136
+ serverName: connection.name,
137
+ mcpToolName: tool.name,
138
+ isError: true,
139
+ provider: connection._source?.provider,
140
+ providerName: connection._source?.providerName,
141
+ },
142
+ };
143
+ }
144
+ },
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Convert all tools from an MCP server to CustomTools.
150
+ */
151
+ export function createMCPTools(
152
+ connection: MCPServerConnection,
153
+ tools: MCPToolDefinition[],
154
+ ): CustomTool<TSchema, MCPToolDetails>[] {
155
+ return tools.map((tool) => createMCPTool(connection, tool));
156
+ }