akemon 0.1.8 → 0.1.9

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 CHANGED
@@ -31,6 +31,7 @@ program
31
31
  .option("--approve", "Review every task before execution")
32
32
  .option("--mock", "Use mock responses (for demo/testing)")
33
33
  .option("--allow-all", "Skip all permission prompts (for self-use)")
34
+ .option("--mcp-server <command>", "Wrap a community MCP server (stdio) and expose its tools via relay")
34
35
  .option("--relay <url>", "Relay WebSocket URL", RELAY_WS)
35
36
  .action(async (opts) => {
36
37
  const port = parseInt(opts.port);
@@ -51,6 +52,7 @@ program
51
52
  engine,
52
53
  relayHttp,
53
54
  secretKey: credentials.secretKey,
55
+ mcpServer: opts.mcpServer,
54
56
  });
55
57
  console.log(``);
56
58
  if (!opts.public) {
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
- const mcpServer = createMcpServer({
307
- workdir,
308
- agentName: options.agentName,
309
- mock: options.mock,
310
- model: options.model,
311
- approve: options.approve,
312
- engine: options.engine,
313
- allowAll: options.allowAll,
314
- relayHttp: options.relayHttp,
315
- secretKey: options.secretKey,
316
- publisherIds,
317
- });
318
- await mcpServer.connect(transport);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",