Build a small MCP server you’d trust, Part 2: Design the tools before the code
Verified by Apex Researcher · Oct 21, 2026 · 1 sourceAI-assisted, fact-checked
Define the read tool, its schema, its refusals and its error behaviour before implementation.
Design the tools before the code
A useful tool contract tells both the agent and the human when a call belongs. For our project-notes server, the first contract should be narrow enough to review without reading implementation code. This lesson turns the boundary sheet into a proposed interface and a set of examples you can later test.
The names used for our own tools below are teaching choices, not SDK function names. The MCP specification supplies the tool-definition fields and protocol behaviour; we choose the notes job and its limits.
Describe the result you need
Take one question from your practice collection. Decide what the server should return before asking the agent to write a description. For this exercise, require the note identifier and its text. The agent can then explain the evidence separately.
Give the agent a near miss as well: a plausible identifier that is absent from the catalogue. Ask what you should see when retrieval is refused. A confident invented note is never the desired answer to that exercise.
Draft a reviewable interface
Propose a single read tool called read_project_note. Explain that this is our invented application name. Give it a description that states the source collection, the operation and the main exclusions. Ask the human to compare that description with the boundary sheet before you proceed.
Prepare example arguments and expected outcomes alongside the definition. Do not treat a readable description as the mechanism that enforces access; write down the checks the implementation must perform.
Make the name specific
The tools specification defines a tool name as its unique identifier within a server and a description as a human-readable explanation of functionality. It also permits a human-readable title. Choose names that describe the bounded operation.
For our exercise, read_project_note communicates more than an all-purpose name such as do_work. Use this proposed description: “Read an existing note from the configured project catalogue by note identifier. Does not change notes, search other directories or save summaries.” This is original teaching text, not a quotation from the specification.
Read the description against an awkward request: “Read the note and update its decision.” The description should help the agent recognise that only the retrieval belongs in this call. The server still needs checks that refuse unsupported arguments and operations.
Specify the input
The specification’s inputSchema is a JSON Schema defining expected parameters. It must be a valid JSON Schema object. For our design, accept an object containing a required string identifier and reject additional properties. The following is a proposed tool definition, not executable SDK registration code:
{
"name": "read_project_note",
"description": "Read an existing note from the configured project catalogue by note identifier. Does not change notes, search other directories or save summaries.",
"inputSchema": {
"type": "object",
"properties": {
"note_id": {
"type": "string",
"description": "Identifier from the approved project catalogue"
}
},
"required": ["note_id"],
"additionalProperties": false
}
}
The schema describes the input’s shape. Our application must additionally check that the identifier belongs to the configured catalogue and resolves to permitted material. A string that passes schema validation is not automatically an approved note.
Do not add a path override for convenience. If an identifier is unknown, the intended behaviour is refusal with an actionable explanation. Broader discovery, if later needed, deserves its own contract and review.
Define refusal deliberately
Write a refusal table before implementation. This is a proposed acceptance plan for our notes tool:
| Attempt | Expected result |
|---|---|
| Approved identifier | Return that note and its identifier |
| Missing identifier | Reject invalid input |
| Unknown identifier | Explain that no approved note matches |
| Extra path argument | Reject the unsupported input |
| Request to change the note | Keep the operation read-only |
| Catalogue entry outside configured scope | Refuse access |
For each row, ask the human to explain why the outcome fits the job. This catches an interface that is technically neat but disagrees with the intended workflow. Keep expected wording flexible while preserving the required meaning.
The specification requires servers to validate all tool inputs, implement proper access controls, rate limit invocations and sanitise outputs. Treat these as implementation responsibilities rather than promises created by the schema alone.
Treat hints as hints
Tool definitions can contain annotations describing behaviour. The specification says clients must consider those annotations untrusted unless they come from trusted servers. A read-only hint is therefore a claim to evaluate, not proof that the underlying operation cannot write.
For our proposed tool, describe its read-only intent and verify that intent by inspecting the implementation and observing the practice collection during tests. If an unfamiliar server advertises reassuring annotations, keep your normal review of its actual behaviour and permissions.
Likewise, do not use a destructive-operation hint as the sole trigger for approval. The specification recommends human involvement with the ability to deny invocations. It recommends visible tool availability, invocation indicators and confirmation prompts. Our workflow will show the intended inputs and let the human decline sensitive actions.
Separate kinds of failure
The tools specification distinguishes protocol errors from tool execution errors. Unknown tools and malformed protocol requests belong to the protocol-error category. Execution errors include input validation problems and business logic failures; they are returned in tool results with isError: true.
For our catalogue, an unknown note identifier should produce an understandable application refusal rather than a fabricated empty success. A request for a nonexistent tool is a different problem: the client and server disagree about the interface. Keep those examples distinct when you test.
Write error explanations that support a permitted correction. “Choose an identifier from the approved catalogue” is useful. Suggesting that the agent search elsewhere would undermine the boundary. When the underlying cause is uncertain, report the uncertainty instead of presenting an invented diagnosis.
Agree the review evidence
Keep the proposed definition, refusal table and sample results together. Decide how the human will observe calls and inspect the returned text. The specification recommends showing inputs before calls, validating results, applying timeouts and logging tool usage for audit purposes.
For this exercise, record which test was attempted and whether the result matched the contract. Later, the implementation will produce call records too. Your present deliverable remains a design that someone else could use to judge the first tool.
Teach it back
Explain the difference between a valid string and an approved note identifier. Show one accepted argument object and one refused object. Then explain why annotations cannot establish trust in an unfamiliar server.
Have the human classify an unknown tool separately from a known tool that cannot retrieve a requested note. Use their explanation to refine the error contract before moving on.
Check your understanding
Q1. Does schema-valid input establish permission to read a note?
Q2. True or false: clients must trust annotations from untrusted servers.
Q3. How does the specification report tool execution errors?
Continue the series
This is Part 2 of Build a small MCP server you’d trust. Parts 1–3 are free; the series continues in this module with the Premium Parts 4–6: Part 4, Build your server’s first tool; Part 5, Add a write action safely; and Part 6, Ship and maintain the boundary.
Sources
- MCP tools — Supports definition fields, schemas, annotation trust, error categories and human oversight guidance.
Free lesson · Last updated · For agents: JSON and MCP