@sammorrowdrums/mcpi-ext 0.2.1 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +426 -151
- package/dist/code-mode/eligibility.d.ts +29 -4
- package/dist/code-mode/eligibility.js +56 -6
- package/dist/code-mode/executor.d.ts +19 -1
- package/dist/code-mode/executor.js +73 -8
- package/dist/code-mode/index.d.ts +54 -12
- package/dist/code-mode/index.js +157 -65
- package/dist/code-mode/isolated-vm.d.ts +65 -0
- package/dist/code-mode/isolated-vm.js +116 -0
- package/dist/code-mode/tools.d.ts +4 -1
- package/dist/code-mode/tools.js +7 -4
- package/dist/code-mode/type-hints.d.ts +2 -2
- package/dist/code-mode/type-hints.js +24 -8
- package/dist/docker-e2e.d.ts +0 -1
- package/dist/docker-e2e.js +0 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +146 -67
- package/dist/mcp/call-tool-result.d.ts +10 -0
- package/dist/mcp/call-tool-result.js +70 -0
- package/dist/mcp/client-factory.d.ts +59 -0
- package/dist/mcp/client-factory.js +76 -0
- package/dist/mcp/client-manager.d.ts +61 -19
- package/dist/mcp/client-manager.js +223 -64
- package/dist/mcp/config-loader.d.ts +0 -1
- package/dist/mcp/config-loader.js +0 -1
- package/dist/mcp/config.d.ts +11 -1
- package/dist/mcp/config.js +15 -1
- package/dist/mcp/gateway-defaults.d.ts +14 -0
- package/dist/mcp/gateway-defaults.js +21 -0
- package/dist/mcp/host-approval.d.ts +16 -0
- package/dist/mcp/host-approval.js +19 -0
- package/dist/mcp/host-elicitation.d.ts +19 -0
- package/dist/mcp/host-elicitation.js +94 -0
- package/dist/mcp/index.d.ts +4 -1
- package/dist/mcp/index.js +4 -1
- package/dist/mcp/policy.d.ts +314 -0
- package/dist/mcp/policy.js +848 -0
- package/dist/routing/facilities.d.ts +109 -0
- package/dist/routing/facilities.js +175 -0
- package/dist/routing/format.d.ts +24 -0
- package/dist/routing/format.js +64 -0
- package/dist/routing/index.d.ts +4 -0
- package/dist/routing/index.js +4 -0
- package/dist/routing/seam.d.ts +35 -0
- package/dist/routing/seam.js +28 -0
- package/dist/routing/tripwire.d.ts +42 -0
- package/dist/routing/tripwire.js +80 -0
- package/dist/skills/discover.d.ts +5 -3
- package/dist/skills/discover.js +37 -12
- package/dist/skills/format.d.ts +4 -1
- package/dist/skills/format.js +8 -4
- package/dist/skills/index.d.ts +2 -2
- package/dist/skills/index.js +1 -1
- package/dist/skills/load-skill-tool.d.ts +23 -7
- package/dist/skills/load-skill-tool.js +112 -33
- package/dist/skills/mcp-tool-proxy.d.ts +4 -8
- package/dist/skills/mcp-tool-proxy.js +22 -97
- package/dist/skills/sep2640/client.d.ts +91 -0
- package/dist/skills/sep2640/client.js +160 -0
- package/dist/skills/sep2640/discover.d.ts +33 -0
- package/dist/skills/sep2640/discover.js +82 -0
- package/dist/skills/sep2640/index.d.ts +13 -0
- package/dist/skills/sep2640/index.js +13 -0
- package/dist/skills/sep2640/integrity.d.ts +58 -0
- package/dist/skills/sep2640/integrity.js +185 -0
- package/dist/skills/sep2640/load.d.ts +61 -0
- package/dist/skills/sep2640/load.js +90 -0
- package/dist/skills/sep2640/protocol.d.ts +146 -0
- package/dist/skills/sep2640/protocol.js +214 -0
- package/dist/skills/sep2640/spec.d.ts +76 -0
- package/dist/skills/sep2640/spec.js +88 -0
- package/dist/skills/skill-registry.d.ts +48 -2
- package/dist/skills/skill-registry.js +40 -3
- package/dist/tool-cli/bridge.d.ts +14 -0
- package/dist/tool-cli/bridge.js +196 -0
- package/dist/tool-cli/format.d.ts +17 -5
- package/dist/tool-cli/format.js +36 -20
- package/dist/tool-cli/index.d.ts +5 -2
- package/dist/tool-cli/index.js +3 -1
- package/dist/tool-cli/provider.d.ts +21 -0
- package/dist/tool-cli/provider.js +114 -0
- package/dist/tool-cli/startup.d.ts +22 -0
- package/dist/tool-cli/startup.js +47 -0
- package/package.json +78 -30
- package/dist/code-mode/code-mode-integration.test.d.ts +0 -2
- package/dist/code-mode/code-mode-integration.test.d.ts.map +0 -1
- package/dist/code-mode/code-mode-integration.test.js +0 -116
- package/dist/code-mode/code-mode-integration.test.js.map +0 -1
- package/dist/code-mode/eligibility.d.ts.map +0 -1
- package/dist/code-mode/eligibility.js.map +0 -1
- package/dist/code-mode/eligibility.test.d.ts +0 -2
- package/dist/code-mode/eligibility.test.d.ts.map +0 -1
- package/dist/code-mode/eligibility.test.js +0 -53
- package/dist/code-mode/eligibility.test.js.map +0 -1
- package/dist/code-mode/executor.d.ts.map +0 -1
- package/dist/code-mode/executor.js.map +0 -1
- package/dist/code-mode/executor.test.d.ts +0 -2
- package/dist/code-mode/executor.test.d.ts.map +0 -1
- package/dist/code-mode/executor.test.js +0 -130
- package/dist/code-mode/executor.test.js.map +0 -1
- package/dist/code-mode/index.d.ts.map +0 -1
- package/dist/code-mode/index.js.map +0 -1
- package/dist/code-mode/tools.d.ts.map +0 -1
- package/dist/code-mode/tools.js.map +0 -1
- package/dist/code-mode/type-hints.d.ts.map +0 -1
- package/dist/code-mode/type-hints.js.map +0 -1
- package/dist/code-mode/type-hints.test.d.ts +0 -2
- package/dist/code-mode/type-hints.test.d.ts.map +0 -1
- package/dist/code-mode/type-hints.test.js +0 -157
- package/dist/code-mode/type-hints.test.js.map +0 -1
- package/dist/docker-e2e.d.ts.map +0 -1
- package/dist/docker-e2e.js.map +0 -1
- package/dist/docker-e2e.test.d.ts +0 -2
- package/dist/docker-e2e.test.d.ts.map +0 -1
- package/dist/docker-e2e.test.js +0 -176
- package/dist/docker-e2e.test.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -8
- package/dist/index.test.js.map +0 -1
- package/dist/mcp/client-manager.d.ts.map +0 -1
- package/dist/mcp/client-manager.js.map +0 -1
- package/dist/mcp/client-manager.test.d.ts +0 -2
- package/dist/mcp/client-manager.test.d.ts.map +0 -1
- package/dist/mcp/client-manager.test.js +0 -204
- package/dist/mcp/client-manager.test.js.map +0 -1
- package/dist/mcp/config-loader.d.ts.map +0 -1
- package/dist/mcp/config-loader.js.map +0 -1
- package/dist/mcp/config-loader.test.d.ts +0 -2
- package/dist/mcp/config-loader.test.d.ts.map +0 -1
- package/dist/mcp/config-loader.test.js +0 -93
- package/dist/mcp/config-loader.test.js.map +0 -1
- package/dist/mcp/config.d.ts.map +0 -1
- package/dist/mcp/config.js.map +0 -1
- package/dist/mcp/index.d.ts.map +0 -1
- package/dist/mcp/index.js.map +0 -1
- package/dist/skills/discover.d.ts.map +0 -1
- package/dist/skills/discover.js.map +0 -1
- package/dist/skills/format.d.ts.map +0 -1
- package/dist/skills/format.js.map +0 -1
- package/dist/skills/format.test.d.ts +0 -2
- package/dist/skills/format.test.d.ts.map +0 -1
- package/dist/skills/format.test.js +0 -63
- package/dist/skills/format.test.js.map +0 -1
- package/dist/skills/index.d.ts.map +0 -1
- package/dist/skills/index.js.map +0 -1
- package/dist/skills/load-skill-tool.d.ts.map +0 -1
- package/dist/skills/load-skill-tool.js.map +0 -1
- package/dist/skills/mcp-tool-proxy.d.ts.map +0 -1
- package/dist/skills/mcp-tool-proxy.js.map +0 -1
- package/dist/skills/skill-integration.test.d.ts +0 -2
- package/dist/skills/skill-integration.test.d.ts.map +0 -1
- package/dist/skills/skill-integration.test.js +0 -119
- package/dist/skills/skill-integration.test.js.map +0 -1
- package/dist/skills/skill-registry.d.ts.map +0 -1
- package/dist/skills/skill-registry.js.map +0 -1
- package/dist/skills/skill-registry.test.d.ts +0 -2
- package/dist/skills/skill-registry.test.d.ts.map +0 -1
- package/dist/skills/skill-registry.test.js +0 -67
- package/dist/skills/skill-registry.test.js.map +0 -1
- package/dist/test-servers/weather-server.d.ts +0 -12
- package/dist/test-servers/weather-server.d.ts.map +0 -1
- package/dist/test-servers/weather-server.js +0 -139
- package/dist/test-servers/weather-server.js.map +0 -1
- package/dist/test-servers/weather-stdio.d.ts +0 -2
- package/dist/test-servers/weather-stdio.d.ts.map +0 -1
- package/dist/test-servers/weather-stdio.js +0 -11
- package/dist/test-servers/weather-stdio.js.map +0 -1
- package/dist/tool-cli/cli.d.ts +0 -3
- package/dist/tool-cli/cli.d.ts.map +0 -1
- package/dist/tool-cli/cli.js +0 -179
- package/dist/tool-cli/cli.js.map +0 -1
- package/dist/tool-cli/constants.d.ts +0 -7
- package/dist/tool-cli/constants.d.ts.map +0 -1
- package/dist/tool-cli/constants.js +0 -15
- package/dist/tool-cli/constants.js.map +0 -1
- package/dist/tool-cli/format.d.ts.map +0 -1
- package/dist/tool-cli/format.js.map +0 -1
- package/dist/tool-cli/format.test.d.ts +0 -2
- package/dist/tool-cli/format.test.d.ts.map +0 -1
- package/dist/tool-cli/format.test.js +0 -30
- package/dist/tool-cli/format.test.js.map +0 -1
- package/dist/tool-cli/index.d.ts.map +0 -1
- package/dist/tool-cli/index.js.map +0 -1
- package/dist/tool-cli/rpc-client.d.ts +0 -6
- package/dist/tool-cli/rpc-client.d.ts.map +0 -1
- package/dist/tool-cli/rpc-client.js +0 -25
- package/dist/tool-cli/rpc-client.js.map +0 -1
- package/dist/tool-cli/rpc-server.d.ts +0 -48
- package/dist/tool-cli/rpc-server.d.ts.map +0 -1
- package/dist/tool-cli/rpc-server.js +0 -220
- package/dist/tool-cli/rpc-server.js.map +0 -1
- package/dist/tool-cli/rpc-server.test.d.ts +0 -2
- package/dist/tool-cli/rpc-server.test.d.ts.map +0 -1
- package/dist/tool-cli/rpc-server.test.js +0 -154
- package/dist/tool-cli/rpc-server.test.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,39 +1,71 @@
|
|
|
1
1
|
import { CodeModeManager } from "./code-mode/index.js";
|
|
2
2
|
import { dockerE2ETool } from "./docker-e2e.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { McpiHostApproval } from "./mcp/host-approval.js";
|
|
4
|
+
import { McpiHostElicitation } from "./mcp/host-elicitation.js";
|
|
5
|
+
import { McpClientManager, McpPolicy, isSkillsExtensionEnabled, loadMcpConfig, } from "./mcp/index.js";
|
|
6
|
+
import { SkillRegistry, SkillsExtensionClient, createLoadSkillTool, discoverSkillsFromServer, discoverSkillsViaExtension, formatMcpSkillsForPrompt, registerMcpToolProxies, skillsExtensionDiagnostic, } from "./skills/index.js";
|
|
7
|
+
import { ToolCliServer, createPolicyToolProvider, formatToolCliForPrompt, startToolCliBridge, withholdToolCliCredentials, } from "./tool-cli/index.js";
|
|
8
|
+
import { buildExecutionFacilities, formatExecutionFacilities, publishExecutionFacilities, } from "./routing/index.js";
|
|
9
|
+
function errorMessage(err) {
|
|
10
|
+
return err instanceof Error ? err.message : String(err);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Report whether the host currently exposes a shell tool to the agent.
|
|
14
|
+
*
|
|
15
|
+
* `getActiveTools` may be missing on an older host or throw if the runner context
|
|
16
|
+
* is not bound, and neither is evidence that bash is absent — so both collapse
|
|
17
|
+
* to `undiscoverable` rather than a false negative.
|
|
18
|
+
*/
|
|
19
|
+
export function detectBashState(pi) {
|
|
20
|
+
try {
|
|
21
|
+
return pi.getActiveTools().includes("bash")
|
|
22
|
+
? { kind: "registered", toolName: "bash" }
|
|
23
|
+
: { kind: "absent" };
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return { kind: "undiscoverable", reason: errorMessage(err) };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
6
29
|
export default function (pi) {
|
|
7
30
|
pi.registerTool(dockerE2ETool);
|
|
8
31
|
pi.registerFlag("mcp-config", {
|
|
9
32
|
description: "Path to MCP server configuration JSON file",
|
|
10
33
|
type: "string",
|
|
11
34
|
});
|
|
12
|
-
|
|
35
|
+
pi.registerFlag("mcp-skills-extension", {
|
|
36
|
+
description: "Enable the DRAFT MCP skills extension (SEP-2640). Unratified; off by default.",
|
|
37
|
+
type: "boolean",
|
|
38
|
+
});
|
|
39
|
+
const hostElicitation = new McpiHostElicitation();
|
|
40
|
+
const hostApproval = new McpiHostApproval();
|
|
41
|
+
const mcpManager = new McpClientManager({ elicitation: hostElicitation });
|
|
42
|
+
const policy = new McpPolicy({ gateway: mcpManager, approvals: hostApproval });
|
|
13
43
|
const skillRegistry = new SkillRegistry();
|
|
44
|
+
const skillsClient = new SkillsExtensionClient({ policy });
|
|
14
45
|
const codeModeManager = new CodeModeManager();
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
//
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
async callTool(server, tool, args) {
|
|
22
|
-
const client = mcpManager.getClient(server);
|
|
23
|
-
if (!client)
|
|
24
|
-
throw new Error(`No client for server "${server}"`);
|
|
25
|
-
const result = await client.callTool({ name: tool, arguments: args });
|
|
26
|
-
return {
|
|
27
|
-
content: result.content,
|
|
28
|
-
isError: result.isError === true ? true : undefined,
|
|
29
|
-
structuredContent: result.structuredContent,
|
|
30
|
-
};
|
|
31
|
-
},
|
|
32
|
-
};
|
|
46
|
+
const { codeSearch, codeExecute } = codeModeManager.createTools();
|
|
47
|
+
// Bridge the shared policy boundary to the ToolProvider interface. tool-cli
|
|
48
|
+
// sees exactly the policy-visible discovered schema set, and every call it
|
|
49
|
+
// makes is re-authorized by the same dispatcher, so it cannot reach a hidden
|
|
50
|
+
// tool by naming it directly.
|
|
51
|
+
const toolProvider = createPolicyToolProvider(policy, { upstream: mcpManager });
|
|
33
52
|
const rpcServer = new ToolCliServer(toolProvider);
|
|
53
|
+
// Routing state the prompt reports on. tool-cli is only ever advertised after
|
|
54
|
+
// a compatible authenticated v1 handshake and confirmed bash availability.
|
|
55
|
+
let toolCliState = {
|
|
56
|
+
kind: "not_started",
|
|
57
|
+
reason: "the session has not finished starting",
|
|
58
|
+
};
|
|
59
|
+
let skillsExtensionEnabled = false;
|
|
60
|
+
let facilitiesPublished = false;
|
|
34
61
|
// Register the load_skill tool so the model can activate MCP skills
|
|
35
|
-
pi.registerTool(createLoadSkillTool({ registry: skillRegistry,
|
|
62
|
+
pi.registerTool(createLoadSkillTool({ registry: skillRegistry, policy, skillsClient }));
|
|
63
|
+
pi.registerTool(codeSearch);
|
|
64
|
+
pi.registerTool(codeExecute);
|
|
36
65
|
pi.on("session_start", async (_event, ctx) => {
|
|
66
|
+
hostElicitation.setContext(ctx);
|
|
67
|
+
hostApproval.setContext(ctx);
|
|
68
|
+
withholdToolCliCredentials(pi);
|
|
37
69
|
if (ctx.hasUI) {
|
|
38
70
|
ctx.ui.notify("mcpi-ext loaded", "info");
|
|
39
71
|
}
|
|
@@ -49,6 +81,15 @@ export default function (pi) {
|
|
|
49
81
|
try {
|
|
50
82
|
const config = await loadMcpConfig(configPath);
|
|
51
83
|
const serverCount = Object.keys(config.mcpServers).length;
|
|
84
|
+
// Draft extension support is opt-in from either the config file or the
|
|
85
|
+
// CLI flag, and must be decided before any connection is opened because
|
|
86
|
+
// capabilities are fixed at initialize.
|
|
87
|
+
const skillsExtensionEnabledNow = pi.getFlag("mcp-skills-extension") === true || isSkillsExtensionEnabled(config);
|
|
88
|
+
skillsExtensionEnabled = skillsExtensionEnabledNow;
|
|
89
|
+
mcpManager.enableSkillsExtension(skillsExtensionEnabledNow);
|
|
90
|
+
if (skillsExtensionEnabledNow) {
|
|
91
|
+
log(skillsExtensionDiagnostic());
|
|
92
|
+
}
|
|
52
93
|
if (serverCount > 0) {
|
|
53
94
|
await mcpManager.connectAll(config, log);
|
|
54
95
|
const tools = mcpManager.getTools();
|
|
@@ -56,49 +97,61 @@ export default function (pi) {
|
|
|
56
97
|
// Pre-register all MCP tools as deferred Pi tool proxies
|
|
57
98
|
// (in tools array for dispatch but excluded from system prompt)
|
|
58
99
|
const allToolNames = tools.map((t) => t.name);
|
|
59
|
-
registerMcpToolProxies(allToolNames, mcpManager, pi);
|
|
60
|
-
// Discover skills from all connected servers
|
|
100
|
+
registerMcpToolProxies(allToolNames, mcpManager, policy, pi);
|
|
101
|
+
// Discover skills from all connected servers. A server that declares
|
|
102
|
+
// the draft extension is served entirely by it: the legacy skill://
|
|
103
|
+
// resource scan is a compatibility fallback for servers that do not,
|
|
104
|
+
// never a second opinion on one that does. An empty extension listing
|
|
105
|
+
// therefore means "no skills right now", not "try the old way".
|
|
61
106
|
for (const serverName of mcpManager.getConnectedServers()) {
|
|
62
|
-
const
|
|
63
|
-
if (!client)
|
|
64
|
-
continue;
|
|
107
|
+
const viaExtension = skillsExtensionEnabled && skillsClient.supports(serverName);
|
|
65
108
|
try {
|
|
66
|
-
|
|
67
|
-
|
|
109
|
+
if (viaExtension) {
|
|
110
|
+
const result = await discoverSkillsViaExtension(policy, skillsClient, serverName, log);
|
|
111
|
+
skillRegistry.registerAll(result.skills);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
const skills = await discoverSkillsFromServer(policy, serverName, log);
|
|
115
|
+
skillRegistry.registerAll(skills);
|
|
116
|
+
}
|
|
68
117
|
}
|
|
69
118
|
catch (err) {
|
|
70
|
-
log(`[skills] Failed to discover skills from "${serverName}": ${err.message}`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (skillRegistry.size > 0) {
|
|
74
|
-
for (const skill of skillRegistry.getAll()) {
|
|
75
|
-
for (const t of skill.allowedTools) {
|
|
76
|
-
gatedToolNames.add(t);
|
|
77
|
-
}
|
|
119
|
+
log(`[skills] Failed to discover skills from "${serverName}" via ${viaExtension ? "the draft skills extension" : "skill:// resources"}: ${err.message}`);
|
|
78
120
|
}
|
|
79
|
-
log(`MCP: ${skillRegistry.size} skill(s) discovered, ${gatedToolNames.size} tool(s) deferred`);
|
|
80
121
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const { port, token } = await rpcServer.start(log);
|
|
84
|
-
pi.setEnv("TOOL_CLI_PORT", String(port));
|
|
85
|
-
pi.setEnv("TOOL_CLI_TOKEN", token);
|
|
122
|
+
for (const collision of skillRegistry.getCollisions()) {
|
|
123
|
+
log(`[skills] Name collision on "${collision.name}": "${collision.challenger.serverName}" exposed as "${collision.registeredAs}" (name held by "${collision.incumbent.serverName}")`);
|
|
86
124
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (codeModeManager.isActive) {
|
|
93
|
-
const { codeSearch, codeExecute } = codeModeManager.createTools();
|
|
94
|
-
pi.registerTool(codeSearch);
|
|
95
|
-
pi.registerTool(codeExecute);
|
|
96
|
-
log(`MCP: Code mode active (${codeModeManager.getEligibleTools().length} eligible tool(s))`);
|
|
125
|
+
// Hand the discovered skills to the policy so their tools are gated
|
|
126
|
+
// everywhere, not just on the mcpi dispatch path.
|
|
127
|
+
policy.registerSkills(skillRegistry.getAll());
|
|
128
|
+
if (skillRegistry.size > 0) {
|
|
129
|
+
log(`MCP: ${skillRegistry.size} skill(s) discovered, ${policy.getGatedToolNames().length} tool(s) deferred`);
|
|
97
130
|
}
|
|
131
|
+
toolCliState = await startToolCliBridge({
|
|
132
|
+
bash: detectBashState(pi),
|
|
133
|
+
server: rpcServer,
|
|
134
|
+
environment: pi,
|
|
135
|
+
log,
|
|
136
|
+
});
|
|
98
137
|
}
|
|
138
|
+
else {
|
|
139
|
+
toolCliState = {
|
|
140
|
+
kind: "not_started",
|
|
141
|
+
reason: "no MCP servers are configured, so there is nothing for it to expose",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
// Code execution remains available even when no MCP servers or callable tools exist.
|
|
145
|
+
codeModeManager.initialize(mcpManager, policy, log);
|
|
146
|
+
// Probe the optional native sandbox backend once, so routing can state
|
|
147
|
+
// plainly whether code mode can run rather than assuming it can.
|
|
148
|
+
await codeModeManager.probeSandbox();
|
|
99
149
|
}
|
|
100
150
|
catch (err) {
|
|
101
|
-
|
|
151
|
+
withholdToolCliCredentials(pi);
|
|
152
|
+
await rpcServer.stop();
|
|
153
|
+
const msg = `MCP config error: ${errorMessage(err)}`;
|
|
154
|
+
toolCliState = { kind: "not_started", reason: msg };
|
|
102
155
|
if (ctx.hasUI) {
|
|
103
156
|
ctx.ui.notify(msg, "warning");
|
|
104
157
|
}
|
|
@@ -107,42 +160,68 @@ export default function (pi) {
|
|
|
107
160
|
}
|
|
108
161
|
}
|
|
109
162
|
});
|
|
110
|
-
// Inject MCP skills, tool-cli
|
|
163
|
+
// Inject execution routing, MCP skills, tool-cli usage docs, and code mode
|
|
164
|
+
// type hints into the system prompt.
|
|
111
165
|
pi.on("before_agent_start", async (event) => {
|
|
112
166
|
let extra = "";
|
|
113
167
|
const skills = skillRegistry.getAll();
|
|
168
|
+
const bashState = detectBashState(pi);
|
|
169
|
+
// Idempotent after the first resolution; covers hosts that reach
|
|
170
|
+
// before_agent_start without a preceding successful session_start.
|
|
171
|
+
const sandbox = await codeModeManager.probeSandbox();
|
|
172
|
+
// The routing section is emitted on every load, including with zero MCP
|
|
173
|
+
// servers — an agent still needs to know that exact computation and the
|
|
174
|
+
// shell are on the table, and why the MCP-backed facilities are not.
|
|
175
|
+
const facilities = buildExecutionFacilities({
|
|
176
|
+
skills: {
|
|
177
|
+
count: skills.length,
|
|
178
|
+
draftExtensionEnabled: skillsExtensionEnabled,
|
|
179
|
+
},
|
|
180
|
+
codeMode: { active: codeModeManager.isActive, reason: sandbox.reason },
|
|
181
|
+
toolCli: toolCliState,
|
|
182
|
+
bash: bashState,
|
|
183
|
+
});
|
|
184
|
+
// Prefer the host's own facility registry when it grows one. The two paths
|
|
185
|
+
// are mutually exclusive, so the section can never be emitted twice.
|
|
186
|
+
if (!facilitiesPublished) {
|
|
187
|
+
facilitiesPublished = publishExecutionFacilities(pi, facilities);
|
|
188
|
+
}
|
|
189
|
+
if (!facilitiesPublished) {
|
|
190
|
+
extra += formatExecutionFacilities(facilities);
|
|
191
|
+
}
|
|
114
192
|
if (skills.length > 0) {
|
|
115
193
|
extra += formatMcpSkillsForPrompt(skills);
|
|
116
194
|
}
|
|
117
|
-
|
|
118
|
-
extra += formatToolCliForPrompt(serverCount);
|
|
195
|
+
extra += formatToolCliForPrompt({ toolCli: toolCliState, bash: bashState });
|
|
119
196
|
if (codeModeManager.isActive) {
|
|
197
|
+
codeModeManager.refresh();
|
|
120
198
|
extra += codeModeManager.formatSystemPromptSection();
|
|
121
199
|
}
|
|
122
200
|
if (extra.length === 0)
|
|
123
201
|
return;
|
|
124
202
|
return { systemPrompt: event.systemPrompt + extra };
|
|
125
203
|
});
|
|
126
|
-
// Block deferred MCP tools until their skill is loaded
|
|
204
|
+
// Block deferred MCP tools until their skill is loaded. The policy owns the
|
|
205
|
+
// decision so mcpi dispatch and the RPC path cannot disagree.
|
|
127
206
|
pi.on("tool_call", async (event) => {
|
|
128
207
|
const name = "toolName" in event ? event.toolName : undefined;
|
|
129
|
-
if (!name || !
|
|
208
|
+
if (!name || !policy.isGated(name))
|
|
130
209
|
return;
|
|
131
|
-
const relevantSkills =
|
|
132
|
-
.getAll()
|
|
133
|
-
.filter((s) => s.allowedTools.includes(name))
|
|
134
|
-
.map((s) => s.name);
|
|
210
|
+
const relevantSkills = policy.getGatingSkills(name);
|
|
135
211
|
return {
|
|
136
212
|
block: true,
|
|
137
213
|
reason: `Tool "${name}" requires loading a skill first. Call load_skill with one of: ${relevantSkills.join(", ")}`,
|
|
138
214
|
};
|
|
139
215
|
});
|
|
140
216
|
pi.on("session_shutdown", async () => {
|
|
141
|
-
|
|
142
|
-
|
|
217
|
+
hostElicitation.setContext(undefined);
|
|
218
|
+
hostApproval.setContext(undefined);
|
|
219
|
+
withholdToolCliCredentials(pi);
|
|
220
|
+
toolCliState = { kind: "not_started", reason: "the session has shut down" };
|
|
221
|
+
facilitiesPublished = false;
|
|
143
222
|
await rpcServer.stop();
|
|
144
223
|
await mcpManager.disconnectAll();
|
|
145
224
|
skillRegistry.clear();
|
|
225
|
+
policy.reset();
|
|
146
226
|
});
|
|
147
227
|
}
|
|
148
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AgentToolResult } from "@sammorrowdrums/mcpi";
|
|
2
|
+
import type { CallToolResult } from "@modelcontextprotocol/client";
|
|
3
|
+
import type { CallToolResult as ToolCliCallToolResult } from "@sammorrowdrums/tool-cli";
|
|
4
|
+
export interface TerminalCallToolResult {
|
|
5
|
+
readonly kind: "terminal";
|
|
6
|
+
readonly result: CallToolResult;
|
|
7
|
+
}
|
|
8
|
+
export declare function adaptTerminalCallToolResult(result: CallToolResult): TerminalCallToolResult;
|
|
9
|
+
export declare function renderTerminalCallToolResult(terminal: TerminalCallToolResult): AgentToolResult<CallToolResult>;
|
|
10
|
+
export declare function toToolCliCallToolResult(terminal: TerminalCallToolResult): ToolCliCallToolResult;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export function adaptTerminalCallToolResult(result) {
|
|
2
|
+
return { kind: "terminal", result };
|
|
3
|
+
}
|
|
4
|
+
export function renderTerminalCallToolResult(terminal) {
|
|
5
|
+
const content = [];
|
|
6
|
+
if (terminal.result.isError) {
|
|
7
|
+
content.push({ type: "text", text: "MCP tool reported an error." });
|
|
8
|
+
}
|
|
9
|
+
for (const block of terminal.result.content) {
|
|
10
|
+
switch (block.type) {
|
|
11
|
+
case "text":
|
|
12
|
+
content.push({ type: "text", text: block.text });
|
|
13
|
+
break;
|
|
14
|
+
case "image":
|
|
15
|
+
content.push({ type: "image", data: block.data, mimeType: block.mimeType });
|
|
16
|
+
break;
|
|
17
|
+
case "audio":
|
|
18
|
+
content.push({
|
|
19
|
+
type: "text",
|
|
20
|
+
text: `[MCP audio: ${block.mimeType}; base64 payload preserved in result details]`,
|
|
21
|
+
});
|
|
22
|
+
break;
|
|
23
|
+
case "resource_link":
|
|
24
|
+
content.push({
|
|
25
|
+
type: "text",
|
|
26
|
+
text: formatResourceLink(block),
|
|
27
|
+
});
|
|
28
|
+
break;
|
|
29
|
+
case "resource":
|
|
30
|
+
content.push({
|
|
31
|
+
type: "text",
|
|
32
|
+
text: formatEmbeddedResource(block),
|
|
33
|
+
});
|
|
34
|
+
break;
|
|
35
|
+
default:
|
|
36
|
+
content.push({ type: "text", text: JSON.stringify(block, null, 2) });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (terminal.result.structuredContent !== undefined) {
|
|
40
|
+
content.push({
|
|
41
|
+
type: "text",
|
|
42
|
+
text: `Structured content:\n${JSON.stringify(terminal.result.structuredContent, null, 2)}`,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (content.length === 0) {
|
|
46
|
+
content.push({ type: "text", text: terminal.result.isError ? "MCP tool failed." : "" });
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
content,
|
|
50
|
+
details: terminal.result,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function toToolCliCallToolResult(terminal) {
|
|
54
|
+
return {
|
|
55
|
+
...terminal.result,
|
|
56
|
+
content: [...terminal.result.content],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function formatResourceLink(block) {
|
|
60
|
+
const label = block.title ?? block.name ?? block.uri;
|
|
61
|
+
const details = [block.mimeType, block.description].filter(Boolean).join("; ");
|
|
62
|
+
return `[MCP resource link: ${label}] ${block.uri}${details ? ` (${details})` : ""}`;
|
|
63
|
+
}
|
|
64
|
+
function formatEmbeddedResource(block) {
|
|
65
|
+
const resource = block.resource;
|
|
66
|
+
if ("text" in resource) {
|
|
67
|
+
return `[MCP embedded resource: ${resource.uri}]\n${resource.text}`;
|
|
68
|
+
}
|
|
69
|
+
return `[MCP embedded resource: ${resource.uri}; ${resource.mimeType ?? "binary"} blob preserved in result details]`;
|
|
70
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Client, type DiscoverResult, type ElicitRequestParams, type ElicitResult, type Implementation, type ListChangedHandlers, type ProtocolEra, type ServerCapabilities } from "@modelcontextprotocol/client";
|
|
2
|
+
export declare const MCP_CLIENT_IDENTITY: {
|
|
3
|
+
readonly name: "@sammorrowdrums/mcpi-ext";
|
|
4
|
+
readonly version: "1.0.1";
|
|
5
|
+
};
|
|
6
|
+
export declare const MCP_CLIENT_POLICY: {
|
|
7
|
+
readonly listMaxPages: 64;
|
|
8
|
+
readonly maxInputRequiredRounds: 4;
|
|
9
|
+
readonly probeTimeoutMs: 5000;
|
|
10
|
+
readonly requestTimeoutMs: 60000;
|
|
11
|
+
readonly maxTotalTimeoutMs: number;
|
|
12
|
+
};
|
|
13
|
+
export declare const MCP_SUPPORTED_PROTOCOL_VERSIONS: readonly ["2026-07-28", ...string[]];
|
|
14
|
+
export interface McpElicitationHandler {
|
|
15
|
+
elicit(params: ElicitRequestParams): Promise<ElicitResult>;
|
|
16
|
+
}
|
|
17
|
+
export interface McpClientDiagnostics {
|
|
18
|
+
protocolVersion: string | undefined;
|
|
19
|
+
protocolEra: ProtocolEra | undefined;
|
|
20
|
+
discoverResult: DiscoverResult | undefined;
|
|
21
|
+
serverImplementation: Implementation | undefined;
|
|
22
|
+
serverCapabilities: ServerCapabilities | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* What this client requested and what the server declared for the draft
|
|
25
|
+
* skills extension, so a draft feature is never silently in play.
|
|
26
|
+
*/
|
|
27
|
+
skillsExtension: {
|
|
28
|
+
/** Whether this client advertised the extension at initialize. */
|
|
29
|
+
requested: boolean;
|
|
30
|
+
/** Pinned draft revision this implementation targets. */
|
|
31
|
+
revision: string;
|
|
32
|
+
/** Always `"draft"` — this is an unratified proposal. */
|
|
33
|
+
status: string;
|
|
34
|
+
/** Whether the server declared the extension back. */
|
|
35
|
+
serverDeclared: boolean;
|
|
36
|
+
/** Settings the server declared, or `undefined` if it declared none. */
|
|
37
|
+
serverCapability: Record<string, unknown> | undefined;
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export interface CreateMcpClientOptions {
|
|
41
|
+
elicitation: McpElicitationHandler;
|
|
42
|
+
listChanged?: ListChangedHandlers;
|
|
43
|
+
/**
|
|
44
|
+
* Advertise the draft skills extension at initialize.
|
|
45
|
+
*
|
|
46
|
+
* Off by default. When false this client never mentions SEP-2640 on the
|
|
47
|
+
* wire, which is what makes the fallback path a genuine absence of the
|
|
48
|
+
* extension rather than a suppressed one.
|
|
49
|
+
*/
|
|
50
|
+
skillsExtension?: boolean;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Creates every MCP client with the same identity, negotiated protocol policy,
|
|
54
|
+
* capabilities, pagination limit, and SDK-managed input_required handling.
|
|
55
|
+
*/
|
|
56
|
+
export declare function createMcpClient(options: CreateMcpClientOptions): Client;
|
|
57
|
+
export declare function getMcpClientDiagnostics(client: Client, options?: {
|
|
58
|
+
skillsExtensionRequested?: boolean;
|
|
59
|
+
}): McpClientDiagnostics;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Client, SUPPORTED_PROTOCOL_VERSIONS, } from "@modelcontextprotocol/client";
|
|
2
|
+
import { SKILLS_EXTENSION_NAME, SKILLS_EXTENSION_REVISION, SKILLS_EXTENSION_STATUS, } from "../skills/sep2640/spec.js";
|
|
3
|
+
export const MCP_CLIENT_IDENTITY = {
|
|
4
|
+
name: "@sammorrowdrums/mcpi-ext",
|
|
5
|
+
version: "1.0.1",
|
|
6
|
+
};
|
|
7
|
+
export const MCP_CLIENT_POLICY = {
|
|
8
|
+
listMaxPages: 64,
|
|
9
|
+
maxInputRequiredRounds: 4,
|
|
10
|
+
probeTimeoutMs: 5_000,
|
|
11
|
+
requestTimeoutMs: 60_000,
|
|
12
|
+
maxTotalTimeoutMs: 10 * 60_000,
|
|
13
|
+
};
|
|
14
|
+
export const MCP_SUPPORTED_PROTOCOL_VERSIONS = [
|
|
15
|
+
"2026-07-28",
|
|
16
|
+
...SUPPORTED_PROTOCOL_VERSIONS,
|
|
17
|
+
];
|
|
18
|
+
/**
|
|
19
|
+
* Creates every MCP client with the same identity, negotiated protocol policy,
|
|
20
|
+
* capabilities, pagination limit, and SDK-managed input_required handling.
|
|
21
|
+
*/
|
|
22
|
+
export function createMcpClient(options) {
|
|
23
|
+
const client = new Client(MCP_CLIENT_IDENTITY, {
|
|
24
|
+
supportedProtocolVersions: [...MCP_SUPPORTED_PROTOCOL_VERSIONS],
|
|
25
|
+
capabilities: {
|
|
26
|
+
elicitation: { form: {} },
|
|
27
|
+
// Per SEP-2133 the extension is offered here, at initialize, and the
|
|
28
|
+
// server answers with its own settings. Every later skills request
|
|
29
|
+
// re-reads that answer instead of trusting a cached yes.
|
|
30
|
+
extensions: options.skillsExtension ? { [SKILLS_EXTENSION_NAME]: {} } : {},
|
|
31
|
+
},
|
|
32
|
+
versionNegotiation: {
|
|
33
|
+
mode: "auto",
|
|
34
|
+
probe: {
|
|
35
|
+
timeoutMs: MCP_CLIENT_POLICY.probeTimeoutMs,
|
|
36
|
+
maxRetries: 0,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
inputRequired: {
|
|
40
|
+
autoFulfill: true,
|
|
41
|
+
maxRounds: MCP_CLIENT_POLICY.maxInputRequiredRounds,
|
|
42
|
+
},
|
|
43
|
+
listChanged: options.listChanged,
|
|
44
|
+
listMaxPages: MCP_CLIENT_POLICY.listMaxPages,
|
|
45
|
+
defaultCacheTtlMs: 0,
|
|
46
|
+
});
|
|
47
|
+
client.setRequestHandler("elicitation/create", (request) => options.elicitation.elicit(request.params));
|
|
48
|
+
return client;
|
|
49
|
+
}
|
|
50
|
+
export function getMcpClientDiagnostics(client, options = {}) {
|
|
51
|
+
// Diagnostics run inside `connectOne`, so this must never be able to fail a
|
|
52
|
+
// connection. `getServerCapabilities` is absent on minimal client doubles and
|
|
53
|
+
// returns undefined before initialize completes; either way an unavailable
|
|
54
|
+
// capability is reported as "not declared", never thrown.
|
|
55
|
+
const serverCapabilities = typeof client.getServerCapabilities === "function" ? client.getServerCapabilities() : undefined;
|
|
56
|
+
const declared = serverCapabilities?.extensions?.[SKILLS_EXTENSION_NAME];
|
|
57
|
+
const capability = declared !== undefined && declared !== null && typeof declared === "object"
|
|
58
|
+
? declared
|
|
59
|
+
: undefined;
|
|
60
|
+
return {
|
|
61
|
+
protocolVersion: typeof client.getNegotiatedProtocolVersion === "function"
|
|
62
|
+
? client.getNegotiatedProtocolVersion()
|
|
63
|
+
: undefined,
|
|
64
|
+
protocolEra: client.getProtocolEra(),
|
|
65
|
+
discoverResult: client.getDiscoverResult(),
|
|
66
|
+
serverImplementation: typeof client.getServerVersion === "function" ? client.getServerVersion() : undefined,
|
|
67
|
+
serverCapabilities,
|
|
68
|
+
skillsExtension: {
|
|
69
|
+
requested: options.skillsExtensionRequested === true,
|
|
70
|
+
revision: SKILLS_EXTENSION_REVISION,
|
|
71
|
+
status: SKILLS_EXTENSION_STATUS,
|
|
72
|
+
serverDeclared: capability !== undefined,
|
|
73
|
+
serverCapability: capability,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
@@ -1,28 +1,48 @@
|
|
|
1
|
-
import { Client } from "@modelcontextprotocol/
|
|
1
|
+
import { Client, type ReadResourceResult, type Resource, type ResourceTemplateType, type Tool, type Transport } from "@modelcontextprotocol/client";
|
|
2
|
+
import { type DirectoryReadResult, type SkillsGetResult, type SkillsListResult } from "../skills/sep2640/protocol.js";
|
|
3
|
+
import { type TerminalCallToolResult } from "./call-tool-result.js";
|
|
4
|
+
import { type CreateMcpClientOptions, type McpClientDiagnostics, type McpElicitationHandler } from "./client-factory.js";
|
|
2
5
|
import type { McpConfig, ServerConfig } from "./config.js";
|
|
3
|
-
/**
|
|
4
|
-
export
|
|
5
|
-
/** Tool name as reported by the server. */
|
|
6
|
-
name: string;
|
|
7
|
-
description?: string;
|
|
8
|
-
/** JSON Schema for the tool's parameters. */
|
|
9
|
-
inputSchema: Record<string, unknown>;
|
|
10
|
-
/** JSON Schema for structured output, if declared. */
|
|
11
|
-
outputSchema?: Record<string, unknown>;
|
|
12
|
-
/** Tool annotations (readOnlyHint, destructiveHint, etc.). */
|
|
13
|
-
annotations?: Record<string, unknown>;
|
|
6
|
+
/** A lossless MCP tool as discovered from a server, tagged with its origin. */
|
|
7
|
+
export type McpTool = Tool & {
|
|
14
8
|
/** Which configured server this tool came from. */
|
|
15
9
|
serverName: string;
|
|
10
|
+
};
|
|
11
|
+
export interface McpClientManagerOptions {
|
|
12
|
+
elicitation?: McpElicitationHandler;
|
|
13
|
+
clientFactory?: (options: CreateMcpClientOptions) => Client;
|
|
14
|
+
transportFactory?: (config: ServerConfig) => Transport;
|
|
15
|
+
/**
|
|
16
|
+
* Advertise the draft skills extension (SEP-2640) at initialize.
|
|
17
|
+
*
|
|
18
|
+
* Off by default; the host turns it on from config. When off, no connection
|
|
19
|
+
* this manager opens mentions the extension at all.
|
|
20
|
+
*/
|
|
21
|
+
skillsExtension?: boolean;
|
|
16
22
|
}
|
|
17
23
|
/**
|
|
18
24
|
* Manages connections to multiple MCP servers and aggregates their tools.
|
|
19
25
|
*
|
|
20
|
-
* Tools are discovered and stored internally. They are NOT registered with
|
|
21
|
-
* automatically — that responsibility belongs to the access tiers
|
|
22
|
-
* Football, Code Mode).
|
|
26
|
+
* Tools are discovered and stored internally. They are NOT registered with mcpi
|
|
27
|
+
* automatically — that responsibility belongs to the access tiers.
|
|
23
28
|
*/
|
|
24
29
|
export declare class McpClientManager {
|
|
25
|
-
private connections;
|
|
30
|
+
private readonly connections;
|
|
31
|
+
private readonly elicitation;
|
|
32
|
+
private readonly clientFactory;
|
|
33
|
+
private readonly transportFactory;
|
|
34
|
+
private skillsExtensionRequested;
|
|
35
|
+
constructor(options?: McpClientManagerOptions);
|
|
36
|
+
/** Whether this manager advertises the draft skills extension. */
|
|
37
|
+
requestsSkillsExtension(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Turn the draft skills extension on or off for future connections.
|
|
40
|
+
*
|
|
41
|
+
* The host reads the gate from config, which loads after this manager is
|
|
42
|
+
* constructed. Already-open connections keep whatever they negotiated at
|
|
43
|
+
* initialize, because the capability set is fixed for a session.
|
|
44
|
+
*/
|
|
45
|
+
enableSkillsExtension(enabled: boolean): void;
|
|
26
46
|
/**
|
|
27
47
|
* Connect to all servers defined in the config.
|
|
28
48
|
* Connections that fail are logged and skipped — partial success is fine.
|
|
@@ -30,6 +50,26 @@ export declare class McpClientManager {
|
|
|
30
50
|
connectAll(config: McpConfig, log?: (msg: string) => void): Promise<void>;
|
|
31
51
|
/** Connect to a single server by name. */
|
|
32
52
|
connectOne(name: string, serverConfig: ServerConfig, log?: (msg: string) => void): Promise<void>;
|
|
53
|
+
/** Call a tool and return only its terminal protocol result. */
|
|
54
|
+
callTool(serverName: string, toolName: string, args: Record<string, unknown>, signal?: AbortSignal): Promise<TerminalCallToolResult>;
|
|
55
|
+
/** List a server's resources. Discovery only; authorization lives in McpPolicy. */
|
|
56
|
+
listResources(serverName: string, signal?: AbortSignal): Promise<Resource[]>;
|
|
57
|
+
/** List a server's resource templates. Authorization lives in McpPolicy. */
|
|
58
|
+
listResourceTemplates(serverName: string, signal?: AbortSignal): Promise<ResourceTemplateType[]>;
|
|
59
|
+
/** Read a single resource. Authorization is the caller-side policy's responsibility. */
|
|
60
|
+
readResource(serverName: string, uri: string, signal?: AbortSignal): Promise<ReadResourceResult>;
|
|
61
|
+
/**
|
|
62
|
+
* The settings a server declared for one extension during `initialize`, or
|
|
63
|
+
* `undefined` if it declared no such extension.
|
|
64
|
+
*/
|
|
65
|
+
getExtensionCapability(serverName: string, extensionName: string): Record<string, unknown> | undefined;
|
|
66
|
+
/** Draft SEP-2640 `skills/list`. */
|
|
67
|
+
requestSkillsList(serverName: string, cursor?: string, signal?: AbortSignal): Promise<SkillsListResult>;
|
|
68
|
+
/** Draft SEP-2640 `skills/get`. */
|
|
69
|
+
requestSkillsGet(serverName: string, uri: string, signal?: AbortSignal): Promise<SkillsGetResult>;
|
|
70
|
+
/** Draft SEP-2640 `resources/directory/read`. */
|
|
71
|
+
requestDirectoryRead(serverName: string, uri: string, cursor?: string, signal?: AbortSignal): Promise<DirectoryReadResult>;
|
|
72
|
+
private requestExtension;
|
|
33
73
|
/** Disconnect a single server. */
|
|
34
74
|
disconnectOne(name: string): Promise<void>;
|
|
35
75
|
/** Disconnect all servers. */
|
|
@@ -38,9 +78,11 @@ export declare class McpClientManager {
|
|
|
38
78
|
getTools(): McpTool[];
|
|
39
79
|
/** Get tools from a specific server. */
|
|
40
80
|
getToolsForServer(name: string): McpTool[];
|
|
41
|
-
/** Get the MCP Client for
|
|
81
|
+
/** Get the MCP Client for discovery APIs that are not tool calls. */
|
|
42
82
|
getClient(name: string): Client | undefined;
|
|
43
|
-
|
|
83
|
+
getDiagnostics(name: string): McpClientDiagnostics | undefined;
|
|
84
|
+
/** List connected server names in deterministic order. */
|
|
44
85
|
getConnectedServers(): string[];
|
|
45
86
|
}
|
|
46
|
-
|
|
87
|
+
/** Build the SDK's safe child environment without leaking this or a parent bridge endpoint. */
|
|
88
|
+
export declare function buildStdioEnvironment(configured: Readonly<Record<string, string>> | undefined): Record<string, string>;
|