@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.
- package/CHANGELOG.md +1629 -0
- package/README.md +1041 -0
- package/docs/compaction.md +403 -0
- package/docs/config-usage.md +113 -0
- package/docs/custom-tools.md +541 -0
- package/docs/extension-loading.md +1004 -0
- package/docs/hooks.md +867 -0
- package/docs/rpc.md +1040 -0
- package/docs/sdk.md +994 -0
- package/docs/session-tree-plan.md +441 -0
- package/docs/session.md +240 -0
- package/docs/skills.md +290 -0
- package/docs/theme.md +670 -0
- package/docs/tree.md +197 -0
- package/docs/tui.md +341 -0
- package/examples/README.md +21 -0
- package/examples/custom-tools/README.md +124 -0
- package/examples/custom-tools/hello/index.ts +20 -0
- package/examples/custom-tools/question/index.ts +84 -0
- package/examples/custom-tools/subagent/README.md +172 -0
- package/examples/custom-tools/subagent/agents/planner.md +37 -0
- package/examples/custom-tools/subagent/agents/scout.md +50 -0
- package/examples/custom-tools/subagent/agents/worker.md +24 -0
- package/examples/custom-tools/subagent/agents.ts +156 -0
- package/examples/custom-tools/subagent/commands/implement-and-review.md +10 -0
- package/examples/custom-tools/subagent/commands/implement.md +10 -0
- package/examples/custom-tools/subagent/commands/scout-and-plan.md +9 -0
- package/examples/custom-tools/subagent/index.ts +1002 -0
- package/examples/custom-tools/todo/index.ts +212 -0
- package/examples/hooks/README.md +56 -0
- package/examples/hooks/auto-commit-on-exit.ts +49 -0
- package/examples/hooks/confirm-destructive.ts +59 -0
- package/examples/hooks/custom-compaction.ts +116 -0
- package/examples/hooks/dirty-repo-guard.ts +52 -0
- package/examples/hooks/file-trigger.ts +41 -0
- package/examples/hooks/git-checkpoint.ts +53 -0
- package/examples/hooks/handoff.ts +150 -0
- package/examples/hooks/permission-gate.ts +34 -0
- package/examples/hooks/protected-paths.ts +30 -0
- package/examples/hooks/qna.ts +119 -0
- package/examples/hooks/snake.ts +343 -0
- package/examples/hooks/status-line.ts +40 -0
- package/examples/sdk/01-minimal.ts +22 -0
- package/examples/sdk/02-custom-model.ts +49 -0
- package/examples/sdk/03-custom-prompt.ts +44 -0
- package/examples/sdk/04-skills.ts +44 -0
- package/examples/sdk/05-tools.ts +90 -0
- package/examples/sdk/06-hooks.ts +61 -0
- package/examples/sdk/07-context-files.ts +36 -0
- package/examples/sdk/08-slash-commands.ts +42 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +55 -0
- package/examples/sdk/10-settings.ts +38 -0
- package/examples/sdk/11-sessions.ts +48 -0
- package/examples/sdk/12-full-control.ts +95 -0
- package/examples/sdk/README.md +154 -0
- package/package.json +89 -0
- package/src/bun-imports.d.ts +16 -0
- package/src/capability/context-file.ts +40 -0
- package/src/capability/extension.ts +48 -0
- package/src/capability/hook.ts +40 -0
- package/src/capability/index.ts +616 -0
- package/src/capability/instruction.ts +37 -0
- package/src/capability/mcp.ts +52 -0
- package/src/capability/prompt.ts +35 -0
- package/src/capability/rule.ts +56 -0
- package/src/capability/settings.ts +35 -0
- package/src/capability/skill.ts +49 -0
- package/src/capability/slash-command.ts +40 -0
- package/src/capability/system-prompt.ts +35 -0
- package/src/capability/tool.ts +38 -0
- package/src/capability/types.ts +166 -0
- package/src/cli/args.ts +259 -0
- package/src/cli/file-processor.ts +121 -0
- package/src/cli/list-models.ts +104 -0
- package/src/cli/plugin-cli.ts +661 -0
- package/src/cli/session-picker.ts +41 -0
- package/src/cli/update-cli.ts +274 -0
- package/src/cli.ts +10 -0
- package/src/config.ts +391 -0
- package/src/core/agent-session.ts +2178 -0
- package/src/core/auth-storage.ts +258 -0
- package/src/core/bash-executor.ts +197 -0
- package/src/core/compaction/branch-summarization.ts +315 -0
- package/src/core/compaction/compaction.ts +664 -0
- package/src/core/compaction/index.ts +7 -0
- package/src/core/compaction/utils.ts +153 -0
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +226 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +22 -0
- package/src/core/custom-tools/loader.ts +248 -0
- package/src/core/custom-tools/types.ts +185 -0
- package/src/core/custom-tools/wrapper.ts +29 -0
- package/src/core/exec.ts +139 -0
- package/src/core/export-html/index.ts +159 -0
- package/src/core/export-html/template.css +774 -0
- package/src/core/export-html/template.generated.ts +2 -0
- package/src/core/export-html/template.html +45 -0
- package/src/core/export-html/template.js +1185 -0
- package/src/core/export-html/template.macro.ts +24 -0
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +16 -0
- package/src/core/hooks/loader.ts +288 -0
- package/src/core/hooks/runner.ts +434 -0
- package/src/core/hooks/tool-wrapper.ts +98 -0
- package/src/core/hooks/types.ts +770 -0
- package/src/core/index.ts +53 -0
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +185 -0
- package/src/core/mcp/config.ts +248 -0
- package/src/core/mcp/index.ts +45 -0
- package/src/core/mcp/loader.ts +99 -0
- package/src/core/mcp/manager.ts +235 -0
- package/src/core/mcp/tool-bridge.ts +156 -0
- package/src/core/mcp/transports/http.ts +316 -0
- package/src/core/mcp/transports/index.ts +6 -0
- package/src/core/mcp/transports/stdio.ts +252 -0
- package/src/core/mcp/types.ts +228 -0
- package/src/core/messages.ts +211 -0
- package/src/core/model-registry.ts +334 -0
- package/src/core/model-resolver.ts +494 -0
- package/src/core/plugins/doctor.ts +67 -0
- package/src/core/plugins/index.ts +38 -0
- package/src/core/plugins/installer.ts +189 -0
- package/src/core/plugins/loader.ts +339 -0
- package/src/core/plugins/manager.ts +672 -0
- package/src/core/plugins/parser.ts +105 -0
- package/src/core/plugins/paths.ts +37 -0
- package/src/core/plugins/types.ts +190 -0
- package/src/core/sdk.ts +900 -0
- package/src/core/session-manager.ts +1837 -0
- package/src/core/settings-manager.ts +860 -0
- package/src/core/skills.ts +352 -0
- package/src/core/slash-commands.ts +132 -0
- package/src/core/system-prompt.ts +442 -0
- package/src/core/timings.ts +25 -0
- package/src/core/title-generator.ts +110 -0
- package/src/core/tools/ask.ts +193 -0
- package/src/core/tools/bash-interceptor.ts +120 -0
- package/src/core/tools/bash.ts +91 -0
- package/src/core/tools/context.ts +32 -0
- package/src/core/tools/edit-diff.ts +487 -0
- package/src/core/tools/edit.ts +140 -0
- package/src/core/tools/exa/company.ts +59 -0
- package/src/core/tools/exa/index.ts +63 -0
- package/src/core/tools/exa/linkedin.ts +59 -0
- package/src/core/tools/exa/mcp-client.ts +368 -0
- package/src/core/tools/exa/render.ts +200 -0
- package/src/core/tools/exa/researcher.ts +90 -0
- package/src/core/tools/exa/search.ts +338 -0
- package/src/core/tools/exa/types.ts +167 -0
- package/src/core/tools/exa/websets.ts +248 -0
- package/src/core/tools/find.ts +244 -0
- package/src/core/tools/grep.ts +584 -0
- package/src/core/tools/index.ts +283 -0
- package/src/core/tools/ls.ts +142 -0
- package/src/core/tools/lsp/client.ts +767 -0
- package/src/core/tools/lsp/clients/biome-client.ts +207 -0
- package/src/core/tools/lsp/clients/index.ts +49 -0
- package/src/core/tools/lsp/clients/lsp-linter-client.ts +98 -0
- package/src/core/tools/lsp/config.ts +845 -0
- package/src/core/tools/lsp/edits.ts +110 -0
- package/src/core/tools/lsp/index.ts +1364 -0
- package/src/core/tools/lsp/render.ts +560 -0
- package/src/core/tools/lsp/rust-analyzer.ts +145 -0
- package/src/core/tools/lsp/types.ts +495 -0
- package/src/core/tools/lsp/utils.ts +526 -0
- package/src/core/tools/notebook.ts +182 -0
- package/src/core/tools/output.ts +198 -0
- package/src/core/tools/path-utils.ts +61 -0
- package/src/core/tools/read.ts +507 -0
- package/src/core/tools/renderers.ts +820 -0
- package/src/core/tools/review.ts +275 -0
- package/src/core/tools/rulebook.ts +124 -0
- package/src/core/tools/task/agents.ts +158 -0
- package/src/core/tools/task/artifacts.ts +114 -0
- package/src/core/tools/task/commands.ts +157 -0
- package/src/core/tools/task/discovery.ts +217 -0
- package/src/core/tools/task/executor.ts +531 -0
- package/src/core/tools/task/index.ts +548 -0
- package/src/core/tools/task/model-resolver.ts +176 -0
- package/src/core/tools/task/parallel.ts +38 -0
- package/src/core/tools/task/render.ts +502 -0
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +142 -0
- package/src/core/tools/truncate.ts +265 -0
- package/src/core/tools/web-fetch.ts +2511 -0
- package/src/core/tools/web-search/auth.ts +199 -0
- package/src/core/tools/web-search/index.ts +583 -0
- package/src/core/tools/web-search/providers/anthropic.ts +198 -0
- package/src/core/tools/web-search/providers/exa.ts +196 -0
- package/src/core/tools/web-search/providers/perplexity.ts +195 -0
- package/src/core/tools/web-search/render.ts +372 -0
- package/src/core/tools/web-search/types.ts +180 -0
- package/src/core/tools/write.ts +63 -0
- package/src/core/ttsr.ts +211 -0
- package/src/core/utils.ts +187 -0
- package/src/discovery/agents-md.ts +75 -0
- package/src/discovery/builtin.ts +647 -0
- package/src/discovery/claude.ts +623 -0
- package/src/discovery/cline.ts +104 -0
- package/src/discovery/codex.ts +571 -0
- package/src/discovery/cursor.ts +266 -0
- package/src/discovery/gemini.ts +368 -0
- package/src/discovery/github.ts +120 -0
- package/src/discovery/helpers.test.ts +127 -0
- package/src/discovery/helpers.ts +249 -0
- package/src/discovery/index.ts +84 -0
- package/src/discovery/mcp-json.ts +127 -0
- package/src/discovery/vscode.ts +99 -0
- package/src/discovery/windsurf.ts +219 -0
- package/src/index.ts +192 -0
- package/src/main.ts +507 -0
- package/src/migrations.ts +156 -0
- package/src/modes/cleanup.ts +23 -0
- package/src/modes/index.ts +48 -0
- package/src/modes/interactive/components/armin.ts +382 -0
- package/src/modes/interactive/components/assistant-message.ts +86 -0
- package/src/modes/interactive/components/bash-execution.ts +199 -0
- package/src/modes/interactive/components/bordered-loader.ts +41 -0
- package/src/modes/interactive/components/branch-summary-message.ts +42 -0
- package/src/modes/interactive/components/compaction-summary-message.ts +45 -0
- package/src/modes/interactive/components/custom-editor.ts +122 -0
- package/src/modes/interactive/components/diff.ts +147 -0
- package/src/modes/interactive/components/dynamic-border.ts +25 -0
- package/src/modes/interactive/components/extensions/extension-dashboard.ts +296 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +479 -0
- package/src/modes/interactive/components/extensions/index.ts +9 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +313 -0
- package/src/modes/interactive/components/extensions/state-manager.ts +558 -0
- package/src/modes/interactive/components/extensions/types.ts +191 -0
- package/src/modes/interactive/components/hook-editor.ts +117 -0
- package/src/modes/interactive/components/hook-input.ts +64 -0
- package/src/modes/interactive/components/hook-message.ts +96 -0
- package/src/modes/interactive/components/hook-selector.ts +91 -0
- package/src/modes/interactive/components/model-selector.ts +560 -0
- package/src/modes/interactive/components/oauth-selector.ts +136 -0
- package/src/modes/interactive/components/plugin-settings.ts +481 -0
- package/src/modes/interactive/components/queue-mode-selector.ts +56 -0
- package/src/modes/interactive/components/session-selector.ts +220 -0
- package/src/modes/interactive/components/settings-defs.ts +597 -0
- package/src/modes/interactive/components/settings-selector.ts +545 -0
- package/src/modes/interactive/components/show-images-selector.ts +45 -0
- package/src/modes/interactive/components/status-line/index.ts +4 -0
- package/src/modes/interactive/components/status-line/presets.ts +94 -0
- package/src/modes/interactive/components/status-line/segments.ts +350 -0
- package/src/modes/interactive/components/status-line/separators.ts +55 -0
- package/src/modes/interactive/components/status-line/types.ts +81 -0
- package/src/modes/interactive/components/status-line-segment-editor.ts +357 -0
- package/src/modes/interactive/components/status-line.ts +384 -0
- package/src/modes/interactive/components/theme-selector.ts +62 -0
- package/src/modes/interactive/components/thinking-selector.ts +64 -0
- package/src/modes/interactive/components/tool-execution.ts +946 -0
- package/src/modes/interactive/components/tree-selector.ts +877 -0
- package/src/modes/interactive/components/ttsr-notification.ts +82 -0
- package/src/modes/interactive/components/user-message-selector.ts +159 -0
- package/src/modes/interactive/components/user-message.ts +18 -0
- package/src/modes/interactive/components/visual-truncate.ts +50 -0
- package/src/modes/interactive/components/welcome.ts +228 -0
- package/src/modes/interactive/interactive-mode.ts +2669 -0
- package/src/modes/interactive/theme/dark.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-arctic.json +111 -0
- package/src/modes/interactive/theme/defaults/dark-catppuccin.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-cyberpunk.json +109 -0
- package/src/modes/interactive/theme/defaults/dark-dracula.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-forest.json +103 -0
- package/src/modes/interactive/theme/defaults/dark-github.json +112 -0
- package/src/modes/interactive/theme/defaults/dark-gruvbox.json +119 -0
- package/src/modes/interactive/theme/defaults/dark-monochrome.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-monokai.json +105 -0
- package/src/modes/interactive/theme/defaults/dark-nord.json +104 -0
- package/src/modes/interactive/theme/defaults/dark-ocean.json +108 -0
- package/src/modes/interactive/theme/defaults/dark-one.json +107 -0
- package/src/modes/interactive/theme/defaults/dark-retro.json +99 -0
- package/src/modes/interactive/theme/defaults/dark-rose-pine.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-solarized.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/dark-synthwave.json +102 -0
- package/src/modes/interactive/theme/defaults/dark-tokyo-night.json +108 -0
- package/src/modes/interactive/theme/defaults/index.ts +67 -0
- package/src/modes/interactive/theme/defaults/light-arctic.json +106 -0
- package/src/modes/interactive/theme/defaults/light-catppuccin.json +105 -0
- package/src/modes/interactive/theme/defaults/light-cyberpunk.json +103 -0
- package/src/modes/interactive/theme/defaults/light-forest.json +107 -0
- package/src/modes/interactive/theme/defaults/light-github.json +114 -0
- package/src/modes/interactive/theme/defaults/light-gruvbox.json +115 -0
- package/src/modes/interactive/theme/defaults/light-monochrome.json +100 -0
- package/src/modes/interactive/theme/defaults/light-ocean.json +106 -0
- package/src/modes/interactive/theme/defaults/light-one.json +105 -0
- package/src/modes/interactive/theme/defaults/light-retro.json +105 -0
- package/src/modes/interactive/theme/defaults/light-solarized.json +101 -0
- package/src/modes/interactive/theme/defaults/light-sunset.json +106 -0
- package/src/modes/interactive/theme/defaults/light-synthwave.json +105 -0
- package/src/modes/interactive/theme/defaults/light-tokyo-night.json +118 -0
- package/src/modes/interactive/theme/light.json +99 -0
- package/src/modes/interactive/theme/theme-schema.json +424 -0
- package/src/modes/interactive/theme/theme.ts +2211 -0
- package/src/modes/print-mode.ts +163 -0
- package/src/modes/rpc/rpc-client.ts +527 -0
- package/src/modes/rpc/rpc-mode.ts +494 -0
- package/src/modes/rpc/rpc-types.ts +203 -0
- package/src/prompts/architect-plan.md +10 -0
- package/src/prompts/branch-summary-preamble.md +3 -0
- package/src/prompts/branch-summary.md +28 -0
- package/src/prompts/browser.md +71 -0
- package/src/prompts/compaction-summary.md +34 -0
- package/src/prompts/compaction-turn-prefix.md +16 -0
- package/src/prompts/compaction-update-summary.md +41 -0
- package/src/prompts/explore.md +82 -0
- package/src/prompts/implement-with-critic.md +11 -0
- package/src/prompts/implement.md +11 -0
- package/src/prompts/init.md +30 -0
- package/src/prompts/plan.md +54 -0
- package/src/prompts/reviewer.md +81 -0
- package/src/prompts/summarization-system.md +3 -0
- package/src/prompts/system-prompt.md +27 -0
- package/src/prompts/task.md +56 -0
- package/src/prompts/title-system.md +8 -0
- package/src/prompts/tools/ask.md +24 -0
- package/src/prompts/tools/bash.md +23 -0
- package/src/prompts/tools/edit.md +9 -0
- package/src/prompts/tools/find.md +6 -0
- package/src/prompts/tools/grep.md +12 -0
- package/src/prompts/tools/lsp.md +14 -0
- package/src/prompts/tools/output.md +23 -0
- package/src/prompts/tools/read.md +25 -0
- package/src/prompts/tools/web-fetch.md +8 -0
- package/src/prompts/tools/web-search.md +10 -0
- package/src/prompts/tools/write.md +10 -0
- package/src/utils/changelog.ts +99 -0
- package/src/utils/clipboard.ts +265 -0
- package/src/utils/fuzzy.ts +108 -0
- package/src/utils/mime.ts +30 -0
- package/src/utils/shell-snapshot.ts +218 -0
- package/src/utils/shell.ts +364 -0
- 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
|
+
}
|