Transcript#
This transcript was generated automatically and may contain errors.
Thanks a lot. I'm really excited to be here. You all are just coming from the keynote, so you're all ready to talk about MCP and Fast MCP and stuff. So I'm going to talk about MCP, and when it's great, maybe when it's not the best tool for the job. Who here has used MCP before? Who has made your own MCP server? Cool. Well, I hope through this talk that you'll develop an understanding of when you might want to do that and how you might do that. And just to tease that a little bit, it's really not that hard.
I want to start by talking about how I first finally came to understand what MCP was about and when I would want to use it. Because when it first came out, I was like, oh, this sounds cool. I should use this. But I didn't quite grasp, like, how. Before I do that, to give a little context, Katrina just introduced me, but that's me. I've been working in open source and data and the intersection of that for most of my career. I'm a member of the Apache Arrow PMC. For a number of years, I developed Apache Arrow. I was the chair of the project management committee last year, and I'm currently VP engineering at Posit.
One of the teams that I work with at Posit is the Posit Connect team. So for those of you who don't know, Connect is a platform for deploying and sharing custom apps, dashboards, reports, APIs, notebooks, things that you write in the language or framework of your choice, whether it's Streamlit or Dash or Gradio or Shiny, FastAPI, now have Node.js support, and so on. And you can also host MCP servers there.
The problem MCP solves
Like many of you, we've really been leaning into what we can do with agentic software development on our engineering teams. And particularly over the last year, we found ourselves just finding more and more things that Claude Code is just really good at doing, as the models have gotten better and the harnesses have gotten better. And this is a very strange time to be making software, I think we can all agree. But one of the things that's really positive about this and really exciting is that there's so many problems that maybe seemed to be hard or out of reach for us in the past that now they're now accessible to us.
You know, we can be much more responsive to suggestions of good ideas. We have a lot of customers that give us lots of feedback about how we can make Connect better. And now it's so much easier for us to say, I don't know, let's see what that would look like. Claude, let's go fire off a get work tree and take a look at that. Without having to do all of the heavy duty context switching that you would have to do to stop what you're doing and really understand what the idea was.
So as we were getting more, finding more and more uses for agentic workflows, you know, it led me to think like, all right, well, what else can I throw Claude at? What other problems are there that we think are hard or painful that maybe aren't anymore? And one of them was responding to customer support escalation. So Posit uses Zendesk for customer support. Customers will file tickets on Zendesk that get answered by our support team. And if the support engineers can't address it right away or it's going to require some more complex debugging, it gets passed off to the engineering team. And so an engineer is going to have to stop what they're doing and try to reason about the customer's environment and the peculiarities of it, because Connect is on prem software that can be run in all sorts of configurations. And so it's just, it's a heavy duty context switch that is hard and not enjoyable.
And so I thought, well, Claude's really good at reading through the code base and understanding how, you know, things that are very far apart work together. What if I could just like, can I just pipe the Zendesk information into my Claude Code session? Because there's great information about, you know, error messages, logs, sometimes, you know, Cloud server diagnostics. And I just want to say, like, take that and tell me what's going on. I want it to do this. I want to paste the link to the ticket and say, help me with this.
But this doesn't work. Because, well, at first it requires authentication. And so Claude will try and say, oh, I can't. You need to, can you just paste me the contents here? Just defeats the purpose. And so I can't, there's not a way for me to just give my credentials to Claude to log in for me. And I wouldn't want to do that anyway. Because I don't want my API key or my username and password, like, in the context here. So that's one problem. But even if I could do that, you know, Zendesk has a REST API. But the API is not designed to work with AI agents. It's not designed for them to be efficient with tokens. And it's not designed for security.
So that's what MCP is for. So MCP provides a way in the specification for you to authenticate securely with APIs without having to give your credentials to your agent. And by writing custom MCP servers, you can control exactly what tools are made available and what capabilities they expose. So this is going to improve the performance of the model, but also protects you against some security vulnerabilities. And then by hosting your own MCP server, you can easily share it with others on your team.
So that's what MCP is for. So MCP provides a way in the specification for you to authenticate securely with APIs without having to give your credentials to your agent. And by writing custom MCP servers, you can control exactly what tools are made available and what capabilities they expose.
So I'm going to talk about MCP today and what it's good for, what it's less suited for, and some best practices. Because when it's the right tool for the job, it can really unlock some great superpowers. A key point that I want to make sure comes across is that you can write and host your own MCP servers. You can do exactly what you need. There's a lot of public MCP servers out there. A lot of companies provide MCP with their products now. You can use those. But I'm really going to be focused on the ones that you write yourself. Maybe you're composing, you're kind of proxying and taking some tools from MCP servers that are out there. Or maybe you're just purely writing your own. And writing your own is a lot easier these days with tools like FastMCP, and agents can write them for you.
What MCP actually is
Before I go further, I feel like it's important to define what MCP is, model context protocol. I've talked with a lot of people recently about it, and they have a lot of questions and a lot of misconceptions I think about what it is. So I first want to say what it is not. It is not magic. It is not illusions. It's not AI. It's not vector search. It's not any of these things. It's just a specification for how to provide tools to your LLM. So tools are functions that the LLMs can call. The model is going to see at the top of its context that there's a function that exists and has this description that says what it does and then what inputs there are. So MCP is a standard for both the servers that can host these tools and how, as in a client and any AI application that supports it, you can consume those.
Importantly, this is any AI application that supports MCP. It's not just coding agents or things running the terminal. It can be any other custom agent that you write yourself or other desktop applications and whatnot. There's two kinds of MCP servers. There's local ones that run on your machine and remote ones that run on somebody else's machine. A common way, this example here from Claude Code of how you would add an MCP server highlights what it is that you're calling — UVX or NPX or something that is executing code on your machine, potentially from some random GitHub repository. It can access things on your local system, which is really useful for some things, but also potentially risky, particularly if you're running some random GitHub repository's code on your computer.
Remote ones, you address them by their URL, which is really a nice thing. If I have a remote MCP running, I can just give you the URL to it, and that's all you need to know to start using it. It can't access things on your local system, so it cannot read the files that you have. You can't ask it to do something with them, but it could have access to other things. In the enterprise, you may have resources that are available inside your network that this server could access that you don't have access to locally, and a remote MCP server can be one way to expose those.
I'm going to focus mostly on remote MCP in part because I think it really helps make very clear what the MCP standard is, which is it's an API standard. It's a way to describe an API. If you look at what it's actually doing, it's making post requests, and it's sending JSON, and it's receiving JSON back. It's basically a JSON RPC. For me, this was really helpful to reason about, that MCP is not some complicated thing. It's certainly not AI, but it's an API over HTTP. I've been working with data for a long time. I've been working in software for a long time. I know how to work with APIs. Ultimately, the technology is much simpler underneath it.
MCP vs. other approaches
MCP came out at the end of 2024. There was a lot of excitement when it first came out because it was really the first way that you could add tools like this, external resources, into your agentic workloads. The first standard way to do this. Naturally, there was a backlash to that. A lot of people were like, well, Claude Code is really good at using command line utilities. Why would I register this MCP server with all of these tools when they can just shell out and use the CLI? Leading some to say that MCP is dead.
This is not the end of my talk, so I do not agree that MCP is dead. I think that, like most things, there's some truth to the excitement, and there's something to the criticism. Since it was the first standard way to add capabilities and tools to your agents, people did things with MCP that, in retrospect, in 2026, you certainly wouldn't use MCP for that.
There were a number of critiques, and I want to step through a few of them and use that to bring that to today for what MCP is good for and what it's not. The first one is whether it's the right tool for the job. Should you use MCP server, should you use a command line utility, or should you use a skill? Now, remember, the agent skills standard came after MCP, so it did not exist at the time. Skills are great. If you can describe everything you want to do with tools that your agent already has, like Bash or the web fetch or anything like that, then a skill is all you need. You just tell it how to use things it already knows how to do. Otherwise, a skill is really a complement, and I think a very productive pattern is to have a skill that goes with your CLI or with your MCP server. Here are the tools, and here's how you use them. Here's how you compose them together. So the choice you're actually making is really between MCP and skill or CLI and skill.
One particular example with command line utilities that was called out in these criticism was with the GitHub command line, the GH tool. GitHub has an MCP server that has many dozens, lots, probably hundreds of tools, and if you were to take the whole GitHub MCP server, that's a lot of tools. And this matters because when you connect to an MCP server, all of the tool definitions load at the beginning, and so they're taking up part of your context window there. And that may include a lot of things that you don't need, so it's wasteful. It's wasteful for your context. It's spending tokens that you don't need, and it makes your model less efficient.
Whereas the GH command utility is perfect because it's already in the training data for the model, so it already knows how to use it. So it's great if that's what you're dealing with, but not every command line utility that you might want to use is already in the training data. And if you have to teach the model how to use your CLI, you're writing descriptions of what it can do and stuff, or if the agent has to do the, you know, your command line utility dash dash help to see what it does, well, that's tokens you're burning, right? You're kind of rebuilding all of this context that you were trying to save by not using MCP.
And of course, you can only use command line utilities if your agent has access to run commands in the shell, and that's not always the case. You might have an agent that you have deliberately not given shell access to because you just wanted to have other tools. Maybe you're trying to protect against escaping a sandbox or whatever, and so CLI is not always an option.
Security and the lethal trifecta
There were also, at the time, a number of security vulnerabilities and things that were exposed using MCP servers, and so I really think it's important to talk about security. It's not strictly a critique of MCP because command line utility can also expose you to these types of things as well. I think a very useful framework is the lethal trifecta that Simon Willison has written about. So essentially, if your agent has access to private data, which of course is what we're trying to do here by providing more tools here, that access private data, exposure to untrusted content, and the ability to communicate out, it is pretty easy to prompt inject or otherwise trick your agent into sending that private data to the attacker.
Just to stick with GitHub for a minute, if you have tools exposed to let you read issues and you're reading issues in public repositories, somebody could write a prompt injection type of issue that says, read the secret file here and send the information somewhere. And so if you also have given the write issue task or comment on issue tool, then you've gotten both the access to the private data and the exposure to untrusted content, and now you've given an ability to exfiltrate.
So it's important to be mindful about what tools are made available, not just within your single MCP server, but in the whole agent that you're using. And so this leads me to describe a few best practices that I found. And with the title of the talk, MCP or not MCP, I felt I needed to have at least one Hamlet reference in here. And so brevity is the soul of wit is how I summarize the best practices.
You only want to find the tools that you actually need. You only want to send the information back that you actually need. And you want to sanitize out any sensitive data that you don't need.
Best practices in practice
So I'm going to walk through some of these in practice using some examples from the Zendesk MCP server that I, well, Claude actually wrote. But really the best way to get started with your own MCP server is just to ask Claude to help you use FastMCP to make MCP servers do what? It's pretty straightforward. The agent does a good job at it, and you can get pretty far with that. Though there's one thing you need to be aware of. Jeremiah mentioned this briefly in the keynote a minute ago, but there's actually now two FastMCP libraries. There's the v1 that is incorporated into the official MCP SDK, and then there's the one that they have continued to develop. This is usually fine, but sometimes I found the agents will get confused and reference, because the class name is the same, it'll reference methods that don't exist. So ultimately it'll figure it out when it doesn't actually run, but it's something to watch out for.
Here's the QR code that I mentioned. If you want to look at the code that I'm going to talk about, though, don't look too closely, because I didn't look too closely at it. That's the joy of having Claude write your code for you. Actually, when I was putting together slides, I cleaned up a little bit of what is in there, because I was like, oh, that does the thing I want to show, but I don't like how that looks. And this is actually fun. I forked a version of this that someone had written in JavaScript and said, Claude, rewrite this in Python, and that's how I got started with it. But hey, it works, and it does the thing, and I can verify that it does what it's supposed to do.
So let me start with just a simple tool example. You kind of got a flavor for this in the keynote, but an MCP tool using FastMCP is essentially a Python function with a decorator on it. It's very similar. It's kind of like how you would set up routes in FastAPI or something. But check it out. So this is just the one to get the Zendesk ticket, and the input is a ticket ID, and then it passes off to this client object. So the client is the thing that actually holds my credentials and talks to the REST API, and then it sends it back. So by wrapping the ultimate REST API client there, my credentials to Zendesk are nowhere in the agentic loop. It's all hidden behind this server.
And so we could stop here and say, I just need to get the ticket so that I could see what's going on and respond to it, and that's great. So now I've got a server with one tool, and maybe that's all I need, actually. And that's a whole lot less than the REST API. You could stop with however many that you need, which is way less than exposing access to the full REST API.
You could think of, I'm just going to make a generic Zendesk API tool, and I'm going to say, you just tell me the path segment and any query parameters or whatever, and it does that. So you have one tool, and I've escaped this trap here of having a bunch of tools that take up context. And I'll just say, I'll just give the open API spec to Claude, and it can read it, and it can figure out which endpoints to do. As you may figure, that actually, that will blow up your context as well, because the API is huge. So having just the tools that we care about referenced in is really important.
The other thing, though, is about security. So under the ticket endpoints, there's this great one where I can bulk delete tickets. And with my API key on Zendesk, I could do that, but I do not want to give that to my agent. So by being selective about what tools we give, we can limit the impact of what our agent could do and also make it more efficient. So many of the MCP servers that I use in production now intentionally only expose read-only tools, because I don't know where they're going to be composed into other agents, but I want to make sure that I just need to get the facts out. I've cut off the ability to exfiltrate with this one.
So the next thing to do is to make our responses concise. So this is a much sanitized version of the next tool call that I have that I use a lot, which is to list the ticket comments. This comes back in a separate resource in the API, and it's all of the back and forth discussions of what if you do this? I did this, and here's what the log is. And the actual REST API is very verbose here. Some of it is information that's probably useful for rendering the web page, the web app for Zendesk, and some of it is just analytics and stuff that's not helpful at all. So here there's actually two or maybe three copies of the body of the comments. There's a plain text one that I'm keeping, but I'm removing the HTML body and whatever the other body is, too. So there's one that's got rich formatting on it. I definitely don't need two copies of it, let alone three.
Metadata is interesting. It will include things like the user agent string and IP and location of the browser of the person when they logged into the portal to leave a comment on the issue, which is probably useful for somebody but is not useful for Claude in trying to help me understand the substance of it. Likewise with attachments, I need to know that there's an attachment. The attachment could be the logs that I want. I have a separate tool for being able to process the attachments, but I don't need the thumbnail of the image or whatever. Let's all just junk.
And if you forget to do this, if you forget to clean your responses, Claude will probably let you know. The reason why I ended up noticing I need to do this was I would make a tool call and it would come back and say large MCP tool response, like 20 kilobytes or whatever. I was like, that's insane. So by trimming this, you're helping focus your tool responses, you're helping focus your agent on the things that it actually needs. So again, we're building an API that is specific for AI agents. So anything else that's in the API response that is not useful for that, we can get rid of. As a human, I can read the JSON dictionary into memory and I can just choose not to look at the two other copies of the comment body. But the agent doesn't have that choice. It's all just tokens, right? So less context means lower cost and more likely to get the answer that you're looking for.
As a human, I can read the JSON dictionary into memory and I can just choose not to look at the two other copies of the comment body. But the agent doesn't have that choice. It's all just tokens, right? So less context means lower cost and more likely to get the answer that you're looking for.
And finally, I didn't actually do this in this particular MCP server, but it's also good to just be mindful of your data hygiene. And so if there's any personally identifying information that you're pulling in potentially and the agent doesn't need it, you can strip it out. So here's just a function that strips out the username but keeps the domain for an email. So I could still see that somebody from Posit left this comment, but the individual is not identified.
So again, when you're building the MCP server, you might not know right away what agents it's going to get pulled into. I have restricted to only read-only tools, so I'm not exposing within this particular MCP server to the lethal trifecta. But if someone else is in their agent, they take this MCP and they take another one that's got the ability, maybe it doesn't expose private data, but it has the ability to write, then we're kind of back in there. But this is something that we can and must, as owners of these projects, take care of.
Hosting and authentication
So there's some practice basically being as concise as you can be in what tools you define and what you include in the response. If we were to host this as a local MCP server, that would still be pretty good. This would have solved the problem that I had initially, which is I couldn't authenticate with Zendesk, because I could take my API key, put it in the environment where my local MCP server is running, and it's still out of the models context. We'd be efficient with tokens. We reduced our exposure to the lethal trifecta. But it's still not as great. The distribution story is not as great, in particular because that means that anyone else that wants to use this needs to get their own API key and put it somewhere on their machine. And now we don't really like having long-lived API tokens around, particularly those that if someone got a hold of it, they could bulk delete tickets, right?
So a remote server would be superior. So we need a place to deploy it. And because this is just an HTTP API, anywhere that you can host APIs would work. You can self-host it inside Docker. If you work at a company that has a platform for deploying things, you probably have a place to do this. There are platforms that are more aware of the MCP spec and all the things it can do. Posit Connect is one of them. There are others that are good choices as well.
If you do deploy on a platform that is aware of some of these things, you can get more out of it. And the first one that's really nice is that there is authentication built into the MCP standard. It's an OAuth 2.1 flow. So what happens is if your server supports it, I can just give you the URL. And when Claude connects to the MCP server, it says authentication is needed. Because we're meeting the standards that is in MCP, it knows how to go register itself and then start this login flow. It kicks you out to the browser, you log in, it says authentication successful. You can close this. And that's built into the MCP standard. So you don't have to have, you can have authentication built in in a way that's only short-lived tokens.
It's also possible that if your MCP server requires auth and supports API keys or API tokens, that you can put them, you can define custom headers when you configure it. And that's still, that gets you kind of like what I was describing with the local MCP. It's in the configuration for MCP. It's not in the model's, the agent's context. So it's still out of there, it still works, but it is a long-lived key that you're maintaining. Other things you can get from a platform that is designed for deploying things like this is, you know, access control. You can limit who has access to this server. We obviously care a lot more about observability and monitoring and all sorts of enterprise-y things with AI because of the potential effects of what it could do.
And there's other enterprise-y things you could do. One thing that I actually used deploying this on Posit Connect is Connect supports integrations, third-party integrations. And so there's no actual API key for Zendesk where I've deployed it. It's got an OAuth app with Zendesk, and so it's a short-lived token exchange there, and then a short-lived token exchange to the users as well. So these are all things that the security teams, my security team in particular, was very interested in making sure that we were doing.
If you don't have a platform that does these things for you, you can build a lot of these things into your server. I think FastMCP has tooling for authentication and authorization. It's totally doable. It's just more work for you or your agent to do.
I've said this a few times, but the great thing about Remote MCP is that distribution is just this URL. You could write a CLI, and you could have the CLI develop its own key store to handle auth for you and stuff. And maybe you could get it to be more efficient with tokens and a context window, but you still have to have everybody go download and install your CLI, and when you update it, you still have to tell them to update it. Whereas this case, I own this MCP server. I own the deployment. If I want to add another tool, or I want to take a tool away, I can do that. And then whoever is just connected to this URL, they automatically get those updates.
And also importantly, like I said, I want to make this available to people that aren't developers, maybe don't live in Claude Code like me and my team do. And so this works in Claude Desktop. It works in other AI applications that aren't code-oriented.
MCP or not MCP?
OK, so to call back to the title, MCP, or not MCP? On the MCP side, it is just an API standard that is tailored for AI agents. And it's something that we all know how to do. We know how APIs work. We know how to make requests and handle responses. And as you see with FastMCP, the basic part of adding a decorator to a function, this is not that difficult. In the past, I would feel really bad about telling you all, this is really easy. You should just do it. But it's easy for your agents to do. And so I'm not trying to imply that there's no skill involved. But you can really prompt your way to getting a well-functioning MCP server pretty quickly. And it's important. By being thoughtful about the design of it, by building your own, you can limit what is exposed. And this is great for getting your agent to do the thing you want it to do more efficiently, more cheaply, but also to limit your security risks.
Or not MCP, command line utilities are great. And if there's one that you want to use that's widely available in the training data, and you're in an environment where your agent can call Bash and call the tool, great. Use it. You don't need to use the GitHub MCP if you have GH. I've got a number of different agent things deployed in different places. Some of them explicitly say, use GH. Do not try to use GitHub MCP. You don't have GitHub MCP. And then there's other ones where I've only given it MCP tools because I don't want it writing things to the file system. It's an ephemeral place. I haven't done a checkout of any code. I don't want it to fetch a GitHub token and then go to the command line. I want it to use the tools I provided here. Depends on the context of what you need.
Skills are great. Not a substitute for MCP or command line utilities, but they really go well with them when you need to describe what you want to do with those tools. And local MCP is better than not. I think remote has the best properties for security and enterprise reliability, but building one that you run locally also works. So I'm going to stop there. There's a link, again, to the repo and to these slides if you want to view them. Thank you very much.
