Ressourcen
Maschinelle Übersetzung
Diese Seite wurde automatisch aus der englischen Dokumentation übersetzt, und die englische Seite ist die maßgebliche Fassung. Wenn sich etwas falsch liest, erklärt Übersetzungen, wie du es melden kannst.
Eine Ressource sind Daten, die du bereitstellst, damit die Anwendung sie lesen kann.
Das ist die Trennlinie. Ein Tool ist etwas, das das Modell aufzurufen beschließt. Eine Ressource ist etwas, das die Anwendung zu laden beschließt (eine Konfigurationsdatei, einen Datensatz, ein Dokument) und dem Modell als Kontext vorlegt.
Du deklarierst eine, indem du @mcp.resource(uri) auf eine ganz normale Python-Funktion setzt.
Deine erste Ressource
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.resource("config://app")
def get_config() -> str:
"""The active shop configuration."""
return "theme=dark\nlanguage=en"
Sie hat dieselbe Form wie ein Tool, plus eine Sache: den URI. Ressourcen werden adressiert, nicht benannt. Ein Client fragt nach config://app, nie nach get_config.
Den Rest liest das SDK weiterhin aus der Funktion:
- Der Name ist der Funktionsname:
get_config. - Die Beschreibung, die der Client sieht, ist der Docstring.
- Der Inhalt ist das, was du zurückgibst.
Bei resources/list bekommt der Client das hier:
{
"name": "get_config",
"uri": "config://app",
"description": "The active shop configuration.",
"mimeType": "text/plain"
}
Und wenn er config://app liest, läuft deine Funktion, und der Rückgabewert kommt als Text zurück:
result.contents # [TextResourceContents(uri="config://app", mime_type="text/plain", text="theme=dark\nlanguage=en")]
Tip
Auflisten ist billig. Deine Funktion wird bei resources/list nicht aufgerufen, nur bei
resources/read, und nur für den URI, nach dem gefragt wurde. Stelle tausend Ressourcen
bereit, und du zahlst nur für die, die jemand öffnet.
Ausprobieren
Starte den Server mit dem MCP Inspector:
uv run mcp dev server.py
Öffne die URL, die er ausgibt, und wechsle zum Tab Resources. config://app steht mit seiner Beschreibung in der Liste. Klicke darauf, und der Inspector liest es: Da sind deine zwei Zeilen Konfiguration.
Ressourcen-Templates
Ein URI pro Datensatz skaliert nicht. Setze einen Platzhalter in den URI und einen passenden Parameter auf die Funktion:
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.resource("config://app")
def get_config() -> str:
"""The active shop configuration."""
return "theme=dark\nlanguage=en"
@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
"""A customer's profile."""
return f"User {user_id}: 12 orders since 2021."
{user_id} im URI, user_id: str an der Funktion. Das ist der ganze Vertrag.
Das ist jetzt ein Ressourcen-Template, und es zieht um: Es verlässt resources/list und taucht stattdessen in resources/templates/list auf – als Muster statt als Adresse:
{
"name": "get_user_profile",
"uriTemplate": "users://{user_id}/profile",
"description": "A customer's profile.",
"mimeType": "text/plain"
}
Der Client füllt den Platzhalter aus und liest einen konkreten URI: users://42/profile, users://ada/profile. Eine einzige Funktion beantwortet sie alle, wobei der erkannte Wert als user_id übergeben wird:
result.contents # [TextResourceContents(uri="users://42/profile", text="User 42: 12 orders since 2021.")]
Beachte den uri im Ergebnis. Es ist der konkrete URI, nach dem der Client gefragt hat, nicht das Template.
Check
Platzhalter und Parameter müssen übereinstimmen. Benenne den Funktionsparameter in
user um, während im URI noch {user_id} steht, und der Dekorator verweigert sich beim Import,
bevor irgendein Client in die Nähe kommt:
ValueError: Mismatch between URI parameters {'user_id'} and function parameters {'user'}
Eine Abweichung kann immer nur ein Bug sein, also macht das SDK es unmöglich, den Server damit zu starten.
Die Platzhalter-Syntax ist RFC 6570: {+path} für Werte über mehrere Segmente, {?q,lang} für optionale Query-Parameter und mehr. Außerdem wendet das SDK standardmäßig Pfadsicherheitsprüfungen auf die extrahierten Werte an. Die vollständige Referenz steht in URI-Templates und Pfadsicherheit.
get_user_profile kann auch einen Parameter mit der Annotation Context entgegennehmen. Das SDK injiziert ihn, ohne ihn je als URI-Parameter zu behandeln, und die Seite Der Context beschreibt, was er dir bietet.
Was du zurückgibst
Du bist nicht auf str beschränkt. Gib jeder Ressource einen mime_type und gib zurück, was passt:
import base64
from mcp.server import MCPServer
mcp = MCPServer("Bookshop")
@mcp.resource("docs://readme", mime_type="text/markdown")
def readme() -> str:
"""How to use this server."""
return "# Bookshop\n\nSearch the catalog with the `search_books` tool."
@mcp.resource("stats://catalog", mime_type="application/json")
def catalog_stats() -> dict[str, int]:
"""Live counts for the catalog."""
return {"books": 1204, "authors": 391}
@mcp.resource("covers://placeholder", mime_type="image/gif")
def placeholder_cover() -> bytes:
"""A 1x1 transparent GIF, shown when a book has no cover."""
return base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")
readmegibt einenstrzurück, also wird er unverändert gesendet. Das ist der Normalfall.-
catalog_statsgibt eindictzurück, also serialisiert das SDK es für dich zu JSON-Text:{ "books": 1204, "authors": 391 } -
placeholder_covergibtbyteszurück, also bekommt der Client einBlobResourceContentsstatt einesTextResourceContents, mit deinen Bytes base64-kodiert im Feldblob.
Dieselbe Regel gilt für alles andere, was JSON-serialisierbar ist: eine Liste, ein Pydantic-Modell, eine Dataclass. Ist es kein str und kein bytes, wird es zu JSON.
mime_type deklarierst du selbst, und der Standardwert ist text/plain. Das SDK untersucht nie, was du zurückgibst, um ihn zu erraten – eine dict-Ressource, die du nicht kennzeichnest, wird also weiterhin als Plain Text angekündigt.
Tip
@mcp.resource() akzeptiert auch name=, title= und description=, wenn du sie nicht
aus der Funktion ableiten willst. Und wenn es gar keine Funktion zu schreiben gibt,
hält mcp.server.mcpserver.resources fertige Resource-Klassen bereit (TextResource,
BinaryResource, FileResource, HttpResource, DirectoryResource), die du
mit mcp.add_resource(...) registrierst.
Ein Client kann eine Ressource außerdem abonnieren und benachrichtigt werden, wenn sie sich ändert; das ist die Client-Hälfte der Geschichte, und sie steht in Der Client.
Zusammenfassung
@mcp.resource(uri)auf einer Funktion macht sie zur Ressource. Der URI ist die Adresse, der Rückgabewert ist der Inhalt, der Docstring ist die Beschreibung.- Ein
{placeholder}im URI macht sie zum Template: Es wird unterresources/templates/listaufgeführt, und eine einzige Funktion bedient jeden URI, der passt. - Die Platzhalternamen müssen den Parameternamen der Funktion entsprechen. Machst du es falsch, erfährst du es beim Import, nicht in Produktion.
- Deine Funktion läuft, wenn die Ressource gelesen wird, nicht wenn sie aufgelistet wird.
strwird zu Text,byteszu einem base64-Blob, alles andere zu JSON-Text. Mitmime_type=kennzeichnest du es.- Tools sind dafür da, dass das Modell handelt. Ressourcen sind dafür da, dass die Anwendung liest.
Das dritte Primitiv – das, das eine Person aus einem Menü auswählt – sind Prompts.