Loading video player...
AI models can be incredibly impressive,
but on their own, they can't actually do
anything.
MCP changes that and provides tools to
AI agents so that they can actually do
something useful. So, that's right. In
today's video, we're going to build our
very own MCP server.
Well, hello wherever you are. Whenever
you are, where am I? Canterbury in the
UK. And when is it? It's January 2026.
So, first of all, happy new year to you
all. Hope you had a good one. And more
importantly, thank you very much for
joining me here today. So yeah, as I
said in the introduction, we're going to
be looking at MCP today. Specifically,
we're going to build our own MCP server
and solve for I think quite an
interesting use case. So before we do
that, just the usual stuff. If you
haven't done so already, think about
subscribing to the channel. If you don't
want to do that, that's fine, but at
least give the video a like. That just
helps me out. Anyway, enough of all that
stuff. Let's now take a look at what
we're going to be doing in today's
video.
So yes, that's right. We are building an
MCP server with the .NET 10 SDK today.
And this is what we're going to cover in
the video. We're actually going to look
at the use case first because there's
actually a few concepts here that if you
jump straight into the theory without
the benefit of actually seeing a use
case, seeing it working. Uh might be a
little abstract and it's also hopefully
going to peique your interest as well
and you can decide whether this video is
something you want to progress watching
or not. Hopefully, you do. Once we've
done the demo, we'll then look at the
theory, the why, and the what and the
why of MCP, what it is, and why it
exists. Uh, we're going to keep that
relatively shortish, but enough so that
you've got enough background as to what
it is and the theory and stuff like
that. And then, of course, the main part
of today's video is a full step-by-step
build in building your MCP server. And
as usual, as always, the complete code
is available for you on GitHub as well
with a link in the description to the
repository. All right, so I think with
that, let's take a look at our use case.
So today's use case is really centered
around software development and software
developers, which I'm sure most of you
are very familiar with if you're
watching this video. And what you can
see on screen here is a tool I'm sure
most of you are very familiar with. Yes,
of course it is VS Code and we actually
have the project we are going to be
creating loaded into VS Code. What you
may be familiar with, may not be
familiar with or may be familiar with,
I'm not sure, is this rightand panel
here. This is actually GitHub Copilot.
And we're going to be using GitHub
Copilot as our AI application for one of
a better term in today's video. And at
the top of the video today, I was saying
models, AI models are very powerful.
Yes, indeed they are. And co-pilot has
access to a number of different models.
You can see here we get GPT models from
OpenAI. We've got all the Claude models
from Entropic. We get the Google models
as well. And of course, I'm not telling
you how to chat [snorts] with an AI LLM,
but you know, you can ask this co-pilot
tool to check your code, write some
code, all that kind of stuff. But what
we're going to use it for today is we're
going to build an MCP server and that
will allow this particular AI app to
talk to our MCP server to do something
else outside of VS Code and automate
some other parts of our developer
workflow. That's basically the entire
value proposition. So what part of the
workflow are we going to be automating?
Well, it's a mandatory one for us
developers. Maybe not the most
interesting one, but certainly a
mandatory one. And that is of course
working off of a project, okay? Working
off of features or creating bugs or
writing your own features, all that kind
of stuff. Now, the tool I've chosen
today to manifest that use case is this
one here on screen. It's called Plane.
It's free to sign up for. I actually use
it personally to manage my channel and
other projects as well. And it's a
really nice little project management
tool. Very lightweight, similarish to
Jira, but again lightweight. Jira can
get quite bloated as I'm sure most of
you have probably used Jira. This is
similar but a lot uh nicer and a lot
lighter weight. By the way, I'm not
affiliated with plane. I'm not trying to
get I'm not trying to sell it to you or
get you to use it. But for today's
video, I recommend you sign up for a
free account and you can follow along.
So, not rocket science. I've got a
project here MCP demo. I can create work
items here. So, we just say test and
description and you obviously have
statuses. So default is backlog.
Save that off. And we have our new work
item. Okay. So what we're really going
to do is we're going to build an MCP
server to talk to the plain REST API and
allow it to create work items in tandem
with a model. Okay. So let's give it a
bit of a go over here. Let's imagine
we're working on some project. Let's say
an API project, one of my favorites, and
we want to implement uh rate limiting.
Ordinarily, you'd have to minimize this.
You'd have to come into plane, you'd
have to open that up, you'd have to type
in whatever you wanted to do. But what
we're going to do is we're going to
stream all streamline all that. We're
just going to ask uh co-pilot to create
that ticket for us. So we'll say can you
create
uh
a new feature
in plane
put it into
and describe
in some detail
a rate limiting
feature
for our API.
Okay. And let's see what it does. Now,
I've obviously configured the MCP server
that we are going to be using.
Our copilot instance is going through
various steps here. And we'll go through
all this in the 3D section in just a
bit. Now, you can see the first thing
it's actually trying to do is get all
possible statuses that tickets can go
into, work items can go into. And that
function that you can see on screen
there's actually a tool that's provided
by our MCP server to whoever wants to
use it. In this case, Copilot. So it's
got a list of statuses and it's then
called the second tool. It's just
scrolled off the screen there. Create
work item and looks like it's worked
successfully. So if we come over to
plane again,
do a quick refresh,
you should see a new ticket in there,
API root limiting feature. And you can
see it's actually put in quite a bit
narrative. Now obviously, and that this
is my main caveat around I love AI
stuff. I think it's brilliant, but you
have to validate it. You have to read
through that and make sure it makes
sense. Okay, 100%. And that is what I
would do here. I wouldn't just go, "All
right, that's great." I'd read that and
made sure it was correct. Okay? So, not
going to do that here. The point is it's
created the work item in an external
system. It's done something useful
hopefully and created that for us. And
you know, I'm sure most of the
developers I've met hated writing uh
tickets and narratives and stuff around
stuff like that. So, you know, could be
a useful use case in that respect. And
you could go further and you could have
templated prompts and stuff like that
and get it to have acceptance criteria.
Anyway, going off of topic a bit, that's
what we're doing in today's video. We're
building an MCP server to enable this
feature set. So, let's have a look at
the what and why of MCP next.
So just before we move on to looking at
our theory, let's have a look at the
ingredients you need if you want to
follow along today. So first of all,
then the .NET 10 SDK.
Next up, I'll be using a tool called MCP
Inspector to actually test out the MCP
server. It allows you to directly call
the tools that your server exposes. And
in order to run MCP inspector, you do
need NodeJS. So you will need to install
that if you want to use that tool.
Again, this is just for today's use
case, of course, but a free account on
plane. If you don't want to use plane,
that's fine. You can you can automate
anything you want really. But again, if
you want to follow along, this is what
we're using. VS Code as our text editor
with the GitHub Copilot extension
installed and there is a free sign up
for that as well that you can use. So,
moving on to our theory now. What is
MCP? Well, the obligatory what does that
stand for? It stands for model context
protocol. We'll talk a bit more about
that as we move through the theory. It
was created by Anthropic and that's the
company responsible for Claude Code and
Claude Desktop and all those products.
Why did they create it? Well, really is
a convenient way to expose external
tools to AI or to an LLM. And I I I
think of MCP servers really just as
wrappers around other systems. And it
doesn't have to be REST APIs. We're
using a REST API today from Plane. It
could be anything. It could be calling
SQL statements. It could be GraphQL
APIs, whatever, some some external
system that you can program against.
That's what MCPs wrap themselves around.
And it's a convenient way to do that.
Why MCP? So, you're probably getting the
picture in your head. We've already sort
of covered this. LMS are trained on
static data sets that are relatively
static. They can't actually do anything
externally. They can do some stuff,
which we'll cover in just a second, but
they couldn't natively, for example,
create work items in our backlog. They
couldn't do that on their own. And even
things like, you know, asking it what's
the time right now or what's today's
weather? It would need to go and do
something else in order to answer that
question. And in terms of asking a
question like what's today's weather,
probably would give you an answer, but
it would go out and do that via
something like a web search. And you
don't really have control over what
search source that's using really. Okay.
So, you're kind of limited in that
respect. It's not particularly targeted.
Okay. Okay. So there are some ways
around this using only static data sets
but um not targeted at all. So what MCP
this is the whole value proposition of
what MCP allows you to do is provide a
specific set of targeted tools that an
LM can use to do some useful thing that
you need it to do such as create a work
item in my backlog.
So architectural concepts let's look at
these. So MCPhost, that is the
application. And I'm just going to refer
to this throughout the rest of the deck
as VS Code. Okay, VS Code is not by
itself an AI app or or an LLM. I'm just
going to use VS Code is the term that
refers to that wrapping around all of
those things. So in our uh use case
today, VS Code is the AI app that
manages an MCP client. And the client,
no surprise, this maintains a connection
to an MCP server. And here's this word
again, context. it obtains context for
the host to use. So again, if you've
done any prompt engineering or anything
like that, LLM love context. The more
context you can provide to an LLM about
what you're trying to do, the better
your response is going to be. And that's
what an MCP server is basically all
about. Providing tools, yes, but
providing good context around what those
tools can do for the LLM. It's critical
importance. You'll see when we come on
to writing it how that manifests.
And then the third sort of architectural
concept is of course the server. This is
what we're building today. And as it
says there, it provides context to
clients via exposed tools.
How does that look graphically? Well,
you have your host, your AI app with a
client, your MCP server, and then some
kind of connection between the two. How
does that look today? Well, today VS
Code with Copilot is our host app with
its own client dedicated connection to
the server that we're going to build.
And I'm just calling it the plain MCP
server. And of course, that talks to the
plain rest API. And again, that bottom
box there, you could swap that out for
anything else. Doesn't have to be an
API, could be anything.
This this next slide, I think, is really
useful to understand the high level
interaction flow of what's actually
going on behind the scenes. And I
actually somewhat simplified this, but
it's I think it's detailed enough for it
to be useful. So user asks a question to
the LLM to VS code in this case. Can you
create me a backlog item for a feature
blah blah blah. What the LLM will do is
we'll request a list of tools that has
available to it. Not just from our
server but from any server that has
access to and the MCP client will make a
list tools request to our MCP server.
Get a list of tools response back and
cascade that back to VS Code the LLM.
Now, those tools have context and again
I've said that already. You'll see what
that looks like when we come on to doing
it. Based on that context, based on your
query, the LLM will do some thinking.
And in this case, it determines that
yeah, one of the tools that's been
provided to it seems like a useful one
that we should use to service this
request. And I'm going to select the
create work item tool. It's then a case
of making a request to the client to run
that tool. that goes down to the server
and then the server that we're going to
build will call the plain rest API to
create a work item. And this step here,
create work item, that's just a standard
REST API call. Okay, it's nothing
special about it. It's just a standard
API call as is the response. That
response then gets cascaded back down to
the LLM. Again, it's providing context
as well to the overall interaction and
will formulate a response back to us as
the consumer of that request.
One thing I've not really put on here is
that obviously we asked it to create a
work item. It also provided some
narrative as part of that work item. So
when we created a work item, it gives it
gave it a description and a title and
all that stuff. And as part of that
creation, that's where the LLM is
bringing value. And it could potentially
even look to our code base that we're
working from to inject even more
relevant values to our narrative. Okay,
not got too much more to go before we
start the build, I promise you. But this
stuff is important to understand. So
layers, there are two main layers we
need to consider when thinking about
MCP. The data layer, this defines the
JSON protocol that defines the
interactions between the client and the
server. And going back to here, this
list tools step here is basically a JSON
spec, part of the JSON spec that allows
clients to talk and list tools that are
available from servers. That's just one
example. The data layer also uh defines
the core primitives that clients and
servers can offer each other. We're only
going to be working with one today and
that's tools. Okay, that's one of the
primitives. We'll cover the other two in
in just a minute, but we're only working
with tools today. The second layer to
consider is the transport layer. This is
slightly further down the stack if you
want to think of it in that way. And
this is really defining the
communication mechanism and channels
that enable the exchange of data between
client and server. And there are two.
First one is standard input output or
local and this just uses standard input
output streams for process communication
between two local processes on the same
machine. Second one is streamable HTTP
or remote and that means the client and
server can be remote from each other not
on the same machine. Now we're going to
implement the first one local. Okay. Now
you might go that sounds a bit limiting.
Uh I think I'd rather have a [snorts]
remote version. Can we not do that
instead? Doesn't really matter to be
honest with you because we come and have
a look at how that's architected. The
data layer sits on top of this transport
layer. And this is what we are going to
be doing today. you can swap out one
transport layer for the other. And I'll
actually put the code up for an
equivalent streamable HTTP server uh in
in the GitHub repository. So the actual
uh server itself doesn't really need to
change that much. Certainly not the way
we're building it today. Doesn't need to
change that much. So you're not really
missing out on anything too much. You're
just swapping out the transport layer
basically. And as it says there, the
transport layer abstracts communication
from the data layer, protocol layer.
Okay, enabling the same JSON messaging
across different transport mechanisms.
All right,
mentioned primitives very briefly. We'll
talk about them here. Uh tools, you're
probably familiar with what this is now.
Executable functions that AI apps can
invoke to perform actions.
Resources are really a way of providing
additional contextual information which
can be very useful. Of course, we're
just not doing that today. and prompts.
These are templates that we could reuse
to help structure interactions. And
again, if you maybe heard of things of
prompt engineering and stuff like that,
it's incredibly useful thing that you
should be doing when you're working with
AI. We're just not uh doing it today.
It's slightly outside the scope of what
we're looking at doing. All right, so
that's enough the enough talking. Well,
I'm going to keep talking obviously, but
let's time to uh start coding. So, let's
go. Right, so let's get going and
scaffold up our project. So over in your
working directory, I'm using PowerShell
today. Um but same rules apply for using
something else. Let's just check that
we've got net install. So net- version
should [clears throat] have 10. And then
if you want to use MCP inspector, you
need node installed. So this is
optional. I'll just check that I've got
node installed. Indeed I do. So we'll
just scaffold up the app. So quick.net
new console
uh dash n and then name your project.
I'm going to call it plain MCP server
directory listing to make sure it's
created. Indeed, it was. And it will
change into that directory. And we'll
open VS Code.
So, that's opened on my other screen.
Let me just pull that over so you can
see what I'm doing. Wouldn't be terribly
useful if I didn't do that. Let [snorts]
me just make it a bit bigger.
And we'll just say yes to this. All that
does is generate the VS Code folder. And
we can get rid of copilot for the
moment. We don't need that. And we'll
get rid of our welcome message. So the
first thing we're going to do is add
some package references to a project. So
I'm going to open CS Project just so I
can see them being added in case I
mistype. And it's a net add package.
And we'll say Microsoft
Microsoft extensions. Let's do hosting
first of all. That just allows us to set
up our application host and wire up
things like dependency injection which
we'll be using and config and stuff like
that. Uh clear the screen there just to
give us a bit of breathing space. Next
one is HTTP.
We'll be using a HTTP client factory
with our API client. The client we're
going to use to talk to plane. So hence
we need this one. And then you did see
it in in the sort of CLI memory. But I'm
just going to go through this a bit more
step by step. If we bring over uh Nougat
package manager,
we'll search for the model context
protocol package
that we need. So
go. It's this one here. And the reason
I'm doing all this is because it at the
time of making this video, it's still in
preview as you can see here. So if you
just type this first bit and no version
number, it won't by default install it
for you. So you've got to specify this
full preview versioning. Now by the time
you come to watch this video, the the
preview version may have changed. Hence
why you should probably do this. Uh or
it may be released, in which case you
may not need to do this. You'll still
need to install the package obviously,
but you won't unless you want to. You
don't need to specify the version.
So we'll just put that in like that.
Fantastic. Okay, that's good. Now, while
we're still in CS Project, we're going
to have to add a little bit of uh
behavior to the way our explicit
behavior to the way our app settings
JSON file is treated. The reason I'm
doing this is because I ran into issues
with config being loaded, not when I ran
the MCP server, you know, locally and
using it as myself, but when something
like VS Code was attaching to it and
running it, I had some issues with
config not being loaded in from app
settings JSON. And the reason for that
was it wasn't resident in the out the
build output file build output directory
of the project and had all sorts of
issues with it. Was very frustrating.
Finally figured out what it was. So we
need to put this little bit of config in
here to make sure you don't have those
issues. So maybe do that is by defining
another item group.
I'm just going to copy that and close it
out with a back tick forward slash not
back tick. And then inside here, we're
going to say none. I'll explain what
this does in just a second. Let me just
uh type it out
update equals
app settings.json.
I'll close this out as well. For
slashnone.
I keep wanting to type node for some
reason. Muscle memory. And inside of
here, we're just basically going to say
what we want to do. So we'll say copy
to output
directory
and the behavior is preserve newest
and we will close that out. Now I don't
know if in the background you can hear
seagulls squawking. I can't do anything
about that unfortunately. I've tried to
soundproof my little shed that I'm
working from but they can be quite loud.
So apologies if you can hear them. They
can be quite annoying. Hopefully they
quieten down in a minute. Um, yeah. So,
basically what this does is it's just
saying that our app settings JSON file
should be copied to our bin folder every
time you build and copy the newest one
or if there's not one there, copy the
the one that we have. And that just
means when you'll see this when we come
to do it, when VS Code runs the MCP
server, it has access to that config. So
before we go any further with coding,
let's just get the config elements we
need to connect to the plane rest API.
So bringing plane onto the screen here.
I've logged in. You'll need to set up an
account obviously if you want to follow
along too. And the first thing we need
by way of config is something called the
workspace ID. And that's this thing here
for me, binary thistle. And you can even
see it in the URL up here. That will be
different for you, of course. Okay. And
the workspace is just the global
container for everything, projects and
all that kind of stuff. And just on the
structure of plane workspace is this top
level container. You then have multiple
projects with work items. So one of the
things we'll need in order to create
work items in a specific project is the
project ID. And we can only really get
that once we get the API up and running.
So we do need that as a config element,
but we'll just park that for the moment.
So going back to what we can get right
now, workspace ID. Next thing is an API
key. So, over in your profile, click
preferences, personal access token, add
a personal access token. I'll just say
live video demo.
And um my comment or description will be
delete
after use. So, by the time this video
goes out, it will be deleted. So, don't
try and use it. It won't work. Uh I've
just set it to never expires. You can
set an expiration date if you want.
generate token and we'll copy that off.
Fantastic. So, while that's in
clipboard, what I'm going to do now is
set this up in our application. So,
let's just move this off to the side.
And back in our project, let's bring up
our integrated terminal again. And we're
going to initialize user secrets. So,
net user- secrets in it. And that will
just put this new attribute in our
project file. This GID relates to a
folder that will get created on your
file system within your profile under
your profile so that nobody else can
access it unless of course they log in
with your account. And then within that
folder there's a JSON file where all the
user secrets will be placed. So let's
add a user secret for an API key. So,
net
user- secrets
set
and yeah, we'll call it plain API key
and then space double quotes. Paste in
your API key. Close off the double
quotes. Okay, so net user- secrets set
plain API key. Make sure you spell this
correctly or use that term. We'll be
using that later. and then the value.
And that should then successfully save
that off to our JSON file. The remainder
of our config elements we're going to
put into app settings JSON. So in the
root of a project, right click, new
file,
app settings.json.
So just standard JSON. So the first
thing we'll set up is the base URL.
And this is the base URL to the API. And
that's just https/
slash api.plain.so
forward slash.
Okay, next thing will be the workspace
ID.
So I'm just going to call that
workspace.
And then for me that's binary thistle.
Fantastic. And then the next thing I
sort of mentioned is the project ID. We
will place that into config, but we
don't have that yet. We'll get that in
just a minute when we test out our API
working and all that kind of stuff. So
we'll just say TBD for now and save that
off. Okay. So that's our config sources
configured. Um now what we'll need to do
is actually read in those configuration
sources into our application and uh yeah
we we can start using them. So we'll do
that next.
So to read in our config values let's
come over to program CS. Let's get rid
of the boilerplate code. There you
first thing we need to do is create an
application builder. So we'll say v
builder
equals
host
create application builder and we can
allow some args to get passed in if we
want
and all that does well say all that does
is very important allows us to use
access dependency injection and allows
us to access configuration. Now just on
the subject of configuration I've
already mentioned I had some issues with
that not when I just ran the application
the CLI here but when VS Code actually
needed to use it was having some issues
with the way it accessed app settings
JSON. So while this create application
builder sets up some default
configuration sources for us we need to
clear those out and rebuild them the way
that we want them. Okay otherwise you'll
get issues or I certainly get issues.
So, first thing we'll do is access the
builder, access configuration,
access the sources, and we'll say clear.
Now, we'll rebuild our stack. So,
builder configuration,
and let's set the base path to app
context
base directory.
Next, we'll add our app settings JSON
file. So add a JSON file. What one do we
want to add? App settings. Make sure you
spell it correctly.
JSON. And we'll say optional is false.
We need it. And reload
and change. We'll say false. There's no
point in setting that to true given the
way our config is read in.
We'll add user secrets as well
as we are using user secrets and we'll
specify the type of program. And
although we're not using it right now,
we may want to at some point add
environment variables. So we'll add
those in now. It doesn't doesn't hurt to
do that. And that's kind of built up our
config stack.
Next we want to do is actually read in
that config from our files and assign
them to some variables. So I'm just
going to say um
create the first one which is plain API
key and we'll make that equal to builder
configuration
will access the element from in this
case user secrets which was plain API
key. Now ensure you get this right.
Okay. So, whatever you however you set
this key here, plain API key, make sure
it's exactly identical here. Otherwise,
you'll have some issues.
I'm just going to copy this line and
paste it in again. And we'll read in the
other config elements that we've got.
Obviously, get some errors here
duplicating stuff. So, let's read in the
base URL next.
And that comes from app settings JSON.
Let's just remind ourselves what it was
called. I'm just going to copy that
and we'll assign it to that.
And then next one is workspace.
So let's call that workspace.
Make it equal to that key. And then
finally, it was project ID, which we'll
need to get. And we'll actually do that
next before we go any further. So back
in here, project ID. And it means we can
exercise and test our API as well.
Make sure that works.
And we'll just name that project ID.
We go. Now, really what we should do is
we need all of these elements in order
for our solution to work. So, we should
really throw some kind of error or
exception more correctly if these are
null. These haven't actually worked. So
it makes our code a little more verbose,
but I think we should do it just um just
to keep things um bit more correct. It
also means we can test that it's being
read incorrectly. So we'll say if string
is null or empty.
Let me try that again. If string is null
or empty, that's much better. And we'll
do the first one. Plain API key.
And we'll just throw
new
invalid operation exception. Yep. And we
give a meaningful message. We'll say
plain API key
is missing
config. Okay. And what we'll do is we'll
copy this and just repeat it for the
other three elements. And again, you
know, it does make the code a little
more uh verbose as it were, but it's
going to just help us out and make sure
everything's being read in correctly.
We're not going to get any weird
errors. So, let's see what was the next
one. Base URL.
Base URL. And we'll update this.
And this one next is workspace.
And we can put that in.
And we can actually then run this up
actually. So we shouldn't see anything
really because we're not doing anything.
But if you see some errors, then we know
that something has gone wrong. So it's
quite a good test at this point to make
sure things have been wired up
correctly. Okay. So save that off. uh
net run
and we get nothing which is what we
expect. Now if we had some kind of issue
let's maybe just double check that that
is working. Let's just take out the
project ID for now. Save that off. We'll
net run it again. We should get an
error. There we go. That's cool. So
plain API key is missing from config.
And I think um that doesn't look quite
right, does it? Let's put this back
first of all. Save that off. And I think
I didn't Yeah, I didn't update this one.
There we go.
So, we'll say project ID. Otherwise,
that could get quite confusing. So,
plain API key, base your workspace.
That's all looking good now. Fantastic.
So, yeah, what we'll do is now we'll
take a quick pause from coding. We'll uh
set up our API in a tool like Bruno or
Insomnia and make some test calls. Get
our project ID and uh we can put that in
our config and then we'll progress with
the build. So we'll do that next. So the
best place to look when you're making
API calls for the first time with any
system is in the API docs of course and
this is the API documentation site for
play and I'll link to this in the
description below. Now in terms of our
MCP server, what we're going to
implement is two tools. The first one
being listing all the states that are
available to create work items in. So
this just maps on to this endpoint here,
list states. And you can see it's just a
standard get request. It requires the
workplace slug. In my case, that's
binary thistle and also the project ID.
The second thing we're going to
implement in our MCP server is the main
thing really which is to create a work
item and that is a post request with a
very similar structured URL workplace
slug and project ID and of course
there's a a JSON payload where you can
supply all the things like description
and the name and the state which is
basically the ID of one of those states
returned from the previous call. This is
actually optional to be honest. If you
don't supply it, it will just get
created in a default state backlog I
think it is. Anyway, those are the two
things we're going to implement in our
MCP server. And therefore, it does
require a project ID which we don't yet
have. Where do we get that? Well, no
surprises. Under the project grouping,
we have this list all projects endpoint.
And all you need to supply there is the
workplace slug and as you can see from
this curl example, a header which is our
API key. Okay. And that's it really. So
I'm going to just use curl just to
exercise this endpoint. Make sure an API
key works and get a list of projects and
we can get the key for the project I'm
going to use. Now as I'm using
PowerShell, if I just try and paste this
straight into PowerShell, it will error
out because these line continuations
don't actually work uh in PowerShell
with doing the Linux uh shell, but not
PowerShell. So going to copy it. I'm
just going to open up a VS Code session
and I'll just take out those line
continuations. I'll just do a new file.
Paste that in like so. Let's make it a
bit bigger so you can see what I'm
doing.
And we'll take these out.
And we'll take this one out as well.
There we go. And all I need to do in
here is just replace workplace slug with
binary thistle in my case.
and then the API key. I'll just paste
that in here
like so. Okay, so that's our PowerShell
safe formatted curl command on one line.
Let's just copy all of that and uh bring
up a terminal.
Start it on my other screen and we'll
just paste that in and we'll see what we
get. Fantastic. So, that looks pretty
good. Um I can't personally read all
that. That's a bit too um
jumbled up for me. So, I'm going to copy
all of this and we'll just paste it into
a tool that I use all the time, JSON
editor online. So, another web browser.
Try that again. Another web browser
and JSON editor online
and copy all this. So you basically
paste in your big blob of JSON here.
Select tree on the second plane. Copy it
across. And you can see we got our
formatted JSON payload. So I am going to
create all my work items in the MCP demo
project. So this is the ID I want. If
you scroll down a bit further, you can
see I've got some other projects,
APIbook.net, all that kind of stuff. But
I want this one here. Copy.
All right. So that's the project ID we
need to inject into all our URLs. So if
we come back over to our project
over here, back in tap app settings
JSON, we can just paste that in here
like so. Okay. And so when we construct
our URLs in our app to make those other
calls, this is what we use. Now let's
just be double double sure and make a
call to the states endpoint which relies
upon this project ID. So let's just uh
cross all the T's and dot all the I's
and come back down to our state endpoint
list all states. This is the curl
request here. So let's just copy that.
Actually, we don't need to copy all
that. We'll just copy this bit here. I
think that's all we need. I think it's
the same as the other request we made.
So let's just do that and come back to
here.
Yep. You can even see it's got projects
in there. So, we don't even need to I
will just overwrite that because we
copied that and we just need to put the
project ID in. So, go back to our app
and copy that back out again.
Copy and back to our
instance of coral.
Paste that in. Okay. And let's copy all
of that.
and we'll paste that into our
PowerShell session. Run that. And that
again returns a much smaller JSON blob.
It looks pretty good to me. Uh we'll
just put it into power p PowerShell. Put
it into JSON editor online.
Copy it over. And yeah, you can see
we've got all our statuses backlog
uh to in progress. That looks pretty
good to me. We won't test out the create
one. I'm pretty confident everything
we've got there is working and really
what we needed there was the project ID.
So that's all looking pretty good. So
what we'll move on to doing next is
actually creating our plane API client
in our project so we can make these API
calls from our codebase. Do that next.
So back over in our project I'm going to
create a new file for our API service.
So right click in the root new file and
we'll call it plain API
service
CS
and it's just a public
class
called plane API
service.
Generate our constructor. So ct
tab and into our constructor we're going
to expect a few parameters. So as I've
said already we're going to use a client
factory a HTTP client factory to
generate our clients. It's just a more
efficient way of doing it.
So inject I HTTP client factory. We'll
call that HTTP
client factory. And we've got a few more
things to pass in here. So I'm just
going to take new lines for those just
to keep the code a bit more readable. So
the next thing we're going to expect is
a string representing our base URL.
Then a string representing our
workspace. And of course we pass all
these config values in when we use this
client. A string representing a project
ID
and a string representing our API key.
Okay. So they're getting injected in and
then we're going to assign these to
private readonly fields. So underscore
or private field http
client
factory equals http
client factory.
Don't worry, we'll clean up these errors
in just a minute. It's just because the
there's no read only field available
yet, but we'll do that in a second. Let
me just finish doing this stuff first.
base URL equals base URL.
Uh named that correctly. It was even
remotely correct, was it? So um next one
is the workspace underscoreworkspace
equals workspace
underscore project ID
equals project ID.
And just a word on AI, you know, if you
had uh code completions enabled when
using GitHub Copilot, this is exactly
the type of thing it would just do for
you. I would not usually uh these days
type stuff out like this. I would have
absolute AI assistance in doing this and
it just does it all for you and it just
saves so much time. But I don't want to
do that in this video. Want to do it
step by step. You might have a different
opinion. Maybe you'd rather I did that,
but I feel um yeah, doing it this way is
just kind of doing it fully step by
step. That's how I like to do it. But
again, yeah, I I would be definitely
leveraging AI completions here at this
point. Now, the only thing I want to do
actually, let's fix up the errors. So,
control and period in the offending
article and we want to generate a read
only field and it puts up here as you
can see. So we'll just do that for all
the remaining
issues and we just got all these private
read only fields created for us.
Fantastic. Now this is complaining. It's
probably because the name is not the
same as the the class or something.
Plain API service. Yeah, I've uh yeah,
I've named it wrongly. There we go.
API say case sensitive. All right. So
just make that same as the class name
and the yeah file name. Cool. Now
another thing I wanted to do was overlap
settings JSON. You can see that we have
this forward slash at the end of our
base URL. Now we could take that out. We
could leave it in. That's caused me so
many issues in other projects as well.
That little forward slash can be a real
offending article. So I'm going to leave
it there in our config. But in our class
here, I'm going to trim off the end. So
when we come to building out our full
request URL, we will put it in there.
Okay? So it just keeps it nice and
consistent and avoids any issues of it
needing to be in config or not being in
config, we will take care of it
programmatically. So the way we do that
is when we are doing the assignments for
our uh base URL, we will just trim off
trim the end. Okay? And maybe let's trim
the character forward slash character
off the end like that. Okay? Okay, that
means we have a nice consistent approach
to building our URL. Now, again, just
coming back to what we're doing here,
probably should have really qualified
that really qualified is this is nothing
to do with MCP or anything like that.
This is just a HTTP or an API client, a
HTT, a web API client. That's all it is.
And you could use something like this if
you're just calling any API. It's
nothing to do with MCP yet. Okay? So,
just being clear on that. And actually,
there's going to be more code in this
than there is in the MCP stuff, which is
quite annoying, but we need to do it.
It's critical to what we're building
out, but just wanted to call that out.
All right, so the first thing we'll
build out is the request to get all
state. So we'll say make it a public um
async
turns a task of type string which is
just a JSON response and we'll call this
get
project
state
async and capitalize the A.
Fantastic. And then the first thing
we'll do is create a HTTP client from
our factory. So
v htv client equals client_client
factory
create client. And this is just a more
efficient way of creating clients rather
than newing up a HTTP client.
So client will add some default headers
to it. Default requests headers. Yep.
Add.
And as you saw from our curl example, it
expects a header of X API key with a
value of whatever is contained in API
key.
Next, we'll build out our URL. So we'll
say var URL equals and we'll use string
interpolation. So dollar sign double
quotes base URL,
excuse me, underscore base URL.
And then we will put the forward slash
in because we've trimmed it off via our
constructor
B1. Now be really super careful
when you build out this URL because you
don't want to get it wrong. Other of
course there's going to be issues if you
do. But just be careful if you do have
any issues with your code. I would
definitely check this first.
Make sure you've not said project rather
than projects and things like that and
put in the project ID and then lastly
states
for slash end. So that should be correct
and we maybe want to just check it with
the API docs. So let's just bring this
on screen here
and I just want to double double check
because this is the kind of thing that
will uh trip you up and drive you crazy.
So base URL for slash API v1 workspaces.
Uh maybe make this a little bit smaller
just so we can see the full thing.
Make it a bit bigger.
I think I found something quite annoying
with these docs. There you go. You can't
actually see the end of it. So let's
just copy it and let's put it in a
comment underneath it. Maybe that would
be better.
Yeah, that's better states. So,
yep, that looks pretty good to me. Yeah.
Okay cool.
So, we'll take that out and then we'll
get a response back. So, new line
response
equals
waiting on our client.
And then we'll do a get async
passing over the URL.
And then a response we'll ensure a
success status code. That's a bit of a
mouthful.
And then we'll get the content from
that. So
our content equals await
on our response
content. And we will read
as string
string async. Make sure you select
string and not stream. I've done that
before as well and obviously get issues
with that. Return content. Okay,
so that's our first endpoint done. Let's
just push on and do our second endpoint
which is the create work item endpoint.
a little bit more complex because it's a
post request but not much more. So let's
go on and do that. So new line
public
async
and it returns a task of type string
and we'll call this create
work item
async.
And we do expect some parameters this
time. So string of name
and a string of comma there the
description
and let's just keep it consistent with
the API. We'll say description HTML
and string
or of the state ID.
Okay,
let's move this up so
it's front and center. Let's just copy
this. I don't like copying code, but you
know me, I I don't want to kind of waste
time either. So, let's copy these three
lines and we'll edit them as necessary.
So, client factory is before adding the
API key, which is cool. The URL is
exactly the same, but rather than
states, it is posting to work items.
And again, you know, we've not actually
called this endpoint yet, but you can
check out the API docs just to check
that this URL is in fact uh the correct
one that we want. Now, unlike the get
request, we have to construct a body.
So, we'll save our
request body. This is just the JSON
payload that we are passing in and make
that equal to a new object. And we'll
say name equals name
and then description
underscore
HTML equals
description HTML. And again check out
the API docs to get the name of these
JSON parameters correct. That's what
they are using in for the plane API.
Okay. And then state
is all they call it in the plane API
equals our state ID.
Okay. And that's our JSON request body.
Move this up a little bit. We'll then
create the JSON content. So JSON
content
equals JSON serializer
serialize
and we'll pass in a request body
and specify our content. So content
equals new
string content
again string content not stream content
pass over the JSON content
specify our encoding
which is UTF8
and we'll specify application JSON
like so.
Then as before, we'll get a response.
Make that wait on our HTTP client and
we'll do a post async this time and
we'll pass over the URL
and our content
that we've just constructed.
uh response
we'll also ensure
success
and then finally
our
response content
equals a weight
on our response
content
read as string async
and then we'll turn
our response content.
Okay. And I think that looks pretty
good. So we'll save that off. Bring up a
[snorts] command prompt. We'll clear the
screen and we'll do a net build. Now
that will pick up whether there's any,
you know, compilation errors or anything
like that. Looks pretty good. So we'll
leave it there for now. Uh we'll
obviously exercise this later and if we
have any issues with it, um we'll
surface those and we'll fix them. But I
think that looks pretty good to me for
now. So what we'll do next is we'll move
on to the more exciting stuff which is
actually to create our MCP tool set.
So moving on now to creating our MCP
server tool set uh in the project in the
root new file and we'll call this plane
tools.cs CS
and it's a public
class
called plane tools.
And we're going to decorate this class
with the following attributes.
MCP
server
tool type. Okay, relatively
self-explanatory. This class is
providing tools. Okay. And then within
the class, we're providing methods that
are the actual tools themselves. So
public
static
async
turns a task of type string.
And I'm going to call this get all
work item
statuses.
And into this method, we're going to
expect a plain API service which we can
call upon to call the API. Before we go
any further, we decorate this method
with MCP
server tool.
And most importantly of all, I would say
is we provide a description for this
tool. Now, this is the context that is
going to go back to the LLM when it
calls list tools so it understands what
this is doing. So, if there's anything
you're going to take away from this
entire video, it's this bit here. Work
on the contextual description of your
tools. And the better the contextual
description of your tools are, the more
effective it's going to be as an MCP
server.
Okay? So I'm going to say something like
get all possible not ass get all uh
possible
statuses
that a work item
could be could be created in
I'll say these statuses
denote
where
a work item would be in a typical
cananban flow. Now canban for those of
you who are not aware canban is just a
sort of an agile variant uh much simpler
than scrum. It's basically just a flow
type workflow. So to-do in progress done
that's super simple. Someone a bit more
complex than that. There's no sprints or
anything. It's just a continuous flow
work item. So really simple. I'm just
going with that kind of model. Okay. And
then more importantly, I will say the
state ids
returned
can be used
with other tools
such as creating
work items.
Now you can work on this context
yourself. If you want to make that more
elaborate or less I wouldn't make it
less elaborate but more precise more
targeted whatever you want to do please
feel free to do that but do provide some
decent context around what the tool is
and yeah where else the the outputs of
these tools could be used such as in
other dependent tools and then it's very
simple we just say okay various for
statuses and then we just wait on our
API service plain API service And we
just get uh
project states effectively.
And then we'll just return uh use the
JSON serializer again.
And we'll just serialize
the response statuses
like that. So you can actually see the
MCP stuff is pretty simple. Okay. The
package that we included does most of
the heavily lift heavy lifting for us
and all we really need to do and work on
is providing good contextual
descriptions.
So we'll create our second tool.
So public
static
async
turns a task
type string.
I'll call this create work
item.
It too expects a plain API service,
but it also expects a couple of other
attributes.
And for the attributes which are name
and the description and the state, we
will provide descriptions for those as
well as a description for the overall
tool. So in fact, let me just make this
a bit more consistent. take the new line
for the plain API service and we'll take
a new line for the next parameter but we
prefix it with a description
and then round brackets and this one is
going to be our name. So the name of the
work item. So the description would be
something along the lines of the title
or main
headline for the work item.
and we'll see keep it keep it brief.
Okay, so you don't want massive long
titles. Okay, so again anything that can
help provide good context
do that. Okay. And next parameter again
we'll provide a description
and this one is also a string and it's
for our description
and we'll provide the description in
here and we'll say the detailed
description
of the work
to be done and I'll just say we're
appropriate. include
acceptance
criteria.
Okay.
And we put a column in it. A column a
colon. I'll call that a comma. Third
time luggy. And then the last
description
is for our state ID.
And we'll see the state
or status use different terminology. It
means pretty much the same thing
of the work item.
Um say derived
from
the
get all
work
item statees
tool.
Okay.
And we need to put that in. It is also a
string
called status.
Call it state ID.
All right, cool. And we'll close that
out if we've not done so already. We had
done already, so we don't need that
there. Okay, cool.
And while we're at it, we will provide
the description for this tool.
So MCP
server tool,
give it a description,
and I'll say something like
this tool
allows for the creation
of a work item
in plane
in the given state.
Okay.
And then of course we need to actually
build out the method which is fairly
fairly simple to be honest with you. So
curly brackets
create variable for our created work
item
and we'll say await on the plane API
service
create work item async and we expect a
name
description
and the state
State ID. Is that what we called it? I
can't remember.
Apologies. Yeah, state ID. Okay, cool.
Semicolon. And then we'll just return
the serialized
work item
like so. Okay, let's do [clears throat]
a quick net build
just to pick up any syntax errors, any
other issues. No, that's all succeeded.
So again, when we run this, we could
still run into difficulties or whatever.
Hopefully not though. So what we've done
so far is done all our config, we have
done our API service, and we have now
done our tools. What we need to do next
is wire all that up in program CS. So,
we'll use dependency injection, all that
kind of cool stuff to get our API
service registered and get our uh MCP
server up and running as well. And then
we can start testing it. So, a little
bit more coding to do and then on to
testing.
So, back over in program, we're going to
wire up all the stuff that we have just
completed, specifically our API client
and our tools.
So new line down here and we'll do our
HTTP client factory first. So accessing
a builder
services
and we're going to add HTTP client that
just registers
our client factory and then we're going
to make sure that whenever one is
requested that we inject the correct
config items into it. So we'll add as a
singleton, register it as a singleton in
the dependency injection container
and
we'll provide config as follows. Let's
put the semicolon in here
and we'll say our
http cloud factory equals sp
get required
service of type i http
client factory
and then we can return that
as plane API service
with the necessary client factory as
well as our
config elements.
And I've just realized that was spelled
incorrectly. So let's just min minimize
that. And that works better. It's good.
And
plane API key.
And yeah, we need to There we go. Check
that as well. Okay, so that's all
looking good. So yeah, just make sure
you probably didn't do what I did, but
just minimize the P there and everything
else looks pretty good. So that's our
client factory wired up. So whenever it
gets requested, we will inject the right
config items into it. [gasps] Now
arguably the more interesting stuff
which is really around MCP. So builder
services
we add MCP server
and then with
this is where we specify a transport
mechanism with stdio server transport
and not a col not a semicolon and then
finally
with tools
from assembly
That just means that we pick up the
tools that we've defined in plain tools.
Okay, that's all.
Finally, we just want to run everything.
So await
builder
build, of course.
And then we want to run async.
That's it. So, we should be able to do a
net run now. And that will run up our
MCP server. And we can then in the next
section we can uh use MCP inspector to
call the tools and see if we get results
back. Hopefully we do. But let's see if
we can run it up for now. So clear the
screen
net build
check the build out
and then we'll do net run.
And it started. It's looking pretty
good. Okay, it's looking pretty good.
So, in the next section, we will use MCP
inspector to exercise our newly created
MCP server. So, we'll do that next.
All right. So, we want to test with MCP
inspector. So, what I'm going to do is
I'm just going to stop this from running
here. Do so if you haven't already done
so already and clear the screen. And of
course, just make sure you are still in
the root of your project. And the way
that we can start MCP inspector is by
using the MPX command. So that means we
don't really have to uh do anything else
other than run MPX. And I'm actually
might just copy in this next bit because
it might I might mistype it and I just
don't want to mistype it at this point
in time. So,
so it's npx at model context
protocol/insspector
and then if you type in not all that
just net run. Okay. And basically what
that's passing over to MCP inspector is
what it needs to where it needs to find
our MCP server. Remember it's a a local
server. What it needs what command it
needs to run to run it up and connect
into it. Okay. So if we do that, it will
start a few things and it's opening a
browser on my other screen. So I just
pull this on so you can see what
happened.
This is MCP inspector.
Okay. Now we've not connected yet, but
what you can see is the transport type
is standard input output. The command is
net and the argument is run. And we've
run that from our project route. Uh if
we connect in now,
that looks pretty good. Okay. And you
can see the first thing that you get
access to is this ability to list tools.
So if we click on that, this is all part
this JSON spec, RPC spec to, you know,
understand what is available to us. We
get two tools back. Get all work item
statuses and create work items. That's
looking pretty good. So if we click on
this first one, doesn't require any
input parameters or anything like that.
We can just run that. And what that
should do is call our MCP server tool.
And the MCP server tool then will call
our API and we should hopefully get a
success response back. We do indeed. And
you can see we have all our statuses
back from plane.
Fantastic. Now what we might do then is
if I can I probably can't read that too
well to be honest with you. I'll need to
actually get one of the work item
statuses to create a work item. So this
one over here uh we'll say test from
MCP inspector
and
this is
the test. Okay. Now we do need to pass
in a state ID. We we would have got it
from this get all work item status is
the only reason I'm saying I can't read
it is because I think I'm slightly
numerically dyslexic. I can't I can't
read text that jumbled up. So, let me uh
I've got one already put aside that I
know works and I'll just put it in here.
Okay. So, this one I believe should be
the in progress status. Okay. So, if we
run this now,
we get a success back and we get a work
item back. That looks pretty good to me.
So if we come into plane
and come into the project that we're
working in MCP demo.
Try that again. Didn't seem to be
responding for some reason. That's quite
weird. Um we should see yeah test from
MCP inspector. And just to make sure
this is a test and it's in progress.
Okay. So again the
this ID I just sourced I sourced that
from an API call I made some time ago.
It would very much be contained in the
get all work item statuses response. I
just couldn't see it. All right. So that
is our MCP server working from the
perspective of MCP inspector. But
there's no AI involved. And in fact if
we look at our sequence diagram
all we have done is basically well we've
done this bit here. So MCP client is MCP
inspector. We've done the list tools
tool result and then
we've done this bit here run tool
and this bit here. So forget there's no
LLM involvement. There's no real you
well I suppose I'm a user but really
we've just done everything from the MCP
client onwards. we've not incorporated
it with an LLM as yet. So next and final
step what we'll do is we will point VS
Code to our newly created MCP server so
we can then use it with in this case
GitHub Copilot. But we'll do that next.
All right. So on to the moment of truth
now where we are going to register our
MCP server with VS Code. So the way I'm
going to do that is by opening up the
chat window, the GitHub Copilot chat
window. And down here you have access to
this configure tools item here. And this
will allow you to look at the MCP
servers you've already got. And we've
got all the built-in ones that come with
VS Code and also the one I'd added for
our demo. Okay. But we're going to add
another one, the one we've just created.
And the way you do that is by just
clicking on the MCP icon here and
selecting what type of MCP server it is.
Now we've already talked about the
transport type and the fact that we've
used standard input output. So that is
what we are going to use. Now this next
bit is absolutely critical. This is the
command that you tell VS code to use to
run up our local server. Okay. So net as
we're using the .NET framework run.
Okay. And you kind of saw that when we
used the MCP inspector. That's what we
passed in. But because we can't
guarantee that we are running it from
the project root, we need to specify the
project route here as part of this
command. So the way we do that is d-
project
and then you pass in the path to your CS
project file. So just so you can see an
example, this is the local path to where
my new MCP server is stored. Okay. So,
it's on my D drive under development in
my YouTube folder. Season 8 episode 1
blah blah blah blah blah. So, I'm going
to copy that
and paste that in here like so. Okay.
Hit enter. And we'll call this plane
server 2. Okay. And we'll add it to
all workspaces.
Okay. Now you can see here what it's
done is it's opened up this MCP JSON
file and you can actually see that I had
my previous plain MCP server registered
and here's the one we've just
registered. Okay. And I had this one in
a slightly different location. Okay. So
this is my YouTube folder that I do
videos for. This is one where I I do my
research and build out at the time. Very
similar just in a slightly different
folder but exactly the same format.
Okay. Okay, [clears throat] so I've got
two MCP servers I've registered
globally. Now, now what I'm going to do,
you can refresh it from the command
prompt. I'm just going to close down VS
Code. Okay, I'm going to close down all
instances of VS Code because I've got a
few running. Don't need to save that
now.
And don't save that. Okay, so all
instances of VS Code have closed. And
I'll reopen
the believe this is the one we've just
done.
And the way I can check to see it's the
one we've just done is in the research
one, I actually implemented a few more
tools. This one should just have two.
Yeah. So, this is the one we've just
done. Okay. And then when we come down
to tools down here, you can see we've
got our plain MCP server 2. Now, this is
actually going to be interesting. Um, it
would be interesting if we left this one
enabled. I'm going to disable this one
and say that we don't use this one and
that we only use the new one we've
added. And so that brings us on to
testing it really. And this is really
just going back to the demo we did at
the start. So say okay. And I'll say
something along the lines of can you
create a
plane
work item.
uh place it as done
to reference
an MCP
server build for YouTube.
Okay, let's see what it does.
Now, this is an interesting point you
didn't see in the demo because this is
the first time. It's a really good point
actually because this is the first time
it's attempting to use this MCP server.
It's asking us if we are allowed to do
it. Okay. And as you would expect,
because you're giving an AI access to
actually do stuff, you wouldn't want to
just allow it to do stuff without any
kind of permissions going on. So, we can
select allow it now. Always allow. I'm
just going to select always allow, you
know, just because it might demo it
again. And it's getting all working item
statuses, which we know is good. It asks
again, can we run the create uh work
item tool? And again, I'll just say
always allow. But it gives you some
shity that there is a little bit of a
safeguard there that it doesn't just go
mad and and do stuff in your environment
without you asking can it do it. So
that'll look pretty good. Um let's come
over to plane and take a look
and bring a new window onto
onto screen and we'll see what it's
done. Let's go into plane
into MCP demo into work items.
And there we go. MCP server build for
YouTube. And there we go. Build a mold
context vertical server integration for
plain project management. Yeah, there we
go. So it's Yeah, it looks like it's
actually pulled out, I would say, some
context there from our project, which I
think is pretty cool. So that brings us
to the end of today's video. I hope you
enjoyed it. I really like MCP. I like AI
tools. It's a really exciting space. I
know some developers are running away
from AI assistance. Some are running
blindly towards it. I sit somewhere in
the middle. I think used in the right
way. They're a fantastic tool set that
can really enhance your workflows. But
as yet at this point in time, they are
not, in my view, replacing human
developers just yet. You do need that
human oversight. And something like
co-pilot, perfect name for me. it is a
co-pilot to you. You are the pilot.
You're in control. It just helps you out
an awful lot. And something like MCP can
just enhance that uh a lot. Anyway, as
always, thank you so much for watching.
I've loved having you here with me. I
hope you're all well. Stay safe, stay
happy, and I'll see you again in my next
video.
In this video, we build a MCP server from scratch using .NET 10 and C#, exposing two practical tools: Get Project Statuses and Create Work Items. The server integrates directly with Plane, allowing tools like VS Code running Copilot to create and manage work items automatically. This is a full, step-by-step build — with no skipped steps, where we cover theory then dive into the build, testing our solution with MCP Inspector, then integrating into VS Code running Copilot. 🌐 Links 🌐 🎓 My other courses: https://lesjackson.net 🤩 Patreon: https://patreon.com/binarythistle 💾 GitHub Repo: https://github.com/binarythistle/S08-E01-MCP-Server 🛬 Sign up for Plane: https://plane.so ☕ Buy me a coffee: https://buymeacoffee.com/binarythistle ⏲️ Time Codes ⏲️ 0:36 – Welcome 1:22 – What we’ll cover 2:30 – Demo 7:49 – Build Ingredients 8:40 – MCP Theory 10:34 – MCP Architecture concepts 12:16 – MCP Interaction walk-through 17:21 – Scaffold app 22:27 – A quick look at Plane 26:17 – Reading in configuration 33:11 – Test API calls 38:59 – Build the Plane API Service 52:49 – Build MCP Tools 1:02:49– Register services 1:06:34 – Test with MCP Inspector 1:11:25 – Test with VS Code and Copilot