@clinebot/core 0.0.28 → 0.0.29
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/README.md +7 -0
- package/dist/ClineCore.d.ts +28 -2
- package/dist/ClineCore.d.ts.map +1 -1
- package/dist/account/cline-account-service.d.ts +1 -1
- package/dist/account/cline-account-service.d.ts.map +1 -1
- package/dist/account/index.d.ts +1 -1
- package/dist/account/index.d.ts.map +1 -1
- package/dist/account/types.d.ts +5 -0
- package/dist/account/types.d.ts.map +1 -1
- package/dist/auth/bounded-ttl-cache.d.ts +14 -0
- package/dist/auth/bounded-ttl-cache.d.ts.map +1 -0
- package/dist/auth/cline.d.ts +27 -2
- package/dist/auth/cline.d.ts.map +1 -1
- package/dist/auth/oca.d.ts.map +1 -1
- package/dist/chat/chat-schema.d.ts +11 -11
- package/dist/extensions/config/agent-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/agent-config-parser.d.ts +2 -2
- package/dist/extensions/config/agent-config-parser.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/hooks-config-loader.d.ts +1 -1
- package/dist/extensions/config/hooks-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/config}/index.d.ts +2 -4
- package/dist/extensions/config/index.d.ts.map +1 -0
- package/dist/{runtime/commands.d.ts → extensions/config/runtime-commands.d.ts} +2 -3
- package/dist/extensions/config/runtime-commands.d.ts.map +1 -0
- package/dist/extensions/config/unified-config-file-watcher.d.ts.map +1 -0
- package/dist/extensions/config/user-instruction-config-loader.d.ts.map +1 -0
- package/dist/extensions/context/agentic-compaction.d.ts +13 -0
- package/dist/extensions/context/agentic-compaction.d.ts.map +1 -0
- package/dist/extensions/context/basic-compaction.d.ts +9 -0
- package/dist/extensions/context/basic-compaction.d.ts.map +1 -0
- package/dist/extensions/context/compaction-shared.d.ts +60 -0
- package/dist/extensions/context/compaction-shared.d.ts.map +1 -0
- package/dist/extensions/context/compaction.d.ts +20 -0
- package/dist/extensions/context/compaction.d.ts.map +1 -0
- package/dist/extensions/index.d.ts +5 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/mcp/client.d.ts +3 -0
- package/dist/extensions/mcp/client.d.ts.map +1 -0
- package/dist/extensions/mcp/config-loader.d.ts.map +1 -0
- package/dist/extensions/mcp/index.d.ts +9 -0
- package/dist/extensions/mcp/index.d.ts.map +1 -0
- package/dist/{mcp → extensions/mcp}/manager.d.ts +1 -2
- package/dist/extensions/mcp/manager.d.ts.map +1 -0
- package/dist/extensions/mcp/name-transform.d.ts +3 -0
- package/dist/extensions/mcp/name-transform.d.ts.map +1 -0
- package/dist/extensions/mcp/policies.d.ts +15 -0
- package/dist/extensions/mcp/policies.d.ts.map +1 -0
- package/dist/extensions/mcp/tools.d.ts +4 -0
- package/dist/extensions/mcp/tools.d.ts.map +1 -0
- package/dist/{mcp → extensions/mcp}/types.d.ts +29 -1
- package/dist/extensions/mcp/types.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-config-loader.d.ts +1 -1
- package/dist/extensions/plugin/plugin-config-loader.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-loader.d.ts +1 -1
- package/dist/extensions/plugin/plugin-loader.d.ts.map +1 -0
- package/dist/extensions/plugin/plugin-module-import.d.ts +5 -0
- package/dist/extensions/plugin/plugin-module-import.d.ts.map +1 -0
- package/dist/{agents → extensions/plugin}/plugin-sandbox.d.ts +1 -1
- package/dist/extensions/plugin/plugin-sandbox.d.ts.map +1 -0
- package/dist/extensions/plugin-sandbox-bootstrap.js +485 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/persistent.d.ts +64 -0
- package/dist/hooks/persistent.d.ts.map +1 -0
- package/dist/hooks/subprocess-runner.d.ts +22 -0
- package/dist/hooks/subprocess-runner.d.ts.map +1 -0
- package/dist/hooks/subprocess.d.ts +189 -0
- package/dist/hooks/subprocess.d.ts.map +1 -0
- package/dist/index.d.ts +22 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +560 -447
- package/dist/prompt/default-system.d.ts +2 -0
- package/dist/prompt/default-system.d.ts.map +1 -0
- package/dist/providers/local-provider-service.d.ts +1 -1
- package/dist/providers/local-provider-service.d.ts.map +1 -1
- package/dist/runtime/checkpoint-hooks.d.ts +21 -0
- package/dist/runtime/checkpoint-hooks.d.ts.map +1 -0
- package/dist/runtime/hook-file-hooks.d.ts +1 -1
- package/dist/runtime/hook-file-hooks.d.ts.map +1 -1
- package/dist/runtime/rules.d.ts +1 -1
- package/dist/runtime/rules.d.ts.map +1 -1
- package/dist/runtime/runtime-builder.d.ts +1 -1
- package/dist/runtime/runtime-builder.d.ts.map +1 -1
- package/dist/runtime/session-runtime.d.ts +25 -5
- package/dist/runtime/session-runtime.d.ts.map +1 -1
- package/dist/runtime/subprocess-sandbox.d.ts.map +1 -0
- package/dist/runtime/team-runtime-registry.d.ts +1 -1
- package/dist/runtime/team-runtime-registry.d.ts.map +1 -1
- package/dist/runtime/tool-approval.d.ts +1 -1
- package/dist/session/default-session-manager.d.ts +4 -3
- package/dist/session/default-session-manager.d.ts.map +1 -1
- package/dist/session/file-session-service.d.ts +1 -1
- package/dist/session/file-session-service.d.ts.map +1 -1
- package/dist/session/{unified-session-persistence-service.d.ts → persistence-service.d.ts} +11 -42
- package/dist/session/persistence-service.d.ts.map +1 -0
- package/dist/session/rpc-session-service.d.ts +1 -1
- package/dist/session/rpc-session-service.d.ts.map +1 -1
- package/dist/session/session-agent-events.d.ts +1 -1
- package/dist/session/session-artifacts.d.ts.map +1 -1
- package/dist/session/session-config-builder.d.ts.map +1 -1
- package/dist/session/session-graph.d.ts +1 -1
- package/dist/session/session-graph.d.ts.map +1 -1
- package/dist/session/session-host.d.ts.map +1 -1
- package/dist/session/session-manager.d.ts +6 -5
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manifest.d.ts +1 -1
- package/dist/session/session-service.d.ts +3 -2
- package/dist/session/session-service.d.ts.map +1 -1
- package/dist/session/session-team-coordination.d.ts +2 -1
- package/dist/session/session-team-coordination.d.ts.map +1 -1
- package/dist/session/utils/helpers.d.ts +51 -3
- package/dist/session/utils/helpers.d.ts.map +1 -1
- package/dist/session/utils/types.d.ts +41 -7
- package/dist/session/utils/types.d.ts.map +1 -1
- package/dist/session/workspace-manager.d.ts +1 -2
- package/dist/session/workspace-manager.d.ts.map +1 -1
- package/dist/session/workspace-manifest.d.ts +1 -22
- package/dist/session/workspace-manifest.d.ts.map +1 -1
- package/dist/storage/file-team-store.d.ts +2 -1
- package/dist/storage/file-team-store.d.ts.map +1 -1
- package/dist/storage/sqlite-team-store.d.ts +4 -1
- package/dist/storage/sqlite-team-store.d.ts.map +1 -1
- package/dist/storage/team-store.d.ts.map +1 -1
- package/dist/team/delegated-agent.d.ts +44 -0
- package/dist/team/delegated-agent.d.ts.map +1 -0
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/multi-agent.d.ts +229 -0
- package/dist/team/multi-agent.d.ts.map +1 -0
- package/dist/team/projections.d.ts +2 -2
- package/dist/team/projections.d.ts.map +1 -1
- package/dist/team/runtime.d.ts +5 -0
- package/dist/team/runtime.d.ts.map +1 -0
- package/dist/team/spawn-agent-tool.d.ts +85 -0
- package/dist/team/spawn-agent-tool.d.ts.map +1 -0
- package/dist/team/subagent-prompts.d.ts +4 -0
- package/dist/team/subagent-prompts.d.ts.map +1 -0
- package/dist/team/team-tools.d.ts +35 -0
- package/dist/team/team-tools.d.ts.map +1 -0
- package/dist/telemetry/OpenTelemetryProvider.d.ts +11 -1
- package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -1
- package/dist/telemetry/{LoggerTelemetryAdapter.d.ts → TelemetryLoggerSink.d.ts} +10 -4
- package/dist/telemetry/TelemetryLoggerSink.d.ts.map +1 -0
- package/dist/telemetry/TelemetryService.d.ts.map +1 -1
- package/dist/telemetry/index.js +15 -28
- package/dist/tools/definitions.d.ts +4 -3
- package/dist/tools/definitions.d.ts.map +1 -1
- package/dist/tools/index.d.ts +5 -5
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/model-tool-routing.d.ts.map +1 -1
- package/dist/tools/presets.d.ts +26 -0
- package/dist/tools/presets.d.ts.map +1 -1
- package/dist/tools/schemas.d.ts +8 -0
- package/dist/tools/schemas.d.ts.map +1 -1
- package/dist/tools/types.d.ts +23 -2
- package/dist/tools/types.d.ts.map +1 -1
- package/dist/types/config.d.ts +47 -3
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/events.d.ts +1 -1
- package/dist/types/provider-settings.d.ts +1 -1
- package/dist/types/provider-settings.d.ts.map +1 -1
- package/dist/types/storage.d.ts +2 -1
- package/dist/types/storage.d.ts.map +1 -1
- package/dist/types.d.ts +7 -16
- package/dist/types.d.ts.map +1 -1
- package/package.json +15 -12
- package/src/ClineCore.test.ts +150 -0
- package/src/ClineCore.ts +114 -8
- package/src/account/cline-account-service.test.ts +84 -0
- package/src/account/cline-account-service.ts +2 -2
- package/src/account/index.ts +1 -0
- package/src/account/types.ts +6 -0
- package/src/auth/bounded-ttl-cache.test.ts +38 -0
- package/src/auth/bounded-ttl-cache.ts +53 -0
- package/src/auth/cline.test.ts +173 -36
- package/src/auth/cline.ts +395 -93
- package/src/auth/oca.test.ts +125 -0
- package/src/auth/oca.ts +17 -4
- package/src/{agents → extensions/config}/agent-config-loader.test.ts +1 -1
- package/src/{agents → extensions/config}/agent-config-parser.ts +2 -2
- package/src/{agents → extensions/config}/hooks-config-loader.ts +1 -1
- package/src/{agents → extensions/config}/index.ts +7 -11
- package/src/{runtime/commands.test.ts → extensions/config/runtime-commands.test.ts} +20 -3
- package/src/{runtime/commands.ts → extensions/config/runtime-commands.ts} +1 -8
- package/src/{agents → extensions/config}/unified-config-file-watcher.ts +15 -2
- package/src/{agents → extensions/config}/user-instruction-config-loader.test.ts +90 -2
- package/src/{agents → extensions/config}/user-instruction-config-loader.ts +126 -12
- package/src/extensions/context/agentic-compaction.ts +119 -0
- package/src/extensions/context/basic-compaction.ts +275 -0
- package/src/extensions/context/compaction-shared.ts +458 -0
- package/src/extensions/context/compaction.test.ts +477 -0
- package/src/extensions/context/compaction.ts +203 -0
- package/src/extensions/index.ts +12 -0
- package/src/extensions/mcp/client.ts +420 -0
- package/src/{mcp → extensions/mcp}/index.ts +16 -0
- package/src/{mcp → extensions/mcp}/manager.test.ts +1 -2
- package/src/{mcp → extensions/mcp}/manager.ts +3 -5
- package/src/extensions/mcp/name-transform.ts +33 -0
- package/src/extensions/mcp/policies.ts +47 -0
- package/src/extensions/mcp/tools.ts +47 -0
- package/src/{mcp → extensions/mcp}/types.ts +35 -7
- package/src/{agents → extensions/plugin}/plugin-config-loader.test.ts +18 -13
- package/src/{agents → extensions/plugin}/plugin-config-loader.ts +1 -1
- package/src/{agents → extensions/plugin}/plugin-loader.test.ts +41 -4
- package/src/extensions/plugin/plugin-loader.ts +106 -0
- package/src/extensions/plugin/plugin-module-import.ts +278 -0
- package/src/{agents → extensions/plugin}/plugin-sandbox-bootstrap.ts +30 -92
- package/src/{agents → extensions/plugin}/plugin-sandbox.test.ts +60 -3
- package/src/{agents → extensions/plugin}/plugin-sandbox.ts +146 -56
- package/src/hooks/index.ts +25 -0
- package/src/hooks/persistent.ts +661 -0
- package/src/hooks/subprocess-runner.ts +196 -0
- package/src/hooks/subprocess.ts +669 -0
- package/src/index.ts +200 -118
- package/src/prompt/default-system.ts +21 -0
- package/src/providers/local-provider-registry.ts +1 -1
- package/src/providers/local-provider-service.test.ts +23 -2
- package/src/providers/local-provider-service.ts +2 -2
- package/src/runtime/checkpoint-hooks.test.ts +167 -0
- package/src/runtime/checkpoint-hooks.ts +186 -0
- package/src/runtime/hook-file-hooks.test.ts +40 -1
- package/src/runtime/hook-file-hooks.ts +35 -16
- package/src/runtime/index.ts +4 -19
- package/src/runtime/rules.ts +4 -1
- package/src/runtime/runtime-builder.team-persistence.test.ts +3 -6
- package/src/runtime/runtime-builder.test.ts +266 -160
- package/src/runtime/runtime-builder.ts +120 -47
- package/src/runtime/runtime-parity.test.ts +22 -22
- package/src/runtime/session-runtime.ts +36 -6
- package/src/runtime/{sandbox/subprocess-sandbox.ts → subprocess-sandbox.ts} +24 -3
- package/src/runtime/team-runtime-registry.ts +1 -4
- package/src/runtime/tool-approval.ts +1 -1
- package/src/session/default-session-manager.e2e.test.ts +2 -2
- package/src/session/default-session-manager.test.ts +553 -9
- package/src/session/default-session-manager.ts +140 -46
- package/src/session/file-session-service.ts +3 -3
- package/src/session/index.ts +6 -6
- package/src/session/persistence-service.test.ts +212 -0
- package/src/session/{unified-session-persistence-service.ts → persistence-service.ts} +106 -172
- package/src/session/rpc-session-service.ts +3 -3
- package/src/session/runtime-oauth-token-manager.ts +1 -1
- package/src/session/session-agent-events.ts +1 -1
- package/src/session/session-artifacts.ts +32 -4
- package/src/session/session-config-builder.ts +22 -9
- package/src/session/session-graph.ts +1 -1
- package/src/session/session-host.ts +19 -11
- package/src/session/session-manager.ts +11 -6
- package/src/session/session-service.team-persistence.test.ts +1 -1
- package/src/session/session-service.ts +6 -9
- package/src/session/session-team-coordination.ts +7 -3
- package/src/session/session-telemetry.ts +1 -1
- package/src/session/utils/helpers.test.ts +160 -0
- package/src/session/utils/helpers.ts +289 -42
- package/src/session/utils/types.ts +47 -7
- package/src/session/workspace-manager.ts +5 -3
- package/src/session/workspace-manifest.ts +3 -49
- package/src/storage/file-team-store.ts +2 -5
- package/src/storage/provider-settings-legacy-migration.ts +2 -2
- package/src/storage/provider-settings-manager.test.ts +1 -1
- package/src/storage/sqlite-team-store.ts +212 -125
- package/src/storage/team-store.ts +1 -5
- package/src/team/delegated-agent.ts +131 -0
- package/src/team/index.ts +1 -0
- package/src/team/multi-agent.lifecycle.test.ts +201 -0
- package/src/team/multi-agent.ts +1666 -0
- package/src/team/projections.ts +2 -4
- package/src/team/runtime.ts +54 -0
- package/src/team/spawn-agent-tool.test.ts +387 -0
- package/src/team/spawn-agent-tool.ts +207 -0
- package/src/team/subagent-prompts.ts +41 -0
- package/src/team/team-tools.test.ts +802 -0
- package/src/team/team-tools.ts +792 -0
- package/src/telemetry/OpenTelemetryProvider.test.ts +25 -3
- package/src/telemetry/OpenTelemetryProvider.ts +108 -18
- package/src/telemetry/TelemetryLoggerSink.test.ts +42 -0
- package/src/telemetry/{LoggerTelemetryAdapter.ts → TelemetryLoggerSink.ts} +21 -14
- package/src/telemetry/TelemetryService.test.ts +7 -7
- package/src/telemetry/TelemetryService.ts +2 -4
- package/src/tools/definitions.test.ts +76 -0
- package/src/tools/definitions.ts +41 -2
- package/src/tools/executors/apply-patch.ts +1 -1
- package/src/tools/executors/editor.ts +1 -1
- package/src/tools/executors/file-read.ts +1 -1
- package/src/tools/executors/search.ts +1 -1
- package/src/tools/executors/web-fetch.ts +1 -1
- package/src/tools/index.ts +6 -1
- package/src/tools/model-tool-routing.ts +2 -0
- package/src/tools/presets.test.ts +8 -0
- package/src/tools/presets.ts +40 -2
- package/src/tools/schemas.ts +19 -0
- package/src/tools/types.ts +31 -2
- package/src/types/config.ts +61 -7
- package/src/types/events.ts +1 -1
- package/src/types/index.ts +0 -1
- package/src/types/provider-settings.ts +1 -1
- package/src/types/storage.ts +2 -5
- package/src/types.ts +32 -44
- package/dist/agents/agent-config-loader.d.ts.map +0 -1
- package/dist/agents/agent-config-parser.d.ts.map +0 -1
- package/dist/agents/hooks-config-loader.d.ts.map +0 -1
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/plugin-config-loader.d.ts.map +0 -1
- package/dist/agents/plugin-loader.d.ts.map +0 -1
- package/dist/agents/plugin-sandbox-bootstrap.js +0 -446
- package/dist/agents/plugin-sandbox.d.ts.map +0 -1
- package/dist/agents/unified-config-file-watcher.d.ts.map +0 -1
- package/dist/agents/user-instruction-config-loader.d.ts.map +0 -1
- package/dist/mcp/config-loader.d.ts.map +0 -1
- package/dist/mcp/index.d.ts +0 -5
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/manager.d.ts.map +0 -1
- package/dist/mcp/types.d.ts.map +0 -1
- package/dist/runtime/commands.d.ts.map +0 -1
- package/dist/runtime/sandbox/subprocess-sandbox.d.ts.map +0 -1
- package/dist/runtime/skills.d.ts +0 -14
- package/dist/runtime/skills.d.ts.map +0 -1
- package/dist/runtime/workflows.d.ts +0 -14
- package/dist/runtime/workflows.d.ts.map +0 -1
- package/dist/session/unified-session-persistence-service.d.ts.map +0 -1
- package/dist/telemetry/LoggerTelemetryAdapter.d.ts.map +0 -1
- package/dist/types/workspace.d.ts +0 -8
- package/dist/types/workspace.d.ts.map +0 -1
- package/src/agents/plugin-loader.ts +0 -175
- package/src/runtime/skills.ts +0 -44
- package/src/runtime/workflows.test.ts +0 -119
- package/src/runtime/workflows.ts +0 -45
- package/src/session/unified-session-persistence-service.test.ts +0 -85
- package/src/telemetry/LoggerTelemetryAdapter.test.ts +0 -42
- package/src/types/workspace.ts +0 -7
- /package/dist/{agents → extensions/config}/agent-config-loader.d.ts +0 -0
- /package/dist/{agents → extensions/config}/unified-config-file-watcher.d.ts +0 -0
- /package/dist/{agents → extensions/config}/user-instruction-config-loader.d.ts +0 -0
- /package/dist/{mcp → extensions/mcp}/config-loader.d.ts +0 -0
- /package/dist/runtime/{sandbox/subprocess-sandbox.d.ts → subprocess-sandbox.d.ts} +0 -0
- /package/src/{agents → extensions/config}/agent-config-loader.ts +0 -0
- /package/src/{agents → extensions/config}/hooks-config-loader.test.ts +0 -0
- /package/src/{agents → extensions/config}/unified-config-file-watcher.test.ts +0 -0
- /package/src/{mcp → extensions/mcp}/config-loader.test.ts +0 -0
- /package/src/{mcp → extensions/mcp}/config-loader.ts +0 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { type ChildProcessWithoutNullStreams, spawn } from "node:child_process";
|
|
2
|
+
import { StringDecoder } from "node:string_decoder";
|
|
3
|
+
import type {
|
|
4
|
+
McpServerClient,
|
|
5
|
+
McpServerClientFactory,
|
|
6
|
+
McpServerRegistration,
|
|
7
|
+
McpToolCallResult,
|
|
8
|
+
McpToolDescriptor,
|
|
9
|
+
} from "./types";
|
|
10
|
+
|
|
11
|
+
type JsonRpcRequest = {
|
|
12
|
+
jsonrpc: "2.0";
|
|
13
|
+
id: number;
|
|
14
|
+
method: string;
|
|
15
|
+
params?: Record<string, unknown>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
type JsonRpcMessage = {
|
|
19
|
+
jsonrpc?: "2.0";
|
|
20
|
+
id?: number;
|
|
21
|
+
method?: string;
|
|
22
|
+
result?: unknown;
|
|
23
|
+
error?: {
|
|
24
|
+
code?: number;
|
|
25
|
+
message?: string;
|
|
26
|
+
data?: unknown;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type StdioProtocolMode = "newline" | "framed";
|
|
31
|
+
|
|
32
|
+
const MCP_PROTOCOL_VERSION = "2024-11-05";
|
|
33
|
+
const MCP_REQUEST_TIMEOUT_MS = 5_000;
|
|
34
|
+
const MCP_CONNECT_TIMEOUT_MS = 1_500;
|
|
35
|
+
|
|
36
|
+
function toErrorMessage(error: unknown): string {
|
|
37
|
+
return error instanceof Error ? error.message : String(error);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function encodeFramedMessage(message: Record<string, unknown>): Buffer {
|
|
41
|
+
const body = Buffer.from(JSON.stringify(message), "utf8");
|
|
42
|
+
const header = Buffer.from(
|
|
43
|
+
`Content-Length: ${body.byteLength}\r\n\r\n`,
|
|
44
|
+
"utf8",
|
|
45
|
+
);
|
|
46
|
+
return Buffer.concat([header, body]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function encodeNewlineMessage(message: Record<string, unknown>): Buffer {
|
|
50
|
+
return Buffer.from(`${JSON.stringify(message)}\n`, "utf8");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
class FramedMessageParser {
|
|
54
|
+
private buffer = "";
|
|
55
|
+
private readonly decoder = new StringDecoder("utf8");
|
|
56
|
+
|
|
57
|
+
push(chunk: Buffer): string[] {
|
|
58
|
+
this.buffer += this.decoder.write(chunk);
|
|
59
|
+
const messages: string[] = [];
|
|
60
|
+
|
|
61
|
+
while (true) {
|
|
62
|
+
const separatorIndex = this.buffer.indexOf("\r\n\r\n");
|
|
63
|
+
if (separatorIndex < 0) {
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const headerText = this.buffer.slice(0, separatorIndex);
|
|
68
|
+
const contentLengthMatch = headerText.match(
|
|
69
|
+
/(?:^|\r\n)Content-Length:\s*(\d+)(?:\r\n|$)/i,
|
|
70
|
+
);
|
|
71
|
+
if (!contentLengthMatch) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
"Invalid MCP stdio frame: missing Content-Length header.",
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const contentLength = Number.parseInt(contentLengthMatch[1], 10);
|
|
78
|
+
const bodyStart = separatorIndex + 4;
|
|
79
|
+
const bodyEnd = bodyStart + contentLength;
|
|
80
|
+
if (this.buffer.length < bodyEnd) {
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
messages.push(this.buffer.slice(bodyStart, bodyEnd));
|
|
85
|
+
this.buffer = this.buffer.slice(bodyEnd);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return messages;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
class NewlineMessageParser {
|
|
93
|
+
private buffer = "";
|
|
94
|
+
private readonly decoder = new StringDecoder("utf8");
|
|
95
|
+
|
|
96
|
+
push(chunk: Buffer): string[] {
|
|
97
|
+
this.buffer += this.decoder.write(chunk);
|
|
98
|
+
const messages: string[] = [];
|
|
99
|
+
|
|
100
|
+
while (true) {
|
|
101
|
+
const newlineIndex = this.buffer.indexOf("\n");
|
|
102
|
+
if (newlineIndex < 0) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const line = this.buffer.slice(0, newlineIndex).trim();
|
|
106
|
+
this.buffer = this.buffer.slice(newlineIndex + 1);
|
|
107
|
+
if (line.length > 0) {
|
|
108
|
+
messages.push(line);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return messages;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
class StdioMcpClient implements McpServerClient {
|
|
117
|
+
private readonly registration: McpServerRegistration;
|
|
118
|
+
private process?: ChildProcessWithoutNullStreams;
|
|
119
|
+
private nextRequestId = 1;
|
|
120
|
+
private readonly pending = new Map<
|
|
121
|
+
number,
|
|
122
|
+
{
|
|
123
|
+
resolve: (value: unknown) => void;
|
|
124
|
+
reject: (error: Error) => void;
|
|
125
|
+
timeout: ReturnType<typeof setTimeout>;
|
|
126
|
+
}
|
|
127
|
+
>();
|
|
128
|
+
private framedParser = new FramedMessageParser();
|
|
129
|
+
private newlineParser = new NewlineMessageParser();
|
|
130
|
+
private stderrBuffer = "";
|
|
131
|
+
private connected = false;
|
|
132
|
+
private protocolMode: StdioProtocolMode = "newline";
|
|
133
|
+
|
|
134
|
+
constructor(registration: McpServerRegistration) {
|
|
135
|
+
this.registration = registration;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async connect(): Promise<void> {
|
|
139
|
+
if (this.connected) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (this.registration.transport.type !== "stdio") {
|
|
143
|
+
throw new Error(
|
|
144
|
+
`Unsupported MCP transport for "${this.registration.name}": ${this.registration.transport.type}`,
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const attempts: StdioProtocolMode[] = ["newline", "framed"];
|
|
149
|
+
let lastError: Error | undefined;
|
|
150
|
+
|
|
151
|
+
for (const protocolMode of attempts) {
|
|
152
|
+
await this.disconnect().catch(() => {});
|
|
153
|
+
this.spawnProcess(protocolMode);
|
|
154
|
+
try {
|
|
155
|
+
await this.request(
|
|
156
|
+
"initialize",
|
|
157
|
+
{
|
|
158
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
159
|
+
capabilities: {},
|
|
160
|
+
clientInfo: {
|
|
161
|
+
name: "@clinebot/core",
|
|
162
|
+
version: "0.0.0",
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
MCP_CONNECT_TIMEOUT_MS,
|
|
166
|
+
);
|
|
167
|
+
this.notify("notifications/initialized");
|
|
168
|
+
this.connected = true;
|
|
169
|
+
this.protocolMode = protocolMode;
|
|
170
|
+
return;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
throw (
|
|
177
|
+
lastError ??
|
|
178
|
+
new Error(`Failed to connect to MCP server "${this.registration.name}".`)
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async disconnect(): Promise<void> {
|
|
183
|
+
const child = this.process;
|
|
184
|
+
this.connected = false;
|
|
185
|
+
this.process = undefined;
|
|
186
|
+
this.failAllPending(
|
|
187
|
+
new Error(`Disconnected from MCP server "${this.registration.name}".`),
|
|
188
|
+
);
|
|
189
|
+
if (!child) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
child.kill();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async listTools(): Promise<readonly McpToolDescriptor[]> {
|
|
196
|
+
const result = (await this.request("tools/list")) as {
|
|
197
|
+
tools?: Array<{
|
|
198
|
+
name?: string;
|
|
199
|
+
description?: string;
|
|
200
|
+
inputSchema?: Record<string, unknown>;
|
|
201
|
+
}>;
|
|
202
|
+
};
|
|
203
|
+
return (result.tools ?? [])
|
|
204
|
+
.filter(
|
|
205
|
+
(
|
|
206
|
+
tool,
|
|
207
|
+
): tool is {
|
|
208
|
+
name: string;
|
|
209
|
+
description?: string;
|
|
210
|
+
inputSchema: Record<string, unknown>;
|
|
211
|
+
} =>
|
|
212
|
+
typeof tool?.name === "string" &&
|
|
213
|
+
typeof tool.inputSchema === "object" &&
|
|
214
|
+
tool.inputSchema !== null,
|
|
215
|
+
)
|
|
216
|
+
.map((tool) => ({
|
|
217
|
+
name: tool.name,
|
|
218
|
+
description: tool.description,
|
|
219
|
+
inputSchema: tool.inputSchema,
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async callTool(request: {
|
|
224
|
+
name: string;
|
|
225
|
+
arguments?: Record<string, unknown>;
|
|
226
|
+
}): Promise<McpToolCallResult> {
|
|
227
|
+
return this.request("tools/call", {
|
|
228
|
+
name: request.name,
|
|
229
|
+
arguments: request.arguments ?? {},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private spawnProcess(protocolMode: StdioProtocolMode): void {
|
|
234
|
+
const transport = this.registration.transport;
|
|
235
|
+
if (transport.type !== "stdio") {
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Unsupported MCP transport for "${this.registration.name}": ${transport.type}`,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
this.framedParser = new FramedMessageParser();
|
|
242
|
+
this.newlineParser = new NewlineMessageParser();
|
|
243
|
+
this.stderrBuffer = "";
|
|
244
|
+
this.protocolMode = protocolMode;
|
|
245
|
+
|
|
246
|
+
const child = spawn(transport.command, transport.args ?? [], {
|
|
247
|
+
cwd: transport.cwd,
|
|
248
|
+
env: {
|
|
249
|
+
...process.env,
|
|
250
|
+
...(transport.env ?? {}),
|
|
251
|
+
},
|
|
252
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
this.process = child;
|
|
256
|
+
child.stdout.on("data", (chunk: Buffer) => this.handleStdout(chunk));
|
|
257
|
+
child.stderr.on("data", (chunk: Buffer) => {
|
|
258
|
+
if (this.process !== child) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
this.stderrBuffer += chunk.toString("utf8");
|
|
262
|
+
if (this.stderrBuffer.length > 16_384) {
|
|
263
|
+
this.stderrBuffer = this.stderrBuffer.slice(-16_384);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
child.once("error", (error) => {
|
|
267
|
+
if (this.process !== child) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.failAllPending(
|
|
271
|
+
new Error(`MCP process error: ${toErrorMessage(error)}`),
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
child.once("exit", (code, signal) => {
|
|
275
|
+
if (this.process !== child) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
this.connected = false;
|
|
279
|
+
this.process = undefined;
|
|
280
|
+
const suffix = this.stderrBuffer.trim()
|
|
281
|
+
? ` stderr: ${this.stderrBuffer.trim()}`
|
|
282
|
+
: "";
|
|
283
|
+
this.failAllPending(
|
|
284
|
+
new Error(
|
|
285
|
+
`MCP process exited for "${this.registration.name}" (code=${code ?? "null"}, signal=${signal ?? "null"}).${suffix}`,
|
|
286
|
+
),
|
|
287
|
+
);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
private handleStdout(chunk: Buffer): void {
|
|
292
|
+
try {
|
|
293
|
+
const messages =
|
|
294
|
+
this.protocolMode === "framed"
|
|
295
|
+
? this.framedParser.push(chunk)
|
|
296
|
+
: this.newlineParser.push(chunk);
|
|
297
|
+
|
|
298
|
+
for (const messageText of messages) {
|
|
299
|
+
const message = JSON.parse(messageText) as JsonRpcMessage;
|
|
300
|
+
if (typeof message.id !== "number") {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const pending = this.pending.get(message.id);
|
|
304
|
+
if (!pending) {
|
|
305
|
+
continue;
|
|
306
|
+
}
|
|
307
|
+
this.pending.delete(message.id);
|
|
308
|
+
clearTimeout(pending.timeout);
|
|
309
|
+
if (message.error) {
|
|
310
|
+
const errorMessage =
|
|
311
|
+
message.error.message ||
|
|
312
|
+
`MCP request failed with code ${message.error.code ?? "unknown"}`;
|
|
313
|
+
pending.reject(new Error(errorMessage));
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
pending.resolve(message.result);
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
this.handleProtocolFailure(error);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private handleProtocolFailure(error: unknown): void {
|
|
324
|
+
const child = this.process;
|
|
325
|
+
if (!child) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.connected = false;
|
|
330
|
+
this.process = undefined;
|
|
331
|
+
const stderrSuffix = this.stderrBuffer.trim()
|
|
332
|
+
? ` stderr: ${this.stderrBuffer.trim()}`
|
|
333
|
+
: "";
|
|
334
|
+
this.failAllPending(
|
|
335
|
+
new Error(
|
|
336
|
+
`Invalid MCP response from "${this.registration.name}": ${toErrorMessage(error)}.${stderrSuffix}`,
|
|
337
|
+
),
|
|
338
|
+
);
|
|
339
|
+
child.kill();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private async request(
|
|
343
|
+
method: string,
|
|
344
|
+
params?: Record<string, unknown>,
|
|
345
|
+
timeoutMs = MCP_REQUEST_TIMEOUT_MS,
|
|
346
|
+
): Promise<unknown> {
|
|
347
|
+
const child = this.process;
|
|
348
|
+
if (!child?.stdin.writable) {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`MCP server "${this.registration.name}" is not connected.`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const id = this.nextRequestId++;
|
|
355
|
+
const payload: JsonRpcRequest = {
|
|
356
|
+
jsonrpc: "2.0",
|
|
357
|
+
id,
|
|
358
|
+
method,
|
|
359
|
+
...(params ? { params } : {}),
|
|
360
|
+
};
|
|
361
|
+
|
|
362
|
+
const resultPromise = new Promise<unknown>((resolve, reject) => {
|
|
363
|
+
const timeout = setTimeout(() => {
|
|
364
|
+
this.pending.delete(id);
|
|
365
|
+
reject(
|
|
366
|
+
new Error(
|
|
367
|
+
`MCP request timed out for "${this.registration.name}" (${method}).`,
|
|
368
|
+
),
|
|
369
|
+
);
|
|
370
|
+
}, timeoutMs);
|
|
371
|
+
this.pending.set(id, { resolve, reject, timeout });
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
child.stdin.write(
|
|
376
|
+
this.protocolMode === "framed"
|
|
377
|
+
? encodeFramedMessage(payload)
|
|
378
|
+
: encodeNewlineMessage(payload),
|
|
379
|
+
);
|
|
380
|
+
} catch (error) {
|
|
381
|
+
const pending = this.pending.get(id);
|
|
382
|
+
if (pending) {
|
|
383
|
+
clearTimeout(pending.timeout);
|
|
384
|
+
this.pending.delete(id);
|
|
385
|
+
}
|
|
386
|
+
throw error;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return resultPromise;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private notify(method: string, params?: Record<string, unknown>): void {
|
|
393
|
+
const child = this.process;
|
|
394
|
+
if (!child?.stdin.writable) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const payload = {
|
|
398
|
+
jsonrpc: "2.0" as const,
|
|
399
|
+
method,
|
|
400
|
+
...(params ? { params } : {}),
|
|
401
|
+
};
|
|
402
|
+
child.stdin.write(
|
|
403
|
+
this.protocolMode === "framed"
|
|
404
|
+
? encodeFramedMessage(payload)
|
|
405
|
+
: encodeNewlineMessage(payload),
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private failAllPending(error: Error): void {
|
|
410
|
+
for (const [id, pending] of this.pending) {
|
|
411
|
+
clearTimeout(pending.timeout);
|
|
412
|
+
this.pending.delete(id);
|
|
413
|
+
pending.reject(error);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function createDefaultMcpServerClientFactory(): McpServerClientFactory {
|
|
419
|
+
return (registration) => new StdioMcpClient(registration);
|
|
420
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { createDefaultMcpServerClientFactory } from "./client";
|
|
1
2
|
export type {
|
|
2
3
|
LoadMcpSettingsOptions,
|
|
3
4
|
McpSettingsFile,
|
|
@@ -12,6 +13,16 @@ export {
|
|
|
12
13
|
} from "./config-loader";
|
|
13
14
|
export { InMemoryMcpManager } from "./manager";
|
|
14
15
|
export type {
|
|
16
|
+
CreateDisabledMcpToolPoliciesOptions,
|
|
17
|
+
CreateDisabledMcpToolPolicyOptions,
|
|
18
|
+
} from "./policies";
|
|
19
|
+
export {
|
|
20
|
+
createDisabledMcpToolPolicies,
|
|
21
|
+
createDisabledMcpToolPolicy,
|
|
22
|
+
} from "./policies";
|
|
23
|
+
export { createMcpTools } from "./tools";
|
|
24
|
+
export type {
|
|
25
|
+
CreateMcpToolsOptions,
|
|
15
26
|
McpConnectionStatus,
|
|
16
27
|
McpManager,
|
|
17
28
|
McpManagerOptions,
|
|
@@ -23,4 +34,9 @@ export type {
|
|
|
23
34
|
McpSseTransportConfig,
|
|
24
35
|
McpStdioTransportConfig,
|
|
25
36
|
McpStreamableHttpTransportConfig,
|
|
37
|
+
McpToolCallRequest,
|
|
38
|
+
McpToolCallResult,
|
|
39
|
+
McpToolDescriptor,
|
|
40
|
+
McpToolNameTransform,
|
|
41
|
+
McpToolProvider,
|
|
26
42
|
} from "./types";
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { McpToolDescriptor } from "@clinebot/agents";
|
|
2
1
|
import { describe, expect, it, vi } from "vitest";
|
|
3
2
|
import { InMemoryMcpManager } from "./manager";
|
|
4
|
-
import type { McpServerClient } from "./types";
|
|
3
|
+
import type { McpServerClient, McpToolDescriptor } from "./types";
|
|
5
4
|
|
|
6
5
|
function createClient(overrides?: Partial<McpServerClient>): McpServerClient {
|
|
7
6
|
return {
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
McpToolCallRequest,
|
|
3
|
-
McpToolCallResult,
|
|
4
|
-
McpToolDescriptor,
|
|
5
|
-
} from "@clinebot/agents";
|
|
6
1
|
import type {
|
|
7
2
|
McpConnectionStatus,
|
|
8
3
|
McpManager,
|
|
@@ -10,6 +5,9 @@ import type {
|
|
|
10
5
|
McpServerClient,
|
|
11
6
|
McpServerRegistration,
|
|
12
7
|
McpServerSnapshot,
|
|
8
|
+
McpToolCallRequest,
|
|
9
|
+
McpToolCallResult,
|
|
10
|
+
McpToolDescriptor,
|
|
13
11
|
} from "./types";
|
|
14
12
|
|
|
15
13
|
const DEFAULT_TOOLS_CACHE_TTL_MS = 5000;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { McpToolNameTransform } from "./types";
|
|
3
|
+
|
|
4
|
+
const MAX_MCP_TOOL_NAME_LENGTH = 128;
|
|
5
|
+
const INVALID_MCP_TOOL_NAME_CHARACTERS = /[^a-zA-Z0-9_-]+/g;
|
|
6
|
+
const HASH_LENGTH = 8;
|
|
7
|
+
const HASH_SEPARATOR_LENGTH = 1;
|
|
8
|
+
const FALLBACK_BASE_NAME = "mcp_tool";
|
|
9
|
+
|
|
10
|
+
function buildMcpToolNameHash(value: string): string {
|
|
11
|
+
return createHash("sha1").update(value).digest("hex").slice(0, HASH_LENGTH);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function sanitizeMcpToolNameCandidate(value: string): string {
|
|
15
|
+
return value.replace(INVALID_MCP_TOOL_NAME_CHARACTERS, "_");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const defaultMcpToolNameTransform: McpToolNameTransform = ({
|
|
19
|
+
serverName,
|
|
20
|
+
toolName,
|
|
21
|
+
}): string => {
|
|
22
|
+
const rawName = `${serverName}__${toolName}`;
|
|
23
|
+
const sanitizedName = sanitizeMcpToolNameCandidate(rawName);
|
|
24
|
+
if (sanitizedName === rawName && rawName.length <= MAX_MCP_TOOL_NAME_LENGTH) {
|
|
25
|
+
return rawName;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const hash = buildMcpToolNameHash(rawName);
|
|
29
|
+
const maxBaseLength =
|
|
30
|
+
MAX_MCP_TOOL_NAME_LENGTH - HASH_SEPARATOR_LENGTH - HASH_LENGTH;
|
|
31
|
+
const baseName = sanitizedName.slice(0, maxBaseLength) || FALLBACK_BASE_NAME;
|
|
32
|
+
return `${baseName}_${hash}`;
|
|
33
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ToolPolicy } from "@clinebot/shared";
|
|
2
|
+
import { defaultMcpToolNameTransform } from "./name-transform";
|
|
3
|
+
import type { McpToolNameTransform } from "./types";
|
|
4
|
+
|
|
5
|
+
export interface CreateDisabledMcpToolPolicyOptions {
|
|
6
|
+
serverName: string;
|
|
7
|
+
toolName: string;
|
|
8
|
+
nameTransform?: McpToolNameTransform;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface CreateDisabledMcpToolPoliciesOptions {
|
|
12
|
+
serverName: string;
|
|
13
|
+
toolNames: readonly string[];
|
|
14
|
+
nameTransform?: McpToolNameTransform;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createDisabledMcpToolPolicy(
|
|
18
|
+
options: CreateDisabledMcpToolPolicyOptions,
|
|
19
|
+
): Record<string, ToolPolicy> {
|
|
20
|
+
const nameTransform = options.nameTransform ?? defaultMcpToolNameTransform;
|
|
21
|
+
const name = nameTransform({
|
|
22
|
+
serverName: options.serverName,
|
|
23
|
+
toolName: options.toolName,
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
[name]: {
|
|
27
|
+
enabled: false,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function createDisabledMcpToolPolicies(
|
|
33
|
+
options: CreateDisabledMcpToolPoliciesOptions,
|
|
34
|
+
): Record<string, ToolPolicy> {
|
|
35
|
+
const policies: Record<string, ToolPolicy> = {};
|
|
36
|
+
for (const toolName of options.toolNames) {
|
|
37
|
+
Object.assign(
|
|
38
|
+
policies,
|
|
39
|
+
createDisabledMcpToolPolicy({
|
|
40
|
+
serverName: options.serverName,
|
|
41
|
+
toolName,
|
|
42
|
+
nameTransform: options.nameTransform,
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return policies;
|
|
47
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createTool, type Tool } from "@clinebot/shared";
|
|
2
|
+
import { defaultMcpToolNameTransform } from "./name-transform";
|
|
3
|
+
import type { CreateMcpToolsOptions, McpToolDescriptor } from "./types";
|
|
4
|
+
|
|
5
|
+
function defaultMcpDescription(
|
|
6
|
+
serverName: string,
|
|
7
|
+
tool: McpToolDescriptor,
|
|
8
|
+
): string {
|
|
9
|
+
const base = tool.description?.trim();
|
|
10
|
+
if (base) {
|
|
11
|
+
return base;
|
|
12
|
+
}
|
|
13
|
+
return `Execute MCP tool "${tool.name}" from server "${serverName}".`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function createMcpTools(
|
|
17
|
+
options: CreateMcpToolsOptions,
|
|
18
|
+
): Promise<Tool[]> {
|
|
19
|
+
const descriptors = await options.provider.listTools(options.serverName);
|
|
20
|
+
const nameTransform = options.nameTransform ?? defaultMcpToolNameTransform;
|
|
21
|
+
|
|
22
|
+
return descriptors.map((descriptor) => {
|
|
23
|
+
const agentToolName = nameTransform({
|
|
24
|
+
serverName: options.serverName,
|
|
25
|
+
toolName: descriptor.name,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return createTool({
|
|
29
|
+
name: agentToolName,
|
|
30
|
+
description: defaultMcpDescription(options.serverName, descriptor),
|
|
31
|
+
inputSchema: descriptor.inputSchema,
|
|
32
|
+
timeoutMs: options.timeoutMs,
|
|
33
|
+
retryable: options.retryable,
|
|
34
|
+
maxRetries: options.maxRetries,
|
|
35
|
+
execute: async (input: unknown, context) =>
|
|
36
|
+
options.provider.callTool({
|
|
37
|
+
serverName: options.serverName,
|
|
38
|
+
toolName: descriptor.name,
|
|
39
|
+
arguments:
|
|
40
|
+
input && typeof input === "object" && !Array.isArray(input)
|
|
41
|
+
? (input as Record<string, unknown>)
|
|
42
|
+
: undefined,
|
|
43
|
+
context,
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -1,10 +1,38 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
}
|
|
1
|
+
import type { ToolContext } from "@clinebot/shared";
|
|
2
|
+
|
|
3
|
+
export interface McpToolDescriptor {
|
|
4
|
+
name: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
inputSchema: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface McpToolCallRequest {
|
|
10
|
+
serverName: string;
|
|
11
|
+
toolName: string;
|
|
12
|
+
arguments?: Record<string, unknown>;
|
|
13
|
+
context?: ToolContext;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type McpToolCallResult = unknown;
|
|
17
|
+
|
|
18
|
+
export interface McpToolProvider {
|
|
19
|
+
listTools(serverName: string): Promise<readonly McpToolDescriptor[]>;
|
|
20
|
+
callTool(request: McpToolCallRequest): Promise<McpToolCallResult>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type McpToolNameTransform = (input: {
|
|
24
|
+
serverName: string;
|
|
25
|
+
toolName: string;
|
|
26
|
+
}) => string;
|
|
27
|
+
|
|
28
|
+
export interface CreateMcpToolsOptions {
|
|
29
|
+
serverName: string;
|
|
30
|
+
provider: McpToolProvider;
|
|
31
|
+
nameTransform?: McpToolNameTransform;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
retryable?: boolean;
|
|
34
|
+
maxRetries?: number;
|
|
35
|
+
}
|
|
8
36
|
|
|
9
37
|
export type McpConnectionStatus = "disconnected" | "connecting" | "connected";
|
|
10
38
|
|