Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita25eb96

Browse files
authored
Adding docs for MCP sampling (#2027)
1 parent473b2ce commita25eb96

File tree

20 files changed

+335
-87
lines changed

20 files changed

+335
-87
lines changed

‎.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ examples/pydantic_ai_examples/.chat_app_messages.sqlite
1919
node_modules/
2020
**.idea/
2121
.coverage*
22+
/test_tmp/

‎docs/api/models/mcp-sampling.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pydantic_ai.models.mcp_sampling
2+
3+
::: pydantic_ai.models.mcp_sampling

‎docs/common-tools.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pip/uv-add "pydantic-ai-slim[duckduckgo]"
2020

2121
Here's an example of how you can use the DuckDuckGo search tool with an agent:
2222

23-
```py {title="main.py" test="skip"}
23+
```py {title="duckduckgo_search.py" test="skip"}
2424
from pydantic_aiimport Agent
2525
from pydantic_ai.common_tools.duckduckgoimport duckduckgo_search_tool
2626

@@ -103,7 +103,7 @@ pip/uv-add "pydantic-ai-slim[tavily]"
103103

104104
Here's an example of how you can use the Tavily search tool with an agent:
105105

106-
```py {title="main.py" test="skip"}
106+
```py {title="tavily_search.py" test="skip"}
107107
import os
108108

109109
from pydantic_ai.agentimport Agent

‎docs/dependencies.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ async def application_code(prompt: str) -> str: # (3)!
276276

277277
_(This example is complete, it can be run "as is")_
278278

279-
```python {title="test_joke_app.py" hl_lines="10-12" call_name="test_application_code"}
279+
```python {title="test_joke_app.py" hl_lines="10-12" call_name="test_application_code" requires="joke_app.py"}
280280
from joke_appimport MyDeps, application_code, joke_agent
281281

282282

‎docs/evals.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ Evaluators are the components that analyze and score the results of your task wh
5555

5656
Pydantic Evals includes several built-in evaluators and allows you to create custom evaluators:
5757

58-
```python {title="simple_eval_evaluator.py"}
58+
```python {title="simple_eval_evaluator.py" requires="simple_eval_dataset.py"}
5959
from dataclassesimport dataclass
6060

6161
from simple_eval_datasetimport dataset
@@ -616,7 +616,7 @@ _(This example is complete, it can be run "as is" — you'll need to add `asynci
616616

617617
You can also write datasets as JSON files:
618618

619-
```python {title="generate_dataset_example_json.py"}
619+
```python {title="generate_dataset_example_json.py" requires="generate_dataset_example.py"}
620620
from pathlibimport Path
621621

622622
from generate_dataset_exampleimport AnswerOutput, MetadataType, QuestionInputs

‎docs/graph.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ _(This example is complete, it can be run "as is" with Python 3.10+)_
167167

168168
A[mermaid diagram](#mermaid-diagrams) for this graph can be generated with the following code:
169169

170-
```py {title="graph_example_diagram.py" py="3.10"}
170+
```py {title="graph_example_diagram.py" py="3.10" requires="graph_example.py"}
171171
from graph_exampleimport DivisibleBy5, fives_graph
172172

173173
fives_graph.mermaid_code(start_node=DivisibleBy5)
@@ -308,7 +308,7 @@ _(This example is complete, it can be run "as is" with Python 3.10+ — you'll n
308308

309309
A[mermaid diagram](#mermaid-diagrams) for this graph can be generated with the following code:
310310

311-
```py {title="vending_machine_diagram.py" py="3.10"}
311+
```py {title="vending_machine_diagram.py" py="3.10" requires="vending_machine.py"}
312312
from vending_machineimport InsertCoin, vending_machine_graph
313313

314314
vending_machine_graph.mermaid_code(start_node=InsertCoin)
@@ -524,7 +524,7 @@ Alternatively, you can drive iteration manually with the [`GraphRun.next`][pydan
524524

525525
Below is a contrived example that stops whenever the counter is at 2, ignoring any node runs beyond that:
526526

527-
```python {title="count_down_next.py" noqa="I001" py="3.10"}
527+
```python {title="count_down_next.py" noqa="I001" py="3.10" requires="count_down.py"}
528528
from pydantic_graphimport End, FullStatePersistence
529529
from count_downimport CountDown, CountDownState, count_down_graph
530530

@@ -593,7 +593,7 @@ We can run the `count_down_graph` from [above](#iterating-over-a-graph), using [
593593

594594
As you can see in this code,`run_node` requires no external application state (apart from state persistence) to be run, meaning graphs can easily be executed by distributed execution and queueing systems.
595595

596-
```python {title="count_down_from_persistence.py" noqa="I001" py="3.10"}
596+
```python {title="count_down_from_persistence.py" noqa="I001" py="3.10" requires="count_down.py"}
597597
from pathlibimport Path
598598

599599
from pydantic_graphimport End
@@ -746,7 +746,7 @@ Instead of running the entire graph in a single process invocation, we run the g
746746

747747
_(This example is complete, it can be run "as is" with Python 3.10+)_
748748

749-
```python {title="ai_q_and_a_run.py" noqa="I001" py="3.10"}
749+
```python {title="ai_q_and_a_run.py" noqa="I001" py="3.10" requires="ai_q_and_a_graph.py"}
750750
import sys
751751
from pathlibimport Path
752752

@@ -965,7 +965,7 @@ You can specify the direction of the state diagram using one of the following va
965965
-`'BT'`: Bottom to top, the diagram flows vertically from bottom to top.
966966

967967
Here is an example of how to do this using 'Left to Right' (LR) instead of the default 'Top to Bottom' (TB):
968-
```py {title="vending_machine_diagram.py" py="3.10"}
968+
```py {title="vending_machine_diagram.py" py="3.10" requires="vending_machine.py"}
969969
from vending_machineimport InsertCoin, vending_machine_graph
970970

971971
vending_machine_graph.mermaid_code(start_node=InsertCoin,direction='LR')

‎docs/input.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Some LLMs are now capable of understanding audio, video, image and document cont
1010

1111
If you have a direct URL for the image, you can use[`ImageUrl`][pydantic_ai.ImageUrl]:
1212

13-
```py {title="main.py" test="skip" lint="skip"}
13+
```py {title="image_input.py" test="skip" lint="skip"}
1414
from pydantic_aiimport Agent, ImageUrl
1515

1616
agent= Agent(model='openai:gpt-4o')
@@ -26,7 +26,7 @@ print(result.output)
2626

2727
If you have the image locally, you can also use[`BinaryContent`][pydantic_ai.BinaryContent]:
2828

29-
```py {title="main.py" test="skip" lint="skip"}
29+
```py {title="local_image_input.py" test="skip" lint="skip"}
3030
import httpx
3131

3232
from pydantic_aiimport Agent, BinaryContent
@@ -69,7 +69,7 @@ You can provide document input using either [`DocumentUrl`][pydantic_ai.Document
6969

7070
If you have a direct URL for the document, you can use[`DocumentUrl`][pydantic_ai.DocumentUrl]:
7171

72-
```py {title="main.py" test="skip" lint="skip"}
72+
```py {title="document_input.py" test="skip" lint="skip"}
7373
from pydantic_aiimport Agent, DocumentUrl
7474

7575
agent= Agent(model='anthropic:claude-3-sonnet')
@@ -87,7 +87,7 @@ The supported document formats vary by model.
8787

8888
You can also use[`BinaryContent`][pydantic_ai.BinaryContent] to pass document data directly:
8989

90-
```py {title="main.py" test="skip" lint="skip"}
90+
```py {title="binary_content_input.py" test="skip" lint="skip"}
9191
from pathlibimport Path
9292
from pydantic_aiimport Agent, BinaryContent
9393

‎docs/mcp/client.md

Lines changed: 110 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Will display as follows:
9898

9999
Before creating the Streamable HTTP client, we need to run a server that supports the Streamable HTTP transport.
100100

101-
```python {title="streamable_http_server.py" py="3.10"test="skip"}
101+
```python {title="streamable_http_server.py" py="3.10"dunder_name="not_main"}
102102
from mcp.server.fastmcpimport FastMCP
103103

104104
app= FastMCP()
@@ -107,7 +107,8 @@ app = FastMCP()
107107
defadd(a:int,b:int) ->int:
108108
return a+ b
109109

110-
app.run(transport='streamable-http')
110+
if__name__=='__main__':
111+
app.run(transport='streamable-http')
111112
```
112113

113114
Then we can create the client:
@@ -194,7 +195,7 @@ async def process_tool_call(
194195
returnawait call_tool(tool_name, args,metadata={'deps': ctx.deps})
195196

196197

197-
server= MCPServerStdio('python', ['-m','tests.mcp_server'],process_tool_call=process_tool_call)
198+
server= MCPServerStdio('python', ['mcp_server.py'],process_tool_call=process_tool_call)
198199
agent= Agent(
199200
model=TestModel(call_tools=['echo_deps']),
200201
deps_type=int,
@@ -275,3 +276,109 @@ agent = Agent('openai:gpt-4o', mcp_servers=[python_server, js_server])
275276
```
276277

277278
When the model interacts with these servers, it will see the prefixed tool names, but the prefixes will be automatically handled when making tool calls.
279+
280+
##MCP Sampling
281+
282+
!!! info "What is MCP Sampling?"
283+
In MCP[sampling](https://modelcontextprotocol.io/docs/concepts/sampling) is a system by which an MCP server can make LLM calls via the MCP client - effectively proxying requests to an LLM via the client over whatever transport is being used.
284+
285+
Sampling is extremely useful when MCP servers need to use Gen AI but you don't want to provision them each with their own LLM credentials or when a public MCP server would like the connecting client to pay for LLM calls.
286+
287+
Confusingly it has nothing to do with the concept of "sampling" in observability, or frankly the concept of "sampling" in any other domain.
288+
289+
??? info "Sampling Diagram"
290+
Here's a mermaid diagram that may or may not make the data flow clearer:
291+
292+
```mermaid
293+
sequenceDiagram
294+
participant LLM
295+
participant MCP_Client as MCP client
296+
participant MCP_Server as MCP server
297+
298+
MCP_Client->>LLM: LLM call
299+
LLM->>MCP_Client: LLM tool call response
300+
301+
MCP_Client->>MCP_Server: tool call
302+
MCP_Server->>MCP_Client: sampling "create message"
303+
304+
MCP_Client->>LLM: LLM call
305+
LLM->>MCP_Client: LLM text response
306+
307+
MCP_Client->>MCP_Server: sampling response
308+
MCP_Server->>MCP_Client: tool call response
309+
```
310+
311+
Pydantic AI supports sampling as both a client and server. See the[server](./server.md#mcp-sampling) documentation for details on how to use sampling within a server.
312+
313+
Sampling is automatically supported by Pydantic AI agents when they act as a client.
314+
315+
Let's say we have an MCP server that wants to use sampling (in this case to generate an SVG as per the tool arguments).
316+
317+
??? example "Sampling MCP Server"
318+
319+
```python {title="generate_svg.py" py="3.10"}
320+
import re
321+
from pathlib import Path
322+
323+
from mcp import SamplingMessage
324+
from mcp.server.fastmcp import Context, FastMCP
325+
from mcp.types import TextContent
326+
327+
app = FastMCP()
328+
329+
330+
@app.tool()
331+
async def image_generator(ctx: Context, subject: str, style: str) -> str:
332+
prompt = f'{subject=} {style=}'
333+
# `ctx.session.create_message` is the sampling call
334+
result = await ctx.session.create_message(
335+
[SamplingMessage(role='user', content=TextContent(type='text', text=prompt))],
336+
max_tokens=1_024,
337+
system_prompt='Generate an SVG image as per the user input',
338+
)
339+
assert isinstance(result.content, TextContent)
340+
341+
path = Path(f'{subject}_{style}.svg')
342+
# remove triple backticks if the svg was returned within markdown
343+
if m := re.search(r'^```\w*$(.+?)```$', result.content.text, re.S | re.M):
344+
path.write_text(m.group(1))
345+
else:
346+
path.write_text(result.content.text)
347+
return f'See {path}'
348+
349+
350+
if __name__ == '__main__':
351+
# run the server via stdio
352+
app.run()
353+
```
354+
355+
Using this server with an`Agent` will automatically allow sampling:
356+
357+
```python {title="sampling_mcp_client.py" py="3.10" requires="generate_svg.py"}
358+
from pydantic_aiimport Agent
359+
from pydantic_ai.mcpimport MCPServerStdio
360+
361+
server= MCPServerStdio(command='python',args=['generate_svg.py'])
362+
agent= Agent('openai:gpt-4o',mcp_servers=[server])
363+
364+
365+
asyncdefmain():
366+
asyncwith agent.run_mcp_servers():
367+
result=await agent.run('Create an image of a robot in a punk style.')
368+
print(result.output)
369+
#> Image file written to robot_punk.svg.
370+
```
371+
372+
_(This example is complete, it can be run "as is" with Python 3.10+)_
373+
374+
You can disallow sampling by settings[`allow_sampling=False`][pydantic_ai.mcp.MCPServerStdio.allow_sampling] when creating the server reference, e.g.:
375+
376+
```python {title="sampling_disallowed.py" hl_lines="6" py="3.10"}
377+
from pydantic_ai.mcpimport MCPServerStdio
378+
379+
server= MCPServerStdio(
380+
command='python',
381+
args=['generate_svg.py'],
382+
allow_sampling=False,
383+
)
384+
```

‎docs/mcp/run-python.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ As introduced in PEP 723, explained [here](https://packaging.python.org/en/lates
122122

123123
This allows use of dependencies that aren't imported in the code, and is more explicit.
124124

125-
```py {title="inline_script_metadata.py" py="3.10"}
125+
```py {title="inline_script_metadata.py" py="3.10" requires="mcp_run_python.py"}
126126
from mcpimport ClientSession
127127
from mcp.client.stdioimport stdio_client
128128

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp