Hi, in this video I'm going to go into more details on the Model Context Protocol,
and in this video specifically about dynamic on-the-fly tools.
So let's jump into some code where we don't have dynamic tools so far,
but we'll put a few things in so they become dynamic.
So in this sample, which is the remote server dynamic tools down here,
I just have a normal MCP server, so we have the builder.addMCPServer
with HTTP to port, so it's a remote server with tools from assembly,
and then we map MCP.
So the absolute minimum you can do, and in here I have three tools,
one called tool1, one called tool2, and admin tool1.
And I've just put in the names in some constants in a file here,
and I put in which of the tools are admin tools.
So if I connect to this, for example via Insomnia, and do the connection,
we can see that we get three tools back, so I can call all three tools,
and when I call tool1, I get a tool1 output, tool2, and get a tool2 output,
and admin tool number 3.
So that's good and well, but in order for us to say we might want to have
certain users to have just tool number 1 and 2, the normal tools,
and a user with more privileges to have the admin tools as well.
So how do we go about doing that?
Well, the first thing we need to do is, of course,
we need to have some kind of authentication.
So in here, we normally put in the builders here, add endpoint filters,
or some of the other ways that you have seen in the previous videos
about authentication.
But in this case, I'm just going to put in an endpoint filter
that have two valid keys, either a normal API key,
which is just normal password in real life,
it would be some kind of grid or something, and an admin key.
And if none of the two are the keys, we just get an unauthorized,
else we get access to the MCP server.
So if we go restart the code, now if we look at Auth,
and I can see we use the normal password, we are logged in,
but we can still see the admin tool, we haven't done anything about that yet.
Or if we take the admin password, we of course also can log in,
and in this case we should of course get all the tools,
but if we put something in that is not correct,
we get an error message, unauthorized here.
So authentication is up and running in a simple manner.
But now we need to take that some of the tools should be there,
and some of them should not be there.
It is possible to use the authentication attribute
if you use OAuth and stuff like that,
but in my case I want to show you that you can control
every single little thing about what goes into the tools
by adding some extra things up here.
Because we can put in something called request filters.
And let me just grab the first request filter that we are going to put in,
and then we will talk about it.
So I just add one more with here.
So I say I want request filters, meaning I want to filter
what happens in the MCP server.
And what I can do, I have a filter builder here,
and I can add various filters for listing of tools,
calling of tools, similar for resources,
and the various things MCP servers can do.
In this case we are only going to do anything with tools.
So tools is essentially two things in an MCP server.
It's the listing of the tools, and it's the calling of the tools.
So what we're doing here is we're going to set a filter
on the listing of the tools.
Because when we go here and log in with a normal user,
we don't want to see this extra tool.
That would only confuse the AI using the MCP server.
Hey, there's a tool, but it's not allowed to call it.
So we want to get rid of that tool on the fly,
and right now we can't because we haven't put anything in.
But what we do is we get a normal next pattern,
the middleware system of .NET,
and whenever we call this next, we actually get all the tools back.
The best way to see this is probably to set some breakpoints.
But before we go through it, let me talk about the code.
So this will actually give us all the tools back
that are registered in this system,
meaning from this with two tools from assembly.
But we need to check what is the user who are actually logged in.
And in order to do that, we use the HTTP context accessor.
Because on the request, we can grab the iTech context accessor
and get something about what the call has been about.
So in our case, we will use the HTTP context,
get the request, and get the header.
Very similar to what we do down here,
only here it's the real context already.
Here we need this access contester before.
And then if we get an API key,
which we should, else we shouldn't have come here this far,
but just the case, and if it's the admin key,
then we just give all the tools back.
But if it's not the admin key, we want to go in and say
all the tools that we have in the result,
meaning all the ones that was found,
only give back the ones that is not contained in the admin tools,
meaning give back this tool, this tool, but not this tool.
And again, this is just a shop.
You could have that certain tools were only available in the start of the month
or whatever you would figure out what to do.
This is a bit cumbersome because it's just authentication,
but you can do whatever you want in these add list filters.
So if we start the code again,
try to do a connection to this one,
and we can see now with a normal password,
we will hit this filter on the fly.
Because it doesn't run at startup, it runs on the fly
because you might need to go down to a database
to check what is the user's permissions and stuff like that.
So everything can be done in here.
So we get our results, and our result makes sense.
We have our three tools, normal and admin tools.
Then we grab the context assessor and the API key.
So we can see that our API key was the normal password,
and since that is not the admin password,
we are sent down here where we actually just take our tools,
but only get the ones that are not admin tools, meaning two tools.
And if we do this, we can now see that I've logged in,
but only with those two tools.
Let's get rid of the breakpoints,
and try to log in as the admin user instead.
If we do that, we get the third tool back.
Because in that case, we hit this if statement, it was true,
so we just gave all the tools back.
So some of you might think now, okay, now we're done, it's okay.
But actually, this is just the listing of the tools.
And most MCP servers would never be able to call a tool that hasn't listed,
but you can technically do it if you begin to write code.
So this is not enough to secure this system.
We need to make sure that this tool cannot be called,
even if it's not even listed in the system.
So for that, we need one more builder.
So let me bring in the last filter we need.
And that should be in here.
So we also add a filter for the add tool call filter.
And in this case, we just say if the tool that we call inside MCP,
they are called match primitives,
this is the common word for resources, tools, and so on.
So if it's an admin tool that we're calling,
then we need to double-check that we can actually allow to call it,
else we can just do the normal await next.
And in here, we do exactly the same,
we just get what the given API key was for the call,
and check if we are admin.
And if we are not admin, we throw an exception
that we are not allowed to call this tool.
So right now, I can't really show you this,
unless I actually take away the filter,
because Insomnia that I use for the testing of the MCPs
will not be able to.
But if I do it like this, and add a breakpoint here,
we can now go in, disconnect, log in as a normal user.
And a normal user in this case will get all tools,
because we just removed the list part filtering.
So we could have access to this tool,
and when we call it, again, the filters will be hit.
This tool we can see is the admin tool I called.
I will check, I put in the normal password.
Am I an admin? No.
Then we are not allowed to call the tool.
Meaning, back here, we get we are not allowed to call that tool.
If I put in the admin instead,
and call the tool,
we see the admin password, we are admin, so we allow it.
And of course, if we just call one of the normal tools,
we will not even go into this section where we check,
because it's just one of the tools everyone is allowed to call.
So, in summary, if we put this back in,
this is how you will see the sample up in GitHub.
We add this request filters, and again, we can do all kinds of things here.
We could make a tool only visible on Fridays for some reason,
or whatever we want to do, we can go down to a database,
check if we have access or not, and so on and so forth.
And if we do that, and we then call our tool,
if there's a way we get around this, we still lock the user.
And in order to get authentication in the first place,
we just add the endpoint filter,
or one of the other ones from the previous videos
on how to do authentication for an MCP.
So, this is how you can make dynamic tool calls,
and on the fly say, some users have these tools,
some users have these tools, depending on what they want.
Because now I'm doing it with a user that needs to have various credentials,
but it could also be that you want to have an MCP server
where the user in their configuration of the MCP server
should be able to say, I want these tools, I don't want these tools,
I'm this type of user, only give these tools back.
So, that's the reason why, when we have normal C Sharp code here,
where we can do anything, we have full control over what tools to go.
So, that's everything. See you on the next one.