← All AI Engineer talks

AI Engineer World's Fair 2025

MCP is all you need

Samuel Colvin· Founder, Pydantic15:24

Read the talk

MCP is all you need: letting tools borrow the client’s model

MCP sampling lets an agent inside a tool request inference through its client. A PyPI research example shows how this separates specialized context, SQL repair and progress reporting from the main agent.

From a talk by Samuel Colvin

Before you start: Familiarity with Python, LLM tool calling and basic SQL will help you follow the demonstration.

How much infrastructure does agent communication need?

When agents need to communicate, how much new infrastructure do we actually need? Samuel Colvin approaches that question from Python’s validation and agent tooling. He created Pydantic, which he reports was receiving about 360 million downloads a month, approximately 140 per second. He describes its use throughout general Python development and broadly across Python’s generative AI SDKs and agent frameworks.

Pydantic became a company at the beginning of 2023, then expanded into Pydantic AI, a Python agent framework built on similar principles, and Logfire, its commercial observability platform. Colvin also describes himself as a somewhat inactive co-maintainer of the MCP Python SDK. Those projects bring together the concerns that recur throughout the demonstration: typed interfaces, agent execution and visibility into what happened.

The title riffs on Jason Liu’s talks, Pydantic Is All You Need and Pydantic Is Still All You Need. Its qualification matters: neither Pydantic nor MCP solves everything. The useful claim is narrower—MCP already provides capabilities that can simplify communication between agents, so inventing another communication mechanism may be unnecessary.

Slide titled “what” explains the talk’s title and highlights MCP’s role in multi-agent communication, with the speaker inset below.
MCP for communication between autonomous agents.

The scope here is autonomous agents and application code, particularly Python, rather than Claude Desktop, Cursor, Zed or Windsurf. Colvin characterizes those interactive applications as MCP’s original focus. MCP’s prompts and resources can be valuable there; for the autonomous application he is building, they play a smaller role. Tool calling is the part that carries the design.

0:170:30
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

0:17 · section reference included

Tool calling needs more than an endpoint description

Why not describe the tools with OpenAPI? Colvin’s answer is that the requirements extend beyond describing a request and its final response. A running agent may need a changing tool inventory, messages during execution, or an inference request flowing in the opposite direction.

CapabilityWhat it enables
Dynamic toolsTools appear or disappear as server state changes.
LoggingInformation reaches the client before a tool finishes.
SamplingA server asks its client to arrange inference.
Tracing and observabilityDevelopers inspect execution across components.
Standard input/outputA tool server runs as a local subprocess.

These are the requirements behind Colvin’s OpenAPI comparison. In particular, subprocess communication over standard input and standard output gives application code a useful way to compose local tools without treating every integration as an HTTP service.

3:113:22
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

3:11 · section reference included

When a tool is itself an agent

The familiar MCP architecture connects an agent to independently developed tools. The agent need not be designed for a particular tool, and the tool need not know the agent that will call it. Colvin compares this to a browser visiting a website: the shared protocol makes composition possible. Today’s browser monoculture weakens the diversity of that example, but the original interoperability principle still applies.

Now let one of those tools be an agent. It can reason with an LLM and call additional tools, either through MCP or through direct integrations. The composition still works, but each nested agent now needs model access, configuration and resources. A remote MCP server also has to account for the inference costs it incurs while answering someone else’s request.

Sampling moves access to inference back through the client. Instead of requiring every agent exposed as a tool to provision its own model connection, the tool can ask the originating client to obtain a model response on its behalf.

4:014:10
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

4:01 · section reference included

The sampling round trip

In MCP, sampling means a server can request a model generation through its client. The server is still serving a tool call; requesting inference is an intermediate step toward completing that call.

The request follows this sequence:

  1. The outer agent calls its LLM, which selects an MCP tool.
  2. The client sends the tool call to the MCP server.
  3. The server determines that it needs an LLM response and sends a sampling request back to the client.
  4. The client obtains the model response and passes it to the server.
  5. The server finishes its work and returns the tool result, allowing the outer agent to continue.

The client retains control over model selection and permissions. Borrowing the client’s model access does not guarantee use of the identical outer model, and it does not make inference free.

At the time of the presentation, Colvin describes sampling as powerful but not widely supported. Pydantic AI’s implementation was still a pull request, covering both sides: client support for proxying model calls and server support for using the MCP client as the model connection. The demonstration therefore reflects that development version.

Sampling slide with a complete sequence diagram connecting LLM, MCP client and MCP server through tool calls, sampling requests and responses.
Sampling routes an MCP server’s LLM request through the client.
5:375:40
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

5:37 · section reference included

A research tool that generates and repairs SQL

The concrete application is a research agent for open-source packages and libraries. A complete research system would need many tools; this demonstration implements one: looking up package download counts in the public PyPI dataset on BigQuery. The tool contains a Pydantic AI agent that turns a question into SQL.

The setup configures Logfire, declares the dependencies available during execution and permits retries when the model returns unsuitable output. A substantial system prompt gives the inner agent the table schema, instructions and examples. That prepares the model to generate a query, but generation alone is not the completion condition.

The output validator connects the generated SQL to the database:

  1. Remove surrounding Markdown formatting when present.
  2. Check that the query targets the expected table, returning corrective feedback if it does not.
  3. Execute the query.
  4. If execution fails, raise ModelRetry so the model receives feedback and can attempt a repair.

The key change is that a database error becomes another input to the agent rather than immediately ending the tool call.

The execution-to-retry boundary can be expressed in Python as a small helper, called after formatting and table checks:

python

from collections.abc import Callable
from typing import TypeVar

from pydantic_ai import ModelRetry

Result = TypeVar("Result")


def execute_or_retry(
    sql: str,
    execute_query: Callable[[str], Result],
    query_error: type[Exception],
) -> Result:
    try:
        return execute_query(sql)
    except query_error as exc:
        raise ModelRetry(
            f"The query failed: {exc}. Correct the SQL and try again."
        ) from exc

The database adapter supplies the execution function and its query-error type. Pydantic AI handles the retry after ModelRetry propagates out of the validator, subject to the configured retry allowance. The demonstrated validator also calls context.deps.mcp_context.log as it works.

7:067:21
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

7:06 · section reference included

Keep the user informed while the tool runs

The MCP call context reaches the inner agent through its typed dependencies. With deps_type describing those dependencies, a validator—or another tool call—can access the MCP context through context.deps.mcp_context. Type hints expose the log method and its signature, so logging remains available inside the agent without losing the interface information supplied by the SDK.

Those log messages return to the client before the tool finishes and can ultimately reach the person waiting for the answer. MCP also has a separate progress-notification capability, which this example does not use. Logging can describe what is happening; progress notifications could report how far an operation has advanced when that information is available.

The same mechanism serves two interfaces. In a Cursor-style application, it can reassure the user that a tool is still working and explain its current activity. In a web research application, it can expose activity during a task that takes minutes. The final answer no longer has to be the first visible sign of work.

Python editor showing a query validator with MCP context logging before execution, on a query error and after success.
Query code logs execution, retries and success through MCP context.
9:099:19
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

9:09 · section reference included

Put specialized context inside the tool

After a successful query, the tool converts the output into a list of dictionaries and then formats it as XML-like text. Colvin chooses that representation because he finds models good at interpreting XML. The result is structured text for the outer model to read, rather than a final answer written directly for the user.

The server uses FastMCP bundled inside the MCP Python SDK, which Colvin distinguishes from the separate implementation sharing that name. This is the historical SDK API used in the recording; the repository’s current v2 quickstart has breaking changes. The server registers one PyPI-downloads tool, and the Python function’s docstring becomes the tool description supplied to the LLM that decides whether to call it.

The tool accepts the user’s question in natural language. An alternative would be to make the central agent generate SQL, placing the table schema and query instructions in its context or in a large tool description. Colvin reports that models do not respond well to excessive information in tool descriptions. More fundamentally, those instructions would occupy the main agent’s context on calls where it never uses the PyPI tool.

SQL generation locationMain agent carriesSpecialized work happens
Central agentSchema and query instructionsIn the outer model call
Inside the toolA description of the tool’s purposeWhen the tool is invoked

Keeping inference inside the tool also keeps its specialized context there. Sampling supplies model access across that boundary without requiring the main agent to carry the SQL-generation prompt. The tool returns a string, and the demonstrated server runs over standard input and standard output by default.

10:3410:43
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

10:34 · section reference included

Ask the question, then inspect the nested execution

The main application registers one MCP server that launches the PyPI server script. The outer agent acts as the MCP client and makes the server’s tool available to its model. It also receives the current date, avoiding stale assumptions about what “this year” means. Colvin then asks how many downloads Pydantic has had this year and runs the agent live.

The live demonstration reports about 1.6 billion Pydantic downloads for the year to date. That is the application’s reported result, not a count of unique users; the recording does not establish the calendar year, exact cutoff or query filters needed to reproduce it.

Colvin next opens Logfire. The trace shown is from a run immediately before he came on stage, rather than the just-completed live run. He leaves the detailed treatment of MCP tracing to the following talk and focuses on reading the application’s execution. The outer agent calls GPT-4o, which selects the PyPI tool. It passes a natural-language description of the question; it does not generate the SQL itself.

The MCP client calls the server, which runs a second Pydantic AI agent. That inner agent requests inference through the client, producing the alternating client and server service entries visible in the trace. The nesting makes the sampling path inspectable: the server’s model work appears inside the tool execution, while the client supplies the model connection.

Expanded trace with MCP client and server labels, nested agent runs, a sampling request, a model call and a SQL query row.
Logfire shows nested MCP calls and sampling in an execution trace.

Opening the outer model exchange shows the next boundary: the query’s XML-like result comes back as the tool response, and the outer LLM turns it into a human-readable answer. Opening the inner server-agent call reveals the SQL that was generated and executed. Colvin inspects it and says it looks correct. The demonstration ends with both levels visible—the answer delivered to the user and the database query underneath it—so a plausible sentence is not the only evidence available for understanding the result.

12:2712:41
Suggest correction

This note stays in this page until you copy or download it. Nothing is submitted; reloading clears the draft.

12:27 · section reference included

Resources

From the talk

  • The March 2025 protocol revision explains how servers request model generations through clients, including capability negotiation and model preferences.

  • Python implementations for MCP clients and servers. The current repository documents SDK v2 and links to the earlier v1 line.

Updates since the talk

Read the complete timestamped transcript
  1. 0:00

    [upbeat music] So yeah, I'm talking about, uh, MCP Is All You Need.

  2. 0:17

    A bit about who I am before we get started. I'm best known as the creator of Pydantic, uh, data validation library for Python that is, uh, fairly ubiquitous, downloaded about three hundred and sixty million times a month.

  3. 0:30

    So someone pointed out to me that's like a hundred and forty times a second. Uh, Pydantic is used in general Python development everywhere, but also in GenAI. So it's used in all of the SDKs and agent frameworks in Python, basically.

  4. 0:45

    Uh, Pydantic became a company, uh, uh, beginning of '23, and we have, uh, built two things beyond Pydantic since then. Pydantic AI, uh, an agent framework for Python built on the same principles as Pydantic.

  5. 0:59

    Um, and Pydantic Logfire observability platform, um, which is our-- which is the commercial part of what we do. Um, I'm also a somewhat inactive co-maintainer of the MCP Python SDK.

  6. 1:14

    Um, so MCP Is All You Need is obviously, uh, a play on Jason Liu's talks, Pydantic Is All You Need, that he gave [REDACTED:username] AI Engineer, I think first of all nearly two years ago, and then the second one, Pydantic Is Still All You Need, maybe li- this time last year.

  7. 1:35

    Um, and it has the same basic idea, that people are overcomplicating something that we can use a single tool for, and I guess also similarly, the title is completely unrealistic.

  8. 1:45

    Of course, Pydantic is not all you need, uh, and neither is MCP for everything. But it has the-- we have the-- I think where, where we agree is that there are an awful lot of things that MCP can do and that people are overcomplicating the situation sometimes trying to come up with new ways of doing agent-to-agent communication.

  9. 2:03

    Um, I'm talking here specifically about autonomous agents or code that you're writing. I'm not talking about the, um,

  10. 2:15

    uh, Claude Desktop or Cursor, uh, Zed, Windsurf, et cetera, use case of coding agents. Those were what MCP was originally primarily designed for. Um, I don't know whether or not David Pereira would say that, that what we're doing using MCP from Python is, uh, he definitely wouldn't say it's a misuse, but it-- I don't think it, it

  11. 2:35

    was the primary, uh, desi-- uh, use case for, um, for MCP. So two of the, of the primitives of MCP, prompts and resources, probably don't come into this use case that much.

  12. 2:52

    They're very useful or, or should be very useful in the kind of Cursor-type use case. They don't really apply in what we're talking about here. Um,

  13. 3:01

    but tool calling, the third primitive, is extremely useful for what we're trying to do here. Um, tool calling is a lot more complicated than you might [REDACTED:username] first think.

  14. 3:11

    A lot of people say to me about MCP, "Ah, but couldn't it just be, uh, OpenAPI? Why do we need this, uh, custom protocol for doing it?" Um, and there's a number of reasons.

  15. 3:22

    The idea of dynamic tools, the tools that come and go during an agent execution, depending on the state of the server. Logging, so being able to return data to the user

  16. 3:32

    while the tool is still executing. Sampling, which I'm gonna talk about a lot today, perhaps the most confusingly named part of MCP, if not tech in general right now.

  17. 3:43

    Uh, and stuff like tracing, observability. Um, and I, I would also add to that actually, uh, uh, MCP's way of being allowed to operate as effectively a subprocess over standard in and standard out is extremely useful for lots of use cases, and OpenAPI wouldn't, wouldn't solve those problems.

  18. 4:01

    So this is the kind of prototypical image that you will see from lots of people of what, uh, MCP is all about. The idea is we have some agent.

  19. 4:10

    We have any number of different tools that we can connect to that agent, and the point is that, like, the agent doesn't need to be designed with those particular tools in mind, and those tools can be designed without knowing anything about the agent.

  20. 4:21

    And we can just compose the two together in the same way that, uh, I can go and use a browser, and the web application or the website I'm going to doesn't need to know anything about the browser.

  21. 4:30

    I mean, I know we live in a kind of monoculture of browsers now, but, like, [REDACTED:username] least the ideal originally was we could have many different browsers all connecting over the same protocol.

  22. 4:37

    MCP is following the same idea. But it can get more complicated than this. We can have situations like this where, uh, we have tools within our system which are themselves agents and are doing agentic things, need access to an LLM.

  23. 4:53

    They, of course, can then in turn connect to other tools over MCP or, or directly connecting to tools. This, this works nicely. This is elegant, but there's a problem.

  24. 5:03

    Every single agent in our system needs access to an LLM. And so we need to go and configure that. We need to work out resources for that. And if we are,

  25. 5:14

    um, using remote MCP servers, if that remote MCP server needs to, um, use an LLM, well, now it's worried about what the cost is gonna be of doing that.

  26. 5:24

    What, what if the, uh, remote agent that's operating as a tool could effectively piggyback off the, uh, the model that the original agent has access to? That's what sampling gives us.

  27. 5:37

    So as I say, I think sampling is a

  28. 5:40

    somewhat, uh... That's not making that any bigger, unfortunately. Um, is that clear on screen? I may-- Maybe I'll make it bigger like that. Um, sampling is this idea of a, of a way where within MCP, the protocol, the, um, server can effectively make a request back through the client to the LLM.

  29. 6:00

    So in this case, client makes a request, uh, starts some sort of-

  30. 6:05

    Agentic query makes a call to the LLM. LLM comes back and says, "I want to call that particular tool," which is an MCP server. A client takes care of making that call to the MCP server.

  31. 6:15

    The MCP server now says, "Hey, I actually need to be able to use an LLM to answer whatever this question is." So that then gets sent back to the client.

  32. 6:23

    The client proxies that request to the LLM, receives the response from the LLM, sends that, uh, on to the MCP server, and the MCP server then returns, and we can continue on our way.

  33. 6:36

    Um, sampling is very powerful, not that widely supported [REDACTED:username] the moment. Um, I'm gonna demo it today with Pydantic AI, where we have support for sampling... Well, I'll be honest, it's a PR right now, but it will be...

  34. 6:49

    Soon it will be merged. Um, we have support for sampling both as a, uh, as the client, so knowing how to proxy the, those LLM calls, and as a server, basically being able to register use the MCP client as, as the LLM.

  35. 7:06

    So this example is obviously, like all examples, trivialized or simplified to be, to fit on screen. The idea is that we- we're building a, like, research agent which is gonna go and research open source, uh, packages or libraries for us.

  36. 7:21

    And we've implemented one of the many tools that you'd, in fact, need for this, and that tool is, um,

  37. 7:28

    making... Uh, I will switch now to code and show you, uh, the one tool that we have.

  38. 7:36

    Uh, I'm in completely the wrong file. Here we are. Um,

  39. 7:41

    so this tool is querying BigQuery, uh, the BigQuery public data set for, uh, PyPI to get, uh, numbers about the number of downloads of a particular package. So this is, this is pretty standard Pydantic AI, uh, Pydantic AI code.

  40. 7:58

    We've configured Logfire, which I'll show you in a moment. We have the dependencies that the, uh, that the agent has access to while it's running. We said we can do some retries, so if the agent returns, if the LLM returns the wrong data, we can send a retry.

  41. 8:13

    Big system prompt where we give it basically the schema of the table, uh, tell it what to do, give it a few examples, yada, yada. But then we get to this, is the, probably the, the powerful bit.

  42. 8:22

    So as an output validator, we are gonna go and first of all, we're gonna strip out, uh, markdown block quotes from the SQL, um, if they're there. Then we will, uh, check that the table name is right that it's querying against and tell it that it shouldn't if it- it shouldn't.

  43. 8:39

    And then we're gonna go and run the query. And critically, if the query fails, we're gonna, uh, raise model retry within Pydantic AI to go and retry, uh, making the, um,

  44. 8:54

    uh, making the request to the, um, LLM again, saying... asking the LLM to, to, uh, attempt to, to retry this. And what we're... The other thing we're doing throughout this, you'll see here, is we have this context.depths.mcp_context.log.

  45. 9:09

    So you'll see here when we defined depths type, we said that that was gonna be an instance of this MCP, uh, context, which is what we get when you call the MCP server.

  46. 9:19

    So what we're doing here is we're having a, we're providing a typesafe way within, in this case, um, the agent validator, but it could be in a tool call if you wanted it to be, to access that context.

  47. 9:31

    And so we can see here that we know, uh, um, in the type hint, uh, uh, the, the, the type is, uh, MCP context. And so we have this log function, and we know its signature, and we can go and make this log call.

  48. 9:44

    The point is this is going to return to the client and ultimately to the user watching before the, the thing is completed. So you can get kind of progress updates as we go.

  49. 9:54

    MCP also has a context, a concept of progress, which I'm not using here, but you could imagine that also being valuable. If you knew how far through the query you were, you could show an update in progress.

  50. 10:04

    So the idea, I think the original principle of, uh, logging like this is that you have the, the cursor-style agent running, and we want to be able to give updates to the user, "Don't worry, I'm still going," before it's finished and exactly what's happening.

  51. 10:18

    But you could also imagine this being useful if you were using MCP. If this was research agent, was, uh, running as a web application, you wanted to show the user what was going on.

  52. 10:27

    This deep research might take, you know, minutes to run. We can give these logs while the tool call is still executing.

  53. 10:34

    And then we're just gonna take the, the output, turn it into a list of dicts, and then format it as XML so you get a nice, uh...

  54. 10:43

    Models are very good [REDACTED:username], [REDACTED:username] basically reviewing XML data, so we basically return whatever the query results are as that kind of XML-ish data, which the LLM will then be good [REDACTED:username], uh, interpreting.

  55. 10:55

    Now we get to the MCP bit. So in this code, we are setting up an MCP server using FastMCP. There are two versions of FastMCP right now, confusingly. This is the one from inside the MCP SDK.

  56. 11:08

    Um, we... The docstring for our function... So we- we're registering one tool here, PyPI downloads, and our docstring from that function will end up becoming the description on the tool that is ultimately fed to the LLM that chooses to go and call it.

  57. 11:24

    Um, and we're going to pass in the user's question. And I think one of the, one of the important things to say here is, of course, you could set this up to generate the SQL within your, uh, central agent.

  58. 11:38

    You could include all of the, um, uh, description of the SQL, the instructions within your, within the, the description of the tool. Uh, models don't seem to like that much data inside a tool description.

  59. 11:51

    But more to the point, we're just gonna blow up the context window of our main agent. If we're gonna ship all of this context on how to make these queries into our main agent, that's just all overhead in all of our calls to that agent, regardless of whether we're gonna call this particular tool.

  60. 12:05

    So doing this kind of thing where we're doing the inference inside a tool is a powerful way of effectively limiting- Uh, the context window of the, of the main running agent.

  61. 12:15

    And then we're just gonna return this output, which will be a string, the value returned from, from here. And we'll just run the, run the MCP server. And by default, the MCP server will run over standard IO.

  62. 12:27

    Um, and then we come to our, our main application. So here we have a definition of our agent, and you see we've defined one MCP server that's just gonna run the, the script I just showed you, the PyPI MCP server.

  63. 12:41

    Um, and so then this agent will act as the client. It has that registered as a tool to be able to call. Uh, we're also gonna set the... give it the current date, uh, so it doesn't, uh, assume it's 20- 2023 as they often do.

  64. 12:56

    Um, and now we can go and ultimately run our main agent, ask it, for example, how many downloads h- Pydantic has had this year. And I'm gonna be brave and run it and see what happens.

  65. 13:07

    Uh, and it has succeeded, and it has, uh, gone and told us, uh, that we had, whatever, 1.6 billion downloads this year. But probably more interesting is to come and look [REDACTED:username] what that looks like in Logfire.

  66. 13:17

    So if you look [REDACTED:username]... Is it gonna come through to Logfire, or are we having a failure here as well? This, I will admit, this is the run from just before, uh, I came on stage, but it, it would look exactly the same.

  67. 13:27

    So I'm not gonna talk too much about observability and how we do, uh, how MCP observability or tracing works within MCP, 'cause I know there's a talk coming up directly after me talking about that.

  68. 13:39

    So think of this as a kind of, uh, spoiler for what's gonna come up. But you can see we, we run our outer agent. It decides to... It calls, uh, uh, GPT-4o, uh, which decides, "Sure enough, I'm gonna go and call this tool."

  69. 13:55

    Uh, it doesn't need to think about generating the SQL. It can just have a natural language description of the query that we're trying to make. We then, um, this is the MCP client, as you can see here.

  70. 14:05

    MCP client then calls into the MCP server, um, makes the... which then, again, runs a different, uh, Pydantic AI, uh, agent, which then makes a call to an LLM, which happens through proxying it through the client.

  71. 14:19

    So that's why you can see the service going client, server, uh, client, server.

  72. 14:26

    Ultimately, if you look [REDACTED:username] the top level, uh, exchange with the model, you'll see here,

  73. 14:32

    yeah, the, the, the out- ultimate output was it, which had the, the return response from running the query was, was this kind of XML-ish data. And then the LLM was able to turn that into a human description of what was going on.

  74. 14:45

    I think the other interesting thing probably is we can go and look in. We should be able to see the actual SQL that was called. So this is the agent call inside, uh, MCP server.

  75. 14:55

    And you can see here the SQL it wrote, and you can confirm that it, it indeed looks correct. Um, I am going to, uh, go on from there and say, um, thank you very much.

  76. 15:08

    Um, we are [REDACTED:username] the booth, the, the Pydantic booth, so if anyone has any questions on this, wants to see this fail in numerous other exciting ways, very happy to, to talk to you.

  77. 15:16

    Yeah, come and say hi. [outro music]