Rubric
Contents — domains, guide and mocks

MCP server development

CCDV-F 8.216 min read · checked 21 September 2026

Task statementMCP Server Development (2.1%) — server authoring, deployment, integration with Claude applications, MCP resources, tools and prompts, and communication patterns (stdio, sockets, client versus server)

Hosts, clients and servers

Host applicationone client per server
  • Local serverstdio subprocess: files, git
  • Remote serverHTTP: your ticketing system
  • Vendor serverHTTP: a SaaS product
The host is the application a person uses. It runs one client per server. Servers never reach across to each other, and a server never initiates a request.

The three server primitives

A server offers any of three features, and picking the right one is the most commonly tested part of this task statement. The specification describes them as: resources, context and data for the user or the model to use; prompts, templated messages and workflows for users; and tools, functions for the model to execute. The distinction is really about who reaches for the thing.

PrimitiveWhat it isWho invokes itTypical methods
ResourceReadable data, addressed by URIThe host or the user selects itresources/list, resources/read
PromptA named, templated workflowThe user picks it, often from a menuprompts/list, prompts/get
ToolA function with a schemaThe model decides to call ittools/list, tools/call

A design that makes everything a tool works, and is what most first servers do — but it puts every decision in the model's hands and fills the context with things nobody asked for. A design that makes everything a resource leaves the model unable to act. The useful test: if the model should decide when to do it, it is a tool. If the user is choosing what to bring in, it is a resource. If it is a repeatable piece of work a user starts by name, it is a prompt.

Which primitive is this?

Who decides that this happens?
  • The model, mid-task
    Toolschema, side effects, approval
  • The user picks data
    Resourceaddressed by URI, read only
  • The user starts a workflow
    Promptnamed template with arguments
Ask who makes the decision. That single question resolves most of the design, and most of the exam items in this task statement.

Authoring a server

You almost never write JSON-RPC by hand. The official SDKs — Python, TypeScript, Java, Kotlin, C# and others — handle framing, method routing and schema generation. In the Python SDK a tool is a decorated function: the function name becomes the tool name, the type hints become the input schema, and the docstring becomes the description the model reads. That last point matters more than it looks: everything in 8.1 about writing tool descriptions applies here, and the docstring is the description.

A minimal MCP server, Python SDKpython
from mcp.server import MCPServer

mcp = MCPServer("weather")

@mcp.tool()
async def get_alerts(state: str) -> str:
    """Get weather alerts for a US state.

    Args:
        state: Two-letter US state code (e.g. CA, NY)
    """
    # The docstring above is what the model reads when choosing this tool,
    # and the type hints become the input schema. Both are the interface.
    data = await make_nws_request(f"{NWS_API_BASE}/alerts/active/area/{state}")
    if not data or not data.get("features"):
        return "No active alerts for this state."
    return "\n---\n".join(format_alert(f) for f in data["features"])

if __name__ == "__main__":
    mcp.run(transport="stdio")

The TypeScript SDK is the same idea in a different shape: construct an McpServer, call registerTool with a name, a description and a schema, and connect it to a StdioServerTransport. Whichever language, the authoring checklist is short — name the server, register the primitives, describe each one as though for a competent stranger, and choose a transport at the bottom of the file.

Communication patterns: stdio and Streamable HTTP

The specification defines two standard transports, and stresses that protocol semantics are identical on both: a transport is a binding that decides how messages are framed and delivered, not what they mean.

stdio. The client launches the server as a subprocess and they talk over its standard streams: JSON-RPC messages in on stdin, messages out on stdout, one per line, with no embedded newlines. Two rules cause most real bugs. The server must not write anything to stdout that is not a valid MCP message — a stray print or a library that logs to standard output corrupts the channel. Logging goes to stderr, which the server may write freely and which the client should not treat as an error signal. Shutdown is by closing the server's input stream and waiting for it to exit.

Streamable HTTP. Each message is an HTTP POST to a single MCP endpoint, and the reply comes back either as a JSON object or as a request-scoped SSE stream. This is what a remote server uses, and it brings the whole HTTP apparatus with it: TLS, authentication, load balancers, rate limits. The older HTTP+SSE transport, with its separate GET endpoint, is deprecated and new servers should use Streamable HTTP.

Choosing a transport

stdio

  • Client launches the server as a subprocess
  • Newline-delimited JSON-RPC on stdin / stdout
  • Same machine only — local files, local tools
  • No network auth; the OS user is the boundary

Streamable HTTP

  • POST to one endpoint; JSON or an SSE stream back
  • Reachable by many clients, anywhere
  • Needs TLS, authentication, rate limiting
  • The only option the MCP connector accepts
This choice is usually made by the deployment, not by preference: where does the server need to run, and who else must reach it?

Client versus server responsibilities

The division is strict, and the current revision made it stricter. Clients send requests and notifications; servers send responses and notifications. Servers do not initiate JSON-RPC requests, and clients do not send responses. When a server needs something from the user mid-request — a confirmation, a missing value — it does not call back: it returns an InputRequiredResult whose inputRequests field says what it needs, and the client retries the original request carrying inputResponses. That is the Multi Round-Trip Requests pattern, and it replaces the older server-initiated calls.

The other structural change is that the protocol is now stateless. The initialize handshake and the notifications/initialized message are gone, as are protocol-level sessions and the session header on Streamable HTTP. Every request carries its own protocol version and client capabilities in _meta, which is what “per-request capability negotiation” means. A server that needs state across calls mints an explicit handle and passes it back as an ordinary tool argument. Servers must implement server/discover, which advertises supported versions, capabilities and identity, and a client may call it first to choose a version.

Deployment and integration with Claude applications

How a server is consumed depends on the host. Claude Code runs both kinds: a local server launched over stdio and a remote one over HTTP. Its tools appear with a namespaced name of the form mcp__<server>__<tool>, which is also how permission rules address them — so an MCP server's tools can be allowed, asked about or denied exactly like built-in tools, and a tool can be marked as requiring user interaction so that it always prompts.

The Messages API reaches remote servers through the MCP connector, so an application can use MCP without implementing an MCP client. You pass an mcp_servers array — each entry with type of url, an https:// URL, a name, and an optional authorization_token — together with the beta header the documentation specifies, and enable the server's tools through an mcp_toolset entry in tools. Three limits matter: it supports tools only, so prompts and resources are not reachable this way; it reaches remote servers only, since local stdio servers are not supported; and its data is not eligible for zero-data-retention arrangements.

Two ways a Claude application reaches a server

from the application down to the server

  1. Claude Codelocal stdio or remote HTTP servers
  2. Messages APIMCP connector — remote HTTP only
  3. mcp_serverstype: url, name, authorization_token
  4. Your servertools reachable; prompts and resources are not
  5. Your systemauth, scopes and audit live here
The constraint that decides most architectures is the second row: the connector reaches remote HTTP servers only, so a stdio server can never serve an API application.

Deployment decisions follow from that. A server that touches a developer's local files belongs on stdio and should never be exposed. A server that several applications share belongs on Streamable HTTP with real authentication, and its identity model should be the caller's, not one shared service account — 7.4 covers the credential side. Version it as you would any API: a tool removed or a schema tightened is a breaking change for every host that depends on it.

Security deserves a paragraph of its own, because the specification is explicit. Tools represent arbitrary code execution and must be treated with appropriate caution; descriptions of tool behaviour, including annotations, should be considered untrusted unless the server itself is trusted; and hosts must obtain explicit user consent before invoking a tool or exposing user data to a server. Installing a third-party MCP server is therefore a supply-chain decision: its tool descriptions enter your model's context, which makes a malicious description a prompt-injection vector — 7.1's subject, arriving through this door.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Writing log lines to standard output from a stdio serverstdout carries only valid MCP messages; logging belongs on stderr, which the specification permits explicitly.
Exposing every capability as a toolUse resources for data a user selects and prompts for named workflows, so the model is only asked to decide what it should decide.
Planning to reach a local stdio server from a server-side applicationThe MCP connector takes remote HTTPS servers only; anything the API must reach has to be deployed, authenticated and addressable.
Building on the initialize handshake, sessions, sampling or rootsThe current revision is stateless with per-request capability negotiation, and those features are removed or deprecated.
Trusting a third-party server's tool descriptions because it installed cleanlyTreat descriptions and annotations from an untrusted server as untrusted input, and require consent before tools run.

You should now be able to

  • Choose between a tool, a resource and a prompt from who initiates the interaction.
  • Author a small server with an SDK and explain how names, type hints and docstrings become the interface.
  • Describe both standard transports and pick one from the deployment's constraints.
  • State the client-server rules, including that servers never initiate requests, and how a server asks for input instead.
  • Attach a remote server to the Messages API through the MCP connector and name its three main limits.
  • Identify the security obligations of hosting or installing an MCP server.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    A team's MCP server works when tested with a script that pipes JSON to it, but the host reports malformed messages intermittently. The server uses a third-party HTTP library that prints connection-pool warnings, and it is launched as a subprocess.

    What is the cause?

    1. AThe messages exceed the line-length limit of the stdio transport and are being split.
    2. BLibrary warnings are going to standard output, which must carry only valid MCP messages.
    3. CThe server is missing the initialize handshake, so the client rejects later messages.
    4. DThe subprocess is being restarted between requests, losing the session state.
    Show answer and reasoning
    1. AIncorrect. There is no such limit; the framing requirement is that each message is one line with no embedded newlines.
    2. BCorrect. On stdio the server must not write anything to stdout that is not a valid MCP message, so any stray output corrupts the channel; logging belongs on stderr.
    3. CIncorrect. The current revision removed the handshake entirely; every request carries its own version and capabilities.
    4. DIncorrect. The protocol is stateless, so a restart loses in-flight requests but would not produce malformed messages.
  2. Question 2

    A documentation team wants Claude to be able to read their style guide when a writer asks for it, run a “draft release note” workflow that a writer starts by name, and look up a ticket's status whenever it becomes relevant mid-conversation.

    How should these three be exposed?

    1. AAll three as tools, so the model can use whichever it needs.
    2. BStyle guide as a resource, release-note workflow as a prompt, ticket lookup as a tool.
    3. CStyle guide as a prompt, release-note workflow as a resource, ticket lookup as a tool.
    4. DAll three as resources, with the host deciding when to include each.
    Show answer and reasoning
    1. AIncorrect. It would work, but it hands the model three decisions when two of them are the user's, and pulls the style guide into context unbidden.
    2. BCorrect. The primitive follows who initiates: the user selects the document, the user starts the named workflow, and the model decides when a ticket status is needed.
    3. CIncorrect. This inverts the first two: a prompt is a templated workflow and a resource is readable data, not the other way round.
    4. DIncorrect. A resource cannot execute anything, so the ticket lookup would have no way to run.
  3. Question 3

    An engineering team has a working stdio MCP server used inside Claude Code. Product now wants the same capabilities available from their customer-facing service, which calls the Messages API directly.

    Which two statements are correct? (Select 2.)

    1. AThe server must be redeployed behind Streamable HTTP, since the connector accepts remote HTTPS servers only.
    2. BAny prompts or resources the server offers will not be reachable through the connector.
    3. CThe connector can launch the stdio server if the command is supplied alongside the URL.
    4. DMoving to HTTP requires re-implementing the tool handlers, since the semantics differ per transport.
    5. EThe protocol's session header will carry the customer's identity across calls.
    Show answer and reasoning
    1. ACorrect. Local stdio servers are not supported by the MCP connector, so the capability has to become an addressable, authenticated HTTP service.
    2. BCorrect. The connector supports MCP tools only, so those capabilities have to be re-expressed as tools or delivered another way.
    3. CIncorrect. There is no launch mechanism: the connector takes a type of url and an HTTPS address, nothing else.
    4. DIncorrect. Protocol semantics are identical on every binding; a transport defines framing and delivery, not meaning.
    5. EIncorrect. Protocol-level sessions and the session header were removed; cross-call state uses explicit server-minted handles.
  4. Question 4

    A developer is writing an MCP server that sometimes needs a missing value from the user — a cost centre it cannot infer — before it can complete a create_request call.

    How should the server obtain it?

    1. ASend an elicitation request from the server to the client and wait for the reply.
    2. BFail the call with an error and rely on the model to ask the user and call again.
    3. CReturn an InputRequiredResult listing what it needs; the client retries with the responses.
    4. DOpen a subscriptions/listen stream and push a question to the client over it.
    Show answer and reasoning
    1. AIncorrect. Servers do not initiate JSON-RPC requests in the current revision; that pattern was replaced.
    2. BIncorrect. Workable in practice but lossy: a bare error carries no structured statement of what is needed, and the protocol defines a pattern for exactly this.
    3. CCorrect. Multi Round-Trip Requests is the defined pattern: the interim result carries inputRequests and the client re-issues the original request with inputResponses.
    4. DIncorrect. That stream carries opted-in change notifications, not questions, and it still would not make the server a requester.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.