@rk0429/agentic-relay 0.6.1 → 0.6.3

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.
Files changed (2) hide show
  1. package/dist/relay.mjs +122 -17
  2. package/package.json +1 -1
package/dist/relay.mjs CHANGED
@@ -245,13 +245,61 @@ function readSkillContext(skillContext) {
245
245
  return null;
246
246
  }
247
247
  }
248
+ function readProjectMcpJson(cwd) {
249
+ try {
250
+ const dir = cwd ?? process.cwd();
251
+ const mcpJsonPath = join6(dir, ".mcp.json");
252
+ if (!existsSync(mcpJsonPath)) {
253
+ return {};
254
+ }
255
+ const raw = readFileSync(mcpJsonPath, "utf-8");
256
+ const parsed = JSON.parse(raw);
257
+ const servers = parsed.mcpServers;
258
+ if (!servers || typeof servers !== "object") {
259
+ return {};
260
+ }
261
+ return servers;
262
+ } catch {
263
+ return {};
264
+ }
265
+ }
266
+ function isRelayEntry(name, config) {
267
+ if (name === "agentic-relay") return true;
268
+ const argsStr = (config.args ?? []).join(" ");
269
+ if (config.command === "npx" && argsStr.includes("@rk0429/agentic-relay")) return true;
270
+ if (argsStr.includes("relay.mjs") && argsStr.includes("mcp")) return true;
271
+ if (config.command?.includes("relay.mjs") || config.command?.includes("agentic-relay")) return true;
272
+ return false;
273
+ }
274
+ function buildChildMcpServers(parentMcpServers, childHttpUrl) {
275
+ const result = {};
276
+ for (const [name, config] of Object.entries(parentMcpServers)) {
277
+ if (isRelayEntry(name, config)) {
278
+ result[name] = { type: "http", url: childHttpUrl };
279
+ } else if (config.type === "http" || config.type === "sse") {
280
+ result[name] = {
281
+ type: config.type,
282
+ url: config.url ?? "",
283
+ ...config.headers ? { headers: config.headers } : {}
284
+ };
285
+ } else {
286
+ result[name] = {
287
+ type: "stdio",
288
+ command: config.command ?? "",
289
+ ...config.args ? { args: config.args } : {},
290
+ ...config.env ? { env: config.env } : {}
291
+ };
292
+ }
293
+ }
294
+ return result;
295
+ }
248
296
  function buildContextFromEnv() {
249
297
  const traceId = process.env["RELAY_TRACE_ID"] ?? `trace-${nanoid2()}`;
250
298
  const parentSessionId = process.env["RELAY_PARENT_SESSION_ID"] ?? null;
251
299
  const depth = Number(process.env["RELAY_DEPTH"] ?? "0");
252
300
  return { traceId, parentSessionId, depth };
253
301
  }
254
- async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector) {
302
+ async function executeSpawnAgent(input, registry2, sessionManager2, guard, hooksEngine2, contextMonitor2, backendSelector, childHttpUrl) {
255
303
  let effectiveBackend = input.backend;
256
304
  if (backendSelector) {
257
305
  const availableBackends = registry2.listIds();
@@ -383,6 +431,13 @@ ${wrapped}` : wrapped;
383
431
  }
384
432
  result = await adapter.continueSession(input.resumeSessionId, input.prompt);
385
433
  } else {
434
+ let mcpServers;
435
+ if (childHttpUrl) {
436
+ const parentMcpServers = readProjectMcpJson();
437
+ if (Object.keys(parentMcpServers).length > 0) {
438
+ mcpServers = buildChildMcpServers(parentMcpServers, childHttpUrl);
439
+ }
440
+ }
386
441
  result = await adapter.execute({
387
442
  prompt: input.prompt,
388
443
  agent: input.agent,
@@ -394,7 +449,8 @@ ${wrapped}` : wrapped;
394
449
  depth: envContext.depth + 1,
395
450
  maxDepth: guard.getConfig().maxDepth,
396
451
  traceId: envContext.traceId
397
- }
452
+ },
453
+ ...mcpServers ? { mcpServers } : {}
398
454
  });
399
455
  }
400
456
  if (contextMonitor2) {
@@ -661,15 +717,26 @@ var init_server = __esm({
661
717
  this.backendSelector = new BackendSelector();
662
718
  this.server = new McpServer({
663
719
  name: "agentic-relay",
664
- version: "0.4.0"
720
+ version: "0.6.3"
665
721
  });
666
- this.registerTools();
722
+ this.registerTools(this.server);
667
723
  }
668
724
  server;
725
+ childServer;
669
726
  guard;
670
727
  backendSelector;
671
- registerTools() {
672
- this.server.tool(
728
+ _childHttpServer;
729
+ _childHttpUrl;
730
+ /** URL for child agents to connect via HTTP. Available after start() in stdio mode. */
731
+ get childHttpUrl() {
732
+ return this._childHttpUrl;
733
+ }
734
+ /** Exposed for testing: the HTTP server serving child agents */
735
+ get childHttpServer() {
736
+ return this._childHttpServer;
737
+ }
738
+ registerTools(server) {
739
+ server.tool(
673
740
  "spawn_agent",
674
741
  "Spawn a sub-agent on the specified backend CLI (Claude Code, Codex CLI, or Gemini CLI). The agent executes the given prompt in non-interactive mode and returns the result. Use 'agent' for named agent configurations (Claude only), 'systemPrompt' for custom role instructions (all backends), 'skillContext' to inject a skill definition (SKILL.md/SUBSKILL.md), 'agentDefinition' to inject an agent definition file into the sub-agent's system prompt, 'preferredBackend' to override automatic backend selection, or 'taskType' to hint at the task nature for backend selection.",
675
742
  {
@@ -701,7 +768,8 @@ var init_server = __esm({
701
768
  this.guard,
702
769
  this.hooksEngine,
703
770
  this.contextMonitor,
704
- this.backendSelector
771
+ this.backendSelector,
772
+ this._childHttpUrl
705
773
  );
706
774
  const isError = result.exitCode !== 0;
707
775
  const text = isError ? `Error (exit ${result.exitCode}): ${result.stderr || result.stdout}` : `Session: ${result.sessionId}
@@ -720,7 +788,7 @@ ${result.stdout}`;
720
788
  }
721
789
  }
722
790
  );
723
- this.server.tool(
791
+ server.tool(
724
792
  "list_sessions",
725
793
  "List relay sessions, optionally filtered by backend.",
726
794
  {
@@ -750,7 +818,7 @@ ${result.stdout}`;
750
818
  }
751
819
  }
752
820
  );
753
- this.server.tool(
821
+ server.tool(
754
822
  "get_context_status",
755
823
  "Get the context usage status of a relay session. Returns usage data from ContextMonitor when available, otherwise estimated values.",
756
824
  {
@@ -780,7 +848,7 @@ ${result.stdout}`;
780
848
  }
781
849
  }
782
850
  );
783
- this.server.tool(
851
+ server.tool(
784
852
  "list_available_backends",
785
853
  "List all registered backends with their health status. Use this before spawn_agent to check which backends are available.",
786
854
  {},
@@ -811,6 +879,7 @@ ${result.stdout}`;
811
879
  logger.info("Starting agentic-relay MCP server (stdio transport)...");
812
880
  const transport = new StdioServerTransport();
813
881
  await this.server.connect(transport);
882
+ await this.startChildHttpServer();
814
883
  return;
815
884
  }
816
885
  const port = options?.port ?? 3100;
@@ -841,6 +910,40 @@ ${result.stdout}`;
841
910
  httpServer.on("close", resolve);
842
911
  });
843
912
  }
913
+ /**
914
+ * Start an HTTP server for child agents.
915
+ * Creates a separate McpServer instance (since McpServer.connect() can only be called once)
916
+ * and binds it to a random port on 127.0.0.1.
917
+ */
918
+ async startChildHttpServer() {
919
+ this.childServer = new McpServer({
920
+ name: "agentic-relay",
921
+ version: "0.6.3"
922
+ });
923
+ this.registerTools(this.childServer);
924
+ const childTransport = new StreamableHTTPServerTransport({
925
+ sessionIdGenerator: () => randomUUID()
926
+ });
927
+ const httpServer = createServer(async (req, res) => {
928
+ const url = req.url ?? "";
929
+ if (url === "/mcp" || url.startsWith("/mcp?")) {
930
+ await childTransport.handleRequest(req, res);
931
+ } else {
932
+ res.writeHead(404, { "Content-Type": "text/plain" });
933
+ res.end("Not found");
934
+ }
935
+ });
936
+ this._childHttpServer = httpServer;
937
+ await this.childServer.connect(childTransport);
938
+ await new Promise((resolve) => {
939
+ httpServer.listen(0, "127.0.0.1", () => {
940
+ const addr = httpServer.address();
941
+ this._childHttpUrl = `http://127.0.0.1:${addr.port}/mcp`;
942
+ logger.info(`Child MCP server listening on ${this._childHttpUrl}`);
943
+ resolve();
944
+ });
945
+ });
946
+ }
844
947
  /** Exposed for testing and graceful shutdown */
845
948
  get httpServer() {
846
949
  return this._httpServer;
@@ -1231,10 +1334,11 @@ var ClaudeAdapter = class extends BaseAdapter {
1231
1334
  abortController,
1232
1335
  env,
1233
1336
  cwd: process.cwd(),
1234
- mcpServers: {},
1337
+ strictMcpConfig: true,
1235
1338
  ...flags.model ? { model: flags.model } : {},
1236
1339
  ...flags.maxTurns ? { maxTurns: flags.maxTurns } : {},
1237
- ...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {}
1340
+ ...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {},
1341
+ ...flags.mcpServers ? { mcpServers: flags.mcpServers } : {}
1238
1342
  };
1239
1343
  if (permissionMode === "bypassPermissions") {
1240
1344
  options.permissionMode = "bypassPermissions";
@@ -1298,10 +1402,11 @@ var ClaudeAdapter = class extends BaseAdapter {
1298
1402
  abortController,
1299
1403
  env,
1300
1404
  cwd: process.cwd(),
1301
- mcpServers: {},
1405
+ strictMcpConfig: true,
1302
1406
  ...flags.model ? { model: flags.model } : {},
1303
1407
  ...flags.maxTurns ? { maxTurns: flags.maxTurns } : {},
1304
- ...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {}
1408
+ ...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {},
1409
+ ...flags.mcpServers ? { mcpServers: flags.mcpServers } : {}
1305
1410
  };
1306
1411
  if (permissionMode === "bypassPermissions") {
1307
1412
  options.permissionMode = "bypassPermissions";
@@ -1380,7 +1485,7 @@ var ClaudeAdapter = class extends BaseAdapter {
1380
1485
  resume: nativeSessionId,
1381
1486
  maxTurns: 1,
1382
1487
  cwd: process.cwd(),
1383
- mcpServers: {}
1488
+ strictMcpConfig: true
1384
1489
  };
1385
1490
  if (permissionMode === "bypassPermissions") {
1386
1491
  options.permissionMode = "bypassPermissions";
@@ -3793,7 +3898,7 @@ function createVersionCommand(registry2) {
3793
3898
  description: "Show relay and backend versions"
3794
3899
  },
3795
3900
  async run() {
3796
- const relayVersion = "0.1.0";
3901
+ const relayVersion = "0.6.3";
3797
3902
  console.log(`agentic-relay v${relayVersion}`);
3798
3903
  console.log("");
3799
3904
  console.log("Backends:");
@@ -4143,7 +4248,7 @@ void configManager.getConfig().then((config) => {
4143
4248
  var main = defineCommand10({
4144
4249
  meta: {
4145
4250
  name: "relay",
4146
- version: "0.1.0",
4251
+ version: "0.6.3",
4147
4252
  description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
4148
4253
  },
4149
4254
  subCommands: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rk0429/agentic-relay",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI with MCP-based multi-layer sub-agent orchestration",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",