Chapter 17 · Core Primitives: Tools / Resources / Prompts
Chapter goals: Master the division of labor and control ownership of the three core primitives an MCP Server can expose — Tools, Resources, Prompts — understand the discovery mechanism, and learn to choose the correct primitive for a capability based on the scenario.
17.1 Three Primitives and Control Ownership
MCP defines three core primitives a Server can expose to a Client. Remembering their division of labor is important, but more importantly remember who decides when to use them (control ownership):
| Primitive | One-sentence Definition | Control | Typical Interaction |
|---|---|---|---|
| Tools | Executable functions callable by the model | Model-controlled | Model自主 decides when to call based on context |
| Resources | Data sources providing context to the model | Application-driven | Host application decides how to纳入 context |
| Prompts | Structured prompt templates | User-controlled | User explicitly selects to trigger (e.g. slash commands) |
The official docs repeatedly emphasize this三角关系: Tools面向 "let the model do things"; Resources面向 "feed data to the application"; Prompts面向 "let the user trigger preset flows with one click." Each primitive配 a */list (discover) and */get (fetch) method; Tools additionally have tools/call (execute). This design让 the list can be dynamic.
17.2 Tools: The Model's Hands
Tools让 the model can execute actions — querying databases, calling APIs, performing computations. Each tool is identified by a unique name and metadata describing its parameter schema.
Servers supporting Tools must声明 tools.listChanged: true in their capabilities, indicating "the tool list may变化; the client should listen for notifications/tools/list_changed and re-fetch." A complete调用流程如下:
// ① Client discovers tools
{ "jsonrpc": "2.0", "id": 1, "method": "tools/list" }
// ② Server returns the tool清单 (with JSON Schema parameter descriptions)
{
"jsonrpc": "2.0", "id": 1,
"result": {
"tools": [{
"name": "get_weather",
"description": "Query the current weather for a specified city",
"inputSchema": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}]
}
}
// ③ After the model decides to call, the Client发起 execution
{
"jsonrpc": "2.0", "id": 2,
"method": "tools/call",
"params": { "name": "get_weather", "arguments": { "city": "Shanghai" } }
}Security Red Line
The spec明确:出于 trust and safety, human-in-the-loop capability should always be保留 to拒绝 tool calls. The application should clearly展示 to the user which tools are exposed, give a visual indication when a tool is called, and弹出 confirmation for sensitive operations.
17.3 Resources: The Application's Pantry
Resources are data sources uniquely identified by URI — file contents, database schemas, API responses, etc. Their key特征 is application-driven: the Server摆出 the data, and the Host application decides how to use it (e.g. letting the user via UI pick which files to挂 into the conversation), not letting the model autonomously抓.
# mcp_server_resources.py —— declare resources with FastMCP
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("knowledge-base")
@mcp.resource("docs://{doc_name}")
def get_doc(doc_name: str) -> str:
"""Expose static document resources; URI形如 docs://readme"""
return open(f"./docs/{doc_name}.md").read()
@mcp.resource("config://app-settings")
def get_settings() -> str:
"""Dynamic resource: read the latest config each time"""
import json
return json.dumps(load_settings(), ensure_ascii=False)Resources support subscription: the client can call resources/subscribe on a resource, and when the resource changes, it receives a notifications/resources/updated notification to re-fetch — avoiding wasteful polling.
17.4 Prompts: User Shortcut Commands
Prompts are reusable prompt templates provided by the Server,带 parameters and customizable. "User-controlled" means the user decides when to使用 (the template content is still written by the Server author). The most natural presentation is slash commands in the chat input box:
/code-review PR#123 ← user types, Client retrieves the prompt template and fills parameters# mcp_server_prompts.py —— declare prompt templates with FastMCP
@mcp.prompt()
def review_code(code: str, focus: str = "security") -> str:
"""Code review template: triggered after the user selects and fills parameters in the UI"""
return f"""Please review the following code, focusing on {focus} issues:
Output format: problem list (sorted by severity) + modification suggestions."""Note the distinction: Prompts are user-initiated templates; the system prompt discussed in Chapter 6 is hardcoded by the application. The former is a "shortcut" the Server provides to the user.
17.5 Primitive Selection Decision Table and Comprehensive Example
Faced with a new capability, use this table to快速定 the primitive:
| Your need | Choose |
|---|---|
| The model needs to execute an action (query/write/compute) | Tool |
| The application wants to提供 a data source as context, and the application/user decides when | Resource |
| The user frequently要 trigger a固定的 complex questioning flow | Prompt |
| Need both data + execution (e.g. "database") | Combine all three |
The official architecture文档's综合案例 is exactly a database MCP Server:
# db_server.py —— a Server exposing all three primitives simultaneously
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("company-db")
# Tool: query action callable by the model (model-controlled)
@mcp.tool()
def run_query(sql: str) -> str:
"""Execute a SELECT query on the read-only replica; return a result table"""
return execute_readonly_sql(sql)
# Resource: database schema as context data (application-driven)
@mcp.resource("db://schema/users")
def users_schema() -> str:
return open("./schemas/users.sql").read()
# Prompt: encapsulate the standard question "analyze user growth" (user-controlled)
@mcp.prompt()
def growth_analysis(metric: str) -> str:
return f"""Based on the users table (schema见 the loaded resource), analyze the近 90-day {metric} trend.
First use the run_query tool to obtain data, then给出 three key conclusions."""
if __name__ == "__main__":
mcp.run() # start with default stdio transportThe three primitives协作: the user types /growth-analysis DAU to trigger the Prompt → Client loads the schema from the Resource into context → the model sees the run_query tool and decides to call → results回流 to生成 the final analysis.
Chapter Summary
- Control三角 of the three primitives: Tools model-controlled, Resources application-driven, Prompts user-triggered;
- All primitives have
*/listdynamic discovery; Tools additionally havetools/callexecution andlistChangedchange notifications; - Resources use URIs and support subscription updates; Prompts are explicitly triggered by users via slash commands, etc.;
- The spec requires retaining human-in-the-loop ability to拒绝 tool calls; high-risk operations must have confirmation;
- Complex scenarios combine all three primitives: Tool管 actions, Resource管 context data, Prompt管 standard flows.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. Which grouping correctly describes the control ownership of the three primitives?
2. What does it mean when a Server declares the tools capability with listChanged: true?
3. In the "database MCP Server"综合案例, which primitive should be used to expose the database schema to the application?
4. Regarding the MCP spec's security requirements for tool calls, which is correct?
🛠️ Hands-on Practice
- Write a "notebook" Server with FastMCP: expose add_note / search_notes two Tools and a notes://all Resource; test all functionality with MCP Inspector.
- Add a weekly-summary Prompt template to your notebook Server with date range parameters; verify its presentation in Inspector.
- Think about an internal system in your work: classify its functions into the three categories of Tool/Resource/Prompt,标注 each ownership, and find boundary cases where classification is ambiguous.
After completing the exercises, proceed to Next chapter: Transport Layer and Lifecycle.