This project demonstrates how to emulate named custom subagents (similar to Claude's subagent feature) using Amp's SDK. Instead of pre-registering subagents, this pattern allows you to dynamically define and invoke specialized AI agents with their own system prompts, tool permissions, and MCP configurations.
Custom subagents let you:
- Define specialized roles with specific system prompts
- Control tool access via fine-grained permission scoping
- Isolate context for focused task execution
- Reuse agent patterns across different projects
Each subagent invocation is completely stateless:
- No memory between calls
- No shared state across invocations
- Fresh execution context every time
Instead of persistent state, the main agent passes focused context:
- Extract relevant conversation history
- Include pertinent file information
- Provide project-specific details
- Pass via
contextparameter
Example:
const context = `
User is working on Next.js project with TypeScript.
Relevant files: package.json, src/utils/db.ts
User wants to migrate from CommonJS to ESM.
`
runSubagent('migration-planner', 'Create migration plan', subagents, { context })This gives the subagent focused background without the full conversation history.
# Clone the repository
git clone https://github.com/sjarmak/amp-custom-subagents
cd custom-subagent
# Install dependencies
npm install
# Optional: Install globally to use as MCP server
npm install -g .- Node.js 18+ or 20+ (recommended)
- Amp SDK (installed automatically as dependency)
- TypeScript 5.0+ (for development)
- MCP SDK (for MCP server functionality)
There are three ways to use custom subagents, each suited for different scenarios:
Best for: Natural language interaction, letting Amp decide when to use subagents
The main agent (Amp) automatically:
- Interprets natural language requests
- Decides which subagent to invoke
- Extracts and passes relevant context
- Receives structured JSON results
When to use:
- Working within Amp conversations
- Want autonomous subagent selection
- Need context automatically extracted from conversation
Best for: Scripting, automation, manual testing
Requires explicit command syntax with structured arguments.
When to use:
- Running subagents from shell scripts
- Testing subagent behavior
- Automation pipelines
- Direct invocation outside of Amp
Best for: Building custom workflows, integrating into your own tools
Full control over execution with TypeScript/JavaScript API.
When to use:
- Custom agent orchestration
- Building tools that use subagents
- Advanced workflows requiring programmatic control
- Testing and development
The easiest way to use these subagents is directly in Amp conversations:
# Install globally
npm install -g .
# Configure Amp (add to VS Code settings.json)
{
"amp.mcpServers": {
"custom-subagents": {
"command": "custom-subagents-mcp"
}
}
}
# Restart Amp, then in any conversation:
"Use the test-runner subagent to run my tests"
"Ask the documentation-writer to create API docs"
"Have the security-auditor scan this code"See SETUP.md for detailed configuration options.
How context works:
When you chat with Amp, it automatically extracts relevant context (files mentioned, user intent, project details) and passes it to the subagent via the context parameter.
# Run the test-runner subagent
npm run dev test-runner "Run unit tests and fix any failures"
# Run the migration-planner subagent
npm run dev migration-planner "Plan migration from CommonJS to ESM"
# Run the security-auditor subagent
npm run dev security-auditor "Audit codebase for security issues"Note: CLI requires explicit goal string. Context is optional via options parameter in code.
import { runSubagent } from './src/index.js'
import { subagents } from './src/subagents.js'
// Example: Main agent extracts context and passes to subagent
const context = `
User is debugging test failures in auth module.
Relevant files: src/auth/login.test.ts, src/auth/login.ts
Previous attempt: tests passed locally but fail in CI
`
const result = await runSubagent(
'test-runner',
'Run all tests and report results',
subagents,
{
context, // Pass focused conversation context
cwd: process.cwd(),
onMessage: (msg) => console.log(msg),
timeout: 60000,
}
)
// Access structured result
console.log('Summary:', result.summary)
console.log('Files changed:', result.filesChanged)
console.log('Duration:', result.metadata.duration, 'ms')Each subagent has:
- system: Role description and behavioral rules
- permissions: Tool access controls (allow/deny/ask)
- mcp: Optional MCP server configurations
import { createPermission, type SubagentRegistry } from './index.js'
export const subagents: SubagentRegistry = {
'my-subagent': {
system: `You are a specialized subagent. Your rules: ...`,
permissions: [
createPermission('Read', 'allow'),
createPermission('Write', 'ask', { matches: { path: '**/*.test.*' } }),
createPermission('Bash', 'allow', { matches: { cmd: 'npm test*' } }),
],
},
}The runSubagent function:
- Validates the subagent exists
- Constructs a prompt with system role + user goal + optional conversation context
- Executes via Amp SDK with scoped permissions
- Returns structured result with summary, transcript, file changes, and metadata
export async function runSubagent(
name: string,
userGoal: string,
registry: SubagentRegistry,
options?: RunSubagentOptions
): Promise<SubagentResult>Permissions control tool access:
// Allow all Read operations
createPermission('Read', 'allow')
// Ask before writing to source files
createPermission('Write', 'ask', { matches: { path: '**/src/**' } })
// Deny all Write operations
createPermission('Write', 'deny')
// Allow specific bash commands
createPermission('Bash', 'allow', { matches: { cmd: 'npm test*' } })This project includes an MCP server that exposes all subagents as tools Amp can use directly.
When configured, Amp can access:
subagent_test-runnersubagent_migration-plannersubagent_security-auditorsubagent_documentation-writersubagent_refactor-assistant
Subagents return structured JSON through MCP:
{
"summary": "Task completion summary",
"filesChanged": ["path/to/file1.ts", "path/to/file2.ts"],
"transcript": ["execution log line 1", "line 2"],
"metadata": {
"subagentName": "test-runner",
"goal": "Run unit tests",
"startTime": "2025-10-27T03:00:00.000Z",
"endTime": "2025-10-27T03:01:00.000Z",
"duration": 60000
}
}The main agent can parse this JSON to programmatically access results.
You: Use the test-runner subagent to fix failing tests
Amp: [invokes subagent_test-runner tool, receives JSON response]
[parses result.filesChanged to see what was modified]
You: Create migration plan with migration-planner
Amp: [invokes subagent_migration-planner tool]
[reads result.summary for the plan]
Runs tests and stabilizes failures without refactoring.
Permissions:
- Run test commands (npm/yarn/pnpm/bun test)
- Read all files
- Ask before modifying test files or source code
Example:
npm run example:test-runnerAnalyzes codebases and produces detailed migration plans (read-only).
Permissions:
- Read all files
- Run git commands
- Deny all write operations
Example:
npm run example:migrationScans for vulnerabilities and compliance issues (read-only).
Permissions:
- Read all files
- Run npm audit and git log
- Deny all write operations
Example:
npm run example:securityGenerates and updates technical documentation.
Permissions:
- Read all files
- Ask before modifying markdown and docs
- Deny modifying source code
Improves code quality without changing behavior.
Permissions:
- Read all files
- Ask before any write operation
- Run tests and build commands
Add to src/subagents.ts:
export const subagents: SubagentRegistry = {
'my-custom-agent': {
system: `You are a specialized agent for [purpose].
Rules:
- [Rule 1]
- [Rule 2]
- [Rule 3]`,
permissions: [
createPermission('Read', 'allow'),
createPermission('Write', 'ask'),
],
mcp: {
// Optional MCP server configuration
servers: {
'my-mcp-server': {
command: 'npx',
args: ['-y', 'my-mcp-server'],
},
},
},
},
}const result = await runSubagent(
'my-custom-agent',
'Your task description',
subagents
)Connect MCP servers to subagents:
{
'database-agent': {
system: 'You query databases and generate reports.',
mcp: {
servers: {
'postgres-mcp': {
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-postgres'],
env: {
DATABASE_URL: process.env.DATABASE_URL,
},
},
},
},
},
}await runSubagent('my-agent', 'task', subagents, {
timeout: 120000, // 2 minutes
})await runSubagent('my-agent', 'task', subagents, {
onMessage: (msg) => {
if (msg.type === 'text') {
console.log(msg.text)
}
},
})permissions: [
createPermission('Read', 'allow'),
createPermission('Bash', 'allow', { matches: { cmd: 'git*' } }),
createPermission('Write', 'deny'),
]permissions: [
createPermission('Read', 'allow'),
createPermission('Write', 'ask'),
createPermission('Bash', 'deny'),
]permissions: [
createPermission('Read', 'allow'),
createPermission('Bash', 'allow', { matches: { cmd: 'npm test*' } }),
createPermission('Write', 'ask', { matches: { path: '**/*.test.*' } }),
]Executes a named subagent with the specified goal.
Parameters:
name: Subagent identifier from registryuserGoal: Task description for the subagentregistry: SubagentRegistry objectoptions:cwd: Working directory (default:process.cwd())onMessage: Message handler callbacktimeout: Execution timeout in milliseconds
Returns: Promise<string> - Subagent result
Creates a permission rule for tool access control.
Parameters:
tool: Tool name ('Read','Write','Bash', etc.)action:'allow'|'deny'|'ask'options:matches: Pattern matching rulespath: File path pattern (for Read/Write)cmd: Command pattern (for Bash)
This implementation aligns with:
- Amp SDK Documentation
- Amp's permission system
- MCP configuration standards
- Amp's subagent execution model
Run pre-built examples:
npm run example:test-runner # Test execution agent
npm run example:migration # Migration planning agent
npm run example:security # Security audit agent
npm run example:documentation # Documentation writer
npm run example:refactor # Refactor assistant
npm run example:custom # Custom subagent creationSee EXAMPLES.md for detailed usage examples and patterns.
# Test the MCP server
npm run mcp
# In another terminal, send a test request:
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | npm run mcpTo add new subagents:
- Define in
src/subagents.ts - Create example in
src/examples/ - Add npm script to
package.json - Update documentation as needed
Problem: Subagent not found
Error: Unknown subagent: my-agentSolution: Check spelling and verify the subagent exists in src/subagents.ts
Problem: Permission denied
Error: Permission denied for tool WriteSolution: Check the subagent's permissions array. You may need to approve the action or adjust permissions.
Problem: Timeout errors
Error: Operation abortedSolution: Increase timeout in runSubagent() options: { timeout: 120000 }
Problem: MCP server not loading
MCP server failed to startSolution:
- Verify installation:
npm run mcp - Check Amp settings.json configuration
- Ensure Node.js and dependencies are installed
# Install globally
npm install -g .
# Run subagent via CLI
npm run dev <subagent-name> "<goal>"
# Run examples
npm run example:test-runner
npm run example:migration
npm run example:security
# Start MCP server
npm run mcp| Subagent | Purpose | Permissions |
|---|---|---|
test-runner |
Run and fix tests | Read, ask before write, test commands only |
migration-planner |
Create migration plans | Read-only, no code changes |
security-auditor |
Security vulnerability scanning | Read-only, audit commands only |
documentation-writer |
Generate/update docs | Read, ask before writing docs |
refactor-assistant |
Improve code quality | Read, ask before write, build/test commands |
// Allow unrestricted
createPermission('Read', 'allow')
// Ask before allowing
createPermission('Write', 'ask')
// Deny silently
createPermission('Bash', 'deny')
// Pattern matching
createPermission('Write', 'ask', { matches: { path: '**/*.test.*' } })
createPermission('Bash', 'allow', { matches: { cmd: 'npm test*' } })- Setup Guide - Installation and configuration
- API Reference - Complete API documentation
- Architecture Guide - Technical design and patterns
- Usage Examples - Practical examples and use cases
Contributions welcome! To add new subagents:
- Define in src/subagents.ts
- Create example in
src/examples/ - Add npm script to package.json
- Update EXAMPLES.md with usage patterns
MIT