Skip to content
3. Capabilities

3. Capabilities

In one glance

  • You will: Add ten capabilities one at a time, each with a limit and a check that fails when it is widened.
  • You need: mise run install done at the repository root. No model, key, or container for the commands here. Assumed, not required: the agent from 2.1. First Agent.
  • Time: about 8 minutes, orientation.

What the agent cannot do yet, and which page adds it

A capability is an addition that widens the agent’s reach: a tool, a reviewed procedure, a memory store, a declared stage order, a specialist. Adding one is easy: anything gets more capable when you hand it more tools. The work is drawing a limit around each addition, so that widening it fails a check instead of surfacing during an incident.

Most of those limits are decidable offline: a Go test or a schema comparison settles them in seconds, with no model or network. Two are not, because the model decides them at runtime — which skill it loads, and which specialist the coordinator picks — and their pages say so.

The agent you ran in 2.1. First Agent answers from a fixed list of reads it holds in-process, and that is all. It cannot answer a single question in the left column below.

Ten pages, one capability each. Each tag names the page kind, then what it costs to run:

When you ask…What the page hands youPage
What am I actually building?one module, one binary, four surfaces3.0. Packaging (hands-on · offline)
How does it reach a real system?typed reads, writes that fail closed3.1. Tools (hands-on · offline; model at the exercise gate)
Where do procedures live?reviewed procedures, loaded only when they match3.2. Skills (hands-on · offline; model at the exercise gate)
Who else can call these tools?six reads on the wire, allowlisted3.3. MCP (hands-on · offline, second terminal)
What should it remember?six stores, and ranking you can watch3.4. Memory (hands-on · offline, MCP server running)
What stops it skipping a step?four stages the model cannot shortcut3.5. Workflows (hands-on · offline, except the live run)
How does another team call it?an address, a card, durable tasks3.6. A2A (hands-on · offline, except the live task)
Who is allowed to act?specialists that cannot borrow authority3.7. Multi-Agent (hands-on · offline, except the coordinator run)
Why is nothing on screen yet?per-token delivery, and its redaction window3.8. Streaming (hands-on · needs a model)
Does the whole thing work?INC-002, read back call by call3.9. Incident Run (hands-on · needs a model)

Those ten pages run to about five hours of hands-on work in total, with a seam in the middle: 3.0 to 3.4 add capabilities the model chooses to use, while 3.5 to 3.9 change who decides the sequence and who is allowed to act. 3.4. Memory closes that first half, a natural stopping point.

Every one of those pages adds an element to the same value you already read in Chapter 2 — a config with an instruction, a tool list, and a toolset list, where a toolset supplies its tools per turn rather than at build time:

func (c *Compose) conversationalConfig() llmagent.Config {
	localReads, readToolsets := c.readTools()

	cfg := c.baseConfig(AgentName, AgentDescription, c.instruction)
	cfg.Tools = concatTools(localReads, c.tools.ActionTools(), c.memory)
	cfg.Toolsets = append(readToolsets, c.skills)
	return cfg
}

// ConversationalAgent builds the default entrypoint.
func (c *Compose) ConversationalAgent() (agent.Agent, error) {
	return newAgent(c.conversationalConfig())
}

How to choose where a new capability lives

Placement is the decision that survives contact with production; there are only five answers. Four questions pick between them, in order.

    flowchart TD
    Q1{"Need model judgment?"} -->|no| Go["plain Go"]
    Q1 -->|yes| Q2{"Fixed bounded order?"}
    Q2 -->|yes| W["ADK workflow"]
    Q2 -->|no| Q3{"Independent deployment boundary?"}
    Q3 -->|yes| A2A["A2A service"]
    Q3 -->|no| Q4{"Distinct specialist authority?"}
    Q4 -->|yes| M["in-process delegation"]
    Q4 -->|no| A["single agent"]
  

Diagram in words: Work needing no model judgment stays plain Go. Model-backed work with a fixed order becomes a workflow. A capability with its own deployment owner becomes an A2A service; distinct authority inside one runtime becomes delegation. Everything else stays one agent.

MCP sits outside that tree: it moves a tool into another process without deciding the orchestration shape, which is why 3.3 changes where the six reads execute and changes nothing the model sees.

Where each capability lives, and what it deliberately lacks

Each capability has one owning package, and its authority stops at that package’s edge.

CapabilitySource authorityAuthority it does not get
Packagingagents/go/go.mod, go.sum, and vendor/ when generatedno second binary per composition
Incident tools and actionsagents/go/toolsno generic SQL or filesystem escape hatch
Skillsagents/go/compose/skills.go, agents/data/skillsno arbitrary file reads from a skill directory
MCP server and clientagents/go/mcpserver, agents/go/compose/mcp.gono write tool, in either direction
Memory and retrievalagents/go/memoryno silent context injection
Workflow and delegationagents/go/compose/workflow.go, delegation.gono write tool on any workflow stage
A2A runtimeagents/go/a2aserverno guarded write without a verified principal
Black-box protocol runsevalsno import of the agent implementation

Typed configuration owns the switches that move those limits; mise run config:check prints the resolved values with secrets masked:

  • Placement: AGENT_MCP_URL moves conversational reads to a remote server; AGENT_ENTRYPOINT selects the agent, workflow, or coordinator composition.
  • Retrieval: AGENT_SEMANTIC_RETRIEVAL swaps the retrieval scorer.
  • Bounds: AGENT_MAX_HISTORY_MESSAGES and AGENT_MAX_TOKENS_PER_SESSION bound context work; AGENT_A2A_MAX_LLM_CALLS and AGENT_A2A_STREAMING bound network execution.
  • Writes: AGENT_WRITES_DISABLED freezes actions without touching a read schema.

What this chapter proved

Run the whole Go module suite, then the standalone evaluation assets. Neither needs a model:

cd agents/go
mise run check
mise run test
cd ../../evals
mise run eval:validate

mise run test ends by reading the coverage profile it just wrote. Here is the tail of a real run in this checkout, with fourteen of the twenty package lines cut for width:

DONE 1815 tests, 1 skipped in 1.964s
[test] $ ../../scripts/check-coverage.sh coverage.out 80 agents/go
  ok      84.2%  agents/go/a2aserver
  ok      91.7%  agents/go/compose
  ok      88.6%  agents/go/mcpserver
  ok      87.6%  agents/go/memory
  ok      90.9%  agents/go/policy
  ok      98.5%  agents/go/tools
agents/go meets the 80% per-package coverage floor

Two seconds, no model, no network. The floor applies per package, not to a repository total, because a total lets a well-tested domain package carry an untested mcpserver: a capability added without tests fails the run instead of diluting an average.

You are done when:

  • The tool, skill, MCP, retrieval, workflow, A2A, and delegation tests pass under the race detector, and the evaluation assets validate without importing the agent.
  • If you took the exercise, you kept the read tool you prototyped in 3.1. Tools — parse-at-the-boundary input, bounded output, and an explicit MCP exposure decision.
  • You can pick between plain Go, a workflow, one agent, delegation, and A2A for a new task, and say what each costs.
  • For any capability here, you can name the authority it deliberately lacks and the test that would fail the moment it gained one.

Every limit in this chapter is a claim about code, and a claim holds only as long as the suite that pins it — which is what 4. Quality establishes next.

Continue to 3.0. Packaging once mise run test is green inside agents/go, because every capability in this chapter is added to the one module that page builds.