The result you are building
An MCP server whose tools are easy for agents to select, reject invalid inputs, expose only necessary authority, return bounded structured results, require confirmation for consequential actions, and pass protocol plus security tests.
Use this guide when:
- Building a local or remote MCP server from an API or business workflow.
- Auditing a third-party MCP server before connecting it to sensitive data or tools.
- Agents choose the wrong tool or send invalid arguments despite a working backend.
Do not use it as a substitute for:
- Treating tool descriptions, annotations, or model instructions as security controls.
- Giving a remote server ambient filesystem, shell, wallet, or account access because the demo is convenient.
Before you change anything, collect:
- MCP specification version and chosen transport.
- Complete tool/resource/prompt inventory with owners and data classifications.
- Backend API contracts, authentication model, rate/cost limits, and side effects.
- Caller identities, tenant boundaries, and consent/approval points.
- Threat model and test client.
Understand the system before fixing it
- A tool schema is a user interface for a model. Names, descriptions, types, enums, defaults, and examples determine whether an agent selects and calls a tool correctly. Ambiguity becomes runtime error or unsafe action.
- Protocol compliance is not authorization. A valid MCP request still needs identity, tenant, resource, scope, policy, and approval checks at the server and downstream service.
- Tool annotations are hints, not trusted policy. Clients should not assume a tool is read-only or safe because an untrusted server says so. Enforce policy outside model reasoning.
- Bounded outputs reduce both cost and injection surface. Return explicit fields, provenance, pagination, and size limits. Raw webpages and unbounded logs can carry malicious instructions and overwhelm context.
Evidence-to-decision map
| Evidence | Likely layer | First decisive check | What the result means |
|---|---|---|---|
| Tool never selected | Discovery/schema | Inspect name, description, overlap, examples | Agent cannot distinguish purpose or required result |
| Tool selected with invalid args | Input schema | Validate generated calls against JSON Schema | Types, required fields, enums, or descriptions are ambiguous/loose |
| Valid call returns wrong user's data | Authorization | Test subject/tenant/resource policy | Backend trusts tool input instead of authenticated identity |
| Read tool causes side effect | Authority/design | Trace backend method and side effects | Tool boundary or annotation is misleading; redesign before use |
| Client disconnects/duplicates | Transport/idempotency | Replay request ID and simulate timeout | Side-effecting tool needs deduplication and resumable result semantics |
Step-by-step procedure
01. Inventory capabilities and trust boundaries. For every tool record data read/written, external calls, side effects, cost, required identity, tenant, approval, and reversibility. Remove capabilities without a concrete use case. Any tool that accepts a path, URL, command, recipient, account ID, or destination crosses a high-risk boundary. Split read, propose, and execute actions where possible.
02. Design names and strict schemas. Use unique action-object names, state when to use and not use each tool, make required fields truly required, bound lengths/numbers, use enums for closed choices, and reject unknown fields. If two tools have overlapping descriptions or one polymorphic action field, selection will be unreliable. Add valid and invalid call examples to tests.
03. Enforce authorization outside the model. Derive subject and tenant from authenticated context, not tool arguments. Check scope and resource ownership server-side. For remote HTTP servers, follow the current MCP authorization specification and OAuth security guidance. Changing userId in tool input must never cross the authenticated resource boundary. Add denial tests for every authority boundary.
04. Add confirmation, idempotency, and bounded execution. Require human confirmation for high-impact actions, stable idempotency keys for side effects, timeouts, concurrency limits, spend/row/result caps, URL allowlists, and sandboxing where appropriate. A timeout response must not imply that no side effect happened. Return a correlation ID and reconciliation method.
05. Return structured, sanitized results. Map backend results to a documented output schema; include status, evidence/provenance, pagination, observed time, warnings, and next safe actions. Redact secrets before logging and response construction. Every partial result must say what is missing rather than inventing fields.
06. Test through a real client and failure matrix. Test initialization, listing, valid call, invalid schema, unauthorized access, confirmation, duplicate, cancellation, timeout, dependency failure, output limit, and reconnect through an MCP client. The server should fail closed with stable protocol errors and no secret-bearing logs. Version the contract and publish supported capabilities.
Worked example
Starting problem: An MCP tool manage_customer accepts an action string and customerId, and the agent occasionally deletes the wrong record.
Evidence collected:
- Description mixes search, update, and delete behaviors.
- action is free text; unknown values reach backend fallthrough.
- Customer ID is trusted from input without tenant ownership check.
- Delete has no confirmation or idempotency key.
Decision: the server has ambiguous selection, loose schema, broken tenant authorization, and excessive authority in one tool.
Actions taken:
- Split into search_customers, propose_customer_update, and delete_customer.
- Added enums/bounds, server-derived tenant, resource ownership check, confirmation token, and idempotency.
- Created cross-tenant, replay, cancel, and malicious-description tests.
Proof of completion: the agent selects the narrow tool in evaluation cases; cross-tenant access is denied; repeated delete returns one terminal result; no deletion occurs without confirmation.
Why this example matters: improving the prompt alone would not fix the authority failure. The corrected boundary is enforced in code and tested independently of the model.
Verify, recover, and hand off
Completion tests:
- A real client initializes and lists exactly the intended capabilities.
- Valid/invalid examples prove schema enforcement and stable errors.
- Cross-user, cross-tenant, and insufficient-scope calls are denied server-side.
- Side effects require declared confirmation and are idempotent under retry.
- Outputs are bounded, typed, sanitized, and include provenance/observed time.
- Logs support correlation without secrets or excessive user content.
Rollback or safe recovery:
- Disable or unregister a tool independently when a security or backend issue appears.
- Revert to the last versioned schema and server release when client compatibility breaks.
- Revoke server credentials and cached tokens if origin or dependency trust is lost.
| What happened | What it usually means | Next safe move |
|---|---|---|
| Agent uses wrong tool | Descriptions overlap or describe implementation rather than outcome | Make action/object and non-use cases explicit; add selection evals |
| Frequent invalid arguments | Schema is loose, nested, or missing examples/bounds | Simplify, require fields, use enums, reject unknown properties |
| Server returns protocol success with business failure | Error model conflates transport and domain state | Return structured domain result or stable tool error consistently |
| Reconnect repeats a side effect | Request identity is not persisted across transport retries | Move idempotency into durable business boundary |
For agents
This guide's source material describes a structured diagnose/propose/execute contract for reviewing an MCP server (required inputs: serverManifest, toolSchemas, authModel, riskPolicy; returned output: diagnosis, plan, verification, handoff) — useful as a reference framework for how to structure your own review, not a live callable endpoint. Refusal/escalation rules worth carrying into your own process: refuse any request requiring a secret, seed phrase, private key, or credential in ordinary input; stop when a requested action exceeds declared authority, budget, or reversible scope; escalate when evidence is missing, contradictory, or too stale. Score confidence from the number and quality of independent observations, not from how familiar the situation looks.
Official reference starting points
- https://modelcontextprotocol.io/specification/2026-07-28/server/tools
- https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/security_best_practices
- https://modelcontextprotocol.io/docs/2026-07-28/tutorials/security/authorization