Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
25 changes: 13 additions & 12 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,22 +475,23 @@ Start local development server with hot-reload.
agentcore dev
agentcore dev --agent MyAgent --port 3000
agentcore dev --logs # Non-interactive
agentcore dev --invoke "Hello" --stream # Direct invoke
agentcore dev "Hello" --stream # Invoke running server
agentcore dev "Hello" --agent MyAgent # Invoke specific agent

# MCP protocol dev commands
agentcore dev --invoke list-tools
agentcore dev --invoke call-tool --tool myTool --input '{"arg": "value"}'
agentcore dev list-tools
agentcore dev call-tool --tool myTool --input '{"arg": "value"}'
```

| Flag | Description |
| ----------------------- | ------------------------------------------------------ |
| `-p, --port <port>` | Port (default: 8080; MCP uses 8000, A2A uses 9000) |
| `-a, --agent <name>` | Agent to run (required if multiple agents) |
| `-i, --invoke <prompt>` | Invoke running server |
| `-s, --stream` | Stream response (with --invoke) |
| `-l, --logs` | Non-interactive stdout logging |
| `--tool <name>` | MCP tool name (with `--invoke call-tool`) |
| `--input <json>` | MCP tool arguments as JSON (with `--invoke call-tool`) |
| Flag / Argument | Description |
| -------------------- | ---------------------------------------------------- |
| `[prompt]` | Invoke running dev server with this prompt |
| `-p, --port <port>` | Port (default: 8080; MCP uses 8000, A2A uses 9000) |
| `-a, --agent <name>` | Agent to run or invoke (required if multiple agents) |
| `-s, --stream` | Stream response when invoking |
| `-l, --logs` | Non-interactive stdout logging |
| `--tool <name>` | MCP tool name (with `call-tool` prompt) |
| `--input <json>` | MCP tool arguments as JSON (with `--tool`) |

### invoke

Expand Down
11 changes: 4 additions & 7 deletions docs/local-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,14 @@ agentcore dev --logs
With the dev server running, open another terminal:

```bash
# Interactive chat
agentcore invoke

# Single prompt
agentcore invoke "What can you do?"
agentcore dev "What can you do?"

# With streaming
agentcore invoke "Tell me a story" --stream
agentcore dev "Tell me a story" --stream

# Direct invoke to running server
agentcore dev --invoke "Hello" --stream
# Specify agent (if multiple)
agentcore dev "Hello" --agent MyAgent
```

## Environment Setup
Expand Down
38 changes: 36 additions & 2 deletions src/cli/commands/dev/__tests__/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@ describe('dev command', () => {
const result = await runCLI(['dev', '--help'], process.cwd());

expect(result.exitCode).toBe(0);
expect(result.stdout.includes('[prompt]'), 'Should show [prompt] positional argument').toBeTruthy();
expect(result.stdout.includes('--port'), 'Should show --port option').toBeTruthy();
expect(result.stdout.includes('--agent'), 'Should show --agent option').toBeTruthy();
expect(result.stdout.includes('--invoke'), 'Should show --invoke option').toBeTruthy();
expect(result.stdout.includes('--stream'), 'Should show --stream option').toBeTruthy();
expect(result.stdout.includes('--logs'), 'Should show --logs option').toBeTruthy();
expect(result.stdout.includes('8080'), 'Should show default port').toBeTruthy();
});

it('does not show --invoke flag', async () => {
const result = await runCLI(['dev', '--help'], process.cwd());

expect(result.exitCode).toBe(0);
expect(result.stdout.includes('--invoke'), 'Should not show removed --invoke option').toBeFalsy();
});
});

describe('requires project context', () => {
Expand All @@ -28,6 +35,34 @@ describe('dev command', () => {
});
});

describe('positional prompt invoke', () => {
it('attempts invoke when positional prompt is provided', async () => {
// With no dev server running, the invoke path triggers a connection error
const result = await runCLI(['dev', 'Hello agent'], process.cwd());

expect(result.exitCode).toBe(1);
// Should attempt to connect to dev server and fail — not show a project error
const output = result.stderr.toLowerCase();
expect(
output.includes('fetch failed') || output.includes('econnrefused') || output.includes('dev server not running'),
`Should attempt invoke and fail with connection error, got: ${result.stderr}`
).toBeTruthy();
});

it('does not require project context when invoking', async () => {
// Invoke path loads config but does not call requireProject()
// So the error should be about connection, not missing project
const result = await runCLI(['dev', 'test prompt'], process.cwd());

expect(result.exitCode).toBe(1);
const output = result.stderr.toLowerCase();
expect(
!output.includes('no agentcore project found'),
`Should not fail with project error when prompt is provided, got: ${result.stderr}`
).toBeTruthy();
});
});

describe('flag validation', () => {
it('rejects invalid port number', async () => {
const result = await runCLI(['dev', '--port', 'abc'], process.cwd());
Expand All @@ -46,7 +81,6 @@ describe('dev command', () => {

expect(result.exitCode).toBe(0);
expect(result.stdout.includes('--stream'), 'Should show --stream option').toBeTruthy();
expect(result.stdout.includes('--invoke'), 'Should show --invoke option').toBeTruthy();
});
});
});
29 changes: 15 additions & 14 deletions src/cli/commands/dev/command.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ async function handleMcpInvoke(
}
} else if (invokeValue === 'call-tool') {
if (!toolName) {
console.error('Error: --tool is required with --invoke call-tool');
console.error('Usage: agentcore dev --invoke call-tool --tool <name> --input \'{"arg": "value"}\'');
console.error('Error: --tool is required with call-tool');
console.error('Usage: agentcore dev call-tool --tool <name> --input \'{"arg": "value"}\'');
process.exit(1);
}
// Initialize session first, then call tool with the session ID
Expand All @@ -117,8 +117,8 @@ async function handleMcpInvoke(
} else {
console.error(`Error: Unknown MCP invoke command "${invokeValue}"`);
console.error('Usage:');
console.error(' agentcore dev --invoke list-tools');
console.error(' agentcore dev --invoke call-tool --tool <name> --input \'{"arg": "value"}\'');
console.error(' agentcore dev list-tools');
console.error(' agentcore dev call-tool --tool <name> --input \'{"arg": "value"}\'');
process.exit(1);
}
} catch (err) {
Expand All @@ -137,20 +137,20 @@ export const registerDev = (program: Command) => {
.command('dev')
.alias('d')
.description(COMMAND_DESCRIPTIONS.dev)
.argument('[prompt]', 'Invoke running dev server with this prompt [non-interactive]')
.option('-p, --port <port>', 'Port for development server', '8080')
.option('-a, --agent <name>', 'Agent to run or invoke (required if multiple agents)')
.option('-i, --invoke <prompt>', 'Invoke running dev server (use --agent if multiple) [non-interactive]')
.option('-s, --stream', 'Stream response when using --invoke [non-interactive]')
.option('-s, --stream', 'Stream response when invoking [non-interactive]')
.option('-l, --logs', 'Run dev server with logs to stdout [non-interactive]')
.option('--tool <name>', 'MCP tool name (used with --invoke call-tool) [non-interactive]')
.option('--input <json>', 'MCP tool arguments as JSON (used with --invoke call-tool) [non-interactive]')
.option('--tool <name>', 'MCP tool name (used with "call-tool" prompt) [non-interactive]')
.option('--input <json>', 'MCP tool arguments as JSON (used with --tool) [non-interactive]')
.option(
'-H, --header <header>',
'Custom header to forward to the agent (format: "Name: Value", repeatable) [non-interactive]',
(val: string, prev: string[]) => [...prev, val],
[] as string[]
)
.action(async opts => {
.action(async (positionalPrompt: string | undefined, opts) => {
try {
const port = parseInt(opts.port, 10);

Expand All @@ -160,8 +160,9 @@ export const registerDev = (program: Command) => {
headers = parseHeaderFlags(opts.header);
}

// If --invoke provided, call the dev server and exit
if (opts.invoke) {
// If a prompt is provided, call the dev server and exit
const invokePrompt = positionalPrompt;
if (invokePrompt) {
const invokeProject = await loadProjectConfig(getWorkingDirectory());

// Determine which agent/port to invoke
Expand Down Expand Up @@ -190,11 +191,11 @@ export const registerDev = (program: Command) => {

// Protocol-aware dispatch
if (protocol === 'MCP') {
await handleMcpInvoke(invokePort, opts.invoke, opts.tool, opts.input, headers);
await handleMcpInvoke(invokePort, invokePrompt, opts.tool, opts.input, headers);
} else if (protocol === 'A2A') {
await invokeA2ADevServer(invokePort, opts.invoke, headers);
await invokeA2ADevServer(invokePort, invokePrompt, headers);
} else {
await invokeDevServer(invokePort, opts.invoke, opts.stream ?? false, headers);
await invokeDevServer(invokePort, invokePrompt, opts.stream ?? false, headers);
}
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/tui/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const COMMAND_DESCRIPTIONS = {
add: 'Add resources (agent, evaluator, online-eval, memory, credential, target)',
create: 'Create a new AgentCore project',
deploy: 'Deploy project infrastructure to AWS via CDK.',
dev: 'Launch local development server with hot-reload.',
dev: 'Launch local dev server, or invoke a running one.',
invoke: 'Invoke a deployed agent endpoint.',
logs: 'Stream or search agent runtime logs.',
package: 'Package agent artifacts without deploying.',
Expand Down
Loading