You'll walk away with
- What MCP is, why it exists, and how it differs from raw tool-calling on the Claude API
- The three MCP primitives (
tools,resources,prompts) and which one to reach for in a given situation - How to scaffold an MCP server in Python with the official SDK and verify it with the MCP Inspector
- How to implement an MCP client that handshakes, lists capabilities, and routes calls to the server
- How resources differ from tools (read-only data exposure vs callable actions) and why that distinction matters
- How prompts work as reusable, parameterized prompt templates the user explicitly invokes
Read these first
Lesson outline
Every lesson from Introduction to Model Context Protocol with our one-line simplification. The Skilljar course is the source; we summarize.
| # | Skilljar lesson | Our simplification |
|---|---|---|
| 1 | Welcome to the course | Course framing: build an MCP server and client from scratch in Python; tools, resources, prompts are the three primitives. |
| 2 | Introducing MCP | MCP is the USB-C of LLM integrations: one open protocol so any client speaks to any tool server without bespoke glue. |
| 3 | MCP clients | Clients (Claude Desktop, Claude Code, Cursor, custom harnesses) discover and call MCP servers over stdio or streamable-http. |
| 4 | Project setup | Scaffold a Python project with uv, install the MCP SDK, and create the empty server entrypoint that the inspector can hit. |
| 5 | Defining tools with MCP | A tool is a callable function the model can decide to invoke; declare it with a name, description, and JSON-Schema input shape. |
| 6 | The server inspector | MCP Inspector is the curl-equivalent for your server; use it to manually fire tools and verify the wire protocol before wiring a client. |
| 7 | Course satisfaction survey | Mid-course feedback checkpoint; no technical content. |
| 8 | Implementing a client | Build a minimal Python client that handshakes, calls list_tools, and routes Claude's tool-use blocks to the server. |
| 9 | Defining resources | A resource is read-only data exposed at a URI (file, DB row, log) that the client can fetch and inject as context. |
| 10 | Accessing resources | Clients enumerate resources via list_resources and fetch bytes via read_resource; the user (not the model) usually picks which to attach. |
| 11 | Defining prompts | Prompts are server-defined, parameterized prompt templates a user explicitly invokes (e.g., a slash command in the client). |
| 12 | Prompts in the client | Wire the client to surface server prompts as user-selectable shortcuts; the user fills in arguments, the server returns a message list. |
| 13 | Final assessment on MCP | Assessment covering primitives, control flow, and which surface to use for which job. |
| 14 | MCP review | Recap: tools are model-controlled actions, resources are user-controlled data, prompts are user-invoked templates. |
The course in 7 paragraphs
MCP exists because every team that integrated Claude with their internal systems was solving the same plumbing problem: write a tool schema, write the dispatcher, write the auth, write it again next quarter when the API changes. Model Context Protocol standardizes that plumbing as an open spec so any MCP-aware client (Claude Desktop, Claude Code, Cursor, your own harness) can talk to any MCP server without bespoke wiring. Anthropic publishes the spec and reference SDKs in Python and TypeScript; the community publishes hundreds of servers for GitHub, Slack, Postgres, filesystems, browsers, and more.
The protocol exposes three primitives, and the exam loves to test which one fits which job. Tools are functions the *model* decides to call (write to a database, run a query, send a Slack message). Resources are read-only data the *user or client* decides to attach (a file's contents, a row from a table, a log snippet). Prompts are parameterized prompt templates the *user* explicitly invokes, often as slash commands. The control point is the load-bearing distinction: model-controlled tools, user-controlled resources, user-invoked prompts.
Mechanically, an MCP server is a process that speaks JSON-RPC over either stdio (local subprocess; default) or streamable-http (remote, multiplexed). The client launches or connects to the server, performs a capability handshake, then calls list_tools, list_resources, and list_prompts to discover what the server offers. Discovery is dynamic: the server can publish or revoke capabilities at runtime, and the client surfaces them to the user or the model accordingly. The Python SDK wraps this so you write decorators (@mcp.tool(), @mcp.resource(), @mcp.prompt()) and the SDK handles the wire format.
Tools are where most beginners over-reach. The temptation is to ship a single do_anything tool with a free-form query string. Don't. The exam, and reality, both reward narrow tools with strict JSON-Schema inputs, like create_issue(title, body, labels[]) rather than github(query). The description field is what the model reads to decide whether to call the tool, so write it like a function docstring aimed at a teammate, not a marketing blurb. Tool errors should return structured failure (isError: true plus a message) rather than raising; the model can then recover.
Resources are the primitive that's easiest to misunderstand. A resource has a URI, a MIME type, and a read_resource handler that returns bytes. Think of them as files that live behind the server. The user (or the client UI) picks which ones to attach to the conversation, the server reads them on demand, and Claude treats the contents as plain context. Resources should never have side effects; if you find yourself wanting one to do work, you wanted a tool. The MCP Inspector lets you enumerate resources and fetch them by URI without writing any client code, which is invaluable while developing.
Prompts round out the trio: they are server-side prompt templates with named arguments, surfaced to the user as shortcuts (Claude Desktop renders them as slash commands; Claude Code surfaces them in its prompt chooser). When the user invokes one, the server returns a list of messages, ready to send. Prompts are a UX primitive, not a model primitive. The model never decides to invoke a prompt; the user does. Treat them as the right home for canonical workflows your team runs over and over: /standup, /review-pr, /draft-incident-postmortem. Each one becomes shareable, version-controlled, and discoverable across every MCP-aware client.
When you've finished the course you should be able to: scaffold a server with the official Python SDK, expose a tool with strict JSON-Schema inputs, expose a resource at a stable URI, define a prompt template with named arguments, verify each surface via the MCP Inspector, and wire a thin Python client that proxies Claude's tool-use blocks to the server. That's the whole loop. The exam is built around recognizing this loop in different shapes. A question that says "Claude should be able to read a project file but not modify it" is testing resources vs tools. A question about a slash command in Claude Desktop is testing prompts. A question about why tool descriptions matter is testing the model-controlled discovery flow. From there, advanced MCP topics (sampling, notifications, roots, transport choice) are the natural next layer, covered in mcp-advanced, and they are where production deployments actually live.
The 3 MCP primitives and who controls each
The single most-tested MCP concept is which primitive fits which job. Memorize the control point.
- Tools: model-controlled actions
The model decides whether and when to call a tool. Use for side-effectful actions: writes, sends, mutations. Declare with a name, description, and JSON-Schema input. Return structured success or
Concept: tool-calling ↗isError: truefor failures. - Resources: user-controlled data
The user (or client UI) decides which resources to attach to the conversation. Use for read-only context: file contents, DB rows, logs. Each resource has a URI and MIME type; the server reads bytes on demand. Never side-effectful.
Concept: mcp ↗ - Prompts: user-invoked templates
The user explicitly invokes a prompt (often as a slash command). The server returns a list of messages with arguments interpolated. Treat as a UX shortcut for canonical team workflows. The model never picks prompts.
Concept: prompt-engineering-techniques ↗
5 anti-patterns when designing MCP tools
Each of these will be a question on the exam in disguise. Watch for the failure mode behind each.
- The kitchen-sink tool
A single
Concept: tool-calling ↗do_thing(query: string)that branches internally on the query. The model can't tell when to call it; descriptions become marketing. Split into narrow tools with typed inputs. - Free-text inputs instead of JSON-Schema
Letting Claude pass
Concept: structured-outputs ↗args: stringand parsing inside the tool. The schema is your validation contract; without it the model hallucinates shapes. Always type every input. - Raising exceptions instead of structured errors
An unhandled exception kills the tool call without giving the model anything to recover from. Return
Concept: agentic-loops ↗isError: truewith a message so the loop can adapt. - Side-effects in resources
Treating resources as triggerable actions. Resources are read-only; the moment one mutates state, you wanted a tool. Mixing them up makes audit and gating impossible.
Concept: mcp ↗ - No description, or a marketing description
The description is what the model reads to decide whether to call the tool. Write it like a function docstring, not a tagline. State what it does, when to use it, and what it returns.
Concept: tool-calling ↗
6 takeaways with cross-pillar bridges
MCP standardizes the LLM-to-system integration plumbing so any MCP-aware client speaks to any MCP server without bespoke glue.
The three primitives split by control point: tools are model-controlled, resources are user-controlled, prompts are user-invoked.
Tools should be narrow with strict JSON-Schema inputs; the description field is what the model reads to decide whether to call them.
Resources are read-only data attached at user discretion; if you want side effects, you wanted a tool, not a resource.
Use the MCP Inspector to verify your server end-to-end before wiring a client; it is the curl-equivalent for the protocol.
Prompts are server-side templates the user invokes (often as slash commands); they are a UX primitive, not a model primitive.
How this maps to the CCA-F exam
3 hand-picked extras
These amplify the Skilljar course beyond what the course itself covers. Each was picked for a specific reason.
Introducing the Model Context Protocol
Anthropic's launch announcement framing the why behind MCP; pair with the Skilljar lessons for the architectural rationale the videos skim past.
Read source ↗Model Context Protocol: Introduction
The canonical spec home with primitive references, SDK guides, and the Inspector docs. The Skilljar course teaches Python; this is where you go for TypeScript, Java, or transport details.
Read source ↗Awesome MCP Servers
Living catalog of community MCP servers (GitHub, Postgres, browser, filesystem, Slack). Useful when designing your own to see what conventions have settled across the ecosystem.
Read source ↗Concepts in this course
Model Context Protocol
Core protocol the course builds around
Concept: mcp ↗Tool calling
How MCP tools become Claude tool-use blocks
Concept: tool-calling ↗Structured outputs
JSON-Schema input validation for tools
Concept: structured-outputs ↗Prompt engineering techniques
Crafting reusable MCP prompt templates
Concept: prompt-engineering-techniques ↗Where you'll see this in production
Other course mirrors you may want next
8 questions answered
Phrased as the way real students search. Tagged by intent so you can scan to what you actually need.
DefinitionWhat is Model Context Protocol and why do I need it?
ComparisonWhat are the three MCP primitives and when do I use each one?
How-toHow do I create my first MCP server in Python?
uv add mcp, create a server.py that instantiates FastMCP, and decorate functions with @mcp.tool(), @mcp.resource(), or @mcp.prompt(). Run it with uv run server.py and verify with mcp dev server.py to launch the MCP Inspector. The Inspector lets you fire tools and read resources without writing a client.ComparisonWhat is the difference between an MCP tool and an MCP resource?
TroubleshootWhy isn't my MCP server showing up in Claude Desktop?
claude_desktop_config.json in the right OS-specific path (~/Library/Application Support/Claude/ on macOS) and confirm the JSON is valid. Then restart Claude Desktop; the config is read at startup, not live-reloaded. Run the same command from the config block in a terminal to confirm the server actually starts. Use the MCP Inspector to bypass Claude Desktop entirely and isolate whether the bug is in the server or the wiring.ComparisonAre MCP tools the same as Claude API tool use?
tool_use block), but the discovery and dispatch layer is different. With raw Claude API tool use, your code declares the tools inline and dispatches the call. With MCP, the client discovers tools dynamically from a server, and the server handles dispatch. MCP shines when many clients need to share the same tool surface.ComparisonHow does MCP differ from agent skills in Claude Code?
agent-skills-intro course goes deeper on the latter.How-toWhat is the MCP Inspector and how do I use it?
mcp dev <server-file>. Treat it as curl for MCP: use it to verify the protocol surface before wiring a real client, and to reproduce bugs without involving Claude.