akemon 0.1.8 → 0.1.10
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/cli.js +4 -0
- package/dist/relay-client.js +3 -0
- package/dist/server.js +99 -13
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -26,11 +26,13 @@ program
|
|
|
26
26
|
.option("-m, --model <model>", "Model to use (e.g. claude-sonnet-4-6, gpt-4o)")
|
|
27
27
|
.option("--engine <engine>", "Engine: claude, codex, opencode, gemini, human, or any CLI", "claude")
|
|
28
28
|
.option("--desc <description>", "Agent description (for discovery)")
|
|
29
|
+
.option("--tags <tags>", "Comma-separated tags (e.g. vue,frontend,review)")
|
|
29
30
|
.option("--public", "Allow anyone to call this agent without a key")
|
|
30
31
|
.option("--max-tasks <n>", "Maximum tasks per day (PP)")
|
|
31
32
|
.option("--approve", "Review every task before execution")
|
|
32
33
|
.option("--mock", "Use mock responses (for demo/testing)")
|
|
33
34
|
.option("--allow-all", "Skip all permission prompts (for self-use)")
|
|
35
|
+
.option("--mcp-server <command>", "Wrap a community MCP server (stdio) and expose its tools via relay")
|
|
34
36
|
.option("--relay <url>", "Relay WebSocket URL", RELAY_WS)
|
|
35
37
|
.action(async (opts) => {
|
|
36
38
|
const port = parseInt(opts.port);
|
|
@@ -51,6 +53,7 @@ program
|
|
|
51
53
|
engine,
|
|
52
54
|
relayHttp,
|
|
53
55
|
secretKey: credentials.secretKey,
|
|
56
|
+
mcpServer: opts.mcpServer,
|
|
54
57
|
});
|
|
55
58
|
console.log(``);
|
|
56
59
|
if (!opts.public) {
|
|
@@ -65,6 +68,7 @@ program
|
|
|
65
68
|
description: opts.desc,
|
|
66
69
|
isPublic: opts.public,
|
|
67
70
|
engine,
|
|
71
|
+
tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
|
|
68
72
|
});
|
|
69
73
|
});
|
|
70
74
|
program
|
package/dist/relay-client.js
CHANGED
package/dist/server.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
4
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
7
|
+
import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
4
8
|
import { z } from "zod";
|
|
5
9
|
import { spawn, exec } from "child_process";
|
|
6
10
|
import { createServer } from "http";
|
|
@@ -259,8 +263,84 @@ function createMcpServer(opts) {
|
|
|
259
263
|
});
|
|
260
264
|
return server;
|
|
261
265
|
}
|
|
266
|
+
async function initMcpProxy(mcpServerCmd, workdir) {
|
|
267
|
+
const parts = mcpServerCmd.match(/(?:[^\s"]+|"[^"]*")+/g) || [mcpServerCmd];
|
|
268
|
+
const [command, ...args] = parts.map(p => p.replace(/^"|"$/g, ""));
|
|
269
|
+
console.log(`[mcp-proxy] Starting child MCP server: ${command} ${args.join(" ")}`);
|
|
270
|
+
const transport = new StdioClientTransport({ command, args, cwd: workdir, stderr: "pipe" });
|
|
271
|
+
const client = new Client({ name: "akemon-proxy", version: "0.1.0" });
|
|
272
|
+
await client.connect(transport);
|
|
273
|
+
const { tools } = await client.listTools();
|
|
274
|
+
console.log(`[mcp-proxy] Connected. ${tools.length} tools: ${tools.map((t) => t.name).join(", ")}`);
|
|
275
|
+
return { client, tools };
|
|
276
|
+
}
|
|
277
|
+
function createMcpProxyServer(proxy, agentName) {
|
|
278
|
+
const server = new Server({ name: agentName, version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
279
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
280
|
+
return {
|
|
281
|
+
tools: [
|
|
282
|
+
...proxy.tools,
|
|
283
|
+
{
|
|
284
|
+
name: "call_agent",
|
|
285
|
+
description: "Call another akemon agent by name. The target agent will execute the task and return the result.",
|
|
286
|
+
inputSchema: {
|
|
287
|
+
type: "object",
|
|
288
|
+
properties: {
|
|
289
|
+
agent: { type: "string", description: "Target agent name" },
|
|
290
|
+
task: { type: "string", description: "Task to send" },
|
|
291
|
+
},
|
|
292
|
+
required: ["agent", "task"],
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
299
|
+
const { name, arguments: toolArgs } = request.params;
|
|
300
|
+
if (name === "call_agent") {
|
|
301
|
+
console.log(`[call_agent] ${agentName} → ${toolArgs?.agent}: ${String(toolArgs?.task).slice(0, 80)}`);
|
|
302
|
+
try {
|
|
303
|
+
const result = await callAgent(toolArgs?.agent, toolArgs?.task);
|
|
304
|
+
return { content: [{ type: "text", text: result }] };
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
return { content: [{ type: "text", text: `[error] ${err.message}` }], isError: true };
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// Forward to child MCP server
|
|
311
|
+
console.log(`[mcp-proxy] → ${name}(${JSON.stringify(toolArgs).slice(0, 100)})`);
|
|
312
|
+
try {
|
|
313
|
+
const result = await proxy.client.callTool({ name, arguments: toolArgs });
|
|
314
|
+
// Normalize response format
|
|
315
|
+
if ("toolResult" in result) {
|
|
316
|
+
return { content: [{ type: "text", text: JSON.stringify(result.toolResult) }] };
|
|
317
|
+
}
|
|
318
|
+
return result;
|
|
319
|
+
}
|
|
320
|
+
catch (err) {
|
|
321
|
+
console.error(`[mcp-proxy] Tool ${name} error: ${err.message}`);
|
|
322
|
+
return { content: [{ type: "text", text: `[error] ${err.message}` }], isError: true };
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
return server;
|
|
326
|
+
}
|
|
262
327
|
export async function serve(options) {
|
|
263
328
|
const workdir = options.workdir || process.cwd();
|
|
329
|
+
// Expose port to engine subprocesses so they can callback to local MCP server
|
|
330
|
+
process.env.AKEMON_PORT = String(options.port);
|
|
331
|
+
if (options.key)
|
|
332
|
+
process.env.AKEMON_KEY = options.key;
|
|
333
|
+
// Initialize MCP proxy if --mcp-server specified
|
|
334
|
+
let mcpProxy = null;
|
|
335
|
+
if (options.mcpServer) {
|
|
336
|
+
try {
|
|
337
|
+
mcpProxy = await initMcpProxy(options.mcpServer, workdir);
|
|
338
|
+
}
|
|
339
|
+
catch (err) {
|
|
340
|
+
console.error(`[mcp-proxy] Failed to start child MCP server: ${err.message}`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
264
344
|
const sessions = new Map();
|
|
265
345
|
const publisherIds = new Map();
|
|
266
346
|
const httpServer = createServer(async (req, res) => {
|
|
@@ -303,19 +383,25 @@ export async function serve(options) {
|
|
|
303
383
|
publisherIds.delete(sid);
|
|
304
384
|
}
|
|
305
385
|
};
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
386
|
+
if (mcpProxy) {
|
|
387
|
+
const proxyServer = createMcpProxyServer(mcpProxy, options.agentName);
|
|
388
|
+
await proxyServer.connect(transport);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
const mcpServer = createMcpServer({
|
|
392
|
+
workdir,
|
|
393
|
+
agentName: options.agentName,
|
|
394
|
+
mock: options.mock,
|
|
395
|
+
model: options.model,
|
|
396
|
+
approve: options.approve,
|
|
397
|
+
engine: options.engine,
|
|
398
|
+
allowAll: options.allowAll,
|
|
399
|
+
relayHttp: options.relayHttp,
|
|
400
|
+
secretKey: options.secretKey,
|
|
401
|
+
publisherIds,
|
|
402
|
+
});
|
|
403
|
+
await mcpServer.connect(transport);
|
|
404
|
+
}
|
|
319
405
|
await transport.handleRequest(req, res);
|
|
320
406
|
if (transport.sessionId) {
|
|
321
407
|
sessions.set(transport.sessionId, transport);
|