back to indexBuilding Protected MCP Servers — Den Delimarsky and Julia Kasper, MCP Steering Committee & Microsoft

00:00:00.000 |
Welcome, folks, to the building protected MCP servers session. I'm Dan Delamarski. I'm a product 00:00:22.120 |
engineer at Microsoft and member of the MCP steering committee. And I'm Julia. I work in 00:00:27.800 |
Azure API Management at Microsoft. And the both of us are part of an MCP squad at Microsoft. So I 00:00:33.580 |
guess that's why we are here. Yeah. So we're again talking about protected MCP servers. And why is 00:00:41.220 |
this even important? Why is this even a topic? Well, the thing is, when we talk about MCP servers 00:00:46.140 |
and MCP servers that folks connect to their whatever clients, whether it's cloud desktop, 00:00:50.360 |
VS Code, VS, not every server should be open because there's a bunch of different APIs that 00:00:56.140 |
might require authorization. They're protected. So naturally, your MCP server needs to be able to 00:01:00.760 |
do the same. It is super important for remote MCP servers because anybody can access them. And 00:01:06.660 |
especially if the servers are open to the broad internet and not behind a VPN. But it's not really 00:01:11.480 |
relevant for local servers. And mainly because locally, you can do a bunch of stuff already 00:01:17.180 |
because it's just a binary. And we'll get to that in a second. Now, if you're building MCP servers and 00:01:22.760 |
remote MCP servers specifically, one of the things that you're connecting to are likely third-party 00:01:28.080 |
APIs, whether it's your own or somebody else's. And that API requires user context. That API needs to 00:01:34.420 |
know who's invoking it because different customers have different permissions. You might have different 00:01:38.860 |
admin policies. So while your API does control through the help of an identity provider as to who actually 00:01:46.680 |
has access, this needs to be somehow exposed to the MCP layer. And, of course, your API will act 00:01:54.160 |
differently depending on the credentials that you give it. If I'm an admin, I get access to a certain set of 00:01:59.140 |
tools. If I'm somebody that's just a contributor, I get access to a bunch of different tools. Now, all these things 00:02:05.620 |
considered, one of the questions I get asked a lot is like, "Well, okay, this is all great for remote, but why not local? 00:02:10.940 |
What's the story for authorization for local servers?" And the answer is kind of simple. Local servers are 00:02:18.900 |
binaries on the box. They're binaries that run within the context of whatever machine you have. That means that 00:02:24.940 |
that they can do auth in any way you can possibly have. Like, it doesn't need to be auth. You can have 00:02:30.260 |
credentials that are secrets. You can have certificates. You can have pass keys that are bound to whatever 00:02:35.260 |
credential you have on the box. Like, it's just like you don't have any constraints whatsoever. That means that 00:02:41.260 |
any APIs you connect to can also be done in any way with any of the off-the-shelf libraries. There is, of course, 00:02:47.260 |
special cases here if you do things like remote desktop and all sorts of kind of multi-user scenarios 00:02:52.580 |
on VMs. But that's kind of out of scope for MCP. That's something that we left to implementers. Now, 00:02:58.420 |
remote servers. Let's get back to that. So, one of the things that if you've browsed kind of the MCP specs, 00:03:04.580 |
you might know that we have two different specifications. There's one that is stable, that is March 26th, 00:03:11.060 |
and there's one that is draft, that we worked very closely with a bunch of security experts to refine. 00:03:16.180 |
So, for the previous specification, the one that is currently marked as stable, MCP required people 00:03:22.740 |
that are building MCP servers to essentially spin up their own authorization server. That means you are 00:03:28.900 |
building your own token factory. So, if you want to issue tokens to an MCP client to authorize users, 00:03:35.700 |
you're the one that has to craft those tokens and manage them and refresh them and assign them. 00:03:40.980 |
Very complicated. It required people to actually be OAuth experts. Like, if you want to build an MCP 00:03:46.980 |
server that does OAuth, you need to understand how OAuth works end-to-end, including to the point of 00:03:52.500 |
minting those tokens, which is very complicated. So, the draft spec that we worked with Anthropic and a 00:03:58.580 |
bunch of security experts to refine actually does this clean separation between the server, the MCP server, 00:04:05.060 |
which we call the resource server, and the authorization server. So, things like token 00:04:08.580 |
minting and managing token lifetime actually is not done by the MCP server anymore. It's done by whatever 00:04:14.740 |
authorization server you're using. So, if you're using Okta, Auth0, EntryID, it doesn't really matter. 00:04:19.860 |
You can just plug it into your MCP server and it's going to work with the clients that those servers can 00:04:25.140 |
connect to. And the spec, by the way, is going to be stable very soon. Now, I want to hand it off to 00:04:30.340 |
Juliet to talk about the fact that... Not every developer wants to be a security expert, 00:04:35.860 |
right? Like, we've seen the current draft, now the new one that's about to go live into production, 00:04:41.860 |
but not everyone, we have some exceptions here, who do want to become security experts. You actually want 00:04:47.540 |
to focus on building the remote MCP server, right? You want to solve a real problem. So, this is why the new 00:04:54.820 |
authorization spec, and that's why Dan, why a bunch of people at Microsoft, we partnered with 00:05:01.300 |
Anthropic in a security committee to get this new auth spec out there. So, let's see if I paid attention. 00:05:09.380 |
What are some of the core things that's going to change now moving forward? First of all, no need to 00:05:15.540 |
implement the authorization servers anymore. So, that means we can now just rather than implementing it from 00:05:23.140 |
scratch, we can actually attend it or append it to our server overall. We can use the standard ways we 00:05:30.260 |
only need to reference metadata that are going to point to our authorization servers. And there, that's 00:05:36.660 |
where we're going to get the token. And then on server side, you're going to have the token, that 00:05:40.660 |
information here. And all of this are still going to continue standard OAuth 2.0. So, we can actually, 00:05:46.580 |
as developers, we can rely on all of the libraries that are already out there, all of the services. So, it's going to 00:05:51.300 |
make our lives a lot, a lot easier. What does this also include on client side? So, on client side, 00:05:57.460 |
they are now, in a way, responsible for the end-to-end token dance. So, this means if the authorization 00:06:03.220 |
code comes in, we can validate it or the client validates, first of all, is the token, has it been 00:06:09.700 |
acquired successfully? And then on server, it's going to pass it through to the server. And on server side, 00:06:15.860 |
which is still very important, you're going to have to make sure if it's been validated correctly. 00:06:20.820 |
And also, of course, on server side, we have to implement the metadata, which I previously just 00:06:26.260 |
talked about. So, again, a lot of enhancements now moving forward with the new spec. 00:06:33.140 |
Saving effort. Saving keystrokes. As developers, they don't need to write a lot of security code 00:06:38.500 |
because, again, the risk is higher. Exactly. If you're not a security expert and you start 00:06:42.500 |
implementing security code, what are the chances that you're going to get it right on the first shot? 00:06:46.420 |
A hundred percent. And you can start relying on these off-shelf identity providers like Microsoft, 00:06:51.780 |
Entra, Okta. So, all of these things. And you, it's just going to be less work for the developer. But, 00:06:57.060 |
yeah. How does it work in practice? Yeah. So, let's talk about, we talked about the new spec. And for 00:07:01.300 |
folks that might be a little confused, like what the heck is the new spec and how exactly it works? 00:07:05.060 |
So, in a new spec, there's a very clear separation of interaction between the MCP client, MCP server, 00:07:12.980 |
and the authorization server. So, in this case, what happens is your MCP client, like, let's say, 00:07:17.860 |
Claude Desktop, is going to request data from the MCP server. The MCP server, at that point, 00:07:22.900 |
because the MCP client doesn't have any user context yet, is going to respond back with a standard 00:07:27.540 |
HTTP 401 saying, "I have no idea who you are, and my server is protected." But, here's a pointer to 00:07:34.020 |
something that we call the PRM, the Protected Resource Metadata Document, that's embedded in one of the 00:07:39.220 |
headers, in dot and dot authenticate, that's going to say, "But, you can go here and learn more about 00:07:43.620 |
how to authorize against me, the MCP server." So, the MCP client, again, Claude Desktop, or VS Code, or any other 00:07:51.380 |
variation, will then talk to the, take the PRM, extract from that PRM information about what 00:07:59.300 |
authorization server it's using, whether it's Okta, Auth0, Entra, Keycloak, it doesn't really matter, 00:08:04.740 |
and is going to then, again, I abstracted this out in a very simplistic way, step four, complete flow, 00:08:09.780 |
but basically, the client is going to do the whole OAuth discovery, step by step, go through the dance, 00:08:15.620 |
get the token, and then, it's going to request data with a token from the server, and the server's 00:08:20.660 |
going to return it back. The client is responsible for completing this entire OAuth dance, where the 00:08:25.540 |
server now doesn't actually need to manage tokens, you only need to make sure that you're validating them. 00:08:30.100 |
I call out this thing called the PRM, and the PRM is something hosted by the MCP server, that is 00:08:36.420 |
essentially a JSON document, there's variation, it could be a JSON web token, but for the purpose of this 00:08:42.900 |
conversation, it's basically a JSON document that says, hey, I am this resource, I am this server, 00:08:49.060 |
and by the way, my authorization servers are the following, and you can give a list of servers, 00:08:53.380 |
again, specify things like bare method supported, as well as scopes, so the client, when it gets this 00:08:59.780 |
document, knows exactly how to bootstrap the end-to-end authorization flow with OAuth, again, 00:09:05.540 |
standard OAuth, you can use, again, off-the-shelf libraries for doing this. 00:09:10.660 |
Let's eat an action in C#, and because we're Microsoft, of course, it's going to be C#, 00:09:14.500 |
you know, so, this is currently in a, in a pull request for the C#, for the official MCP C# SDK, 00:09:24.260 |
by the way, to show you just how easy it is to set up an MCP server that is protected by an OAuth 00:09:30.900 |
provider, by an identity provider, I have essentially an ASP.NET Core application, it could work for any other 00:09:37.380 |
application, but what I'm doing is, all I'm configuring is add authentication, to make sure that 00:09:42.900 |
I'm actually adding auth to my server, I'm saying that it's using the MCP auth scheme, I'm adding some 00:09:48.980 |
validation logic that, again, is built into the framework, oh, no, I see, I see what the problem is, 00:09:58.260 |
I see, great call out, yeah, see, it's a fantastic demo, I'm going to duplicate my screen, that's, 00:10:06.340 |
that's, I think, what it's going to do, there we go, all right, so, say it again, uh, I'm adding 00:10:12.580 |
authentication, I'm adding MCP authentication defaults here, because it's all baked into the framework, 00:10:18.420 |
I'm adding some logic to validate the JOT, the token, and this is, again, standard embedded into 00:10:23.780 |
the framework components, uh, I have some metadata events that are relevant here, and then I'm saying 00:10:29.300 |
add MCP, and within that MCP, I'm saying that I'm adding some PRM metadata that I just talked about, 00:10:36.020 |
which is my server supporting header auth, and because I'm using EntraID, it can be, again, Okta, Auth0, 00:10:42.660 |
I'll specify the metadata, and that's it, and then I'll say use authentication, use authorization, 00:10:47.860 |
there's a lot of boilerplate code here, because it's an MCP server that uses, again, the stock SDK, 00:10:52.980 |
but the auth, that's the complexity, that's all, I needed to add this metadata, and that's kind of it, 00:10:57.380 |
when I start the server, so it's going to run locally, let's take this on, and what I'm going to do 00:11:05.620 |
now is I'm going to go to the browser here, I'm going to refresh this, and you'll notice that this is 00:11:11.860 |
the PRM that I talked about, I have a local host server, it says my authorization server is 00:11:17.620 |
login to microsoftonline.com, because I'm using EntraID, again, it could be any of them, I define 00:11:22.740 |
the scopes, and I say that it's using headers, that's it, that's what the server says to the client 00:11:27.940 |
in terms of auth, now, I have another instance here, that is the client, and the client definition 00:11:33.380 |
with the C# SDK, again, it's super, super simplistic, what I have here is, I'm essentially on the client side, 00:11:39.940 |
saying that I'm using a generic auth provider, there's nothing Microsoft specific here, it's 00:11:44.900 |
generic auth, I'm specifying the client ID for my MCP client, some scopes, and that's kind of it, 00:11:52.260 |
the rest is standard boilerplate for transport setup, as you would with any other client, 00:11:56.740 |
this client is already set up to connect to my test server, my local server, so if I run this, 00:12:04.500 |
let's see, is it going to connect, it's going to go ahead and discover the metadata document, 00:12:10.500 |
it actually went really fast, because I'm already authorized here, perfect demo, but behind the scenes 00:12:16.180 |
that actually what it did is, it did request the token from EntraID, it verified, it sent the token 00:12:22.420 |
to the server, the server verified it, and then invoked a tool that I had on the MCP server, and said that 00:12:27.700 |
there's no alerts, it's a simple weather example, it's nothing too complex here, but the end-to-end 00:12:33.300 |
developer experience here is extremely simple, like we worked very, very hard to not expose any of the 00:12:37.940 |
security intricacies to developers, that's all you do, so C# SDK, it's there, it's in a PR, it's going to be 00:12:45.540 |
in production very, very soon, so Julia, do you want to talk about VS Code and API management? 00:12:51.700 |
Yeah, totally, so okay, we saw this working locally now, we've used the libraries, so what 00:12:56.340 |
if you now want to take it to production? I might be biased because I'm part of the Azure API management 00:13:01.220 |
team, but I always recommend putting a gateway in between to make it more secure, and actually use 00:13:06.740 |
it to protect and secure your remote MCP servers, and so what I'm going to show you today is, 00:13:14.260 |
for this, we have a public available GitHub repository out there, and it's going to help 00:13:19.060 |
you, and it's going to spin up an example, and in our example, let's scroll down and go to the overview 00:13:24.900 |
here, and we're going to use Azure API management in the middle to do and help with authentication, 00:13:32.020 |
and the beauty about this example is, it only uses an ACD app, so we wanted to give developers, 00:13:39.060 |
especially, we've heard a lot of complaints about sometimes it takes some time, so we wanted to go 00:13:43.860 |
super fast, so with an ACD app, it will spin up all of the resources. So while I'm doing this, 00:13:48.980 |
and everything is going to be deploying here, let's talk a little bit about what is going to be set up 00:13:54.340 |
under the hood, and what's going to help make our remote MCP server more secure. So as I've mentioned, 00:14:01.460 |
Azure API management as your remote MCP proxy, and we are hosting it, our remote MCP server on Azure, 00:14:09.060 |
where we have three tools implemented. We want to make sure the connection here is secured, 00:14:13.700 |
and that's where we're going to use the gateway in between to help us do the dance, 00:14:18.820 |
and for this, we have an OAuth API that's going to help the play between our identity provider. In our 00:14:24.980 |
case, it's Microsoft EnterID. That's going to help essentially with the login and also the content flow 00:14:31.220 |
here to truly make sure that the connection here is secured to our backend, to our remote MCP server, 00:14:38.580 |
right? All right. So let's see how the deployment of my resources are looking. I told you it truly only 00:14:46.820 |
takes maybe five minutes max. It does spin a bunch of other things like an app service plan, log analytics, 00:14:54.180 |
to make sure that this is truly production ready, and it gives you a way to monitor and also analyze and log it. 00:15:01.860 |
So I'm going to copy my endpoint here. Before I'm going to do something with it, let's just check on Azure API 00:15:06.980 |
management side if all of the things have been implemented correctly, because what we should see 00:15:12.020 |
now in my APIs tab is we should see the endpoint to my remote MCP server, which is the MCP API, and also the 00:15:20.900 |
endpoint that's going to help us handle OAuth. And of course, while the spec evolves, we are also going to evolve the 00:15:27.540 |
samples and all of it that comes with it. So let's test it. I'm going to start MCP inspector here. 00:15:33.060 |
And let's open MCP inspector and provide the URL endpoint. So once I connect, now what I should see 00:15:42.820 |
is if I click the connect button here, this is going to pop up my content page, right? So it's going to 00:15:50.020 |
show the application name. It's going to make sure that I'm providing the right content. I'm going to allow 00:15:54.900 |
access to it. The first time I'm doing it, I also have to provide the right permissions. So I'm going 00:16:00.980 |
to accept this here. And once I'm back, now you can see I'm actually connected. And now I'm going to list 00:16:07.540 |
the tools. And as we talked about it, I currently just have three tools implemented here. All right. 00:16:14.180 |
VS Code. How does it work actually now in VS Code, for example? VS Code has MCP support as well. I'm just going to 00:16:20.580 |
provide the end point. And I'm going to find a name for my MCP remote server here, which let's call it AI 00:16:28.340 |
engineer. And it's going to add it to my user settings. And immediately it's going to start running it. And you 00:16:34.500 |
can see now in VS Code, because they now also support OAuth, they're going to pop up the window. They're going to 00:16:41.060 |
show a notification about, hey, let's authenticate towards it. I'm going to open it. And we're going to see the same 00:16:46.820 |
content page, right? This time with VS Code, different application name here, where we are again, 00:16:52.820 |
we're going to provide our M content, we're going to allow it. And now back, I'm authorized, I'm allowed 00:16:58.900 |
to use it in VS Code Insider. So once that's done, you saw now I have access to the three tools here in 00:17:06.260 |
VS Code. So let's test this, what it's going to do in Copilot Studio, GitHub Copilot. And so first, 00:17:14.980 |
I'm going to select and see, perfect, it was able to select my tools, and it's able to detect these. 00:17:22.580 |
And now once I start interacting, I have my text prompt here, and just kind of do the hello worlds, 00:17:29.140 |
and very simple example, and GitHub Copilot is now going to run it, it's going to detect the tool, 00:17:35.860 |
because I've been authenticated, it's going to run it, and hopefully successfully, perfect. And now we can 00:17:42.740 |
also double check. So if I open the tool calling here, I can see that the output of Hello, I'm an MCP 00:17:48.340 |
tool has been successfully provided. And something new, and I know the world is spinning very, very fast. So 00:17:57.540 |
as being part of Azure API management, we want to make it easier as well. So we know the similarities 00:18:04.100 |
between MCP servers and APIs. So one of the latest announcements that we have done is, you can now 00:18:10.020 |
also start transforming your REST APIs into remote MCP servers using our tools, using our platform here. And 00:18:18.260 |
you can configure your already existing endpoint, REST endpoints, into tools that you want to expose 00:18:25.700 |
to get like the VS code. And we're going to do this all with our platform, because we want to make it 00:18:32.260 |
easier for enterprise customers, but also developers to get started with this. So here in my use case, 00:18:38.020 |
I just copied the endpoint that was provided by me by API management, I'm going to hit start and running. 00:18:44.660 |
And of course, always make sure to implement auth. And that's the beauty about our platform. You can use it 00:18:52.180 |
for securing it, but also for hosting in this case. And now I'm able to also just immediately call it. 00:18:58.420 |
And let's just check if GitHub Copilot was able to detect the MCP server here. Perfect. You can see it 00:19:05.620 |
right under my service AI engineering one. I'm seeing the three tools that is all based on my REST API 00:19:12.660 |
in VS code. And now I'm ready to go and interact with it in VS code right here. With this being said, 00:19:20.820 |
lots of things are changing in this space. Any good words for the people out there who want to 00:19:28.340 |
get started with remote MCP server then? Yeah. So there's links on the screen that you can go to. 00:19:33.620 |
These are the specification documents that you can learn more about how MCP auth works. We also have 00:19:38.740 |
a document that we partnered with Anthropic on. It's called security best practices that outline 00:19:43.060 |
what are the best practices you should be adopting in your MCP service. You want to make sure that you're 00:19:47.380 |
not pawned. Very, very important. And then I'll also mention that starting with VS code insiders, 00:19:53.140 |
we do support the new authorization spec. So you should check it out, download it, install it, give us your 00:19:57.540 |
feedback. And if anything doesn't work, we have Harold at this conference who can help you debug it. 00:20:03.060 |
So thank you, folks. It's great. Stop by at the booth.