diff --git a/src/cli/operations/dev/__tests__/codezip-dev-server.test.ts b/src/cli/operations/dev/__tests__/codezip-dev-server.test.ts index f0e0a2fe1..176aeb1d2 100644 --- a/src/cli/operations/dev/__tests__/codezip-dev-server.test.ts +++ b/src/cli/operations/dev/__tests__/codezip-dev-server.test.ts @@ -127,6 +127,8 @@ describe('CodeZipDevServer spawn config', () => { expect(env.AGENTCORE_RUNTIME_URL).toBe('http://localhost:8080/'); expect(env.LOCAL_DEV).toBe('1'); expect(env.MY_KEY).toBe('secret'); + // serve_a2a() reads A2A_PORT, not PORT, so the dev port must reach it here. + expect(env.A2A_PORT).toBe('8080'); }); it('TypeScript HTTP: uses npx tsx watch with the entry file', async () => { diff --git a/src/cli/operations/dev/__tests__/container-dev-server.test.ts b/src/cli/operations/dev/__tests__/container-dev-server.test.ts index babb19b98..57772446b 100644 --- a/src/cli/operations/dev/__tests__/container-dev-server.test.ts +++ b/src/cli/operations/dev/__tests__/container-dev-server.test.ts @@ -355,6 +355,18 @@ describe('ContainerDevServer', () => { expect(spawnArgs).toContain('9001:9000'); expect(spawnArgs).toContain('PORT=9000'); expect(spawnArgs).toContain('AGENTCORE_RUNTIME_URL=http://localhost:9001/'); + // serve_a2a() reads A2A_PORT, not PORT. + expect(spawnArgs).toContain('A2A_PORT=9000'); + }); + + it('does not set A2A_PORT for non-A2A protocols', async () => { + mockSuccessfulPrepare(); + + const server = new ContainerDevServer(defaultConfig, defaultOptions); + await server.start(); + + const spawnArgs = getSpawnArgs(); + expect(spawnArgs.some((a: string) => a.startsWith('A2A_PORT='))).toBe(false); }); it('maps an MCP host port to the MCP container port', async () => { diff --git a/src/cli/operations/dev/codezip-dev-server.ts b/src/cli/operations/dev/codezip-dev-server.ts index 3a6f9f98a..76047749c 100644 --- a/src/cli/operations/dev/codezip-dev-server.ts +++ b/src/cli/operations/dev/codezip-dev-server.ts @@ -1,5 +1,6 @@ import { getVenvExecutable } from '../../../lib/utils/platform'; import type { ProtocolMode } from '../../../schema'; +import { A2A_PORT_ENV } from './constants'; import { DevServer, type LogLevel, type SpawnConfig } from './dev-server'; import { convertEntrypointToModule } from './utils'; import { spawnSync } from 'child_process'; @@ -147,6 +148,8 @@ export class CodeZipDevServer extends DevServer { } if (protocol === 'A2A') { env.AGENTCORE_RUNTIME_URL = `http://localhost:${port}/`; + // serve_a2a() reads A2A_PORT, not PORT, so the host-side dev port reaches it. + env[A2A_PORT_ENV] = String(port); } if (!isPython) { diff --git a/src/cli/operations/dev/constants.ts b/src/cli/operations/dev/constants.ts index 1519da92a..70745d3a9 100644 --- a/src/cli/operations/dev/constants.ts +++ b/src/cli/operations/dev/constants.ts @@ -1,2 +1,9 @@ export const MCP_DEFAULT_PORT = 8000; export const A2A_DEFAULT_PORT = 9000; + +/** + * Protocol-scoped port override read by the SDK's `serve_a2a()`. The generic + * `PORT` is not used for A2A: shared images set it to another protocol's port + * (8080 for HTTP, 8000 for MCP), which would bind the A2A server off-contract. + */ +export const A2A_PORT_ENV = 'A2A_PORT'; diff --git a/src/cli/operations/dev/container-dev-server.ts b/src/cli/operations/dev/container-dev-server.ts index d53c7e372..647646a8e 100644 --- a/src/cli/operations/dev/container-dev-server.ts +++ b/src/cli/operations/dev/container-dev-server.ts @@ -2,7 +2,7 @@ import { CONTAINER_INTERNAL_PORT, DOCKERFILE_NAME, getDockerfilePath } from '../ import { getCustomBuildArgs, getUvBuildArgs } from '../../../lib/packaging/build-args'; import { ensureBuildContextDockerignore } from '../../../lib/packaging/build-context-dockerignore'; import { detectContainerRuntime } from '../../external-requirements/detect'; -import { A2A_DEFAULT_PORT, MCP_DEFAULT_PORT } from './constants'; +import { A2A_DEFAULT_PORT, A2A_PORT_ENV, MCP_DEFAULT_PORT } from './constants'; import { DevServer, type LogLevel, type SpawnConfig } from './dev-server'; import { waitForServerReady } from './utils'; import { type ChildProcess, spawn, spawnSync } from 'child_process'; @@ -250,7 +250,9 @@ export class ContainerDevServer extends DevServer { ...containerEnvVars, LOCAL_DEV: '1', PORT: String(internalPort), - ...(this.config.protocol === 'A2A' ? { AGENTCORE_RUNTIME_URL: `http://localhost:${port}/` } : {}), + ...(this.config.protocol === 'A2A' + ? { AGENTCORE_RUNTIME_URL: `http://localhost:${port}/`, [A2A_PORT_ENV]: String(internalPort) } + : {}), }).flatMap(([k, v]) => ['-e', `${k}=${v}`]); return {