Tool Engineering · ~8 min
Everything so far applies to a single tool. MCP is where a server exposes a whole set across hosts — and the first decisions, made before naming or schemas, are whether something should even be a tool.
The Model Context Protocol standardises how agents connect to external tools: the host speaks MCP on one side, your server on the other, and the same server works in Claude Code, Copilot, or Cursor without rewriting. That portability is the point — and the reason a sloppy exposure decision compounds, because every connected agent inherits it.
| Primitive | Controlled by | Use when |
|---|---|---|
| Tool | Model (agent invokes) | The agent takes an action or fetches dynamic data — create_issue, search_logs |
| Resource | Application (client attaches) | Read-only context the agent sees but can't invoke — project config, schema, env info |
| Prompt | User (slash command) | A reusable multi-step workflow — /summarize-pr, /deploy-staging |
Exposing static project config as a tool forces the agent to decide when to call it; exposing it as a resource lets the client attach it as context with no selection step at all. The primitive choice is the first place an MCP server can make the agent's job easier or harder.
Tool search matches on names and descriptions — so opaque names cause routing failures, the same search-miss you met in the previous lesson, now at the naming layer. The conventions that hold up:
snake_case is used by the large majority of public MCP servers; the verb_noun pattern names the
action and its object; and descriptions should carry 1–5 realistic examples plus negative guidance —
"do NOT use for metrics, use query_metrics instead" — to prevent the misrouting that distinct names
alone can't fix.
An outputSchema lets a server return validated structuredContent the agent can rely on,
rather than parsing free text — return both the structured form and serialized JSON for backwards compatibility. And
MCP separates errors into two channels with different audiences, a distinction that decides whether the
agent can recover:
| Channel | Audience & purpose |
|---|---|
| Protocol error (JSON-RPC code) | The client — structural faults: missing params, invalid method. Not the agent's to fix. |
Tool execution error (isError: true) | The agent — business-logic failures. Should carry actionable feedback the model can self-correct on. |
This is where you set the readOnlyHint and idempotentHint from Lesson 8, and the
destructiveHint the harness may gate on. They're metadata, not trustable from an untrusted server — so
set them honestly on your own server and audit them on others. Exposure isn't only the
shape of the call; it's the promises the call advertises about its effects.
The server-side checklist that falls out: verb_noun names, every parameter described with constraints
and an example, enums and defaults wherever possible, descriptions that say when not to call, errors that
carry the constraint and recovery context, read-only context as resources, and a tool list kept small enough to stay
discoverable.
verb_noun in snake_case, ≤32 chars, no versions or abbreviations — tool search matches names.structuredContent via outputSchema, and split protocol errors from agent-facing tool errors.Retrieval practice — recall, don't peek
Question 1Read-only context the agent should see but never invoke belongs as an MCP…
Question 2The recommended MCP tool-naming pattern is…
Question 3An isError: true tool execution error is meant for the…
Question 4Distinct tool names alone won't stop misrouting; the description also needs…
Question 5 · spaced recall from Lesson 09To keep selection sharp on a large catalog, you eager-load…