The MCP connector, and its two-part requirement
MCP is the Model Context Protocol: a standard way for a server to publish capability so any client can use it, instead of every integration being bespoke. The connector is how a Messages API request reaches one of those servers.
Tools and MCPs is 10.6% of the paper. Most of that is tool implementation, which you already have. This lesson is the connector, and it turns on one configuration fact that the API checks for you.
Both objects, or nothing
A working MCP call needs two things in the request, and they work together:
An mcp_servers entry. This declares the connection: where the server lives and how to authenticate.
A matching mcp_toolset. This exposes tools to the model.
{
"mcp_servers": [
{
"type": "url",
"url": "https://example.com/mcp",
"name": "my-server"
}
],
"tools": [
{ "type": "mcp_toolset", "mcp_server_name": "my-server" }
]
}Be honest about what you know here. The per-tool selector fields inside default_config and configs are something to check on the day. What is not up for debate is that both objects have to be present and that mcp_server_name has to match a declared server. Memorise the requirement, look up the rest.
The header, and the dead header
Current: mcp-client-2025-11-20.
Deprecated: mcp-client-2025-04-04.
The old one shows up in blog posts, in Stack Overflow answers, and in your own repo from earlier in the year. Seeing 2025-04-04 in a snippet is a dating signal, the same way thinking.type was in Module 2. Treat the whole snippet as old, not just that line.
Why the split exists
It looks like ceremony until you name the two concerns.
You have already built this distinction, more than once, without calling it that. A database connection string is not a query. Installing a package is not importing a symbol from it. In both cases the expensive, credentialled, infrastructure-shaped step is separate from the cheap per-use decision about what you actually want right now, and nobody would want them fused.
mcp_servers is connection: address and auth, the expensive-to-set-up part. mcp_toolset is exposure: what this particular request puts in front of the model.
Separating them means you can connect to a server with forty tools and expose four of them on a request where only four are relevant. Every tool definition you expose costs context on every single turn, so exposing everything a server has, on every call, is a real bill and a real distraction for the model. Connection is a fact about your infrastructure. Exposure is a decision per request.
The gap you already met
Recap, because it is the seam between two exam domains: strict is not available on mcp_toolset.
So moving a tool out of your own definitions and into an MCP server is not a pure refactor. You lose strict tool use in the process, and nothing in the diff will tell you.
One note on where to confirm this on the day, because it is not where you would look first. The strict tool use page places strict on a tool definition alongside name, description and input_schema and never mentions MCP at all, so reading that page leaves you inferring from silence. The quotable sentence is on the Tool reference page, in the table of optional tool definition properties: the strict row’s “Available on” column reads “All tools except mcp_toolset”. One row, explicit, and it also tells you allowed_callers is excluded on mcp_toolset for the same reason.
Worth knowing that the Tool reference is where this class of fact lives generally. It is the page that carries the type strings, the GA-versus-beta status, the client-versus-server execution column, and which optional properties each tool accepts. If a question is about a property rather than a behaviour, go there first.
Try it yourself
The half-configured connector
Your request has an mcp_servers entry pointing at a working, reachable server. There is no mcp_toolset. What happens?
Show answer
Correct answer: B — The request is rejected, because the API requires every declared server to be referenced by a toolset
Both halves are required, and the requirement is enforced rather than merely conventional: the connector docs list under Validation rules that every MCP server defined in mcp_servers must be referenced by exactly one MCPToolset. So a declared-but-unused server is a rejected request, not a quiet one. The silent answer is the designed trap and it is genuinely tempting, because half-configured integrations usually do fail quietly and because the split itself invites that guess: the connection worked, so surely only exposure is missing. Read the Validation rules section rather than reasoning from the shape. The read-only answer is invented plausibility, there is no such tier.
Predict what happens
The server is reachable and the credentials are valid. Predict what comes back.
{
"model": "claude-sonnet-5",
"mcp_servers": [
{ "type": "url", "url": "https://example.com/mcp", "name": "invoices" }
],
"tools": [
{ "name": "get_weather", "description": "Current conditions for a city", "input_schema": { "type": "object" } }
],
"messages": [{ "role": "user", "content": "List my unpaid invoices." }]
}Show answer
Correct answer: B — The request is rejected, because the invoices server is declared but never referenced by a toolset
The tools array carries a plain tool definition but no mcp_toolset, so the invoices server is declared and unused, and the connector's Validation rules make that a rejected request rather than a degraded one. The answers-without-tools option is the genuinely tempting one and it describes what the model would do if the request ever reached it: the connection is fine, the credentials are fine, and only exposure is missing, which sounds like a degradation rather than an error. It never gets that far. The get_weather answer is wrong for a separate reason worth keeping: being the only available tool does not make a tool applicable.
The current header and the dead one
Two values, one live, one deprecated. Both worth knowing, because reading an old blog post is how you end up with the wrong one.
What is the MCP connector's beta header, and which earlier one is deprecated?
Reveal answer
The current header is mcp-client-2025-11-20. The earlier mcp-client-2025-04-04 is deprecated. If you find the 04-04 value in a snippet, you are looking at code from an earlier generation of the connector and should not copy it forward.
Why it takes two objects at all
Not a trivia card. If you can explain the split, you will never forget that both are needed.
What does each of the two objects actually do, and why is that a sensible split?
Reveal answer
mcp_servers declares the connection: where the server is and how to authenticate to it. mcp_toolset controls exposure: which of that server's tools this request puts in front of the model. Connection and exposure are genuinely different concerns, and separating them means you can connect once and expose different subsets per request instead of dumping every tool a server has into every call.
Strict, revisited
Deliberate repeat from Module 2, because this is the one place two domains of the exam overlap.
Show answer
Correct answer: B — strict: true is not available on mcp_toolset
Strict tool use is unavailable on mcp_toolset. The moves-to-mcp_servers answer is the sharpest trap, because it rewards someone who half-remembers that placement is the theme of this topic and then guesses the other object. The capability is simply absent, so the right answer is a gap, not a different location.
Write both halves, then break one
Ninety seconds in a scratch file. The point is the pairing, not the field names, which are a lookup on the day.
- Write an mcp_servers entry with a type, a url and a name.
- Write the tools array containing the mcp_toolset entry that pairs with it, with mcp_server_name matching the name above.
- Now delete the mcp_toolset and write one sentence saying which validation rule that breaks.
- Then restore it and instead change mcp_server_name to a name no server has, and write which rule that breaks.
- Underneath, write the current connector beta header and the deprecated one, each labelled.