AIAI EngineerJul 18, 2025· 15:24

MCP is all you need — Samuel Colvin, Pydantic

Samuel Colvin, creator of Pydantic, argues that MCP (Model Context Protocol) is all you need for agent-to-agent communication, simplifying what many are overcomplicating. He explains that MCP's tool calling primitive offers advantages over OpenAPI, such as dynamic tools, logging, and sampling—a mechanism for MCP servers to request LLM calls through the client, enabling nested agentic workflows. Colvin demonstrates a research agent using Pydantic AI that queries the BigQuery public PyPI dataset via an MCP server over stdio, showing how sampling allows the server to generate SQL itself while keeping the main agent's context lean. The demo logs progress to Logfire, an observability platform, and outputs human-readable results. He emphasizes that MCP’s standard I/O mode and extensibility make it ideal for autonomous agents beyond its original desktop coding use case.

Transcript

Intro0:00

Samuel Colvin0:14

So yeah, I'm talking about "MCP is all you need." A bit about who I am before we get started: I'm best known as the creator of Pydantic, a data validation library for Python that is fairly ubiquitous, downloaded about 360 million times a month.

So someone pointed out to me that's like 140 times a second. 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. Pydantic became a company beginning in '23, and we have built two things beyond Pydantic since then: Pydantic AI, an agent framework for Python built on the same principles as Pydantic, and Pydantic Logfire, an observability platform, which is the commercial part of what we do.

I'm also a somewhat inactive co-maintainer of the MCP Python SDK.

So "MCP is all you need" is obviously a play on Jason Liu's talks, "Pydantic is all you need," that he gave at AI Engineer, I think first of all nearly two years ago, and then the second one, "Pydantic is still all you need," maybe this time last year.

Thesis1:17

Samuel Colvin1:36

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. Of course, Pydantic is not all you need, and neither is MCP for everything, but we have the, I think, 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.

Tool Calling2:05

Samuel Colvin2:05

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

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

use case for

MCP. So two of the primitives of MCP, prompts and resources, probably don't come into this use case that much. They're very useful, 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.

But tool calling, the third primitive, is extremely useful for what we're trying to do here. Tool calling is a lot more complicated than you might at first think. A lot of people say to me about MCP, "Ah, but couldn't it just be OpenAPI?

Why do we need this custom protocol for doing it?" And there's a number of reasons. The idea of dynamic tools, so 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 while the tool is still executing.

Sampling, which I'm going to talk about a lot today, perhaps the most confusingly named part of MCP, if not tech in generalright now, and stuff like tracing, observability. And I would also add to that, actually, MCP's way of being allowed to operate as effectively a sub-process over standard in and standard out is extremely useful for lots of use cases, and OpenAPI wouldn't solve those problems.

This is the kind of prototypical image that you will see from lots of people of what MCP is all about. The idea is we have some agent, we have any number of different tools that we can connect to that agent, and the point is that 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, and we can just compose the two together in the same way that I can go and use a browser and the web application that the website I'm going to doesn't need to know anything about the browser.

I mean, I know we live in a kind of monoculture of browsers now, but at least the idea originally was we could have many different browsers all connecting over the same protocol. MCP is following the same idea. But it could get more complicated than this.

We can have situations like this where we have tools within our system which are themselves agents and are doing agentic things, need access to an LLM, and they, of course, can then in turn connect to other tools over MCP or directly connecting to tools.

Sampling5:00

Samuel Colvin5:00

This works nicely, this is elegant, but there's a problem. 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

using remote MCP servers, if that remote MCP server needs to

use an LLM, well now it's worried about what the cost is going to be of doing that. What if the remote agent that's operating as a tool could effectively piggyback off

the model that the original agent has access to? That's what sampling gives us. So as I say, I think sampling is a somewhat, that's not making that any bigger, unfortunately. Is that clear on screen? Maybe I'll make it bigger like that.

Sampling is this idea of a way where within MCP, the protocol, the server can effectively make a request back through the client to the LLM. So in this case, the client makes a request, starts some sort of 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.

The client takes care of making that call to the MCP server. 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, the client proxies that request to the LLM, receives the response from the LLM, sends that onto the MCP server, and the MCP server then returns, and we can continue on our way.

Sampling is very powerful, not that widely supported at the moment. I'm going to demo it today with Pydantic AI, where we have support for sampling. Well, I'll be honest, it's a PRright now, but soon it will be merged.

We have support for sampling both

as the client, so knowing how to proxy those LLM calls, and as a server, basically being able to register, use the MCP client as the LLM. So this example is obviously, like all examples, trivialized or simplified to fit on screen.

The idea is that we're building a research agent which is going to go and research open-source packages or libraries for us. And we have implemented one of the many tools that you'd, in fact, need for this, and that tool is

Demo7:14

Samuel Colvin7:29

making, I will switch now to code and show you the one tool that we have. I'm in completely the wrong file. Here we are. So this tool is querying BigQuery, the BigQuery public dataset for PyPI, to get numbers about the number of downloads of a particular package.

So this is pretty standard Pydantic AI code. We've configured Logfire, which I'll show you in a moment. We have the dependencies

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. We have a big system prompt where we give it basically the schema of the table, tell it what to do, give it a few examples, yada yada.

But then we get to this is probably the powerful bit. So as an output validator, we are going to go and, first of all, we're going to strip out, mark down, block quotes from the SQL, if they're there.

Then we will check that the table name isright, that it's querying against, and tell it that it shouldn't, if it shouldn't, and then we're going to go and run the query. And critically, if the query fails, we're going to raise model retry within Pydantic AI to go and retry,

making the request to the

LLM again, asking the LLM to attempt to retry this. And the other thing we're doing throughout this, you'll see here, is we have this context.depth.mcpcontext.log. So you'll see here, when we defined depth type, we said that that was going to be an instance of this MCP context, which is what we get when you call the MCP server.

And so what we're doing here is we're providing a type-safe way within, in this case, the agent validator, but it could be in a tool call if you wanted it to be, to access that context. And so we can see here that we know in the type hint that the type is MCP context, and so we have this log function and we know its signature, and we can go and make this log call.

The point is this is going to return to the client and ultimately to the user watching before the thing is completed. So you can get kind of progress updates as we go. MCP also has a concept of progress, which I'm not using here, but you can imagine that also being valuable.

If you knew how far through the query you were, you could show an update in progress. So the idea, I think the original principle of logging like this is that you have 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. But you could also imagine this being useful if you were using MCP. If this research agent was running as a web application, you wanted to show the user what was going on.

This deep research might take minutes to run. We can give these logs while the tool call is still executing. And then we're just going to take the output, turn it into a list of dicts, and then format it as XML so you get a nice

models are very good at 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 at interpreting. 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 FastMCPright now, confusingly. This is the one from inside the MCP SDK.

Server10:57

Samuel Colvin11:11

The docstring for our function, so 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, and we're going to pass in the user's question.

And I think one of the important things to say here is, of course, you could set this up to generate the SQL within your

central agent. You could include all of the

description of the SQL, the instructions, within the description of the tool. Models don't seem to like that much data inside a tool description, but more to the point, we're just going to blow up the context window of our main agent.

If we're going to 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 going to call this particular tool.

So doing this kind of thing where we're doing the inference inside a tool is a powerful way of effectively limiting the context window of the main running agent. And then we're just going to return this output, which will be a string, the value returned from here.

Agent12:21

Samuel Colvin12:21

And we'll just run the MCP server, and by default, the MCP server will run over standard I/O. And then we come to our main application. So here we have a definition of our agent, and you see we've defined one MCP server that's just going to run the script I just showed you, the PyPI MCP server.

And so then this agent will act as the client and has that registered as a tool to be able to call. We're also going to give it the current date so it doesn't assume it's 2023, as they often do.

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

And it has gone and told us that we had, whatever, 1.6 billion downloads this year. But probably more interesting is to come and look at what that looks like in Logfire. So if you look at, is it going to come through to Logfire, or are we having a failure here as well?

This, I will admit, this is the run from just before I came on stage, but it would look exactly the same. So I'm not going to talk too much about observability and how we do, how MCP observability or tracing works within MCP, because I know there's a talk coming up directly after me talking about that.

Tracing13:23

Samuel Colvin13:40

So think of this as a kind of spoiler for what's going to come up. But you can see we run our outer agent. It calls

GPT-4.0, which decides, sure enough, I'm going to go and call this tool. It doesn't need to think about regenerating the SQL. It can just have a natural language description of the query that we're trying to make. We then, this is the MCP client, as you can see here, MCP client then calls into the MCP server, which then again runs a different Pydantic AI agent, which then makes a call to an LLM, which happens through proxying it through the client.

So that's where you can see the service going client-server, client-server. Ultimately, if you look at the top-level exchange with the model, you'll see here, yeah, the ultimate output was it, which the response from running the query 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.

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 MCP server, and you can see here the SQL it wrote, and you can confirm that it indeed looked correct.

I am going to

Wrap-up15:02

Samuel Colvin15:05

go on from there and say thank you very much. We are at the booth, the Pydantic booth, so if anyone has any questions on this, wants to see this fail in numerous other exciting ways, very happy to talk to you.

Yeah, come and say hi.