@rk0429/agentic-relay 0.6.2 → 0.6.4
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/relay.mjs +143 -17
- 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,25 @@ 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
|
|
720
|
+
version: "0.6.4"
|
|
665
721
|
});
|
|
666
|
-
this.registerTools();
|
|
722
|
+
this.registerTools(this.server);
|
|
667
723
|
}
|
|
668
724
|
server;
|
|
669
725
|
guard;
|
|
670
726
|
backendSelector;
|
|
671
|
-
|
|
672
|
-
|
|
727
|
+
_childHttpServer;
|
|
728
|
+
_childHttpUrl;
|
|
729
|
+
/** URL for child agents to connect via HTTP. Available after start() in stdio mode. */
|
|
730
|
+
get childHttpUrl() {
|
|
731
|
+
return this._childHttpUrl;
|
|
732
|
+
}
|
|
733
|
+
/** Exposed for testing: the HTTP server serving child agents */
|
|
734
|
+
get childHttpServer() {
|
|
735
|
+
return this._childHttpServer;
|
|
736
|
+
}
|
|
737
|
+
registerTools(server) {
|
|
738
|
+
server.tool(
|
|
673
739
|
"spawn_agent",
|
|
674
740
|
"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
741
|
{
|
|
@@ -701,7 +767,8 @@ var init_server = __esm({
|
|
|
701
767
|
this.guard,
|
|
702
768
|
this.hooksEngine,
|
|
703
769
|
this.contextMonitor,
|
|
704
|
-
this.backendSelector
|
|
770
|
+
this.backendSelector,
|
|
771
|
+
this._childHttpUrl
|
|
705
772
|
);
|
|
706
773
|
const isError = result.exitCode !== 0;
|
|
707
774
|
const text = isError ? `Error (exit ${result.exitCode}): ${result.stderr || result.stdout}` : `Session: ${result.sessionId}
|
|
@@ -720,7 +787,7 @@ ${result.stdout}`;
|
|
|
720
787
|
}
|
|
721
788
|
}
|
|
722
789
|
);
|
|
723
|
-
|
|
790
|
+
server.tool(
|
|
724
791
|
"list_sessions",
|
|
725
792
|
"List relay sessions, optionally filtered by backend.",
|
|
726
793
|
{
|
|
@@ -750,7 +817,7 @@ ${result.stdout}`;
|
|
|
750
817
|
}
|
|
751
818
|
}
|
|
752
819
|
);
|
|
753
|
-
|
|
820
|
+
server.tool(
|
|
754
821
|
"get_context_status",
|
|
755
822
|
"Get the context usage status of a relay session. Returns usage data from ContextMonitor when available, otherwise estimated values.",
|
|
756
823
|
{
|
|
@@ -780,7 +847,7 @@ ${result.stdout}`;
|
|
|
780
847
|
}
|
|
781
848
|
}
|
|
782
849
|
);
|
|
783
|
-
|
|
850
|
+
server.tool(
|
|
784
851
|
"list_available_backends",
|
|
785
852
|
"List all registered backends with their health status. Use this before spawn_agent to check which backends are available.",
|
|
786
853
|
{},
|
|
@@ -811,6 +878,7 @@ ${result.stdout}`;
|
|
|
811
878
|
logger.info("Starting agentic-relay MCP server (stdio transport)...");
|
|
812
879
|
const transport = new StdioServerTransport();
|
|
813
880
|
await this.server.connect(transport);
|
|
881
|
+
await this.startChildHttpServer();
|
|
814
882
|
return;
|
|
815
883
|
}
|
|
816
884
|
const port = options?.port ?? 3100;
|
|
@@ -841,6 +909,62 @@ ${result.stdout}`;
|
|
|
841
909
|
httpServer.on("close", resolve);
|
|
842
910
|
});
|
|
843
911
|
}
|
|
912
|
+
/**
|
|
913
|
+
* Start an HTTP server for child agents.
|
|
914
|
+
* Each client connection gets its own McpServer + StreamableHTTPServerTransport pair
|
|
915
|
+
* because StreamableHTTPServerTransport only supports a single session per instance.
|
|
916
|
+
* Routes requests to the correct session based on the Mcp-Session-Id header.
|
|
917
|
+
*/
|
|
918
|
+
async startChildHttpServer() {
|
|
919
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
920
|
+
const httpServer = createServer(async (req, res) => {
|
|
921
|
+
const url = req.url ?? "";
|
|
922
|
+
if (url !== "/mcp" && !url.startsWith("/mcp?")) {
|
|
923
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
924
|
+
res.end("Not found");
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
928
|
+
if (sessionId) {
|
|
929
|
+
const session = sessions.get(sessionId);
|
|
930
|
+
if (session) {
|
|
931
|
+
await session.transport.handleRequest(req, res);
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
const transport = new StreamableHTTPServerTransport({
|
|
936
|
+
sessionIdGenerator: () => randomUUID()
|
|
937
|
+
});
|
|
938
|
+
const server = new McpServer({
|
|
939
|
+
name: "agentic-relay",
|
|
940
|
+
version: "0.6.4"
|
|
941
|
+
});
|
|
942
|
+
this.registerTools(server);
|
|
943
|
+
transport.onclose = () => {
|
|
944
|
+
const sid2 = transport.sessionId;
|
|
945
|
+
if (sid2) {
|
|
946
|
+
sessions.delete(sid2);
|
|
947
|
+
logger.debug(`Child MCP session closed: ${sid2}`);
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
await server.connect(transport);
|
|
951
|
+
await transport.handleRequest(req, res);
|
|
952
|
+
const sid = transport.sessionId;
|
|
953
|
+
if (sid) {
|
|
954
|
+
sessions.set(sid, { transport, server });
|
|
955
|
+
logger.debug(`Child MCP session created: ${sid}`);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
this._childHttpServer = httpServer;
|
|
959
|
+
await new Promise((resolve) => {
|
|
960
|
+
httpServer.listen(0, "127.0.0.1", () => {
|
|
961
|
+
const addr = httpServer.address();
|
|
962
|
+
this._childHttpUrl = `http://127.0.0.1:${addr.port}/mcp`;
|
|
963
|
+
logger.info(`Child MCP server listening on ${this._childHttpUrl}`);
|
|
964
|
+
resolve();
|
|
965
|
+
});
|
|
966
|
+
});
|
|
967
|
+
}
|
|
844
968
|
/** Exposed for testing and graceful shutdown */
|
|
845
969
|
get httpServer() {
|
|
846
970
|
return this._httpServer;
|
|
@@ -1231,10 +1355,11 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
1231
1355
|
abortController,
|
|
1232
1356
|
env,
|
|
1233
1357
|
cwd: process.cwd(),
|
|
1234
|
-
|
|
1358
|
+
strictMcpConfig: true,
|
|
1235
1359
|
...flags.model ? { model: flags.model } : {},
|
|
1236
1360
|
...flags.maxTurns ? { maxTurns: flags.maxTurns } : {},
|
|
1237
|
-
...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {}
|
|
1361
|
+
...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {},
|
|
1362
|
+
...flags.mcpServers ? { mcpServers: flags.mcpServers } : {}
|
|
1238
1363
|
};
|
|
1239
1364
|
if (permissionMode === "bypassPermissions") {
|
|
1240
1365
|
options.permissionMode = "bypassPermissions";
|
|
@@ -1298,10 +1423,11 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
1298
1423
|
abortController,
|
|
1299
1424
|
env,
|
|
1300
1425
|
cwd: process.cwd(),
|
|
1301
|
-
|
|
1426
|
+
strictMcpConfig: true,
|
|
1302
1427
|
...flags.model ? { model: flags.model } : {},
|
|
1303
1428
|
...flags.maxTurns ? { maxTurns: flags.maxTurns } : {},
|
|
1304
|
-
...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {}
|
|
1429
|
+
...flags.systemPrompt ? { systemPrompt: flags.systemPrompt } : {},
|
|
1430
|
+
...flags.mcpServers ? { mcpServers: flags.mcpServers } : {}
|
|
1305
1431
|
};
|
|
1306
1432
|
if (permissionMode === "bypassPermissions") {
|
|
1307
1433
|
options.permissionMode = "bypassPermissions";
|
|
@@ -1380,7 +1506,7 @@ var ClaudeAdapter = class extends BaseAdapter {
|
|
|
1380
1506
|
resume: nativeSessionId,
|
|
1381
1507
|
maxTurns: 1,
|
|
1382
1508
|
cwd: process.cwd(),
|
|
1383
|
-
|
|
1509
|
+
strictMcpConfig: true
|
|
1384
1510
|
};
|
|
1385
1511
|
if (permissionMode === "bypassPermissions") {
|
|
1386
1512
|
options.permissionMode = "bypassPermissions";
|
|
@@ -3793,7 +3919,7 @@ function createVersionCommand(registry2) {
|
|
|
3793
3919
|
description: "Show relay and backend versions"
|
|
3794
3920
|
},
|
|
3795
3921
|
async run() {
|
|
3796
|
-
const relayVersion = "0.6.
|
|
3922
|
+
const relayVersion = "0.6.4";
|
|
3797
3923
|
console.log(`agentic-relay v${relayVersion}`);
|
|
3798
3924
|
console.log("");
|
|
3799
3925
|
console.log("Backends:");
|
|
@@ -4143,7 +4269,7 @@ void configManager.getConfig().then((config) => {
|
|
|
4143
4269
|
var main = defineCommand10({
|
|
4144
4270
|
meta: {
|
|
4145
4271
|
name: "relay",
|
|
4146
|
-
version: "0.6.
|
|
4272
|
+
version: "0.6.4",
|
|
4147
4273
|
description: "Unified CLI proxy for Claude Code, Codex CLI, and Gemini CLI"
|
|
4148
4274
|
},
|
|
4149
4275
|
subCommands: {
|
package/package.json
CHANGED