Three Surfaces and Nothing Else (8.1)
When the model decides whether to call a tool, it reads the name, the description and the input_schema. It cannot see your handler, your docstring, your API contract or the meeting where the team agreed which tool to use when.
That single fact does most of the diagnostic work in the sub-skill. If the model chose badly, the information it needed was absent from one of those three surfaces, and the fix is to put it there. Options reaching for a bigger model, a lower temperature or an extra layer of routing all propose to solve an information problem with something that supplies no information.
What a description must carry, from the docs' own checklist:
- what the tool does
- when it should be used, and when it should not
- what each parameter means and how it affects the tool's behaviour
- caveats and limitations, explicitly including what the tool does not return
The stated floor is at least three to four sentences per tool, more if the tool is complex, and the ranking is unusually blunt: detailed descriptions are "by far the most important factor in tool performance". Not the schema. Not the model. The failure mode is a description that paraphrases the name, because it adds nothing the name did not already carry.
Disambiguation, Consolidation, and the Router Trap (8.1)
Two faults look similar and take opposite remedies. Getting the pairing the wrong way round is the strongest distractor an item in this sub-skill can offer, because both moves are recommended somewhere in the docs.
| Fault | What the stem says | Remedy |
|---|---|---|
| Overlap | Two tools over adjacent parts of the same system, chosen inconsistently | Rewrite both descriptions: each names the sibling and says when not to use itself |
| Sprawl | One tool per endpoint, several thin wrappers over adjacent operations | Consolidate behind an action-style parameter: fewer, more capable tools |
Disambiguation is a sentence you write, never a property the model infers, and the when-not-to-use clause is the half candidates leave out and the half the exam keys. If the boundary is genuinely hard to state in one sentence, that is evidence the two tools want to be one tool with a parameter.
The routing tool is the trap that sounds like engineering. Adding a third tool to decide between two confusable ones relocates the ambiguity rather than removing it. The router draws the same distinction from the same uninformative descriptions, the model must now also decide whether to call the router, and a turn is spent arriving back where it started. Indirection cannot supply information a description is missing.
Two more properties keep a growing surface legible: namespacing by service or resource (github_list_prs, not list_prs), which matters more once tool search is in play, and result shape: return only high-signal fields, with semantic stable identifiers such as slugs or UUIDs rather than opaque internal references.
Two dated additions, checked 2026-09-30:
- Clashes between MCP servers. The MCP specification scopes tool-name uniqueness to a single server, so a client aggregating several servers can meet two tools both called
search. Prefix each with a server identifier, not with the self-reportedserverInfoname, which is not guaranteed to be unique. - Attribution in results. A client tool's
tool_resultcan carrysearch_resultblocks with asourceandtitleyou supply. With citations enabled (off by default), Claude cites them the way it cites web search results, so attribution comes from your data rather than from a reference Claude copies out.
The Schema and the Optional Properties (8.1)
The description says when to reach for a tool. The schema says what a well-formed call looks like, and unlike the description it is enforced rather than suggested. Closed sets belong in enum, mandatory fields in required, units and formats in the per-property description. A schema that permits nonsense will receive nonsense.
Optional properties on a tool definition, verified 2026-08-21 and rechecked 2026-09-30:
| Property | What it does | Availability |
|---|---|---|
strict | Guarantees Claude's tool names and inputs match your schema exactly, removing the missing-parameter class of failure at source | All tools except mcp_toolset and the computer use and browser use toolsets |
input_examples | An array of example input objects, each validated against the tool's own input_schema; an invalid example returns a 400 | User-defined and Anthropic-schema client tools; not on server tools, nor on the computer use or browser use toolsets |
defer_loading | Excludes the definition from the initial system prompt, expanding it inline when tool search returns a tool_reference for it | All tools; set per member inside configs on the client toolsets |
cache_control | Sets a prompt-cache breakpoint at this tool definition | All tools |
allowed_callers | Restricts which callers may call the tool ("direct", or code running in a code-execution sandbox, which is programmatic tool calling) | All tools except mcp_toolset; only "direct" on the computer use and browser use toolsets |
eager_input_streaming | Turns fine-grained input streaming on (true) or keeps standard buffered streaming (false) for this tool | User-defined tools only |
input_examples costs roughly 20–50 tokens for a simple example and 100–200 for a complex nested object, which is why it earns its place on tools with nested objects, optional parameters or format-sensitive fields and nowhere else. Neither property substitutes for the description: both constrain the shape of a call the model has already decided to make. Deferred definitions sit outside the cached prefix, so put the cache breakpoint on a tool that is not deferred. Each deferred tool also costs a search step before Claude can call it, so the tool search docs advise keeping your three to five most frequently used tools non-deferred.
Programmatic tool calling (generally available as of September 2026) is the lever for results rather than definitions. Add a code execution caller such as code_execution_20260120 to a tool's allowed_callers, and Claude can call that tool from code it writes in the code execution container. Each call still comes back to your application to run, but intermediate results never enter Claude's context, so a fan-out over many records or a large result to filter takes one trip through the model instead of one per call. Not available for strict: true tools or for tools from the MCP connector.
What a Tool Returns When It Fails (8.1)
This is interface design, not exception handling, and it holds the most consequential item in the sub-skill.
A handler that catches a timeout and returns an empty list hands the model a well-formed result asserting that nothing went wrong and that there is nothing there. The most probable continuation from that position is a confident answer assembled from surrounding context, which is what fabrication looks like from the outside. Returning the last known good value is worse: confidently wrong, current-looking, and leaving no trace that anything failed.
The pattern that works is a tool_result carrying is_error: true and a body written for a reader who has to decide what to do next. The docs put it directly: instead of generic errors like failed, include what went wrong and what Claude should try next, for example Rate limit exceeded. Retry after 60 seconds.
Two honest qualifications, both places candidates over-claim:
- On genuinely invalid tool calls the documented behaviour is that Claude "will retry 2-3 times with corrections before apologizing to the user". The folk story of an endless identical retry is not a documented mechanism.
- Server tools are handled differently. The API deals with their errors itself, and you do not need to handle
is_errorresults for them.
A longer timeout and a bounded retry are both good engineering and both appear as confident, fully-stated distractors. Neither changes what the model receives when the call finally fails; they only change how often it fails.
Client, Server, and the Dated Inventory (8.1)
A client tool is yours: the model emits a tool_use block, your process runs the function under your credentials inside your network, and you return a tool_result. Anthropic's infrastructure never executes it, which is exactly why a client tool can reach a database on a private subnet.
A server tool is Anthropic's: the call appears as a server_tool_use block whose id carries the srvtoolu_ prefix, the API executes it internally, its result arrives in the same assistant turn paired by tool_use_id, and you return no tool_result at all.
Anthropic-provided is not Anthropic-executed. Anthropic ships both kinds, and several familiar Anthropic-schema tools sit on the client side with your process running them. Answer from the capability boundary first. Where a tool runs only matters when the question says so. Don't assume a location it leaves out, and don't rely on today's list of built-in tools, because it changes between releases. That inventory, verified against the Tool reference on 2026-08-21 and unchanged on 2026-09-30:
| Execution | Tools, with current type strings |
|---|---|
| Server | Web search (web_search_20260318, _20260209, _20250305), web fetch (web_fetch_20260318, _20260309, _20260209, _20250910), code execution (code_execution_20260521, _20260120, _20250825), advisor (advisor_20260301, beta), tool search (tool_search_tool_regex_20251119, tool_search_tool_bm25_20251119), MCP connector (mcp_toolset, beta) |
| Client | Memory (memory_20250818), bash (bash_20250124), text editor (text_editor_20250728, _20250124), computer use (computer_toolset_20260801, computer_20251124, computer_20250124), browser use (browser_toolset_20260801) |
Read the shape rather than the list: search, fetch and sandboxed execution are the platform's environment, so they never reach a private network; memory, bash, editing and driving a machine or a browser are operations on your environment, so they have to be yours to run.
Dispatch, Approval Bands, and Symptom-to-Surface (8.1)
The harness receives the tool-use request, resolves the name to a handler, validates the input, applies whatever policy the operation's classification demands, executes under a timeout, logs the call and formats the result. Putting those concerns at the dispatch point rather than inside each handler is what keeps a tool function a plain function you can unit-test with no model anywhere near it.
Execution order is yours too. When one turn carries several tool_use blocks, the API does not prescribe an order (checked 2026-09-30). Run independent, read-only calls in parallel. Anything with side effects, shared state or ordering requirements goes one at a time. If you stop a sequential batch at a failure, still return a tool_result for each skipped call, with is_error: true and a short explanation.
Approval is the policy living in that layer, and the classification is a design decision made once, when the tool set is designed:
| Band | Operations | Example |
|---|---|---|
| Automatic | Read-only, reversible | Read a record |
| Confirm | Consequential, reversible with effort | Amend a draft |
| Two-party gate | Genuinely dangerous, not quietly undone | Submit an irreversible return |
Unknown tools are never automatic. Nor is a tool that merely declares itself safe: as of September 2026 MCP tool annotations such as readOnlyHint and destructiveHint are hints, not guaranteed to describe the tool faithfully, and clients must treat them as untrusted unless the server is trusted. Deciding the bands and expressing them in the tool surface is 8.1. Making the gate an enforceable control that fires whether or not the model cooperates is Domain 7. An option relying on an operator noticing something is not a control on either side of that line.
Half the items arrive as a symptom, so run it as a routine:
- Wrong tool chosen → the descriptions. Ask whether any sentence in either one distinguishes the two cases; if none does, write the disambiguating sentence plus a when-not-to-use clause in both.
- Invented data → what the tool returns on failure. Something returned a well-formed result carrying no information and no error.
- Repeated identical calls, or a stall → an unactionable result: one saying a request failed without saying whether a retry helps or what a valid input looks like.
What MCP Buys, and the Three Participants (8.2)
Start from the failure it prevents. One internal system, several Claude applications, no shared protocol: each application grows its own integration, and they drift silently until a field changes and breaks two of them in a way nobody notices for a month. MCP replaces that with one server: the capability implemented once, exposed over a standard protocol, connected to by any compliant client. The gain becomes visible at the next schema change: made in one place, by the team that owns the underlying system, seen by every connected client.
That is a maintenance and ownership argument, not a capability one, and holding the two apart settles most items here. A custom tool inside one application can do the same job. That is not what a stem naming several applications and independent maintenance is asking.
The specification names three participants, and the direction matters:
- MCP Host: "The AI application that coordinates and manages one or multiple MCP clients."
- MCP Client: "A component that maintains a connection to an MCP server and obtains context from an MCP server for the MCP host to use."
- MCP Server: "A program that provides context to MCP clients."
The host creates one client per server, so a host connected to four servers runs four clients. Your Claude application sits on the client side. The thing wrapping your internal system is the server. And local and remote describe where a server runs, not what it is, because the same program is an MCP server whether it runs as a child process on one laptop or as a service several teams reach over the network. Any option denying that is a flat statement of fact that happens to be false.
What the 2026-07-28 revision changed about the connection (checked 2026-09-30):
- No
initializehandshake: every request carries its own protocol version and client capabilities. - Every server must answer
server/discoverwith its supported versions and capabilities. - Change notifications such as
toolsListChangedare opt-in, through asubscriptions/listenstream. - No protocol-level sessions and no
Mcp-Session-Idheader. State across calls uses an explicit, opaque handle returned by a creation tool and passed back as an ordinary argument; an expired or unknown handle gets a tool error that says so. - Roots, Sampling and Logging are deprecated: still working, but not for new implementations.
Three Primitives, Three Controllers (8.2)
The sharpest examinable distinction in the sub-skill. Ask who decides a capability gets used.
| Primitive | What it is | Controlled by | Methods |
|---|---|---|---|
| Tools | Functions the LLM can actively call, deciding when based on user requests | Model | tools/list, tools/call |
| Resources | Passive read-only data for context: file contents, database schemas, API documentation | Application | resources/list, resources/templates/list, resources/read |
| Prompts | Pre-built instruction templates telling the model to work with specific tools and resources; user-controlled, requiring explicit invocation rather than automatic triggering | User | prompts/list, prompts/get |
Resources carry a URI, either fixed (records://schema/batch) or a parameterised template (records://batch/{batch_id}). Applications typically surface prompts as slash commands.
The trap is flattening all three into tools, because tools are the primitive most authors already understand. Its sharpest form is a standard document template people want to pull in on demand: user-initiated, and a template, therefore a prompt. Making it a tool hands the decision to the model, which is the opposite of what the person starting the job wanted. The mirror case is passive reference data such as a schema or a fixed dataset, which belongs in a resource the application fetches by URI.
Two result details from the 2026-07-28 revision:
| Field | What it does |
|---|---|
outputSchema and structuredContent | A tool can declare an output schema; its results must then carry structuredContent that conforms to it, which the client should validate, and the same JSON also goes in a text block for older clients. Server-produced data, unrelated to strict or to the model's structured outputs |
ttlMs and cacheScope | Required caching hints on list and read results. ttlMs is a freshness hint in milliseconds; public lets a shared gateway serve one cached tool list to every user, private keeps a per-user resources/read result inside one authorisation context |
Transport Follows Topology (8.2)
Two transport mechanisms in the current specification, and the choice is a reading of the deployment, never a preference:
| Transport | Mechanism | Typical topology |
|---|---|---|
| stdio | Standard input/output streams between local processes on the same machine; no network overhead | Local server, typically one client |
| Streamable HTTP | HTTP POST for client-to-server messages, optional Server-Sent Events for streaming; standard HTTP authentication including bearer tokens, API keys and custom headers | Remote server, typically many clients |
The question is never which transport is better. It is who needs to reach this server, and from where. One developer wrapping a local command line is single-user and local, which points at stdio. A capability three applications and several machines must share is remote and multi-user, which points at the HTTP-based transport and pulls authentication, deployment, availability and access control into scope; the specification recommends OAuth for obtaining tokens.
The inverted pairing is the standing distractor: stdio proposed for the genuinely shared capability (one copy per machine, which is the drift MCP was adopted to remove) and a remote transport for the local wrapper (deployment, authentication and availability obligations bought for no reuse). Both halves sound like transport knowledge; only the topology reading separates them.
The stdio stdout rule. On stdio the server's stdout is the protocol channel, and the server must not write anything there that is not a valid MCP message. A stray print() in a handler corrupts the JSON-RPC messages and breaks the server. Log to stderr or a file; the 2026-07-28 revision points servers that used the deprecated Logging feature to stderr on stdio, or to OpenTelemetry.
Don't answer from a transport name. This part of the specification has already moved once. The exam guide's own wording for this sub-skill says "stdio, sockets"; the current specification lists two standard mechanisms, with sockets appearing only as a custom transport that should reuse the stdio framing; and Anthropic's MCP connector page still names SSE alongside Streamable HTTP for its own remote requirement, although the 2026-07-28 revision marks the old HTTP+SSE transport Deprecated. Current protocol revision, checked 2026-09-30: 2026-07-28. Learn the topology rule, which is stable.
A remote server is a service: a version, an owner, an availability expectation, an authentication story and a list of who may connect. The moment several surfaces depend on it, its outages are their outages. The reuse benefit and the operational obligation arrive together.
Reaching a Server from a Claude Application (8.2)
One server can serve several Claude surfaces at once, each configuring its connection its own way. That is the reusability claim, made concrete.
On the Claude API the MCP connector reaches remote servers straight from the Messages API without a separate MCP client. Its two limitations are examinable as consequences rather than trivia, and both are stated verbatim on the docs page:
- "Of the feature set of the MCP specification, only tool calls are currently supported."
- "The server must be publicly exposed through HTTP (supports both Streamable HTTP and SSE transports). Local STDIO servers cannot be connected directly."
The request carries an mcp_servers array plus an mcp_toolset entry in tools naming one of them, and Claude's calls return as mcp_tool_use and mcp_tool_result blocks. For local stdio servers, or when you need MCP prompts and resources rather than tools alone, the SDKs ship client-side helper functions instead. As of September 2026 the connector is a beta, not available on Amazon Bedrock or Google Cloud. A newer beta header, mcp-client-2026-09-15, pins a server's tool list, so a server that changes its tools mid-conversation does not change what Claude sees. Claude Code's own configuration specifics belong to Domain 3.
The tools an MCP server exposes are ordinary tools and answer to everything in 8.1. Being reachable over a protocol changes nothing about what a good description or a good error result looks like.
Four Mechanisms, One Decision Sequence (8.3)
| Mechanism | One sentence | Cannot |
|---|---|---|
| Built-in tools | Capabilities Anthropic provides: the platform defines the schema and, for the server-executed ones, runs them. No code, no handler, no deployment | Automatically reach arbitrary internal APIs; the server-executed ones cannot reach your private network at all |
| Custom tools | Functions you define and your code executes, under your credentials inside your network | Be free of maintenance; you own them entirely |
| Skills | Packaged procedural knowledge: a SKILL.md with YAML frontmatter, optionally bundled with reference files and scripts, loaded when relevant | Grant a capability; a Skill changes how Claude does something it can already do |
| MCP servers | A capability exposed over a standard protocol so several clients share one implementation, versioned and owned independently | Avoid being a service, with everything that implies |
Four questions, in order. Most items are settled by the first or the second.
- Does the platform already provide this? Searching the public web, fetching a page, running code in a sandbox are covered, and as of September 2026 so is acting on webpages in a browser your application runs (the browser use tool, a client toolset). If the requirement matches one, building anything is work you did not need.
- Does the model need to DO something in a system, or to KNOW how to do something it can already do? Doing means a tool. Knowing means a Skill.
- If it is a tool, will more than one application or surface need it, and does another team own the underlying system? Yes to either points at an MCP server. No to both points at a custom tool.
- If it is knowledge, is it procedural and only occasionally relevant? Then a Skill loads it on demand instead of carrying it in every prompt.
The guide's own Domain 8 rationale states the boundary that disposes of a whole family of distractors: built-in tools "do not automatically reach arbitrary internal APIs". Reaching an internal system needs code executing where that system is reachable, which means a custom tool or an MCP server, and nothing else on the list qualifies.
Browser use against its neighbours (checked 2026-09-30). The docs say to choose browser use when the task stays inside webpages and means acting on them, or when pages build their content with JavaScript. For reading pages, web fetch and web search are lighter; for a task that needs a whole desktop, use computer use. Your application runs every browser use call against its own browser, and nothing runs on Anthropic's side.
Skill or Tool: Ask What Is Missing (8.3)
If the model lacks information it cannot reach or an action it cannot perform, that is a tool, and no volume of instruction substitutes. A records lookup, a write to a database, a job trigger. On the Claude API the boundary has teeth: Skills there run in a sandboxed container with no network access and no runtime package installation, so a Skill genuinely cannot fetch your records however precisely it documents them.
If the model can already perform the task but does it inconsistently, in the wrong format, or against the wrong conventions, that is knowledge, and packaging it as a Skill is how it gets applied without occupying every request. House reporting conventions, a document template, a review checklist, a written procedure a team keeps re-pasting.
The two near-misses are mirror images and both are deliberately attractive. A checklist requirement dressed as "build a tool that returns the checklist" is the first; the tell is that the model can already perform the task and is simply doing it inconsistently. A lookup requirement dressed as "write a Skill that documents the system thoroughly" is the second; the tell is that no amount of instruction supplies access.
A Skill and a tool are usually two halves of one requirement, not rivals: the tool supplies what the model cannot reach, the Skill governs how it handles what comes back. An option assigning every requirement in a mixed stem to the same mechanism is almost always wrong on that uniformity alone.
Against a custom tool, an MCP server is decided by reuse and ownership, never capability, because both can do the same job for one application. The single sharpest signal in a stem is a second consumer, actual or imminent. The move is also cheap. Taking a capability from a custom tool to an MCP server is a relocation of the same implementation behind a protocol, not a rewrite, so "we would have to rebuild it" is a false objection when a distractor raises it.
Progressive Disclosure and the Skill Field Limits (8.3)
Progressive disclosure is what makes Skills cheap to install, and the reason a Skill beats pasting a procedure into every prompt:
| Level | When loaded | Token cost | Content |
|---|---|---|---|
| Metadata | Always, at startup | ~100 tokens per Skill | name and description from the YAML frontmatter |
| Instructions | When the Skill is triggered | Under 5k tokens | The SKILL.md body |
| Resources | As needed | None until accessed | Bundled files; reference files load when read, scripts run through bash and only their output enters context |
So an installed but untriggered Skill costs roughly a hundred tokens. That reframes the comparison: not Skill against tool, but a Skill against carrying several pages of house conventions on every request, including the four jobs in five that never touch them.
The description is the trigger. It is what Claude matches the request against, so it must say both what the Skill does and when to use it. A description saying only what the Skill contains will sit there unused, which is the commonest reason a shipped Skill never fires.
Field requirements, verified 2026-08-21:
| Field | Constraints |
|---|---|
name | Maximum 64 characters; lowercase letters, numbers and hyphens only; no XML tags; cannot contain the reserved words "anthropic" or "claude" |
description | Non-empty; maximum 1024 characters; no XML tags |
Skills by Surface, and the Four Postures (8.3)
Where a Skill runs differs by surface, and the differences bear on the answers. Verified 2026-08-21:
| Surface | How Skills arrive | Sharing | Network |
|---|---|---|---|
| Claude API | Upload through the Skills API; needs the code execution tool and a skill_id in the container parameter | Workspace-wide: all workspace members can access them | None, and no runtime package installation |
| Claude Code | Filesystem-based: ~/.claude/skills/ (personal) or .claude/skills/ (project) | Personal or project, also shareable through plugins | Full |
| claude.ai | Uploaded as zip files in settings | Individual to each user; no org-wide distribution | Varies with user and admin settings |
Custom Skills do not sync across surfaces. A Skill uploaded to one is not available on another, and a Claude Code Skill is separate from both.
Skills compose with each other (checked 2026-09-30). One API request can carry up to 20 Skills, so a pre-built Skill such as xlsx can sit beside a custom Skill holding the house layout rules and template. The Skills guide's own example pairs Excel with a custom DCF analysis Skill. A new custom Skill version is a complete snapshot, so a revised template means uploading the full file set again.
Four wrong moves recur, each one something a real developer has genuinely proposed:
- Pasting the data into every request. Stale by construction for a system whose value is its current state. It also spends context on every call including the majority that need none of it, and scales with the size of the data rather than the size of the question. The guide rejects it in its own words: pasting data gives no live access and wastes context.
- Hard-coding the procedure into every system prompt. Works until there are three applications, then there are three copies that drift and nobody knows which is authoritative. Same drift argument MCP makes about integrations, applied to knowledge.
- Assuming a built-in tool can reach an internal service. The reasoning is that the tool already fetches things over HTTP. It cannot, because a server-executed tool such as web fetch is the platform's capability running in the platform's environment.
- Reaching for fine-tuning. A genuine technique aimed at the wrong class of problem: it changes how a model behaves on a distribution of inputs, grants access to nothing, and keeps no procedure current when the procedure changes next quarter.