Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions agentchatbus-ts/scripts/build-bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ async function prepareDistDir() {
async function copyWorkerAssets() {
const workers = [
['src/core/services/adapters/workers/interactivePtyWorker.mjs', 'dist/workers/interactivePtyWorker.mjs'],
['src/transports/stdio/mcpProxy.mjs', 'dist/workers/mcpProxy.mjs'],
];
for (const [source, target] of workers) {
await copyFile(path.join(projectRoot, source), path.join(projectRoot, target));
Expand Down
3 changes: 2 additions & 1 deletion agentchatbus-ts/src/core/services/acpAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
getAcpAgent,
type AcpAgentConfig,
} from "./acpRegistry.js";
import { resolveAcpMcpProxyScript } from "./acpMcpProxyPath.js";
import type {
CliAdapterActivityEvent,
CliAdapterNativeRuntimeEvent,
Expand Down Expand Up @@ -406,7 +407,7 @@ export class AcpAdapter implements CliSessionAdapter {
// Try stdio MCP proxy as a fallback for agents that don't have
// pre-configured MCP servers. Agents with pre-configured MCP will
// use their own config.
const proxyScript = new URL("../../transports/stdio/mcpProxy.mjs", import.meta.url).pathname;
const proxyScript = resolveAcpMcpProxyScript();
const envVars: acp.EnvVariable[] = [
{ name: "AGENTCHATBUS_BASE_URL", value: baseUrl },
];
Expand Down
35 changes: 35 additions & 0 deletions agentchatbus-ts/src/core/services/acpMcpProxyPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { existsSync } from "node:fs";
import path from "node:path";

const PROXY_FILE_NAME = "mcpProxy.mjs";

/**
* Resolve the standalone ACP stdio MCP proxy script.
*
* The CLI is bundled as CJS (`dist/cli/index.js`), where `import.meta.url` is
* empty. The proxy stays an ESM `.mjs` asset spawned as a child process, copied
* next to the bundle as `dist/workers/mcpProxy.mjs` (same pattern as
* `resolveWorkerPath` in interactivePtyChildBridge).
*/
export function resolveAcpMcpProxyScript(): string {
const entryFile = path.resolve(process.argv[1] || process.cwd());
const entryDir = path.dirname(entryFile);
const candidates = [
path.resolve(entryDir, "../workers", PROXY_FILE_NAME),
path.resolve(entryDir, "../../transports/stdio", PROXY_FILE_NAME),
path.resolve(entryDir, "../../src/transports/stdio", PROXY_FILE_NAME),
path.resolve(process.cwd(), "dist/workers", PROXY_FILE_NAME),
path.resolve(process.cwd(), "src/transports/stdio", PROXY_FILE_NAME),
];

for (const candidate of candidates) {
if (existsSync(candidate)) {
return candidate;
}
}

throw new Error(
`Unable to locate ACP stdio MCP proxy '${PROXY_FILE_NAME}'. ` +
`Checked: ${candidates.join(", ")}`,
);
}
30 changes: 30 additions & 0 deletions agentchatbus-ts/tests/unit/test_acp_mcp_proxy_path.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import { resolveAcpMcpProxyScript } from "../../src/core/services/acpMcpProxyPath.js";

const SOURCE_PROXY = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
"../../src/transports/stdio/mcpProxy.mjs",
);

describe("ACP stdio MCP proxy path", () => {
it("resolves an existing mcpProxy.mjs", () => {
const resolved = resolveAcpMcpProxyScript();
expect(existsSync(resolved)).toBe(true);
expect(path.basename(resolved)).toBe("mcpProxy.mjs");
expect(resolved).not.toMatch(/^\/[A-Za-z]:/);
});

it("finds the source script when the bundled worker copy is absent", () => {
const originalArgv1 = process.argv[1];
process.argv[1] = path.join(path.dirname(SOURCE_PROXY), "not-the-cli.js");
try {
const resolved = resolveAcpMcpProxyScript();
expect(path.normalize(resolved)).toBe(path.normalize(SOURCE_PROXY));
} finally {
process.argv[1] = originalArgv1;
}
});
});
Loading