Intro0:00
I really do appreciate that you're all here. I'm going to try to make this as painless as possible. We're not going to do an interactive part; we're going to talk through stuff. I'm happy to go off-script, I'm happy to take questions if there's stuff we want to explore at any moment in this.
My goal is I'd like to share with you a lot of things that I've learned. I'm going to try to make them as actionable as possible, so there is real stuff to do here, more than we might in, like, a more high-level talk.
But let's be very honest: it is late, it is a lot, it is long. Let's, uh, let's talk about MCP. I'm hoping that folks here are interested in MCP, and that's why you came to this talk. If you're here to learn about MCP, this might be a little bit of a different bent.
Just show of hands. Heard of MCP? Used MCP? Written an MCP server? Okay. Anyone feel uncomfortable with MCP, which is 100% fine. We can tailor. Okay. Then I would say let's, let's just go. Let's dive in. This is who I am.
FastMCP1:22
I'm the founder and CEO of a company called Prefect Technologies. For the last 7 or 8 years, we've been building data automation software and orchestration software. Before that, I was a member of the Apache Airflow PMC. I originally started Prefect to graduate those same orchestration ideas into data science.
Today we operate the full stack. And then, a few years ago, I developed an agent framework called Marvin, which I would not describe as wildly popular, but it was my leg into the world of AI, at least from a developer experience standpoint, and learned a lot from that.
And then more recently, I introduced a piece of software called FastMCP, which is wildly, wildly popular, maybe even too popular. And, hence my status today. I'm a little overwhelmed. I find myself back in an open-source maintenance seat, which I haven't been in in a few years, which has been a hell of a lot of fun.
But the most important thing is that FastMCP has given me a very specific vantage point that is really the basis for this talk today. This is our downloads. I've never seen anything like this. I've never worked on a project like this.
It was downloaded a million and a half times yesterday. There's a lot of MCP servers out there, and FastMCP has just, it's become the de facto standard way to build MCP servers. I introduced it almost exactly a year ago, as many of you are probably aware.
MCP itself was introduced almost exactly a year ago, and a few days later I introduced the first version of FastMCP. David at Anthropic called me up, said, "I think this is great. I think this is how people should build servers."
We put a version of it into the official SDK, which was amazing. And then, as MCP has gone crazy in the last year, we found it actually to be constructive to position FastMCP, as I'm maintaining it, as the high-level interface to the MCP ecosystem, while the SDK focuses on the low-level primitives.
And actually, we're going to remove the FastMCP vocabulary from the low-level SDK in a couple of months. It's become a little bit of, it's too confusing that there are these two things called FastMCP. So FastMCP will be a high-level interface to the world.
And as a result, we see a lot of not-great MCP servers. I named the talk after this meme, and then it occurred to me, like, do people even know what this meme is anymore? Like, this, this to me is very funny and very topical, and then it's from, like, a 1999 episode of Futurama.
So if you haven't seen this, my talk's title is not meant to be mean. I'm sort of an optimist. I choose to interpret this as, "But you can do better." And so we're going to find ways to do better.
Agentic Design3:53
That is the goal of today's talk. In fact, to be more precise, what I want to do today is I would really like to build an intuition for agentic product design. I don't see this talked about nearly as much as it should be, given how many agents are using how many products today.
And what I mean by this is the exact analog of what it would be if I were giving a talk on how to just build a good product for a user, for a human. And we would talk about human interface guidelines, and we'd talk about user experience, and we'd talk about stories.
And I found it really instructive to start talking about those things from an agentic perspective, because what else is an MCP server but an interface for an agent? And we should design it for the strengths and weaknesses of those agents in the same way that we do everything else.
Now, when I put this thought in the world, I very, very, very frequently get this pushback, which is, "But if a human can use an API, why can't an AI?" And there are so many things wrong with this question.
And the number one thing that's wrong with this question is that it has an assumption that I see in so much of AI product design, and it drives me nuts, which is that AIs are perfect, or they're oracles, or they're good at everything.
And they are very, very, very powerful tools. But I'm assuming, based on your responses before, I think everyone in this room has some scars of the fact that they are fallible, or they are limited, or, you know, they're imperfect.
And so I don't like this question because it presumes that they're, like, magically amazing and everything. But I really don't like this question. This is a literal question I've got, and I didn't paraphrase it. I really don't like this question because humans don't use APIs.
Very, very rarely do humans use APIs. Humans use products. We do anything we can to put something between us and an API. We put a website, we put an SDK, we put a client, we put a mobile app.
We do not like to use APIs unless we have to or we are the person responsible for building that interface. And so one of my core arguments, and why I love MCP so much, is that I believe that agents deserve their own interface that is optimized for them and their own use case.
And in order to design that interface, which is what I want to motivate today, we have to think a little bit about what is the difference between a human and an AI. And it's one of these questions that, like, sounds really stupid when you say it out loud, but it's instructive to actually go through.
And I'd like to make the argument to you that it exists on these three dimensions of discovery, iteration, and context. And so just to begin, humans, we find discovery really cheap. We tend to do it once. If you think, if any of you have had to implement something against a REST API, what do you do?
You call up the docs, or you go and swagger whatever it is. You call it up, you look at it. One time you figure out what you need, you're never going to do that again. And so while it may take you some time to do the discovery, it is cheap in the lifetime of the application you are building.
AIs? Not so much. Every single time that thing turns on, it shakes hands with a server, it learns about the server, it enumerates every single tool and every single description on that server. So discovery is actually really expensive for agents.
It consumes a lot of tokens. Next, iteration. Same idea. If you're a human developer and you're writing code against an API, you can iterate really quickly. Why? Because you do your one-time discovery, you figure out the three routes you're going to call, and then you write a script that calls them one after another as fast as your language allows.
So iteration is really cheap. And if that doesn't work, you just run it again until it does. Iteration is cheap. It's fast. For agents, I think we all know iteration is slow. Iteration is the enemy. Every additional call, subject to your caching setup, also sends the entire history of all previous calls over the wire.
Like, it is just, you do not want to iterate if you can avoid it. And so that's going to be an important thing that we take into consideration. And the last thing is on context, and this is a little bit hand-wavy, but it is important.
As humans, in this conversation, I'm talking, you're hearing me, and you're comparing this to different memories you have and different experiences you have on different timescales, and it's all doing wonderful, amazing things in your brain. And when you plug an LLM into any given use case, it remembers the last 200,000 tokens it saw, and that's the extent of its memory plus whatever is, you know, embedded somewhere in its weights.
And that's it. And so we need to be very, very, very conscious of the fact that it has a very small brain at this moment. I think it is a lot closer to when people talk about sending, you know, Apollo 11 to the moon and with, like, one kilobyte of RAM, whatever it was.
I think that's actually how we need to think about these things that, frankly, feel quite magical because they go and open my PRs for me or whatever it is that they do. So these are the three key dimensions in my mind of what is different.
And we should not build APIs that are good for humans on any of these dimensions and pretend that they are also good for agents. And one way that I've kind of started talking about this is this idea, which is an agent can find a needle in a haystack.
MCP as Interface8:38
The problem is it's going to look at every piece of hay and decide if it's a needle. And that's, like, not literally true, but it is, in an intuitive sense, how we should think about what we're putting in front of the agents and how we're posing a problem.
And an MCP server is nothing but an interface to that problem and/or solution. And so finally, to go back to our product intuition statement, I argued to you that the most important word in the universe for MCP developers is curate.
How do you curate from a huge amount of information which might be amenable for a human developer an interface that is appropriate for one of these extremely limited AI agents, at least on the dimensions that we just went through?
And that sort of brings us to this slide, "Why MCP." And I almost made this, like, the Derek Zoolander slide, like, "But why MCP?" Like, "But I just told you why MCP, Derek." It's because it does all of these things.
It gives us a standard way of communicating information to agents in a way that's controllable, where we can control not only how it's discovered but also how it is acted on. There's a big asterisk on that because client implementations in the MCP spaceright now are not amazing, and they do some things that are themselves not compliant with the MCP spec.
Maybe at the end we'll get into that. It's not directly relevant to now, except that all we can do is try to build the best servers we can, subject to the limitations of the clients that will use them.
And again, I put this in here. I think we don't need to go through what MCP is for this audience, so we're going to move quickly through this. But it is, of course, for the sake of the transcript, the cliché is that it's USB-C for the internet.
It is a standard way to connect LLMs and either tools or data. And if you haven't seen FastMCP, this is what it looks like to build a fully functional MCP server. This one, I live in Washington, DC. The subway is often on fire there, and so this checks whether or not the subway is on fire.
And indeed, it is. Now, the question we are here to actually explore is, why are there so many bad MCP servers? Maybe a better question is, do you all agree with me that there are many bad MCP servers?
Bad Servers10:46
I sort of declare this as if it's true. I'm not trying to make a controversial statement. There are many bad MCP servers in the world. I see a lot of them because people are using my framework to build them.
Does that surprise anyone that I'm sort of declaring that? I'm genuinely, I'm curious if that's a, if I'm made an assumption. I don't.
In my experience,
I won't say every MCP I came up to is like that, but a lot of them are, like, API wrappers. They just put a, like, stringify the content of the API and that's.
And that's it.
They call it an MCP.
Yeah. And I think even I'll make the argument, I'm going a little off-script here, but I'll make the argument that a lot of them, even when they're not wrappers, are just bad products because no thought was put into them.
And I mean, one comparison that I talk about sometimes with my team is if you go to a bad website, you know it's a bad website. We don't need to sit there and figure out why it's ugly or it's hard to use or it's hard to find what you're looking for or it's all flash.
I don't know what makes a bad website exactly, but you know what a bad website is when you go to one. We don't like to point out all the things because there's an infinite number of them. Instead, we try to find great examples of good websites.
And so what I think we need, more than anything else, are MCP best practices. And so a big push of mineright now, and part of where this talk came from, is I want to make sure that we have as many best practices in the world and documented.
And I do want to applaud. There are a few firms. These are screenshots from Block, who has an amazing playbook, which, if you hate this talk, read their blog post. It's like a better version of what I'm doingright now.
And GitHub recently put out one, and many other companies have done as well. I could have put a lot here, but these are two that I've referred to quite frequently, and so I recommend them to you. The Block team in particular is just phenomenal at what they're doing on MCP.
By coincidence, the same team has been my customer for six years on the data side, and they're, I really love the work that they do. And the blog post they put out are very thoughtful, and I highly recommend them to you.
I want to see more of this, and today is sort of one of my humble efforts to try and put some of that in the world. And so what I thought we would do today, because I did not want to ask you to open your laptops up and set up environments and actually write code with me, because it's 4:25 on Saturday, I thought that we would fix a server together, sort of through slides, to make this, again, as I said, hopefully actionable, but a gentle approach to this.
And so here is the server that you were describing a moment ago,right? So someone wrote this server. I hope that the notation is clear enough to folks. We have a decorator that says that a function is a tool, and then we have the tool itself.
And forgive me, I didn't bore you with the details because we think this is a bad server to begin with. I think in this server, what's our example here? Right, we want to check an order status. And so in order to check an order status, we need to learn a lot of things about the user and what their orders are.
We need to filter it. We need to actually check the status. And if this were a REST API, which presumably it is, we know exactly what we would do here. We would make one call to each of the functions in a sequence and return that as some user-facing output.
And it would be easy, and it would be observable, and it would be fast, and it would be testable. Everything would be good. And instead, if we expose this to an agent, what order is it going to call these in?
Does it know what the format of the arguments are? How long is it going to take for the minimum three round trips this is going to require? These are all the problems that we're exposing just by looking at this.
We're not, I mean, solve them, but that's the problems I see if I were reviewing this as a product-facing effort. And so the first thing that we are going to think about, and I think this is probably the most important thing when we think about an effective MCP server because it is product thinking, is outcomes, not operations.
What do we want to achieve? And this is a little bit annoying for engineers sometimes because it's forced product thinking. It's not someone coming along with a user story and mapping it all out and saying, "This is what we need to implement."
Outcomes14:53
We cannot put something in this server unless we know for a fact it's going to be useful and have a good outcome. We have to start there. There's just not enough context for us to be frivolous. And so here's kind of what this feels like so that we can get a sense for it.
The trap, when you're falling into the trap, you have a whole bunch of atomic operations. This is amazing if you're building a REST API. It is best practice if you're building a REST API. It is bad if you're building an MCP server.
Instead, we want things like track latest order and given email. It's hard to screw up, and you know what the outcome is when you call it. The other version of the trap is agent is glue or agent is orchestrator.
Please believe me, since I've spent my career building orchestration software and automation software, that there are things that are really good at doing orchestration, and there are things that are really bad at orchestration. And agents areright in the middle because they can do it, but it's expensive and slow and annoying and hard to debug and stochastic.
And so if you can avoid that, please do. If you can't, there are times when you don't know the algorithm and you don't know how to write the code and it's not programmatic, that's a perfect time to use an LLM as an orchestrator.
Finding out an order status, really bad time, really expensive time to choose to use an LLM as your orchestration service. So don't. Instead, focus on this sort of one tool equals one agent story. And again, even here, we're trying to introduce a new vocabulary.
It's not a user story, because user stories, everyone thinks human, even though it isn't a user. It's an agent story. It's something that a programmatic autonomous agent with an objective and a limited context window is trying to achieve, and we need to satisfy that as much as we can.
And then this is one of those, like, little tips that feels obvious but I think is important. Name the tool for the agent. Don't name it for you. It's not a REST API. It's not supposed to be clear to future developers who need to write, you know, you're not writing an API for change.
You're writing an API so that the agent picks theright tool at theright time. Don't be afraid about using silly but explanatory names for your tools. I shouldn't say silly. They might feel a little silly, but they're very user-facing in this moment, even though it feels like a deep API.
This, just in case any of you didn't go read the Block blog post, I just found this section of it so important, where they essentially say something very similar. Design top-down from the workflow, not bottom-up from the API endpoints.
Two different ways to get to the same place, but they will result in very different forms of product thinking and very different MCP servers. So again, I just, I really encourage you to go and take a look at that blog post.
And if we were to go back to that bad code example I showed you a moment ago and start rewriting this, and if we had our laptop, you're welcome to have your laptops out and follow along. The code will essentially run, but there's no need.
Here's what that could look like. We did the thing that you would do as a human. We made three calls in sequence that are configured, that are to our API, but we buried them in one agent-facing tool. And that's how we went from operations to outcomes.
The API calls still have to happen. There's no magic happening here. But the question is, are we going to ask an agent to figure out the outcome and how to stitch them together to achieve it, or are we going to just do it because we know how to do it on its behalf?
Arguments18:21
So thing number one is outcomes over operations. Thing number two, another thing, a lot of these, frankly, are going to seem kind of silly, actually, when I say them out loud. Please just trust me from the download graph that these are the most important things that I could offer as advice.
And if none of them apply to you, think of yourself as in the top 1% of MCP developers. Flatten your arguments.
I see this so often where I do this myself, I'll confess to you, where you say, "Here's my tool," and one of the inputs is a configuration dictionary. Hopefully, presumably, it's documented somewhere in maybe in the agent's instructions.
Maybe it's in the doc string. You have a real problem when, by the way, I don't remember if I have a point for this later, so I'll say it now. A very frequent trap that you can fall into with arguments that are complex is you'll put the explanation of how to use them in something like a system prompt or a sub-agent definition or something like that, and then you'll change the tool in the server.
And now it's almost worse than a poorly documented tool. You have a doubly documented tool, and one is wrong and one isright, and only error messages will save you. That's really bad. This is a more gentle version of that.
Just don't ask your LLM to invent complex arguments. Now, you could ask, "What if it's a pedantic model with every field annotated?" And fine, that's better than the dictionary, but it's still going to be hard. There was, until very recently, there may still be a bug in, maybe it's not a bug because no one seems to fix it, but in Claude Desktop,
all structured arguments, like object arguments, would be sent as a string. And this created a real problem because we do not want to support automatic string conversion to object. But Claude Desktop is one of the most popular MCP clients, and so we actually bowed to this as a matter of, like, necessity.
And so FastMCP will now try, if you are supplying a string argument to something that is very clearly a structured object, it will try to deserialize it. It will try to do theright thing. I really hate that we have to do that.
That feels very deeply wrong to me that we have a type schema that said I need an object, and yet we're doing clutty stuff like that. And so this is an example of where this is an evolving ecosystem.
It's a little messy, but what does it look like when you do itright? Top-level primitives. These are the arguments into the function. What's the limit? What is the status? What is the email? Clearly defined, just like naming your tool for the agent, name the arguments for the agent.
And here's sort of what that looks like when we get that into code. Instead of having config:dict, we have an email, which is a string. We have include:cancelled, which is a flag. And then I highly, highly recommend literals or enums whenever you can.
Much better than a string if you know what the options are. At this time, very few LLMs know that this kind of syntax is supported, and so they would typically write this. If you had Claude code or something, write this.
It would usually write format:string equals basic, which works. It just doesn't know to do this. And so it's one of those little actionable tips. Use literal or use enum equivalently. When you have a constrained choice, your agent will thank you.
Instructions21:44
And I do have instructions or context. So I did get ahead of myself. I'm sorry, everybody. It is 4:35 on a Saturday. The next thing, though, I want to talk about is the instructions that you give to the agent.
This cuts both ways. The most obvious way is when you have none. We mentioned that a moment ago. If you don't tell your agent how to use your MCP server, it will guess. It will try. It will probably confuse itself, and all of those guesses will show up in its history, and that's not a great outcome.
Please document your MCP server. Document the server itself. Document all the tools on it.
Give examples. Examples are a little bit of a double-edged sword. On the one hand, they're extremely helpful for showing the agent how it should use a tool. On the other hand, it will almost always do whatever is in the example.
This is just one of those quirks. Perhaps as models improve, it will stop doing that. But in my experience, if you have an example, let's say you have a field for tags. You want to collect tags for something.
If your example has two tags, you will never get 10 tags. You will get two tags pretty much every time. They'll be accurate. It's not going to do a bad job, but it really uses those examples for a lot more dimensions than just the fact that they work, if that makes sense.
So use examples, but be careful with your examples. Yes, sir.
Giving out-of-distribution examples as a way to solve for that? Have you seen that?
By out-of-distribution, do you mean?
Examples that would not be representative of actual outputs that have the same structure?
It's so interesting. So I don't have a strong opinion on that. That seems super reasonable to me. I don't have an opinion on it. In my experience, the fact that an example has some implicit pattern, like the number of objects in an array, becomes such a strong signal that I almost give this its own bullet point called examples are contracts.
Like, if you give one, expect to get something like it. Out-of-distribution is a really interesting way to sort of fight against, I guess, that inertia. I would imagine it is better to do it that way. I would just be careful of falling into this sort of more base layer trap, I think.
So that's completely reasonable, and I would endorse it. I think this is just a more broad. Whatever example you put out there, weird quirks of it will show up. On an MCP server that I'm building, I encountered this tag thing just yesterday, and it really confused me.
No matter how much, I was like, "Use at least 10 tags." It always was two, and I finally figured it was because one of my examples had two tags. So yes, good strategy. May or may not be enough to overcome these basic caveats.
Oh, I do have examples are contracts. I'm sorry. We're 37. This one, I think, is one of the most interesting things on this slide. Errors are prompts. So
every response that comes out of the tool, your LLM doesn't know that it's bad. It's not like it gets a 400 or a 500 or something like that. It gets what it sees as information about the fact that it didn't succeed in what it was attempting to do.
And so if you just allow Python, in FastMCP's case or whatever your tool of choice is, to raise, for example, an empty value error or a cryptic MCP error with an integer code, that's the information that goes back to your LLM and does it know what to do with it or not?
Probably it knows at least to retry because it knows it was an error, but you actually have an opportunity to document your API through errors. And this leads to some interesting strategies that I don't want to wholeheartedly endorse, but I will mention, where, for example, if you do have a complex API because you can't get away from that, then instead of documenting every possibility in the doc string that documents the entire tool, you might actually document how to recover from the most common failures.
And so it's a very weird form of progressive disclosure of information where you are acknowledging that it is likely that this agent will get its first call wrong, but based on how it gets it wrong, you actually have an opportunity to send more information back in an error message.
As I said, this is kind of not an amazing way to think about building software, but it is the ultimate version of what I'm recommending, which is be as helpful as possible in your error messages. Do go overboard.
They become part of, as far as the agent is concerned, its next prompt. And so they do matter. If they are too aggressive or too scary, it may avoid the tool permanently. It may decide the tool is inoperable.
So errors really matter. And I don't think this needs too much of an explanation, but this is what it looks like when you have a full doc string and an example, et cetera. Block, in their blog post, makes a point, which I haven't seen used too widely, although ChatGPT does take advantage of this in their developer mode, which is this read-only hint.
So the MCP spec has support for annotations, which is a restricted subset of annotations that you can place on various components. One of them for tools is whether or not it's read-only. And if you supply this optionally, clients can choose to treat that tool a little bit differently.
And so the motivation behind the read-only hint was
basically to help with setting permissions. And I don't know who here is a fan of --yolo or --dangerous disable permissions or whatever they're called in different terminals, but then you don't care about this. But for example, ChatGPT will ask you for extra permission if a tool does not have this annotation set because it presumes that it can take a side effect and can have an adverse effect.
So use those to your advantage. It is one other form of design that the client can choose to provide a better experience with. I've talked about this a bit now. Respects the token budget.
I think the memeright now is that the GitHub server ships like 200,000 tokens when you handshake with it, something like that. This is a real thing. And I don't think it makes the GitHub server automatically bad. I think it actually makes it endemic on folks like myself who build frameworks and folks who build clients to find ways to actually solve this problem because the answer can't always be do less.
Token Budget27:31
In fact,right now, we want to do more. We want an abundance of functionality. And so we'll talk about that maybe a little bit later. But respect for the token budget really matters. It is a very scarce resource, and your server is not the only one that the agent is going to talk to.
So I was on a call with a customer of mine recently who is so excited that they're rolling out MCP. And I met with the engineering team. And just to be clear, this is an incredibly forward-thinking, high-performing, massive company that I incredibly respect.
I won't say who they are, but I really respect them. And they got on the call, and they were so excited, and they were like, "We're in the process of converting our stuff to MCP so that we can use it."
And they had a strong argument why it actually had to be their API. So that's not even the punchline of this story, which is a whole other story in and of itself. But it fundamentally came down to this.
They had 800 endpoints that had to be exposed, to which I had this thought, which by the time you finish reading this, this is the token budget for each of those 800 tools if you assume 200,000
tokens in the context window. So if each of those 800 tools had only this much space to document itself, not even document itself, share its schema, share its name, plus documentation, this is the amount of space you would get.
And when you were done taking up the space because you were so careful and each tool really fit in this, you would lobotomize the agent on handshake because it would have no room for anything else. So the token budget really matters.
If this agent connected to a server with one more tool that had a one-word doc string, it would just fail. It would just have effectively an overflow,right? So the token budget matters. There is probably a budget that's appropriate for whatever work you're doing.
You may know what it is. You may not know what it is. Pretend you know what it is and be mindful of it in a worst-case scenario. Try to be parsimonious. Try to be as efficient as possible. That's why we do experiments like sending additional instructions in the error message.
It's one way to save on the token budget on handshake. And the handshake is painful. I'm not sure folks know that when an LLM connects to an MCP server, it typically does download all of the descriptions in one go so that it knows what's available to it.
And it's usually not done in a progressively disclosed way. That is done outright. Yes.
Absolutely.
Actually, based on that problem,
if it's not only a centralized configuration connected to this website, you have Connect, Gmail, Notion, all of those in one place, and then you get one instance of it. But I also built this progressive disclosure mechanism where when it first initializes the instance, it just comes back with the list of Google Notion and FACT services.
And then there's an additional describe step for each one of the connected services. So it's 95% less context window for lots of services up front. And then
you can use whatever service. It doesn't actually expose that to the LLM unless it needs to do that.
OK, so that's awesome. Let's talk about this idea for one second because it's a really interesting design.
There's a debateright now about what you can do that's compliant with the spec versus what you do that's not compliant with the spec. And as long as you do things that are compliant with the spec, then by all means, do them.
Who cares? One of the problems is that there are clients that are not compliant with the spec. Claude Desktop is one of them. I've mentioned it a few times. I have a history with Claude Desktop. Claude Desktop hashes all of the tools it receives on the first contact and puts them in a SQLite database, and it doesn't care what you do.
It doesn't care about the fact that the spec allows you to send more information. I think your solution would get around this because it's a tool call. But many of the first attempts that people use to use spec-compliant techniques for getting around this problem, such as notifications, fail in Claude Desktop.
Usually, you've failed before this in Claude Desktop. I'm not a fan of Claude Desktop from MCP server. I think it's a real missed opportunity because it is such a flagship product of the company that has introduced MCP. I think it's a real missed opportunity.
Claude Code is great.
It caches everything in SQLite database. So it doesn't matter what you do. Techniques similar to what you've described where you provide mechanisms for learning more about a tool, that's a great idea. I really like that. There is a challenge where now you are back in a sort of flattened arguments world because you have meta tools now where I need to use tools to learn about tools and use tools to call tools in some extreme cases or beyond.
So you need to design this very carefully. That's why it usually does show up as a dedicated product. So thank you for sharing that. There are many really interesting techniques for trying to solve this problem. Yes.
So you talk about progressive disclosure. Do you use format schemes? So for example, I connect to my Kubernetes server, and my credentials only give me certain rights. So therefore, there are 28 tools that I don't have access to.
So therefore, you don't need to tell me about it.
So when you say, "Do I support that?" Do you mean does MCP support that, or do I, in my product, support that?
Yeah, no, I'm just asking because it's something I've read here where the thing that would work around
formats.
OK, so the spec makes no claim about this. The spec says when you call list tools, you get tools back. And how that happens is up to implementation. FastMCP makes that an overrideable hook through middleware, but again, makes no claim on how that is.
Prefect's commercial products, which I'm not here to pitch, allow per-tool masking on any basis. And we see that as a place to have an opinion aided in the commercial landscape as opposed to an opinion in the open source landscape as opposed to the protocol, which should have no opinion at all.
So if that's interesting, we can chat about this.
You might be getting into this, but if you take this problem example, there's a general dimension of a kind of table of contents approach. I guess the approach is what? Split it up into four different chunks, or maybe the 800 don't all justify having their own MCP server?
Or what was the solution?
For them?
Yeah.
They can't do it. There's no solution that allowed them to have as much information as they wanted on the context window they have. They didn't need it. They didn't need it. And it became a design question. And frankly, this call was probably four months ago now.
And it was just call after call after call after call like this, which made me realize we need to have talks more like this and just talk about what it is to design a product for an agent. My worry is MCP is viewed as infrastructure or a transport technology.
And it is. And I'm very excited. I think by a year from now, we will be talking about context products as opposed to MCP servers. I'm very excited about that. We'll move past the transport. But we need to figure out how to use it.
And so I think that's how we talk about it. The only other alternative that I have discussed with a few folks, a few companies, when you have a problem like this is if you control the client, much more interesting things become available to you.
Curation34:43
If you can instruct your client to do things a certain way, for example, if you have a mobile app that presents an agentic interface to an end user, you control the client is what I mean by that. Or if it's internal and you can dictate what client or what custom client a team uses, now you can do much more interesting things because you actually do know a lot more about that token budget and how to optimize it.
But for an external-facing server, that's not a good solution.
I think by now we have talked through all of this, so I'll leave it for posterity in the interest of time. We talked about curate as a key verb earlier in this talk. It is, I would argue, what we have been doing in each of these little vignettes that we've been working through with the code.
We are curating the same information set down to one that is more amenable and more recognizable for an agent. 50 tools is where I draw the line where you're going to have performance problems. I think it seems really low to a lot of people.
Some people will talk about it even lower than that. Some people might talk about it higher. If you have more than 50 tools on a server without knowing anything else about it, I'm going to start to think that it's not a great server.
The GitHub server has, I think, 170 tools. Does that mean it's not a great server? No. There is a good argument there, and the GitHub team has put out a lot of really interesting blog posts on semantic routing that they're doing.
They had one just yesterday, actually, on some interesting techniques that they're using. There's software like the one you mentioned a moment ago, sir, which helps with this problem. So having a lot of tools like that does not automatically make it a bad server, but it is a smell.
And it does make me wonder, can we split them up? Do you have admin tools mixed in with user tools? Could we namespace these tools differently? Would it be worthwhile having two servers instead of one? That is a little bit of a smell.
If you can get down to 515, that would be ideal. I know that's not achievable for most people. So it's one of those actionable, but maybe not so actionable little tips. It's an aspiration that you should have. And just be careful unless you are prepared to invest in a lot of care and evaluation, 50 tools per agent.
I should have said per agent. If I have a 50-tool server and you have a 50-tool server, that's 100 tools to the agent. That's where the performance bottleneck is, not on the server. Sorry, the slides should be corrected.
It's 50 tools to the agent is where you start to see performance degradation. I love this. Kelly Callefo is someone who I've known a long time. He's at FiveTrain now. And while I was putting this talk together, I happened to come across these two blog posts of his, which are a little bit of like a shot and a chaser.
They're written almost exactly a month apart, ones from October, ones from November. In the first one, he talks about building up a FiveTrain server, and he goes from a couple of basic tools to, I think, 155, 188. And in the second blog post, he talks about how he curated that server from 188 down to 5.
You could read either of these blog posts. You could view them independently as a success story on what his adventure was in learning MCP. I think taken together, they tell a really interesting story about making something work and then making something work well, which is, of course, the product journey in some sense.
And so where this takes us is sort of the thing that I sorry, do you have a question? Oh, sorry. Where this takes us is sort of the thing that I have found is the most obvious version of this.
I wrote a blog post that went a little bit viral on this, which is why I talk about it a lot, which is please, please, just if nothing else, stop converting REST APIs into MCP servers. It is the fastest way to violate every single thing we've talked about today, every single one of the heuristics that we laid out about agents.
It really doesn't work. And, comma, it's really complicated because this is the FastMCP documentation. That's a blog post I had to write. And the blog post basically says, I know I introduced the capability to do this. Please stop.
And that's a really complicated thing. That could be a workshop in and of itself. I do bear a little bit of responsibility here. This is not just a feature of FastMCP. It's one of the most popular features of FastMCP, which is why, candidly, it's not going anywhere.
And instead, we're going to document around that fact. But here's the problem,right? You just can't. You just can't. You just can't convert it to REST. I'm not going to explain it. You just can't convert REST APIs into MCP servers.
But, comma, but it is an amazing way to bootstrap. When you are trying to figure out if something is working, do not write a lot of code where you introduce new ways to figure out if you have failed.
Do start by picking a couple of key endpoints, mirroring them out with FastMCP's autoconverter or any other tool you like, or even just write that code yourself. Make sure you solve one problem at a time and make the first problem being, can you get an agent to use your tool at all?
Once it's using it, by all means, strip out the part of it that just regurgitates the REST API and start to curate it and start to apply some of what we've talked about today. This is just one of those candid things,right?
It is the fastest way to get started. You don't have to do it this way. I start this way. Just don't end up. Don't ship the REST API to prod as an MCP server. You will regret it. You will pay for it a little bit later, even though there's a dopamine hit up front.
So
these are the five major things that we talked about today in our pseudo workshop, workshop that wasn't really a workshop, actionable talk. Outcomes, not operations. Focus on the workflow. Focus on the top-down. Don't get caught up in all the little operations.
Takeaways40:06
Don't ask your agent to be an orchestrator unless you absolutely have to. Flatten your arguments. Try not to ship large payloads. Try not to confuse the agent. Try not to give it too much choice. I don't think I said out loud when we talked about that, but try not to have tightly coupled arguments that really confuses the agent.
See if you can design around that. If possible. It's not always possible, but if you can. Instructions are context. Seems obvious to say out loud. Of course, they are. They're information for it. Use them as context. Design them as context.
Really put thought into your instructions the same way as you would into your tool signature and schema. Respect the token budget. Have to do it. This is the only one on this list where, if you don't actually do it, you will simply not have a usable server.
The other ones you can get away with. And frankly, the art of this intuition is start with these rules and then work backwards into practicality. But this is the only one where I think you can't actually cross the line.
And then curate ruthlessly. If you can do nothing else, start with what works and then just tear it down to the essentials. I have been writing MCP servers about as long as anyone at this point, a year. And I still find myself starting by putting too many tools in the world, sometimes because I'm not sure which one it will use or where I'm experimenting.
And I have to remind myself to go back and get rid of them. And it's hard. I think as an engineer, especially designing normal APIs, you're like, OK, here's my tool. Here's v2. It's backwards compatible,right? And you keep adding stuff.
And that's a really natural way to work. And it can be a best practice. And it doesn't work here. It would be like using a UI that just showed a REST API to a user. This is a criticism I have offered of my own products at times when I'm like, this looks a little bit too much like our REST API docs,right?
We're not doing our job to actually give this to our users in a consumable way. So if I can leave you with just one thought, it's this. You are not building a tool. You are building a user interface.
And treat it like a user interface because it is the interface that your agent is going to use. And you can do a better job, or you can do a worse job. And either you or your users will benefit from that.
I think we are at our time, so I'm going to just open it up for questions or what's next or what other challenges we can solve. I hope I walked the tightrope between things that are useful to you all but don't require you to write any code at 4:54 on a Saturday now.
Q&A42:31
But I hope I had some useful nuggets in there for you more than you came in with. And happy to take any questions.
What are attempts to type the coupled arguments?
That would be where you have one argument that's like, what is the file type? And another argument that's like, how should we process the file? And your input to the file type argument determines the valid inputs for the other argument.
So they're now tightly coupled. Some arguments on the second thing are invalid depending on what you said for the first thing. It's just one extra thing to keep track of. That's a good question. Sorry, I didn't define that.
I have two. I will start with the first one.
When you are giving an agent an MCP server, you have to document the tools or the capabilities of the server in the server and in the agent. And that is not ideal.
Yes.
What would you recommend? That or only in the MCP server?
So this comes down to do you control the client or not. If you control the client, then this is a real choice. And there are different ways to think about it. So for example, in some of my stuff that I write that I know I'm using, for example, Claude Code to access, I might actually document my MCP server as files or Claude skills because I know what the workflows are going to be.
I know that some of my workflows are infrequent, and I don't want to pollute the context space with them. So if you control the client, you have a real choice to make there. If you don't control the client, then you don't have so much of a choice.
You have to document it here because you have to assume you're working with the worst possible client.
Honestly, many of the answers in MCP space boil down to do you control the client? Then you can do really interesting things on both sides of the protocol. From a server author perspective, you really do need to document everything in its doc string.
The one escape hatch is that you can document the server itself. So every server has an instructions field. It is not respected by every client. I believe my team has filed bugs where we have determined that to be the case.
So hopefully, that's not a permanent thing. But most clients will, on Handshake, download not only the tools and resources and everything, but an instructions blob for the server itself. How much information you can put in there, I'd be careful.
I don't think it wants to read a novel. But you do have this one other opportunity to document maybe the high level of your server.
I have another one.
Oh, yeah. Well, why don't we let's mix it up and we'll come back. Did you have a question?
Yeah. What's the relationship to the MCP spec changes?
I'm not a member of the core committee, but I'm in very close contact with them. So maybe I can answer your question.
I'm so excited about this.
Yes. This I know a lot about.
What do you think it's going to change?
It's going to expand. It's not actually going to change so much because of the way it's implemented. What question could I answer? What is it? Am I excited about it? I am excited about it. So.
Apply?
All the rules still apply. That is a fantastic question. Let's talk about this for one second. Some of you, I don't know if any of you were at a meetup we hosted last night where my colleague actually gave a presentation on that.
Oh, you were. Yes, that'sright. I was like, I know at least somebody is coming. My colleague Adam gave a very good talk on this, which I can we'll chat after this. I'll send you a link to a recording of it.
But the nutshell version is this is SEP 1686 is the name of the proposal. And it adds asynchronous background tasks to the MCP protocol, not just for tools, but for every operation. And we don't need to talk too much about what that is.
The reason it doesn't involve changes to any of these rules is this is essentially an opt-in mode of operating in which the client is saying, I want this to be run asynchronously. And therefore, the client takes on new responsibilities about checking in on it and polling for the result and actually collecting the result.
But the actual interface of learning about the tool or calling the tool, et cetera, is exactly the same as it is today. So this is fully opt-in on the client side. And that's why, from a design standpoint, nothing changes.
The only question from a server designer standpoint is, is this an appropriate thing to be backgrounded as opposed to be done synchronously on the server? Or, sorry, let me take that back. You can background anything because it's a Python framework.
So you can chuck anything in a Python framework. The question is, should the client wait for it or not? Should it be a blocking task is really theright vocabulary for this. And that's just a design question for the server maintainer.
Am I in the zone of what you were looking for?
I think so.
Oh, no kidding.
There's now a format one. I have code issues for them. When it gets an error in the first code, the LLM is thinking it has.
Yes. This happens a lot, actually. But until you said this, I didn't think of it as like a pattern. But I've seen this a lot. It's a real problem. Maybe we'll write a blog post on it. That'll be fun.
Yes.
I was going to say the rules still apply, but as far as elicitation is concerned, how do you do that in terms of?
Elicitation is really interesting. So now we're in advanced MCP. Elicitation. Anyone not familiar with what that is? Yes. So elicitation is basically a way to ask the client for more input halfway through a tool execution. So you take your initial arguments for the tool, you do an elicitation.
It's a formal MCP request. And you say, I need more information. And it's structured is what's kind of cool about it. So the most common use case of this in clients that support it is for approvals where you say, I need a yes or no of whether I can proceed on maybe some irreversible side effect or something like that.
When it works, it works amazingly. Again, it's one of those things that doesn't have amazing client support. And therefore, a lot of people don't put it in their servers because it'll brick your server if you send out this thing and the client doesn't know what to do with it.
So you got to be a little bit careful. Does it change the design? It's a fantastic question. I wish it were used more so I could say yes, and you should depend on it. If all clients supported it and it was widely used.
And the reason all clients don't support this one, by the way, I'm not trying to it's not like a meme that clients are bad. It's complicated to know how to handle elicitation because some clients are user-facing. Then it's super easy.
Just ask the user and give them a form. Some clients are automated. Some are backgrounded. And so what you do with an elicitation is actually kind of complicated. If you just fill it in as an LLM,
maybe you satisfied it. Maybe you didn't. It's a little tough to know. So if it were widely used, I would say absolutely it gives you an opportunity to put in particular tightly coupled arguments into an elicitation prompt or confirmations.
A lot of times you'll see for destructive tools, you'll see confirm, and it'll default to false. And you're forcing the LLM to acknowledge at least as a way of hopefully tipping it into a more sane operating mode. Elicitation is a better way to design for that.
I don't think that made it into any of these examples. So great question. Wish I could say yes. I hope to say yes about that. You had a second question.
Yeah. So in my job, the main thing I do is build agents. And I do like Dangra, OpenAI SDK, or something like that. And I usually just write the tools and the tools calling the APIs. And I don't really see the need for the MCPs in that space.
Do you agree that the MCPs are not needed there? Or do you have a.
I do. I think
I would not tell you to write an MCP server. I think that within a year, the reason you would choose to write an MCP server is because you'll get better observability and understanding of what failed. Whereas the agent frameworks are not great because part of the whole agent framework's job is to not fail on tool call and actually surface it back to the LLM, similar to what we were talking about a moment ago.
So you often don't get good observability into tool call failures. Some do, but not all. And so one of the reasons to use an MCP server, even for a local case like that, is just because now you have an automatic infrastructure so you can actually debug and diagnose and stuff.
I don't think that's the strongest reason to do it. I think that's going to be in a year when the ecosystems more mature. I think if you fully control the client and you're doing client orchestration and you are writing if you are writing the agentic loop and you're the only one, do whatever you want.
I think that all of the advice you gave today also applies when you're building tools.
It absolutely does.
Yes. Everything we said today applies to a Python tool. Absolutely. And that's how FastMCP treats it. It's a good question. Any last questions? I'm happy to yes.
Yes.
Can we get on FastMCP client side? What does that look like?
Yes. So code mode is something that Cloudflare actually blogged about first. And then Anthropic followed up where you actually ask you solve some of the problems I just described here. You ask the LLM to write code that calls MCP tools in sequence.
And it's a really interesting sidestep of a lot of what I just talked through here. The reason that I don't recommend it wholeheartedly is because it brings into other sandboxing and code there's other problems with it. But if you're in a position to do it, it can be super cool.
I actually have a colleague who wrote the day that came out, he wrote a FastMCP extension that supports it, which we put in a package somewhere. We at first didn't want to put it in FastMCP main because we weren't sure FastMCP tries to be opinionated.
And we weren't sure how to fit that in. And then actually, it was so successful that we decided we're going to add an experiments flag to the CLI and have it. But I don't know if it's in yet.
I can probably
Yeah. This will go into this new I forget if we called it experiments or optimized. It's on our roadmapright now. And this would go in there. And then there's like a whole worldright now of optimizing tool calls and stuff.
But I would like to be respectful of your time and allow you all to go back to your lives. You're very kind to spend an hour talking about MCPs with me. I'm more than happy to keep talking if anybody has questions.
But I would like to free you all from the conference. I hope you all enjoyed the talk. And thank you very much for attending.





