Course Content
Agents & Tools Interview Prep
6 sections · 40 lessons
What are the main primitives in MCP (tools, resources, prompts)?
What you need to know
| Primitive | Controlled by | What it is | Example |
|---|---|---|---|
| Tool | Model | A function with an input schema; may have side effects | create_issue(title, body, labels) |
| Resource | Application / user | Read-only content identified by a URI | postgres://analytics/schema/orders |
| Prompt | User | A named template with arguments | /triage-issue number=4812 |
A simple way to remember it: tools are verbs, resources are nouns, prompts are saved recipes.
A server exposing all three
1from mcp.server import MCPServer # SDK v1: from mcp.server.fastmcp import FastMCP2from mcp.types import ToolAnnotations34mcp = MCPServer("github-triage")56@mcp.tool(annotations=ToolAnnotations(read_only_hint=False, destructive_hint=False))7def add_label(repo: str, issue_number: int, label: str) -> str:8 """Add one label to an issue. Use after deciding the issue type."""9 return github.add_label(repo, issue_number, label)1011@mcp.resource("github://{repo}/labels")12def label_guide(repo: str) -> str:13 """The repository's label definitions and when to use each."""14 return github.read_file(repo, ".github/labels.md")1516@mcp.prompt()17def triage_issue(issue_number: int) -> str:18 """Triage one issue using the team's checklist."""19 return f"Triage issue #{issue_number}: find duplicates, pick one type label, ..."2021if __name__ == "__main__":22 mcp.run() # stdio by default; mcp.run(transport="streamable-http") for remoteThe SDK builds each tool's JSON Schema from the type hints and uses the docstring as the description — so the docstring is the tool description the model reads.
Tool annotations are hints, not guarantees
Tools can carry annotations such as readOnlyHint, destructiveHint, idempotentHint and openWorldHint. A host can use them to decide when to ask for confirmation. The spec says clients must treat them as untrusted unless the server is trusted — a malicious server can mark a delete tool as read-only.
Client-side features
- Elicitation — the server asks the host to collect input from the user (a form, or a URL to visit). In the current spec this is returned as an "input required" result; the host asks the user and retries.
- Sampling (server borrows the host's model) and roots (host tells the server which folders it may use) — deprecated in 2026-07-28. Pass paths as tool arguments or configuration instead, and call model APIs directly if a server needs a model.
A real-life example
A SQL analytics server for a retail company exposes:
- Tools:
run_readonly_query(sql, max_rows)andexplain_query(sql). The model calls these as it works. - Resources:
warehouse://schema/sales,warehouse://glossary(what "net revenue" means). In the analyst's desktop app, these appear in a picker; the analyst attaches the sales schema to the chat before asking questions, so the model starts with the right table names. - Prompts:
weekly-sales-review(region)— a template the analyst runs as a slash command every Monday, which fills in the standard questions and date range.
The split matters for safety. The glossary is data the user chose to share; the model cannot pull arbitrary resources on its own. The query tool is model-controlled, so it is the one that runs under a read-only database role with row limits.
Follow-up questions to expect
- "Why not make everything a tool?" — You can, and many hosts only support tools. But resources let the user or app choose context deliberately, and prompts give users repeatable workflows without the model deciding.
- "Can a tool return a resource?" — Yes: tool results can include resource links or embedded resources.
- "Do all hosts support all primitives?" — No. Tool support is nearly universal; resource and prompt support varies by host.