AI Engineer World's Fair 2025
MCP is all you need
About this talk
Pydantic creator Samuel Colvin argues that Model Context Protocol can simplify communication among autonomous agents beyond its original desktop-assistant use cases. He contrasts MCP with OpenAPI, explains how sampling lets agentic MCP servers access an LLM through their client, and demonstrates Pydantic AI querying PyPI download data in BigQuery with retries and Logfire observability.
Chapters
- 0:00Samuel Colvin and the Pydantic ecosystem
- 1:14MCP for autonomous agents and its advantages over OpenAPI
- 4:01Agent composition and client-mediated MCP sampling
- 7:41Pydantic AI demo: BigQuery, PyPI, retries, and Logfire
- 11:38Tool-context tradeoffs and closing
Talk transcript
- 0:00
[upbeat music] So yeah, I'm talking about, uh, MCP Is All You Need.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 2:03
Um, I'm talking here specifically about autonomous agents or code that you're writing. I'm not talking about the, um,
- 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
- 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.
- 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,
- 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.
- 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.
- 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
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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,
- 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.
- 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.
- 5:37
So as I say, I think sampling is a
- 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.
- 6:00
So in this case, client makes a request, uh, starts some sort of-
- 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.
- 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.
- 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.
- 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...
- 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.
- 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.
- 7:21
And we've implemented one of the many tools that you'd, in fact, need for this, and that tool is, um,
- 7:28
making... Uh, I will switch now to code and show you, uh, the one tool that we have.
- 7:36
Uh, I'm in completely the wrong file. Here we are. Um,
- 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.
- 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.
- 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.
- 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.
- 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,
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 10:27
This deep research might take, you know, minutes to run. We can give these logs while the tool call is still executing.
- 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...
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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."
- 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.
- 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.
- 14:19
So that's why you can see the service going client, server, uh, client, server.
- 14:26
Ultimately, if you look [REDACTED:username] the top level, uh, exchange with the model, you'll see here,
- 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.
- 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.
- 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.
- 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.
- 15:16
Yeah, come and say hi. [outro music]