Prelude

Claude Code has three ways to extend what it can do. Skills. Subagents. MCP servers. All three add capabilities. All three live in project directories. All three can be shared with a team. From the outside, they look like three names for the same thing.

They are not. Each one solves a different problem at a different layer of the system. Confuse them and you waste time building the wrong thing. We know because we did exactly that, more than once. Forty hours on an MCP server that should have been a ten-minute markdown file. Weeks trying to make a skill query a database it could never reach.

This guide gives you the mental model we wish we had from the start. Not a theoretical comparison, but a practical decision framework built from running 8 production plugins with 34+ skills, multiple subagents, and MCP servers connecting to real systems.

The Problem

The moment you need Claude Code to do something beyond its defaults, you face a three-way decision. The skills system lets you create reusable prompts as slash commands. Subagents give you specialised AI sessions with their own model, tools, and context. MCP servers connect Claude to external tools and data sources.

Each mechanism has its own directory structure, its own configuration format, and its own documentation. What none of the documentation makes clear is how the three relate to each other, when to reach for which one, and how to compose them together.

Choosing wrong is expensive. Not just in development time, but in ongoing maintenance, team confusion, and missed capabilities. A skill trying to do what an MCP server should do will hallucinate data. An MCP server doing what a skill should do wastes engineering effort on infrastructure that serves static text. And running everything in the main Opus session when subagents could handle routine tasks at a fraction of the cost burns money on every conversation.

The Journey

What Skills Are

Skills are the simplest mechanism. A skill is a markdown file that becomes a slash command. When invoked, the entire file contents are injected into the conversation as instructions for Claude to follow.

Skills live in .claude/skills/ with each skill in its own subdirectory:

.claude/skills/
  review/SKILL.md       → /review
  migration/SKILL.md    → /migration
  deploy-check/SKILL.md → /deploy-check

Here is a real skill from production that enforces code review standards:

Review the code changes for:
- SQL injection vulnerabilities in any database queries
- Missing error handling on external API calls
- British English in all user-facing strings
- Consistent use of snake_case in Rust code

Focus on: $ARGUMENTS

Provide fixes as code blocks, not just descriptions.

Type /review src/handlers/auth.rs and every developer on the team applies the same review standards. The $ARGUMENTS placeholder is replaced with whatever follows the command name.

Skills accept arguments, can inject dynamic context via shell commands, and get invoked manually with slash commands or automatically when Claude detects relevance. They are version controlled with your project, so when conventions change, one file update propagates to the entire team.

The overhead is near zero. Creating a skill takes minutes. Modifying it takes seconds. No code, no compilation, no protocol knowledge. Anyone who can write a paragraph can write a skill, which makes them particularly valuable for teams that include non-developers. Our guide on skills for non-technical teams covers how to get everyone involved.

Here is a second production skill that generates database migrations following team conventions:

Generate a database migration for: $ARGUMENTS

Rules:
1. Use the migration framework in db/migrations/
2. Include both up and down migrations
3. Use British English in all comments
4. Add an index for any new foreign key column
5. Name format: YYYYMMDD_HHMMSS_description.sql
6. Never use CASCADE on production tables without explicit approval
7. Include a rollback verification query at the end

Reference the current schema in db/schema.sql for column types and naming.

And a third that standardises how the team writes API endpoint documentation:

Generate API documentation for the endpoint: $ARGUMENTS

Follow these conventions:
1. Start with a one-sentence description of what the endpoint does
2. List all path parameters with types and constraints
3. Show the request body schema as a JSON example
4. Show a success response and at least two error responses
5. Include a curl example with realistic test data
6. Note any authentication requirements
7. Note any rate limits that apply

Output format: Markdown suitable for inclusion in docs/api/

Each of these skills encodes knowledge that would otherwise live in a wiki page nobody reads. The difference is that a wiki page is passive. A skill is active. It participates in the work rather than sitting next to it.

When to use skills: The capability is about reusable knowledge, conventions, or workflow templates. Code review checklists. Migration patterns. Deployment procedures. Commit message formatting. Anything where Claude needs to follow instructions rather than access external data.

What Subagents Are

Subagents are specialised AI agents that run inside Claude Code with their own system prompt, tool restrictions, model selection, and context window. They handle specific tasks and return results to the main conversation without polluting it.

Subagents live as markdown files in .claude/agents/ for project scope or ~/.claude/agents/ for personal scope. YAML frontmatter defines the configuration. The body becomes the system prompt.

---
name: code-reviewer
description: Reviews code for quality, security, and style
model: haiku
tools: Read, Grep, Glob, Bash
disallowedTools: Write, Edit
mcpServers:
  - github
skills:
  - review
---

You are a code review specialist. You have read-only access to the
codebase and the GitHub MCP server. Review code against the team's
standards with a focus on security, performance, and maintainability.
Never suggest changes without providing specific code examples.

Several things are happening in that configuration:

Model selection. The model: haiku field routes this subagent to Haiku instead of Opus. For routine code review (pattern matching, style enforcement, obvious bug detection), Haiku is more than sufficient. It costs a fraction of Opus and runs faster.

Tool restrictions. The disallowedTools: Write, Edit field means this subagent literally cannot modify files. It reads, analyses, and reports. Read-only by design, not by convention.

Scoped MCP access. The mcpServers: - github field gives the subagent access to the GitHub MCP server but nothing else. Your database, your deployment pipeline, your internal APIs are all invisible to this subagent.

Skill preloading. The skills: - review field loads the review skill into the subagent's context, so it follows your team's exact review conventions.

Isolated context. The subagent runs in its own context window. Exploration, analysis, and file reads do not accumulate in the main conversation. When the subagent finishes, only its results come back.

Full Subagent Frontmatter Reference

The YAML frontmatter supports the following fields, each controlling a different aspect of subagent behaviour:

  • name (required). The identifier used to invoke the subagent. Must be unique within the project. This becomes the agent name you reference when delegating tasks.
  • description (required). A short description of what the subagent does. Claude uses this to determine when to delegate tasks to the subagent automatically, so make it specific. "Reviews code" is too vague. "Reviews Rust code for security vulnerabilities and clippy compliance" is useful.
  • model. Which model to use. Options include opus, sonnet, and haiku. Defaults to the session's current model if omitted. This is where cost optimisation happens. Routine tasks on Haiku, analytical tasks on Sonnet, complex reasoning on Opus.
  • tools. A comma-separated list of tools the subagent can use. If omitted, the subagent inherits the default tool set. Specifying this field explicitly is good practice because it documents what the subagent is designed to do.
  • disallowedTools. Tools the subagent must never use. This is a safety mechanism. A review subagent with disallowedTools: Write, Edit physically cannot modify your codebase, regardless of what its system prompt says.
  • mcpServers. A list of MCP server names the subagent can access. Only servers already configured in your project's .mcp.json or settings can be listed here. The subagent sees only these servers. All others are invisible.
  • skills. A list of skill names to preload into the subagent's context. The skill contents are injected into the subagent's system prompt, so it follows your team conventions without being told to.
  • permissionMode. Controls how the subagent handles tool approval. Set to bypassPermissions for fully autonomous operation, or leave at default for standard approval workflows. Use with caution in production.
  • maxTurns. Limits how many conversation turns the subagent can take before returning. Prevents runaway subagents that spiral into endless exploration. A value of 10-20 is reasonable for most tasks.
  • background. When set to true, the subagent runs concurrently with the main session. The main conversation continues while the subagent works. Results are returned when the subagent finishes. Useful for long-running analysis tasks that should not block interactive work.
  • isolation. Set to worktree to give the subagent its own git working tree. This means the subagent can checkout branches, stage files, and make commits without affecting the main working directory. Essential for subagents that perform git operations.
  • hooks. Define lifecycle hooks that run at specific points during the subagent's execution. Hooks can run shell commands before or after the subagent starts, enabling setup tasks like environment preparation or cleanup.
  • memory. Controls whether the subagent retains memory across invocations. When enabled, the subagent builds up context over time, which is useful for subagents that need to learn project patterns incrementally.

When to use subagents: The capability requires different behaviour: a different model for cost or capability reasons, restricted tool access for safety, isolated context to keep the main conversation clean, or a specialised system prompt for a specific domain.

What MCP Servers Are

MCP servers connect Claude Code to external tools and data sources. They are programs that speak the Model Context Protocol and expose capabilities (tools, resources, and prompts) that Claude discovers at startup and calls when needed.

An MCP server runs as a separate process, either locally over stdio or remotely over HTTP. It can be written in any language. Claude communicates with it via JSON-RPC.

{
  "mcpServers": {
    "analytics": {
      "command": "node",
      "args": ["./mcp-servers/analytics.js"],
      "env": {
        "DATABASE_URL": "postgresql://localhost:5432/analytics"
      }
    }
  }
}

The key characteristic of MCP servers is that they bridge Claude to things outside its session. A database. An API. A deployment pipeline. A monitoring system. If the data or action lives externally, an MCP server is the mechanism that provides access.

MCP servers maintain their own connections, state, and lifecycle. They handle error recovery, caching, and rate limiting independently of Claude Code. This separation of concerns means your MCP server can be as sophisticated as it needs to be without affecting Claude's session.

The trade-off is development effort. You write real code, handle real errors, manage real dependencies. For our performance-critical integrations, we build MCP servers in Rust. For simpler use cases, TypeScript or Python work fine.

When a CLI Is Enough

Not everything external needs an MCP server. CLIs invoked through the Bash tool work brilliantly for local, single-agent workflows. Running a database migration, calling an API endpoint, or checking deployment status with a one-shot command are all tasks where a CLI is simpler and sufficient.

MCP servers become necessary when you need:

  • Remote execution. The tool must run on a different machine or in a different environment from the Claude Code session.
  • Permission scoping. You want to expose specific capabilities without giving Claude full shell access.
  • Persistent connections. The integration requires a long-lived connection (database connection pools, WebSocket streams, authenticated API sessions).
  • Stateful operations. The tool maintains state across multiple calls that a one-shot CLI invocation cannot preserve.

If your use case does not require any of these, a CLI tool called through Bash is the right choice. Over-reaching for MCP servers adds unnecessary complexity and development cost.

When to use MCP servers: Claude needs to access external data or perform side effects that require remote execution, permission scoping, persistent connections, or stateful operations. If a one-shot CLI call through the Bash tool can accomplish the task, prefer that simpler approach. Reserve MCP servers for integrations that genuinely need the protocol's capabilities.

The Decision Framework

After building the wrong thing three times, this framework has not failed once:

Need to extend Claude Code?
  │
  ├── Does Claude need external data or side effects?
  │   ├── Can a one-shot CLI call handle it?
  │   │   └── Yes → Bash tool with CLI
  │   └── Needs remote execution, permission scoping,
  │       persistent connections, or stateful operations?
  │       └── Yes → MCP Server
  │
  ├── Does Claude need different behaviour?
  │   (different model, restricted tools, isolated context)
  │   └── Yes → Subagent
  │
  ├── Does Claude need reusable instructions?
  │   (conventions, checklists, templates, workflows)
  │   └── Yes → Skill
  │
  └── Complex requirement?
      └── Compose all three

The questions are ordered by cost. Skills cost minutes to create. Subagents cost a markdown file with frontmatter. MCP servers cost real engineering time. Start at the cheapest option and only escalate when necessary.

Common Mistakes and Anti-patterns

Building an MCP server for static prompts. If your server returns the same text every time, it should be a skill. MCP servers are for dynamic data, not static instructions. We built a 400-line TypeScript MCP server that provided code review prompts. It had a health check endpoint, error handling, logging, and structured JSON responses. The entire thing was replaced by a 15-line markdown file. The markdown file was also easier to update because anyone on the team could edit it without knowing TypeScript.

Writing a skill that needs external data. A skill saying "check the deployment status" will produce hallucinations. Claude cannot query your deployment system through a markdown prompt. That is an MCP server problem. The tell-tale sign is when your skill includes phrases like "look up," "query," "fetch," or "check the current state of." If the skill needs information that does not exist in the codebase, it needs an MCP server.

Running everything in the main Opus session. Routine code review, documentation generation, and style checking do not need Opus. A Haiku subagent with read-only access handles them faster and cheaper. We tracked our token usage over a month and found that 60% of Opus tokens were spent on tasks that Haiku could handle identically. That is money set on fire.

Ignoring composition. The three mechanisms are not alternatives. They are layers. A subagent with an MCP server and a preloaded skill is more powerful than any of the three alone.

Giving subagents too many tools. A subagent that can do everything is just another Opus session. The value of subagents comes from restrictions. A documentation subagent should not be able to run arbitrary bash commands. A review subagent should not be able to write files. If your subagent's tool list looks like the main session's tool list, you have not scoped it properly.

Skipping the description field. Claude uses the subagent's description to decide when to delegate automatically. A vague description like "helps with code" means Claude will either delegate too aggressively or not at all. Write descriptions that are specific enough for Claude to match against incoming tasks. "Analyses PostgreSQL query plans and suggests index optimisations" is a description Claude can act on.

Making subagents too autonomous too early. Starting with permissionMode: bypassPermissions and maxTurns: 100 is asking for trouble. Begin with default permissions and a low maxTurns value. Watch what the subagent does. Increase autonomy only after you have seen it behave correctly across a range of tasks. A runaway subagent with full permissions can make a mess faster than you can notice.

Using isolation when you do not need it. The isolation: worktree option creates a separate git working tree. This is powerful for subagents that need to experiment with branches, but it adds overhead. If your subagent only reads files and reports findings, it does not need its own worktree. Reserve isolation for subagents that perform git operations or need to modify files without affecting your working directory.

Composing All Three

The real architecture is layered:

Layer 3: Skills (Team Knowledge)
  /review, /migration, /deploy-check, /sql-standards

Layer 2: Subagents (Specialised Behaviour)
  code-reviewer (Haiku, read-only, GitHub MCP)
  debugger (Opus, full access)
  database-analyst (Sonnet, PostgreSQL MCP, sql-standards skill)

Layer 1: MCP Servers (External Connections)
  analytics-db, deploy-pipeline, github, content-api

Consider a database-analyst subagent that composes all three:

---
name: database-analyst
description: Analyses database performance and suggests optimisations
model: sonnet
tools: Read, Grep, Bash
mcpServers:
  - postgresql
skills:
  - sql-standards
disallowedTools: Write, Edit
maxTurns: 20
---

You are a database performance specialist. You have read-only access
to the PostgreSQL MCP server. Analyse schemas, query plans, and index
usage. Follow the SQL standards defined in the preloaded skill.
Never suggest destructive operations without explicit confirmation.

This subagent runs on Sonnet (capable enough for analysis, cheaper than Opus). It can query PostgreSQL through the MCP server but cannot access any other external system. It follows the team's SQL conventions from the preloaded skill. It cannot modify files. And it runs in its own context, so schema analysis and query plan inspection do not clutter the main conversation.

Here is a second composition for a documentation generator:

---
name: docs-generator
description: Generates and updates API documentation from source code
model: haiku
tools: Read, Grep, Glob
disallowedTools: Write, Edit, Bash
mcpServers:
  - content-api
skills:
  - docs-api
maxTurns: 15
background: true
---

You are a documentation specialist. Read the source code to understand
endpoint signatures, request/response types, and error handling. Use
the content-api MCP server to check existing published documentation
for gaps. Generate documentation following the conventions in the
preloaded docs-api skill. Output your documentation as markdown that
can be reviewed before publishing.

This subagent runs on Haiku because documentation generation is primarily pattern matching and formatting. It runs in the background so the developer can continue working while documentation is generated. It has no write access, so it produces documentation as output text rather than modifying files directly. The content-api MCP server lets it check what documentation already exists, so it focuses on gaps rather than duplicating content.

A third composition for a security auditor:

---
name: security-auditor
description: Audits code for security vulnerabilities including SQL injection, XSS, and authentication bypasses
model: sonnet
tools: Read, Grep, Glob, Bash
disallowedTools: Write, Edit
mcpServers:
  - github
skills:
  - review
isolation: worktree
maxTurns: 30
---

You are a security specialist. Audit the codebase for vulnerabilities
with particular attention to input validation, authentication flows,
and data sanitisation. Check git history for recently changed files
in security-sensitive areas. Cross-reference open issues on GitHub
for any reported security concerns. Produce a structured report with
severity ratings (critical, high, medium, low) and specific remediation
steps for each finding.

This subagent uses isolation: worktree because it needs to inspect git history and potentially checkout different branches to compare implementations. It runs on Sonnet because security analysis requires reasoning about code flow, not just pattern matching. The GitHub MCP server lets it check for security-related issues. The review skill ensures it follows the team's existing review conventions while adding the security-specific focus from its system prompt.

Three mechanisms, composed into exactly the specialist needed. Scoped access, scoped cost, scoped purpose.

For teams building production workflows, the composition model is how you get the most out of Claude Code. Plugins can bundle all three. Our guide on publishing plugins to the marketplace covers how to package skills, agents, and MCP servers for distribution.

Cost Implications

Before subagents, every task runs in one Opus session. Code review on Opus. Debugging on Opus. Documentation on Opus. The context window grows with every task, and you pay for every token.

After introducing subagents, the cost structure changes fundamentally:

Routine tasks run on cheaper models. Code review on Haiku. Documentation generation on Sonnet. Style enforcement on Haiku. These are pattern-matching tasks where the smaller models perform well.

Context stays clean. Each subagent gets a fresh context window containing only what it needs: its system prompt, the relevant files, and the preloaded skill. No accumulated context from earlier conversation turns.

The main session stays focused. Deep architectural decisions, complex debugging, and multi-file refactoring stay in the main Opus session where the full context is valuable.

For teams doing heavy Claude Code usage, subagent-based model routing is the single biggest cost optimisation available. Our cost optimisation guide covers the broader strategies, but the subagent pattern is the foundation.

Migrating from a Single Session to a Layered Architecture

Most teams start with a single Opus session handling everything. Moving to a layered architecture does not need to happen all at once. Here is a practical migration path.

Week one. Identify your repeating prompts. Spend a week noting every time you give Claude the same instruction twice. "Review this for security issues." "Generate a migration following our conventions." "Write documentation for this endpoint." Each repeated instruction is a skill waiting to be created. Extract five or six of them into .claude/skills/ by the end of the week.

Week two. Identify your routine tasks. Look at your Claude Code usage and categorise tasks by complexity. Which tasks are routine pattern matching that Haiku could handle? Which require genuine reasoning that needs Sonnet or Opus? Create your first subagent for the most common routine task. A code-reviewer subagent on Haiku with read-only access is a good starting point because it is low risk and high frequency.

Week three. Add MCP servers for external access. If your subagents or main session need to query databases, check deployment status, or interact with APIs, build MCP servers for those integrations. Start with the integration you use most frequently. A PostgreSQL MCP server or a GitHub MCP server covers most teams' primary needs.

Week four. Compose and refine. Now that you have skills, subagents, and MCP servers, start composing them. Give your subagents access to relevant MCP servers and preload relevant skills. Adjust maxTurns based on what you observe. Tune model selection based on output quality. This is an iterative process.

The key principle is to start with skills because they are free. Then add subagents because they are cheap. Then add MCP servers because they are necessary. Do not build all three at once. Each layer should prove its value before you add the next.

Introducing Subagents to Your Team

Getting a team to adopt subagents requires more than dropping agent files into the repository. Teams that have been using skills and MCP servers have established workflows. Subagents change the dynamics by introducing delegation, and delegation requires trust.

Start with a read-only subagent. The safest introduction is a subagent that cannot modify anything. A code-reviewer or documentation-checker that only reads and reports. No one feels threatened by a read-only agent. It adds value without risk. Let the team use it for a sprint and see the results.

Share the cost data. Run a two-week comparison. Track token usage and cost with and without subagents for comparable tasks. When developers see that a Haiku subagent costs a tenth of what the same task costs on Opus, adoption becomes a pragmatic decision rather than a philosophical one.

Let developers create their own subagents. Mandate nothing. Provide the pattern, the reference examples, and the frontmatter documentation. Developers who see the benefit will create subagents for their own workflows. The ones that prove useful will naturally get adopted by the rest of the team through code review and word of mouth.

Establish naming conventions early. Once multiple developers start creating subagents, naming collisions and unclear purposes become a problem. Agree on a naming convention upfront. We use the pattern domain-action, so db-analyst, code-reviewer, docs-generator, security-auditor. The name should make the subagent's purpose obvious without reading the file.

Document what each subagent can and cannot do. Not in a separate document. In the subagent file itself. The system prompt body should clearly state the subagent's scope, capabilities, and limitations. When a developer invokes a subagent and it does exactly what was promised, trust builds quickly.

Graduate permissions gradually. Start every new subagent with disallowedTools: Write, Edit and a low maxTurns. As the team gains confidence in a subagent's behaviour, relax the restrictions. A subagent that has been reliably reviewing code for a month can probably be trusted with background: true for async operation. But start restricted and earn trust.

Building Custom Agents

For a deeper dive into building and configuring subagents, including composition patterns and real-world examples from production plugin development, our guide on building custom Claude agents walks through the complete process.

The relationship between subagents and the other extensibility mechanisms in the plugins, MCP servers, and skills comparison provides additional architectural context for teams planning their extension strategy.

The Lesson

Claude Code's three extensibility mechanisms are not interchangeable options. They are layers in an extension architecture, each solving a distinct class of problem.

Skills encode knowledge. They are fast to create, require no code, and make team conventions executable. Start here.

Subagents specialise behaviour. They run different models, restrict tools, isolate context, and compose with MCP servers and skills. Use them when you need Claude to work differently for specific tasks.

MCP servers bridge to external systems. They connect Claude to databases, APIs, and tools that exist outside its session. Build them when Claude needs to reach outside.

The decision takes one second once you internalise the framework. And the three compose beautifully into scoped specialists that do exactly what you need at exactly the right cost.

Conclusion

We spent months learning this by building the wrong thing. An MCP server that should have been a skill. A skill that needed to be an MCP server. Opus sessions that should have been Haiku subagents.

The extensibility triangle is simple. Skills for knowledge. Subagents for behaviour. MCP servers for connections. And composition for anything complex.

Start by auditing your current Claude Code setup. If you are running everything in one session on one model, you are almost certainly overpaying for routine tasks. If you are building MCP servers that serve static prompts, you are over-engineering markdown files. If you have no skills at all, you are repeating instructions that should be codified once.

The migration path is incremental. Extract your repeated prompts into skills this week. Create your first read-only subagent next week. Build an MCP server for your most-used external system the week after. Each layer proves its value independently, and the composition of all three is where the real leverage appears.

The right mechanism for the right problem. Three layers, three purposes, zero overlap.