MCP Server Development: CCDV-F study guide
CCDV-F · Tools and MCPs, topic weight 2.1% of the exam
MCP Server Development is a topic in Domain 8, Tools and MCPs (10.6% of the CCDV-F exam), and carries 2.1% on its own. It tests whether you can build a Model Context Protocol server: choose between a tool, a resource and a prompt for each capability, pick the right transport, and deploy the server so Claude applications can connect to it. Configuring existing servers in Claude Code is covered in CCAR-F 2.4.
What the official guide covers
The Claude Certified Developer Foundations exam guide (version 1.0, effective July 2026) describes this topic as MCP server development practices:
| What the guide lists | What it means in practice |
|---|---|
| Server authoring | Write the server with an official MCP SDK (Python, TypeScript and others) and declare its tools, resources and prompts |
| Deployment | Ship it as a local process the client launches over stdio, or as a remote service over Streamable HTTP |
| Integration with Claude applications | Connect it from Claude Code, Claude Desktop or the Agent SDK |
| MCP resources, tools and prompts | The three building blocks a server offers, each controlled by a different party |
| Communication patterns (stdio, sockets, client vs server) | Who starts whom, and how messages travel: standard streams, HTTP, or the same framing over a socket |
Client, server and host
An MCP server is a program that exposes capabilities through the protocol. The host application, such as Claude Code, runs an MCP client for each server it connects to. The client sends JSON-RPC requests such as tools/list and tools/call; the server answers. The same server works with any MCP client, which is why one server can serve several Claude applications.
MCP does not change how Claude calls tools. The host lists the server's tools, hands them to Claude as ordinary tool definitions and routes each call to the server; the tool_use and tool_result loop is the same. What changes is who writes and maintains the tools.
The three building blocks
| Building block | Who controls it | Protocol methods | Use it for |
|---|---|---|---|
| Tools | The model: Claude decides when to call them | tools/list, tools/call | Actions and queries that take parameters |
| Resources | The application decides what to read and include | resources/list, resources/read, resources/templates/list | Read-only context such as files, schemas and docs, each with a URI |
| Prompts | The user invokes them explicitly | prompts/list, prompts/get | Reusable templates for common workflows |
Resources can be fixed (inventory://skus) or templates with parameters (inventory://sku/{sku}). In Claude Code, prompts appear as /mcp__<server>__<prompt> commands and resources are referenced with @<server>:<protocol>://<path>.
A server in Python
The current Python SDK (uv add "mcp[cli]") builds a server from type hints and docstrings with the MCPServer class. Older code imports FastMCP from mcp.server.fastmcp; the decorators work the same way.
import logging
from mcp.server import MCPServer
from mcp.server.mcpserver.exceptions import ToolError
logging.basicConfig(level=logging.INFO) # logging writes to stderr
logger = logging.getLogger(__name__)
mcp = MCPServer("inventory")
STOCK = {"SKU-100": 42, "SKU-200": 0}
@mcp.tool()
def check_stock(sku: str) -> str:
"""Return units in stock for one SKU. Use for availability questions about a
single product. SKUs look like SKU-100; read inventory://skus for the list."""
if sku not in STOCK:
raise ToolError(f"Unknown SKU {sku!r}. Read inventory://skus for valid SKUs.")
logger.info("stock check for %s", sku)
return f"{sku}: {STOCK[sku]} units"
@mcp.resource("inventory://skus")
def list_skus() -> str:
"""Every SKU the warehouse tracks."""
return "\n".join(STOCK)
@mcp.resource("inventory://sku/{sku}", mime_type="application/json")
def sku_detail(sku: str) -> dict:
"""Stock level for one SKU, as JSON."""
return {"sku": sku, "units": STOCK.get(sku, 0)}
@mcp.prompt()
def restock_plan(sku: str) -> str:
"""Draft a restock plan for one SKU."""
return f"Check stock for {sku} with check_stock, then draft a restock plan with quantities."
if __name__ == "__main__":
mcp.run() # stdio by default
The {sku} placeholder makes the second resource a template: the SDK passes the value from the URI to the function, and a return value that is not a string is converted to JSON. mime_type tells the client how to parse it.
Raising ToolError returns a result marked as an error, and Claude sees your message. Do not return an error string as a normal result, because Claude reads it as success. In TypeScript, the SDK's McpServer class registers tools with registerTool and a Zod input schema, and connects through StdioServerTransport.
stdio or Streamable HTTP
| stdio | Streamable HTTP | |
|---|---|---|
| Who starts the server | The client launches it as a subprocess | It runs on its own and can serve many clients |
| How messages travel | Newline-delimited JSON-RPC on stdin and stdout | Each client message is an HTTP POST to one MCP endpoint, such as /mcp; the reply is one JSON object or an SSE stream for that request |
| Logging | stderr only; stdout must carry nothing but MCP messages | Normal logging is fine |
| Security | Runs with the local user's permissions | Validate the Origin header, bind to 127.0.0.1 when local, and authenticate every connection |
| Good for | Personal tools, local files, wrapping a CLI | Shared team services and remote deployment |
In the Python SDK, mcp.run(transport="streamable-http") serves the endpoint at /mcp on 127.0.0.1 port 8000 by default; transport options go to run(), not to the constructor. The older HTTP+SSE transport is deprecated; new servers should use Streamable HTTP.
The stdio framing (one JSON-RPC message per line) is not tied to standard streams. The specification says it works unchanged over Unix domain sockets or TCP, and custom transports over a byte stream should reuse it.
Test, deploy and connect
- Test with the MCP Inspector:
uv run mcp dev server.pywith the Python SDK's CLI, ornpx @modelcontextprotocol/inspector python server.py, opens a browser client where you can list and call tools, read resources and get prompts. - Connect a local server to Claude Code:
claude mcp add --transport stdio inventory -- python /path/to/server.py. Everything after--is the command that starts the server. - Connect a remote server:
claude mcp add --transport http inventory https://mcp.example.com/mcp. - Use it from an agent: pass the server in the Agent SDK's
mcp_serversoption. Its tools appear asmcp__inventory__check_stock.
Where the configuration is stored, and how to keep tokens out of .mcp.json, is CCAR-F 2.4 territory. One deployment trap belongs here: a stdio server in a committed .mcp.json is only a launch command. Each teammate's Claude Code starts its own copy, so each machine needs the runtime and the server's dependencies, and each copy runs with that person's access. It looks shared but is not. A service the whole team should reach in one place runs over Streamable HTTP. In interactive sessions, Claude Code also asks each person to approve project-scoped servers before first use.
Know what each client supports
Not every MCP client uses all three building blocks. Claude Code offers resources through @ mentions and prompts as slash commands. The Claude API's MCP connector, which lets a Messages API request call a server directly, reaches only remote servers over HTTP, not stdio, and of the protocol's features supports only tool calls. If API applications will reach your server through the connector, anything they need must be a tool; resources and prompts need a client you run yourself with the MCP SDK. The connector's mcp_toolset can also enable only some of a server's tools, or defer loading their definitions.
The server is the privileged component
An MCP server usually holds the credential to the system behind it, so it is the part a misled agent would most like to reach. Scope that credential to the actions the tools need, not the account's full rights. Return only the fields Claude needs. Log each call with the caller and the result. Content the server returns from user-written records is untrusted data for whatever step reads it next.
Which design fits
| Situation | Choose | Why |
|---|---|---|
| Several Claude applications need the same capability | An MCP server | Build once, maintain in one place, connect many |
| Claude must act or query with parameters | A tool | Model-controlled, takes input |
| The app should load context such as a schema without tool calls | A resource | Application-controlled, read-only |
| Users repeat the same multi-step request | A prompt | User-invoked template |
| Only you use it, on your machine | stdio | No network service to secure |
| A team shares it, or it runs in the cloud | Streamable HTTP with authentication | One service, many clients |
| API apps will call the server through the MCP connector | Remote HTTP server, capabilities exposed as tools | The connector supports tool calls only |
| One Agent SDK app needs a function in-process | The SDK's in-process server (create_sdk_mcp_server) | No separate process to deploy |
| Every developer in the organisation needs the server | Streamable HTTP, deployed through Claude Code's managed MCP configuration | Admins set it once for every user |
Rules that decide exam answers
- Never write to stdout in a stdio server. A stray
print()corrupts the JSON-RPC stream. Log to stderr. - Match the building block to who controls it. Model: tool. Application: resource. User: prompt.
- Remote means authenticate and check
Origin. Local HTTP servers bind to localhost, not 0.0.0.0. - Report tool failures as error results. Raise the SDK's tool error; do not return an error string as success.
- Build once, connect many. Reuse across applications is the main reason to choose a server over per-app tool code.
- Check what the client supports. The API's MCP connector reaches remote servers only, and only their tools.
Where it appears in the exam
Tools and MCPs is Domain 8, 10.6% of the exam, and MCP Server Development is 2.1% of scored items, so expect about one question in a 53-item exam. The guide's own Domain 8 sample is about sharing one capability across several Claude applications. Expect servers that fail to connect, choices between tools, resources and prompts, and local versus remote deployment.
Two sample questions
These are original Timo practice questions. They are not official exam questions.
Build exercise
- Install the Python SDK and save the inventory server above as
server.py. - Run it under the MCP Inspector. Call
check_stockwith a valid and an invalid SKU, readinventory://skus, and get therestock_planprompt. - Add it to Claude Code with
claude mcp add --transport stdio. Ask a stock question, reference the resource with@, and run the prompt as a slash command. Then add aprint()to the tool and watch the connection fail. - Remove the
print(), switch tomcp.run(transport="streamable-http"), and connect withclaude mcp add --transport http inventory http://127.0.0.1:8000/mcp.
Practise this topic
- Claude Certified Developer practice exam: free, 20 questions, no sign-up
- CCDV-F study guide: all topics
- Worked example: Claude MCP server integration
- Same topic in another exam: 2.4 MCP server integration
- Previous topic: Tool Implementation
- Next topic: Agentic Customization
Sources
- Claude Certified Developer Foundations Exam Guide, version 1.0, effective July 2026 (Anthropic), Domain 8 topic: MCP Server Development
- Model Context Protocol: Build an MCP server
- Model Context Protocol: Understanding MCP servers
- Model Context Protocol specification: Streamable HTTP
- Claude Platform documentation: MCP connector
By Amotion AI