@sammorrowdrums/mcpi-ext 0.2.0 → 0.2.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/dist/code-mode/code-mode-integration.test.d.ts +2 -0
- package/dist/code-mode/code-mode-integration.test.d.ts.map +1 -0
- package/dist/code-mode/code-mode-integration.test.js +116 -0
- package/dist/code-mode/code-mode-integration.test.js.map +1 -0
- package/dist/code-mode/eligibility.d.ts +10 -0
- package/dist/code-mode/eligibility.d.ts.map +1 -0
- package/dist/code-mode/eligibility.js +14 -0
- package/dist/code-mode/eligibility.js.map +1 -0
- package/dist/code-mode/eligibility.test.d.ts +2 -0
- package/dist/code-mode/eligibility.test.d.ts.map +1 -0
- package/dist/code-mode/eligibility.test.js +53 -0
- package/dist/code-mode/eligibility.test.js.map +1 -0
- package/dist/code-mode/executor.d.ts +35 -0
- package/dist/code-mode/executor.d.ts.map +1 -0
- package/dist/code-mode/executor.js +120 -0
- package/dist/code-mode/executor.js.map +1 -0
- package/dist/code-mode/executor.test.d.ts +2 -0
- package/dist/code-mode/executor.test.d.ts.map +1 -0
- package/dist/code-mode/executor.test.js +130 -0
- package/dist/code-mode/executor.test.js.map +1 -0
- package/dist/code-mode/index.d.ts +65 -0
- package/dist/code-mode/index.d.ts.map +1 -0
- package/dist/code-mode/index.js +175 -0
- package/dist/code-mode/index.js.map +1 -0
- package/dist/code-mode/tools.d.ts +45 -0
- package/dist/code-mode/tools.d.ts.map +1 -0
- package/dist/code-mode/tools.js +70 -0
- package/dist/code-mode/tools.js.map +1 -0
- package/dist/code-mode/type-hints.d.ts +20 -0
- package/dist/code-mode/type-hints.d.ts.map +1 -0
- package/dist/code-mode/type-hints.js +189 -0
- package/dist/code-mode/type-hints.js.map +1 -0
- package/dist/code-mode/type-hints.test.d.ts +2 -0
- package/dist/code-mode/type-hints.test.d.ts.map +1 -0
- package/dist/code-mode/type-hints.test.js +157 -0
- package/dist/code-mode/type-hints.test.js.map +1 -0
- package/dist/docker-e2e.d.ts +55 -0
- package/dist/docker-e2e.d.ts.map +1 -0
- package/dist/docker-e2e.js +235 -0
- package/dist/docker-e2e.js.map +1 -0
- package/dist/docker-e2e.test.d.ts +2 -0
- package/dist/docker-e2e.test.d.ts.map +1 -0
- package/dist/docker-e2e.test.js +176 -0
- package/dist/docker-e2e.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +148 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +8 -0
- package/dist/index.test.js.map +1 -0
- package/dist/mcp/client-manager.d.ts +46 -0
- package/dist/mcp/client-manager.d.ts.map +1 -0
- package/dist/mcp/client-manager.js +148 -0
- package/dist/mcp/client-manager.js.map +1 -0
- package/dist/mcp/client-manager.test.d.ts +2 -0
- package/dist/mcp/client-manager.test.d.ts.map +1 -0
- package/dist/mcp/client-manager.test.js +204 -0
- package/dist/mcp/client-manager.test.js.map +1 -0
- package/dist/mcp/config-loader.d.ts +8 -0
- package/dist/mcp/config-loader.d.ts.map +1 -0
- package/dist/mcp/config-loader.js +43 -0
- package/dist/mcp/config-loader.js.map +1 -0
- package/dist/mcp/config-loader.test.d.ts +2 -0
- package/dist/mcp/config-loader.test.d.ts.map +1 -0
- package/dist/mcp/config-loader.test.js +93 -0
- package/dist/mcp/config-loader.test.js.map +1 -0
- package/dist/mcp/config.d.ts +46 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +28 -0
- package/dist/mcp/config.js.map +1 -0
- package/{src/mcp/index.ts → dist/mcp/index.d.ts} +1 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +4 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/skills/discover.d.ts +10 -0
- package/dist/skills/discover.d.ts.map +1 -0
- package/dist/skills/discover.js +60 -0
- package/dist/skills/discover.js.map +1 -0
- package/dist/skills/format.d.ts +10 -0
- package/dist/skills/format.d.ts.map +1 -0
- package/dist/skills/format.js +38 -0
- package/dist/skills/format.js.map +1 -0
- package/dist/skills/format.test.d.ts +2 -0
- package/dist/skills/format.test.d.ts.map +1 -0
- package/dist/skills/format.test.js +63 -0
- package/dist/skills/format.test.js.map +1 -0
- package/{src/skills/index.ts → dist/skills/index.d.ts} +2 -5
- package/dist/skills/index.d.ts.map +1 -0
- package/dist/skills/index.js +6 -0
- package/dist/skills/index.js.map +1 -0
- package/dist/skills/load-skill-tool.d.ts +40 -0
- package/dist/skills/load-skill-tool.d.ts.map +1 -0
- package/dist/skills/load-skill-tool.js +104 -0
- package/dist/skills/load-skill-tool.js.map +1 -0
- package/dist/skills/mcp-tool-proxy.d.ts +11 -0
- package/dist/skills/mcp-tool-proxy.d.ts.map +1 -0
- package/dist/skills/mcp-tool-proxy.js +116 -0
- package/dist/skills/mcp-tool-proxy.js.map +1 -0
- package/dist/skills/skill-integration.test.d.ts +2 -0
- package/dist/skills/skill-integration.test.d.ts.map +1 -0
- package/dist/skills/skill-integration.test.js +119 -0
- package/dist/skills/skill-integration.test.js.map +1 -0
- package/dist/skills/skill-registry.d.ts +39 -0
- package/dist/skills/skill-registry.d.ts.map +1 -0
- package/dist/skills/skill-registry.js +48 -0
- package/dist/skills/skill-registry.js.map +1 -0
- package/dist/skills/skill-registry.test.d.ts +2 -0
- package/dist/skills/skill-registry.test.d.ts.map +1 -0
- package/dist/skills/skill-registry.test.js +67 -0
- package/dist/skills/skill-registry.test.js.map +1 -0
- package/dist/test-servers/weather-server.d.ts +12 -0
- package/dist/test-servers/weather-server.d.ts.map +1 -0
- package/dist/test-servers/weather-server.js +139 -0
- package/dist/test-servers/weather-server.js.map +1 -0
- package/dist/test-servers/weather-stdio.d.ts +2 -0
- package/dist/test-servers/weather-stdio.d.ts.map +1 -0
- package/{src/test-servers/weather-stdio.ts → dist/test-servers/weather-stdio.js} +1 -1
- package/dist/test-servers/weather-stdio.js.map +1 -0
- package/dist/tool-cli/cli.d.ts +3 -0
- package/dist/tool-cli/cli.d.ts.map +1 -0
- package/dist/tool-cli/cli.js +179 -0
- package/dist/tool-cli/cli.js.map +1 -0
- package/dist/tool-cli/constants.d.ts +7 -0
- package/dist/tool-cli/constants.d.ts.map +1 -0
- package/dist/tool-cli/constants.js +15 -0
- package/dist/tool-cli/constants.js.map +1 -0
- package/dist/tool-cli/format.d.ts +8 -0
- package/dist/tool-cli/format.d.ts.map +1 -0
- package/{src/tool-cli/format.ts → dist/tool-cli/format.js} +5 -4
- package/dist/tool-cli/format.js.map +1 -0
- package/dist/tool-cli/format.test.d.ts +2 -0
- package/dist/tool-cli/format.test.d.ts.map +1 -0
- package/dist/tool-cli/format.test.js +30 -0
- package/dist/tool-cli/format.test.js.map +1 -0
- package/{src/tool-cli/index.ts → dist/tool-cli/index.d.ts} +1 -0
- package/dist/tool-cli/index.d.ts.map +1 -0
- package/dist/tool-cli/index.js +3 -0
- package/dist/tool-cli/index.js.map +1 -0
- package/dist/tool-cli/rpc-client.d.ts +6 -0
- package/dist/tool-cli/rpc-client.d.ts.map +1 -0
- package/dist/tool-cli/rpc-client.js +25 -0
- package/dist/tool-cli/rpc-client.js.map +1 -0
- package/dist/tool-cli/rpc-server.d.ts +48 -0
- package/dist/tool-cli/rpc-server.d.ts.map +1 -0
- package/dist/tool-cli/rpc-server.js +220 -0
- package/dist/tool-cli/rpc-server.js.map +1 -0
- package/dist/tool-cli/rpc-server.test.d.ts +2 -0
- package/dist/tool-cli/rpc-server.test.d.ts.map +1 -0
- package/dist/tool-cli/rpc-server.test.js +154 -0
- package/dist/tool-cli/rpc-server.test.js.map +1 -0
- package/package.json +5 -1
- package/.dockerignore +0 -6
- package/.github/dependabot.yml +0 -25
- package/.github/workflows/ci.yml +0 -50
- package/.hadolint.yaml +0 -6
- package/.prettierignore +0 -4
- package/.prettierrc +0 -7
- package/AGENTS.md +0 -118
- package/DECISIONS.md +0 -99
- package/Dockerfile +0 -39
- package/aube-lock.yaml +0 -4146
- package/docs/code-mode.md +0 -38
- package/docs/skills.md +0 -57
- package/docs/tool-cli.md +0 -54
- package/eslint.config.js +0 -27
- package/images/banner.webp +0 -0
- package/images/code-c-maude.webp +0 -0
- package/images/code-mode-histogram.png +0 -0
- package/images/nuclear-mcp-football.webp +0 -0
- package/images/skills-enabling-mcp-tools.png +0 -0
- package/images/the-skill-dealer.webp +0 -0
- package/images/tool-cli-grep.png +0 -0
- package/mise.toml +0 -53
- package/scripts/docker-entrypoint.sh +0 -29
- package/scripts/test-echo-server.mjs +0 -28
- package/scripts/test-mcp-integration.mjs +0 -59
- package/scripts/test-tool-cli-smoke.mjs +0 -93
- package/src/code-mode/code-mode-integration.test.ts +0 -135
- package/src/code-mode/eligibility.test.ts +0 -60
- package/src/code-mode/eligibility.ts +0 -16
- package/src/code-mode/executor.test.ts +0 -168
- package/src/code-mode/executor.ts +0 -163
- package/src/code-mode/index.ts +0 -208
- package/src/code-mode/tools.ts +0 -110
- package/src/code-mode/type-hints.test.ts +0 -187
- package/src/code-mode/type-hints.ts +0 -249
- package/src/docker-e2e.test.ts +0 -195
- package/src/docker-e2e.ts +0 -277
- package/src/index.test.ts +0 -8
- package/src/index.ts +0 -179
- package/src/mcp/client-manager.test.ts +0 -250
- package/src/mcp/client-manager.ts +0 -199
- package/src/mcp/config-loader.test.ts +0 -115
- package/src/mcp/config-loader.ts +0 -46
- package/src/mcp/config.ts +0 -40
- package/src/skills/discover.ts +0 -81
- package/src/skills/format.test.ts +0 -70
- package/src/skills/format.ts +0 -42
- package/src/skills/load-skill-tool.ts +0 -136
- package/src/skills/mcp-tool-proxy.ts +0 -143
- package/src/skills/skill-integration.test.ts +0 -139
- package/src/skills/skill-registry.test.ts +0 -83
- package/src/skills/skill-registry.ts +0 -69
- package/src/test-servers/weather-server.ts +0 -171
- package/src/tool-cli/format.test.ts +0 -33
- package/tsconfig.json +0 -21
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import { resolvePort } from "./constants.js";
|
|
3
|
+
/**
|
|
4
|
+
* JSON-RPC 2.0 server that exposes MCP tool discovery and execution.
|
|
5
|
+
*
|
|
6
|
+
* Methods:
|
|
7
|
+
* - listServers → server names + tool counts + random examples
|
|
8
|
+
* - listTools(server) → tool names + descriptions
|
|
9
|
+
* - describeTool(server, tool) → full schema
|
|
10
|
+
* - callTool(server, tool, arguments) → execute and return result
|
|
11
|
+
*/
|
|
12
|
+
export class ToolCliRpcServer {
|
|
13
|
+
mcpManager;
|
|
14
|
+
server = null;
|
|
15
|
+
port;
|
|
16
|
+
constructor(mcpManager, port) {
|
|
17
|
+
this.mcpManager = mcpManager;
|
|
18
|
+
this.port = port ?? resolvePort();
|
|
19
|
+
}
|
|
20
|
+
/** Start the HTTP server. Resolves once listening. */
|
|
21
|
+
async start(log) {
|
|
22
|
+
if (this.server)
|
|
23
|
+
return;
|
|
24
|
+
const MAX_BODY_SIZE = 1024 * 1024; // 1 MB
|
|
25
|
+
const server = http.createServer((req, res) => {
|
|
26
|
+
if (req.method !== "POST") {
|
|
27
|
+
res.writeHead(405, { "Content-Type": "application/json" });
|
|
28
|
+
res.end(JSON.stringify(rpcError(null, -32600, "Only POST accepted")));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
let body = "";
|
|
32
|
+
let aborted = false;
|
|
33
|
+
req.on("error", () => {
|
|
34
|
+
aborted = true;
|
|
35
|
+
});
|
|
36
|
+
req.on("data", (chunk) => {
|
|
37
|
+
if (aborted)
|
|
38
|
+
return;
|
|
39
|
+
body += chunk.toString();
|
|
40
|
+
if (body.length > MAX_BODY_SIZE) {
|
|
41
|
+
aborted = true;
|
|
42
|
+
res.writeHead(413, { "Content-Type": "application/json" });
|
|
43
|
+
res.end(JSON.stringify(rpcError(null, -32600, "Request too large")));
|
|
44
|
+
req.destroy();
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
req.on("end", () => {
|
|
48
|
+
if (aborted)
|
|
49
|
+
return;
|
|
50
|
+
void this.handleBody(body)
|
|
51
|
+
.then((response) => {
|
|
52
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
53
|
+
res.end(JSON.stringify(response));
|
|
54
|
+
})
|
|
55
|
+
.catch(() => {
|
|
56
|
+
try {
|
|
57
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
58
|
+
res.end(JSON.stringify(rpcError(null, -32603, "Internal error")));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
res.end();
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
return new Promise((resolve, reject) => {
|
|
67
|
+
server.once("error", (err) => {
|
|
68
|
+
this.server = null;
|
|
69
|
+
reject(err);
|
|
70
|
+
});
|
|
71
|
+
server.listen(this.port, "127.0.0.1", () => {
|
|
72
|
+
this.server = server;
|
|
73
|
+
log?.(`[tool-cli] RPC server listening on 127.0.0.1:${this.port}`);
|
|
74
|
+
resolve();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** Stop the HTTP server. */
|
|
79
|
+
async stop() {
|
|
80
|
+
const server = this.server;
|
|
81
|
+
if (!server)
|
|
82
|
+
return;
|
|
83
|
+
this.server = null;
|
|
84
|
+
return new Promise((resolve) => {
|
|
85
|
+
server.close(() => resolve());
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/** Returns the port the server is listening on (resolved after start for port 0). */
|
|
89
|
+
getPort() {
|
|
90
|
+
if (this.server) {
|
|
91
|
+
const addr = this.server.address();
|
|
92
|
+
if (addr && typeof addr === "object")
|
|
93
|
+
return addr.port;
|
|
94
|
+
}
|
|
95
|
+
return this.port;
|
|
96
|
+
}
|
|
97
|
+
async handleBody(body) {
|
|
98
|
+
let parsed;
|
|
99
|
+
try {
|
|
100
|
+
parsed = JSON.parse(body);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return rpcError(null, -32700, "Parse error");
|
|
104
|
+
}
|
|
105
|
+
if (parsed.jsonrpc !== "2.0" || !parsed.method) {
|
|
106
|
+
return rpcError(parsed.id ?? null, -32600, "Invalid Request");
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const result = await this.dispatch(parsed.method, parsed.params ?? {});
|
|
110
|
+
return { jsonrpc: "2.0", result, id: parsed.id ?? null };
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
114
|
+
const code = err instanceof RpcMethodError ? err.code : -32603;
|
|
115
|
+
return rpcError(parsed.id ?? null, code, message);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async dispatch(method, params) {
|
|
119
|
+
switch (method) {
|
|
120
|
+
case "listServers":
|
|
121
|
+
return this.listServers();
|
|
122
|
+
case "listTools":
|
|
123
|
+
return this.listTools(params);
|
|
124
|
+
case "describeTool":
|
|
125
|
+
return this.describeTool(params);
|
|
126
|
+
case "callTool":
|
|
127
|
+
return this.callTool(params);
|
|
128
|
+
default:
|
|
129
|
+
throw new RpcMethodError(-32601, `Method not found: ${method}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
listServers() {
|
|
133
|
+
const serverNames = this.mcpManager.getConnectedServers();
|
|
134
|
+
const servers = serverNames.map((name) => {
|
|
135
|
+
const tools = this.mcpManager.getToolsForServer(name);
|
|
136
|
+
return {
|
|
137
|
+
name,
|
|
138
|
+
toolCount: tools.length,
|
|
139
|
+
examples: pickRandomExamples(tools, 3),
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
return { servers };
|
|
143
|
+
}
|
|
144
|
+
listTools(params) {
|
|
145
|
+
const server = requireString(params, "server");
|
|
146
|
+
this.assertServerExists(server);
|
|
147
|
+
const tools = this.mcpManager.getToolsForServer(server);
|
|
148
|
+
return {
|
|
149
|
+
server,
|
|
150
|
+
tools: tools.map((t) => ({
|
|
151
|
+
name: t.name,
|
|
152
|
+
description: t.description ?? "(no description)",
|
|
153
|
+
hasStructuredOutput: t.outputSchema != null,
|
|
154
|
+
})),
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
describeTool(params) {
|
|
158
|
+
const server = requireString(params, "server");
|
|
159
|
+
const toolName = requireString(params, "tool");
|
|
160
|
+
this.assertServerExists(server);
|
|
161
|
+
const tool = this.mcpManager.getToolsForServer(server).find((t) => t.name === toolName);
|
|
162
|
+
if (!tool) {
|
|
163
|
+
throw new RpcMethodError(-32602, `Tool "${toolName}" not found on server "${server}"`);
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
name: tool.name,
|
|
167
|
+
description: tool.description ?? "(no description)",
|
|
168
|
+
inputSchema: tool.inputSchema,
|
|
169
|
+
outputSchema: tool.outputSchema,
|
|
170
|
+
annotations: tool.annotations,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
async callTool(params) {
|
|
174
|
+
const server = requireString(params, "server");
|
|
175
|
+
const toolName = requireString(params, "tool");
|
|
176
|
+
this.assertServerExists(server);
|
|
177
|
+
const client = this.mcpManager.getClient(server);
|
|
178
|
+
if (!client) {
|
|
179
|
+
throw new RpcMethodError(-32603, `No client for server "${server}"`);
|
|
180
|
+
}
|
|
181
|
+
const toolArgs = params.arguments ?? {};
|
|
182
|
+
const result = await client.callTool({ name: toolName, arguments: toolArgs });
|
|
183
|
+
return {
|
|
184
|
+
content: result.content,
|
|
185
|
+
isError: result.isError === true ? true : undefined,
|
|
186
|
+
structuredContent: result.structuredContent,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
assertServerExists(server) {
|
|
190
|
+
if (!this.mcpManager.getConnectedServers().includes(server)) {
|
|
191
|
+
throw new RpcMethodError(-32602, `Server "${server}" not found. Connected: ${this.mcpManager.getConnectedServers().join(", ") || "(none)"}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
class RpcMethodError extends Error {
|
|
196
|
+
code;
|
|
197
|
+
constructor(code, message) {
|
|
198
|
+
super(message);
|
|
199
|
+
this.code = code;
|
|
200
|
+
this.name = "RpcMethodError";
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function rpcError(id, code, message) {
|
|
204
|
+
return { jsonrpc: "2.0", error: { code, message }, id };
|
|
205
|
+
}
|
|
206
|
+
function requireString(params, key) {
|
|
207
|
+
const val = params[key];
|
|
208
|
+
if (typeof val !== "string" || val.length === 0) {
|
|
209
|
+
throw new RpcMethodError(-32602, `Missing or invalid parameter: "${key}" (string required)`);
|
|
210
|
+
}
|
|
211
|
+
return val;
|
|
212
|
+
}
|
|
213
|
+
/** Pick up to `n` random tool names from a list. */
|
|
214
|
+
function pickRandomExamples(tools, n) {
|
|
215
|
+
if (tools.length <= n)
|
|
216
|
+
return tools.map((t) => t.name);
|
|
217
|
+
const shuffled = [...tools].sort(() => Math.random() - 0.5);
|
|
218
|
+
return shuffled.slice(0, n).map((t) => t.name);
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=rpc-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-server.js","sourceRoot":"","sources":["../../src/tool-cli/rpc-server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-server.test.d.ts","sourceRoot":"","sources":["../../src/tool-cli/rpc-server.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { ToolCliRpcServer } from "./rpc-server.js";
|
|
3
|
+
/** Minimal mock of McpClientManager for RPC server testing. */
|
|
4
|
+
function createMockManager() {
|
|
5
|
+
const tools = [
|
|
6
|
+
{
|
|
7
|
+
name: "get_weather",
|
|
8
|
+
description: "Get current weather for a city",
|
|
9
|
+
inputSchema: {
|
|
10
|
+
type: "object",
|
|
11
|
+
properties: { city: { type: "string", description: "City name" } },
|
|
12
|
+
required: ["city"],
|
|
13
|
+
},
|
|
14
|
+
annotations: { readOnlyHint: true },
|
|
15
|
+
serverName: "weather",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: "set_alarm",
|
|
19
|
+
description: "Set a weather alarm",
|
|
20
|
+
inputSchema: {
|
|
21
|
+
type: "object",
|
|
22
|
+
properties: {
|
|
23
|
+
city: { type: "string" },
|
|
24
|
+
threshold: { type: "number" },
|
|
25
|
+
},
|
|
26
|
+
required: ["city", "threshold"],
|
|
27
|
+
},
|
|
28
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
29
|
+
serverName: "weather",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "search_docs",
|
|
33
|
+
description: "Search documentation",
|
|
34
|
+
inputSchema: {
|
|
35
|
+
type: "object",
|
|
36
|
+
properties: { query: { type: "string", description: "Search query" } },
|
|
37
|
+
required: ["query"],
|
|
38
|
+
},
|
|
39
|
+
outputSchema: {
|
|
40
|
+
type: "object",
|
|
41
|
+
properties: { results: { type: "array" } },
|
|
42
|
+
},
|
|
43
|
+
annotations: { readOnlyHint: true },
|
|
44
|
+
serverName: "docs",
|
|
45
|
+
},
|
|
46
|
+
];
|
|
47
|
+
return {
|
|
48
|
+
getConnectedServers: () => ["weather", "docs"],
|
|
49
|
+
getToolsForServer: (name) => tools.filter((t) => t.serverName === name),
|
|
50
|
+
getTools: () => tools,
|
|
51
|
+
getClient: (name) => {
|
|
52
|
+
if (name === "weather" || name === "docs") {
|
|
53
|
+
return {
|
|
54
|
+
callTool: async (params) => ({
|
|
55
|
+
content: [{ type: "text", text: `Called ${params.name}` }],
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Use port 0 so the OS assigns a free port — avoids collisions when src + dist tests run together
|
|
64
|
+
describe("ToolCliRpcServer", () => {
|
|
65
|
+
let server;
|
|
66
|
+
let port;
|
|
67
|
+
beforeAll(async () => {
|
|
68
|
+
const manager = createMockManager();
|
|
69
|
+
server = new ToolCliRpcServer(manager, 0);
|
|
70
|
+
await server.start();
|
|
71
|
+
port = server.getPort();
|
|
72
|
+
});
|
|
73
|
+
afterAll(async () => {
|
|
74
|
+
await server.stop();
|
|
75
|
+
});
|
|
76
|
+
async function rpc(method, params = {}) {
|
|
77
|
+
const res = await fetch(`http://127.0.0.1:${port}`, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
headers: { "Content-Type": "application/json" },
|
|
80
|
+
body: JSON.stringify({ jsonrpc: "2.0", method, params, id: 1 }),
|
|
81
|
+
});
|
|
82
|
+
return res.json();
|
|
83
|
+
}
|
|
84
|
+
it("lists connected servers with tool counts", async () => {
|
|
85
|
+
const resp = (await rpc("listServers"));
|
|
86
|
+
const { servers } = resp.result;
|
|
87
|
+
expect(servers).toHaveLength(2);
|
|
88
|
+
const weather = servers.find((s) => s.name === "weather");
|
|
89
|
+
expect(weather).toBeDefined();
|
|
90
|
+
expect(weather?.toolCount).toBe(2);
|
|
91
|
+
expect(weather?.examples.length).toBeGreaterThan(0);
|
|
92
|
+
const docs = servers.find((s) => s.name === "docs");
|
|
93
|
+
expect(docs).toBeDefined();
|
|
94
|
+
expect(docs?.toolCount).toBe(1);
|
|
95
|
+
});
|
|
96
|
+
it("lists tools for a specific server", async () => {
|
|
97
|
+
const resp = (await rpc("listTools", { server: "weather" }));
|
|
98
|
+
expect(resp.result.server).toBe("weather");
|
|
99
|
+
expect(resp.result.tools).toHaveLength(2);
|
|
100
|
+
expect(resp.result.tools[0].name).toBe("get_weather");
|
|
101
|
+
expect(resp.result.tools[1].name).toBe("set_alarm");
|
|
102
|
+
});
|
|
103
|
+
it("describes a tool with full schema", async () => {
|
|
104
|
+
const resp = (await rpc("describeTool", { server: "weather", tool: "get_weather" }));
|
|
105
|
+
expect(resp.result.name).toBe("get_weather");
|
|
106
|
+
expect(resp.result.description).toBe("Get current weather for a city");
|
|
107
|
+
expect(resp.result.inputSchema).toHaveProperty("properties");
|
|
108
|
+
expect(resp.result.annotations).toEqual({ readOnlyHint: true });
|
|
109
|
+
});
|
|
110
|
+
it("calls a tool and returns result", async () => {
|
|
111
|
+
const resp = (await rpc("callTool", {
|
|
112
|
+
server: "weather",
|
|
113
|
+
tool: "get_weather",
|
|
114
|
+
arguments: { city: "London" },
|
|
115
|
+
}));
|
|
116
|
+
expect(resp.result.content).toHaveLength(1);
|
|
117
|
+
expect(resp.result.content[0].text).toBe("Called get_weather");
|
|
118
|
+
});
|
|
119
|
+
it("returns error for unknown server", async () => {
|
|
120
|
+
const resp = (await rpc("listTools", { server: "nonexistent" }));
|
|
121
|
+
expect(resp.error).toBeDefined();
|
|
122
|
+
expect(resp.error.code).toBe(-32602);
|
|
123
|
+
expect(resp.error.message).toContain("not found");
|
|
124
|
+
});
|
|
125
|
+
it("returns error for unknown tool", async () => {
|
|
126
|
+
const resp = (await rpc("describeTool", { server: "weather", tool: "nonexistent" }));
|
|
127
|
+
expect(resp.error).toBeDefined();
|
|
128
|
+
expect(resp.error.message).toContain("not found");
|
|
129
|
+
});
|
|
130
|
+
it("returns error for unknown method", async () => {
|
|
131
|
+
const resp = (await rpc("unknownMethod"));
|
|
132
|
+
expect(resp.error).toBeDefined();
|
|
133
|
+
expect(resp.error.code).toBe(-32601);
|
|
134
|
+
});
|
|
135
|
+
it("returns error for missing required params", async () => {
|
|
136
|
+
const resp = (await rpc("listTools", {}));
|
|
137
|
+
expect(resp.error).toBeDefined();
|
|
138
|
+
expect(resp.error.code).toBe(-32602);
|
|
139
|
+
});
|
|
140
|
+
it("rejects non-POST requests", async () => {
|
|
141
|
+
const res = await fetch(`http://127.0.0.1:${port}`, { method: "GET" });
|
|
142
|
+
expect(res.status).toBe(405);
|
|
143
|
+
});
|
|
144
|
+
it("handles malformed JSON gracefully", async () => {
|
|
145
|
+
const res = await fetch(`http://127.0.0.1:${port}`, {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: { "Content-Type": "application/json" },
|
|
148
|
+
body: "not json at all",
|
|
149
|
+
});
|
|
150
|
+
const json = (await res.json());
|
|
151
|
+
expect(json.error.code).toBe(-32700);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
//# sourceMappingURL=rpc-server.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-server.test.js","sourceRoot":"","sources":["../../src/tool-cli/rpc-server.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sammorrowdrums/mcpi-ext",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
6
6
|
"@sammorrowdrums/tool-cli": "^0.3.0",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"format:check": "prettier --check .",
|
|
31
31
|
"lint": "eslint .",
|
|
32
32
|
"lint:fix": "eslint --fix .",
|
|
33
|
+
"prepublishOnly": "npm run build",
|
|
33
34
|
"start": "node dist/index.js",
|
|
34
35
|
"test": "vitest --run"
|
|
35
36
|
},
|
|
@@ -37,6 +38,9 @@
|
|
|
37
38
|
"node": ">=22.13.0"
|
|
38
39
|
},
|
|
39
40
|
"description": "MCP extension for mcpi — tiered progressive tool discovery",
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
40
44
|
"license": "MIT",
|
|
41
45
|
"main": "./dist/index.js",
|
|
42
46
|
"type": "module",
|
package/.dockerignore
DELETED
package/.github/dependabot.yml
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
|
|
3
|
-
updates:
|
|
4
|
-
- package-ecosystem: npm
|
|
5
|
-
directory: /
|
|
6
|
-
schedule:
|
|
7
|
-
interval: weekly
|
|
8
|
-
open-pull-requests-limit: 10
|
|
9
|
-
groups:
|
|
10
|
-
dev-dependencies:
|
|
11
|
-
dependency-type: development
|
|
12
|
-
update-types:
|
|
13
|
-
- minor
|
|
14
|
-
- patch
|
|
15
|
-
production-dependencies:
|
|
16
|
-
dependency-type: production
|
|
17
|
-
update-types:
|
|
18
|
-
- minor
|
|
19
|
-
- patch
|
|
20
|
-
|
|
21
|
-
- package-ecosystem: github-actions
|
|
22
|
-
directory: /
|
|
23
|
-
schedule:
|
|
24
|
-
interval: weekly
|
|
25
|
-
open-pull-requests-limit: 5
|
package/.github/workflows/ci.yml
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
name: CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [main]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [main]
|
|
8
|
-
|
|
9
|
-
concurrency:
|
|
10
|
-
group: ci-${{ github.ref }}
|
|
11
|
-
cancel-in-progress: true
|
|
12
|
-
|
|
13
|
-
permissions:
|
|
14
|
-
contents: read
|
|
15
|
-
models: read
|
|
16
|
-
|
|
17
|
-
jobs:
|
|
18
|
-
ci:
|
|
19
|
-
name: Lint, Build & Test
|
|
20
|
-
runs-on: ubuntu-latest
|
|
21
|
-
steps:
|
|
22
|
-
- uses: actions/checkout@v6
|
|
23
|
-
|
|
24
|
-
- uses: jdx/mise-action@v4
|
|
25
|
-
|
|
26
|
-
- name: Install dependencies
|
|
27
|
-
run: npm install
|
|
28
|
-
|
|
29
|
-
- name: Lint
|
|
30
|
-
run: mise run lint
|
|
31
|
-
|
|
32
|
-
- name: Lint Dockerfile
|
|
33
|
-
uses: hadolint/hadolint-action@v3.3.0
|
|
34
|
-
with:
|
|
35
|
-
dockerfile: Dockerfile
|
|
36
|
-
|
|
37
|
-
- name: Check formatting
|
|
38
|
-
run: mise run format:check
|
|
39
|
-
|
|
40
|
-
- name: Type-check
|
|
41
|
-
run: mise run check
|
|
42
|
-
|
|
43
|
-
- name: Build
|
|
44
|
-
run: mise run build
|
|
45
|
-
|
|
46
|
-
- name: Test
|
|
47
|
-
run: mise run test
|
|
48
|
-
|
|
49
|
-
- name: MCP integration smoke test
|
|
50
|
-
run: mise run test:integration
|
package/.hadolint.yaml
DELETED
package/.prettierignore
DELETED
package/.prettierrc
DELETED
package/AGENTS.md
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# AGENTS.md
|
|
2
|
-
|
|
3
|
-
## Tooling
|
|
4
|
-
|
|
5
|
-
This project uses **mise** for tool version management and **npm** for package management. If you are an AI agent or new contributor, read this first.
|
|
6
|
-
|
|
7
|
-
### mise (tool version manager)
|
|
8
|
-
|
|
9
|
-
- **What:** [mise](https://mise.jdx.dev/) manages tool versions (node) and project tasks. Think asdf + direnv + make in one tool.
|
|
10
|
-
- **Why:** Reproducible dev environments. `mise.toml` pins exact tool versions so every contributor and CI run uses the same stack.
|
|
11
|
-
- **Install:** `curl https://mise.run | sh` then activate: `eval "$(~/.local/bin/mise activate bash)"`
|
|
12
|
-
- **Usage:**
|
|
13
|
-
- `mise install` — install all tools from `mise.toml`
|
|
14
|
-
- `mise run <task>` — run a project task (build, test, dev, check, start)
|
|
15
|
-
- `mise use <tool>@<version>` — add/update a tool version
|
|
16
|
-
|
|
17
|
-
### mcpi (coding agent)
|
|
18
|
-
|
|
19
|
-
- **What:** [mcpi](https://github.com/SamMorrowDrums/mcpi) is an extensible terminal coding agent (fork of pi). This project builds extensions for it.
|
|
20
|
-
- **Extension pattern:** Export a default function receiving `ExtensionAPI`, register tools/commands/hooks.
|
|
21
|
-
- **Load extension:** `mcpi --extension ./dist/index.js`
|
|
22
|
-
|
|
23
|
-
## Dev Loop
|
|
24
|
-
|
|
25
|
-
```sh
|
|
26
|
-
mise install # one-time: install node
|
|
27
|
-
npm install # install npm dependencies
|
|
28
|
-
mise run build # compile TypeScript
|
|
29
|
-
mise run test # run tests
|
|
30
|
-
mise run dev # watch mode for development
|
|
31
|
-
mise run check # type-check only
|
|
32
|
-
mise run lint # lint TypeScript files
|
|
33
|
-
mise run format # auto-format code with Prettier
|
|
34
|
-
mise run format:check # check formatting (CI mode)
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
## Project Structure
|
|
38
|
-
|
|
39
|
-
```
|
|
40
|
-
src/
|
|
41
|
-
index.ts Extension entry point (lifecycle hooks, wiring)
|
|
42
|
-
mcp/ MCP client management (connections, tool discovery)
|
|
43
|
-
skills/ Skill registry, discovery, gating, tool proxies
|
|
44
|
-
tool-cli/ tool-cli RPC server, client, CLI binary, prompt
|
|
45
|
-
test-servers/ Test MCP servers (weather, echo)
|
|
46
|
-
dist/ Compiled output (gitignored)
|
|
47
|
-
scripts/ Integration and smoke test scripts
|
|
48
|
-
mise.toml Tool versions and tasks
|
|
49
|
-
package.json Dependencies and npm scripts
|
|
50
|
-
tsconfig.json TypeScript configuration
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Architecture
|
|
54
|
-
|
|
55
|
-
### Tiered MCP Tool Access
|
|
56
|
-
|
|
57
|
-
The extension provides three tiers for exposing MCP tools to the agent:
|
|
58
|
-
|
|
59
|
-
| Tier | Mechanism | When Used |
|
|
60
|
-
| ------------- | ------------------------------------------------------------------ | ------------------------------------------------ |
|
|
61
|
-
| 1 — Skills | `deferred: true` + `tool_call` gate → tools unlocked by load_skill | MCP server ships skills |
|
|
62
|
-
| 2 — tool-cli | CLI progressive discovery via shell | Ad-hoc exploration, no skills |
|
|
63
|
-
| 3 — Code Mode | search+execute, no HITL | Read-only tools with structured output (planned) |
|
|
64
|
-
|
|
65
|
-
### tool-cli Architecture
|
|
66
|
-
|
|
67
|
-
tool-cli is a thin CLI binary that communicates with the extension via JSON-RPC 2.0 over HTTP. The agent uses it as a standard shell command, composable with pipes, grep, jq, loops, etc.
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
Agent (mcpi)
|
|
71
|
-
│
|
|
72
|
-
│ shell exec
|
|
73
|
-
▼
|
|
74
|
-
tool-cli <server> <tool> '{"args"}'
|
|
75
|
-
│
|
|
76
|
-
│ HTTP JSON-RPC (localhost:7179)
|
|
77
|
-
▼
|
|
78
|
-
ToolCliRpcServer (in extension process)
|
|
79
|
-
│
|
|
80
|
-
│ MCP protocol (stdio/HTTP)
|
|
81
|
-
▼
|
|
82
|
-
MCP Server(s)
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
**Key design points:**
|
|
86
|
-
|
|
87
|
-
- **No auth (temporary)** — the RPC server binds to `127.0.0.1` only, limiting access to the local machine. This is acceptable for development but not a finished security posture — any local process can call the server and execute MCP tools. Future work should add a shared secret or token (e.g. passed via environment variable to the CLI) so only the intended agent process can make calls.
|
|
88
|
-
- **Interception point for HITL** — the RPC server's `callTool` method is the single choke point for all tool execution. Future work can check tool annotations (`readOnlyHint`, `destructiveHint`) here and gate non-read-only calls through user confirmation before forwarding to the MCP server.
|
|
89
|
-
- **Progressive discovery** — the agent discovers servers → tools → schemas incrementally, paying only the tokens it needs.
|
|
90
|
-
- **Shell-native** — plain text output composes with grep, jq, xargs, pipes, loops. The agent can chain tool calls using standard bash idioms.
|
|
91
|
-
|
|
92
|
-
## Code Quality
|
|
93
|
-
|
|
94
|
-
- **ESLint** — flat config with `typescript-eslint` (strict + stylistic) and Prettier compat
|
|
95
|
-
- **Prettier** — auto-formatting (double quotes, semicolons, trailing commas, 100 char width)
|
|
96
|
-
- **CI** — GitHub Actions runs lint → format:check → type-check → build → test on every PR
|
|
97
|
-
- **Dependabot** — automated dependency updates (npm + GitHub Actions)
|
|
98
|
-
|
|
99
|
-
Run `mise run lint` and `mise run format` before committing. CI will reject PRs that fail any check.
|
|
100
|
-
|
|
101
|
-
**Before pushing**, always run:
|
|
102
|
-
|
|
103
|
-
```sh
|
|
104
|
-
mise run lint # lint must pass
|
|
105
|
-
mise run test # tests must pass
|
|
106
|
-
mise run format:check # formatting must pass
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
## Decision Log
|
|
110
|
-
|
|
111
|
-
We maintain a [DECISIONS.md](DECISIONS.md) file recording key architectural and design decisions. Keep it up to date when making significant choices — add a new numbered entry with date, context, decision, and rationale.
|
|
112
|
-
|
|
113
|
-
## Conventions
|
|
114
|
-
|
|
115
|
-
- ESM (`"type": "module"`)
|
|
116
|
-
- Target: ES2022, module: Node16
|
|
117
|
-
- Strict TypeScript
|
|
118
|
-
- Tests with vitest (co-located `*.test.ts` files)
|