Python 服务器

本章用官方 SDK 一次暴露 Tool + Resource + Prompt。API 以 First steps 为准:类名是 MCPServer(v1 叫 FastMCP),导入是 from mcp.server import MCPServer


完整示例

from pathlib import Path

from mcp.server import MCPServer

mcp = MCPServer(
    "workshop",
    instructions="Local demo: add numbers, read a greeting, summarize text.",
    version="0.1.0",
)

NOTES = Path(__file__).with_name("notes")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two integers and return the sum."""
    return a + b


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Return a short greeting for the given name."""
    return f"Hello, {name}!"


@mcp.resource("notes://inbox")
def notes_inbox() -> str:
    """Read the local notes/inbox.md file if it exists."""
    path = NOTES / "inbox.md"
    if not path.is_file():
        return "(missing notes/inbox.md — create it next to server.py)"
    return path.read_text(encoding="utf-8")


@mcp.prompt()
def summarize(text: str) -> str:
    """Ask the model to summarize the given text in one sentence."""
    return f"Summarize the following text in one sentence:\n\n{text}"


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

在同级目录建 notes/inbox.md,写几行自己的笔记,Inspector 的 Resources 里就能读到固定 URI notes://inboxgreeting://{name}模板,出现在 Resource Templates,填 name=Ada 再读。


三个装饰器分别做了什么

装饰器谁触发你返回什么
@mcp.tool()模型选中后,Host 发 tools/call动作结果(数字、字符串、结构化数据)
@mcp.resource("uri")Host 决定读入上下文只读文本或字节
@mcp.prompt()用户从菜单 / 斜杠命令选取一条用户消息(或消息列表)

URI 里写 {param} 就成为模板,参数必须与函数签名一致。没有花括号的 URI 是固定资源,会出现在 resources/list

说明文字来自 docstring,参数 schema 来自 类型注解。需要更细的字段说明时,用 Annotated + pydantic.Field(与官方 Tools / Prompts 文档相同)。


启动方式

Stdio(给 Cursor / Claude Code / mcp dev):

python .\server.py
python ./server.py

或让 CLI 找到模块级的 mcp 对象:

mcp run .\server.py

HTTP:

if __name__ == "__main__":
    mcp.run(transport="streamable-http", host="127.0.0.1", port=8000)
mcp run .\server.py --transport streamable-http

传输相关参数只传给 run()。传给构造函数会得到:TypeError: MCPServer.__init__() got an unexpected keyword argument 'port'


在 Inspector 里验收

mcp dev .\server.py

按标签走一遍:

  1. Toolsadd(2, 5)7
  2. Resources → 读 notes://inbox
  3. Resource Templatesgreeting / AdaHello, Ada!
  4. Promptssummarize,填一段文字 → 得到 role: user 的渲染结果

Host 不一定把 Resource / Prompt 暴露得像 Inspector 这么完整;先在 Inspector 证明 Server 正确,再查客户端 UI。


日志与密钥

  • logging,不要 print 到 stdout
  • Token 读环境变量,不要写进 Resource 正文
  • 读本地文件时限制在你自己的目录(本例只用 notes/
import logging
import os

log = logging.getLogger("workshop")

@mcp.tool()
def masked_env_name() -> str:
    """Return whether NOTES_TOKEN is set, never the raw value."""
    present = bool(os.environ.get("NOTES_TOKEN"))
    log.info("NOTES_TOKEN set=%s", present)
    return "configured" if present else "missing"

与 v1 FastMCP 对照

v1v2(本课)
from mcp.server.fastmcp import FastMCPfrom mcp.server import MCPServer
FastMCP("demo")MCPServer("demo")
@mcp.tool() / resource / prompt相同
mcp.run()相同(HTTP 选项改挂在 run() 上)

把旧文件里的类名与导入换成 MCPServer,其余装饰器代码通常可以直接用。


下一步

评论