You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 53ca740
Browse filesBrowse the repository at this point in the historyBrowse files
docs: embedded resources and resource links on the media page
The media page claimed to cover everything a tool can return besides
text but stopped at Image/Audio. Add "Embedding a resource" (a tool
returning an EmbeddedResource with the registered resource's URI and
MIME type; TextResourceContents vs BlobResourceContents) and "Linking to
a resource" (a mixed list result with a ResourceLink the client follows,
plus the dangling-link failure), with two new docs_src modules and the
tests that pin every claim.
No-Verification-Needed: docs, docs_src examples and tests only
Copy file name to clipboardExpand all lines: docs/servers/media.md
+145-3Lines changed: 145 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
Text is not the only thing a tool can return.
4
4
5
-
The SDK ships two helpers for binary results (**`Image`** and **`Audio`**) and an **`Icon`** type for giving your server, tools, resources, and prompts a face in the client's UI.
5
+
The SDK ships two helpers for binary results (**`Image`** and **`Audio`**) and an **`Icon`** type for giving your server, tools, resources, and prompts a face in the client's UI. Two protocol types cover results that are documents rather than pictures or sound: an **`EmbeddedResource`** carries a resource's contents inside the result, and a **`ResourceLink`** points at one for the client to read.
6
6
7
7
## Returning an image
8
8
@@ -31,7 +31,9 @@ Two things to notice:
31
31
!!! info
32
32
`ImageContent` and `AudioContent` live in `mcp.types`, right next to the `TextContent`
33
33
that a plain `str` result becomes (**[Tools](tools.md)**). A tool result is a list of content blocks; `Image` and `Audio` are
34
-
the shortest way to produce the two binary kinds.
34
+
the shortest way to produce the two binary kinds. The other two kinds, `EmbeddedResource` and
35
+
`ResourceLink`, are for documents rather than media: one carries the contents, the other points
36
+
at them. Each has its own section below.
35
37
36
38
### Try it
37
39
@@ -62,7 +64,7 @@ Same deal: a file on disk in, base64 and a MIME type out, no output schema.
62
64
63
65
## Bytes or a file
64
66
65
-
Both helpers also accept `data=` (raw bytes) instead of `path=`. That is the mode for bytes that never came from a file of their own — a database column, an HTTP response, something Pillow just drew:
67
+
Both helpers also accept `data=` (raw bytes) instead of `path=`. That is the mode for bytes that never came from a file of their own (a database column, an HTTP response, something Pillow just drew):
66
68
67
69
```python title="server.py" hl_lines="14 15"
68
70
--8<--"docs_src/media/tutorial003.py"
@@ -81,6 +83,143 @@ A suffix it doesn't recognise falls back to `application/octet-stream`.
81
83
`Audio` from MP3 bytes that way and the client is told `mime_type="audio/wav"`, then
82
84
faithfully fails to decode it. When you pass `data=`, pass `format=`.
83
85
86
+
## Embedding a resource
87
+
88
+
An **embedded resource** is a document carried inside a tool result: its text or bytes, the URI it lives at, and a MIME type.
89
+
90
+
A `str` result puts words in front of the model and an `Image` puts pixels there, but neither says what the content *is* or where it came from. An embedded resource does, which is what lets a client treat the block as a file rather than a string: it can render the Markdown, offer a download, or recognise a URI it already has open as context.
91
+
92
+
Say the example server, a brand kit, publishes its usage guidelines as a resource at `brand://guidelines`. That is how the *application* reads them, as **[Resources](resources.md)** describes. To hand the same document to the *model* on request, return it from a tool:
93
+
94
+
```python title="server.py" hl_lines="7 14 16-18"
95
+
--8<--"docs_src/media/tutorial005.py"
96
+
```
97
+
98
+
*`brand://guidelines` is an ordinary resource, declared exactly as it would be if no tool embedded it.
99
+
*`brand_guidelines` is a tool that returns the same document as an `EmbeddedResource`. There is no SDK helper here as there is for images: `EmbeddedResource` and `TextResourceContents` are the protocol types from `mcp.types`, and the block you build goes into the result untouched.
100
+
* The inner `TextResourceContents` holds the `uri`, the `mime_type`, and the `text`, exactly what a client receives when it reads a text resource. The text comes from calling `guidelines()` directly, an ordinary function call (the decorator hands your function back unchanged), so the resource and the tool serve one document from one place and the client needs no follow-up `resources/read`.
101
+
102
+
On the wire that is one block whose `type` is `"resource"`, with the contents nested one level down, and no `structuredContent` key at all: like an `Image`, a document is content for the model to read, not data for the application to parse.
103
+
104
+
```json
105
+
{
106
+
"content": [
107
+
{
108
+
"type": "resource",
109
+
"resource": {
110
+
"uri": "brand://guidelines",
111
+
"mimeType": "text/markdown",
112
+
"text": "# Brand guidelines\n\nUse the primary colour for calls to action.\n"
113
+
}
114
+
}
115
+
],
116
+
"isError": false,
117
+
"resultType": "complete"
118
+
}
119
+
```
120
+
121
+
The URI does not have to name a registered resource: any scheme is legal, and the tool call succeeds either way. Prefer one that resolves, because the URI is what a client uses to reopen or refresh the document later, and to tell that this block and `brand://guidelines` are the same thing.
122
+
123
+
### Text or bytes
124
+
125
+
`TextResourceContents` carries `text`. A binary document (the full brand book as a PDF, say) goes in a `BlobResourceContents` instead, and the base64 encoding that `Image` did for you is now yours to do:
126
+
127
+
```python
128
+
import base64
129
+
from pathlib import Path
130
+
131
+
from mcp.types import BlobResourceContents, EmbeddedResource
For a picture the model should *look at*, `Image` is still the better choice: an `ImageContent` is the block clients know to display, while what a client does with an embedded `image/png` blob is up to that client. Embed bytes for something that is genuinely a file, such as a PDF, a font, or an archive of assets.
143
+
144
+
## Linking to a resource
145
+
146
+
A **resource link** is a pointer: the URI and name of a resource the client can read, plus a little metadata about it, and none of its contents.
147
+
148
+
Embedding copies the document into the result, which is wasteful when the document is large or the model may never need it. A logo PNG is tens of kilobytes that most callers only need to know exists; a link to it is a couple of hundred bytes, and the client decides whether to spend a `resources/read` on the rest.
149
+
150
+
Put any PNG next to `server.py` as `logo.png`, publish it as a resource at `brand://logo`, and return one result that mixes text, an embedded document, and a link:
*`brand://logo` is a resource whose function returns `bytes`, so a client that reads it receives a blob.
157
+
*`brand_brief` returns a **list**, and each item becomes one content block, in order: the `str` becomes a `TextContent` and the two protocol blocks pass through untouched. This is how any tool returns several blocks of different kinds.
158
+
* The guidelines are a few lines of Markdown the model needs now, so they are embedded. `annotations=` is a field of every content block and sits on the `EmbeddedResource` beside `resource=`, not inside the `TextResourceContents`; `audience=["assistant"]` marks the block as context for the model rather than something to display.
159
+
* The logo is linked. `ResourceLink` has the fields of a `resources/list` entry: `name` and `uri` are required; `title`, `description`, `mime_type`, and `size` are what a client shows a person deciding whether to fetch it. `size` is the raw byte count before base64, so the client can also estimate what reading it would cost in context window.
160
+
161
+
On the wire the three blocks arrive in order. The link is flat where the embedded resource is nested:
162
+
163
+
```json
164
+
{
165
+
"content": [
166
+
{
167
+
"type": "text",
168
+
"text": "Follow the attached guidelines. The logo is linked; read it if you need the pixels."
169
+
},
170
+
{
171
+
"type": "resource",
172
+
"resource": {
173
+
"uri": "brand://guidelines",
174
+
"mimeType": "text/markdown",
175
+
"text": "# Brand guidelines\n\nUse the primary colour for calls to action.\n"
176
+
},
177
+
"annotations": {"audience": ["assistant"]}
178
+
},
179
+
{
180
+
"type": "resource_link",
181
+
"name": "logo",
182
+
"title": "Brand logo",
183
+
"uri": "brand://logo",
184
+
"description": "Full-colour logo, 2000 by 2000 pixels.",
185
+
"mimeType": "image/png",
186
+
"size": 48213
187
+
}
188
+
],
189
+
"isError": false,
190
+
"resultType": "complete"
191
+
}
192
+
```
193
+
194
+
None of the logo's bytes are in the result, only its description and size (`48213` here; yours is the size of whichever `logo.png` you used). A client that wants the pixels narrows the block to a `ResourceLink` and reads its URI like any other resource; what comes back is the logo as a blob:
That is two requests instead of one, and the second happens only if somebody wants it. As a rule, embed what the model needs now and can read (mostly text), and link what is large, optional, or one of many: a tool that finds forty matching files returns forty links, not forty files.
205
+
206
+
!!! check
207
+
A link is a promise that `resources/read` will answer for that URI, and nothing checks the
208
+
promise when the tool returns. Add a second link, to `brand://wordmark`, without registering
209
+
a resource there: the tool call succeeds, and the client that follows the link is the one
That is a JSON-RPC error (code `-32602`) raised at the caller, not a tool result the model
217
+
can read. A link does not have to appear in `resources/list` (a URI served by a resource
218
+
template never does), but it does have to resolve. Register the resource, then hand out its
219
+
address.
220
+
221
+
A prompt message can carry either block too; **[Prompts](prompts.md#more-than-text)** shows a file embedded in one.
222
+
84
223
## Icons
85
224
86
225
An `Icon` is metadata, not content. It doesn't carry the image; it points at one with a URI, and a client may fetch it and show it next to your server's name, a tool, a resource, or a prompt.
@@ -110,6 +249,9 @@ A tool's icons are on the `Tool` object from `tools/list`, a resource's on the `
110
249
111
250
* Return an `Image` or `Audio` from a tool and the client receives an `ImageContent` / `AudioContent` block: your bytes base64-encoded, with a MIME type.
112
251
* Build one from a `path=` and let the suffix decide the MIME type, or from in-memory `data=` plus an explicit `format=`.
252
+
* Return an `EmbeddedResource` to put a whole document in the result: a `TextResourceContents` (or a `BlobResourceContents` with a base64 `blob`) carrying its `uri`, a MIME type, and the contents.
253
+
* Return a `ResourceLink` to send only the pointer. The client follows it with `resources/read`, so link to URIs that resolve.
254
+
* A list return value becomes one content block per item, so one result can mix text, media, documents and links.
113
255
* Media results carry no `structured_content` and no output schema.
114
256
* An `Icon` is a pointer: a `src` URI plus optional `mime_type`, `sizes`, and `theme`.
115
257
*`icons=[...]` works on the server, on tools, on resources, and on prompts, and clients find them on the matching objects.
Copy file name to clipboardExpand all lines: docs/servers/structured-output.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -210,7 +210,7 @@ The opposite, `structured_output=True`, turns the automatic detection into a req
210
210
211
211
## Content blocks and media
212
212
213
-
Content blocks and media (`TextContent`, `EmbeddedResource`, `Image`, `Audio` and friends, on their own, as the items of a `list`, `tuple` or `Sequence`, or as the arms of a union) are opted out for you: they are for the model to read, so auto-detection derives no schema from them (**[Images, audio & icons](media.md)**covers `Image` and `Audio`). `structured_output=True` still forces one for the content-block classes.
213
+
Content blocks and media (`TextContent`, `EmbeddedResource`, `Image`, `Audio` and friends, on their own, as the items of a `list`, `tuple` or `Sequence`, or as the arms of a union) are opted out for you: they are for the model to read, so auto-detection derives no schema from them (**[Images, audio & icons](media.md)**shows a tool returning each of them). `structured_output=True` still forces one for the content-block classes.
0 commit comments