Transcripts
1. Course Introduction: Hi, everyone. Welcome to
this course on MCP and eightway which is
Model Context Protocol and agent to Agent protocol. I'm Karthick, an alumnus of IIT Deli and University
of Illinois. Currently, I'm
building AI agents that can write software
at my startup, the AI language, and I'm incredibly excited to share
this journey with you. The Model Context protocol allows host agents
to connect with and manage external tools and resources from servers
called MCP servers. This helps to extend
the capabilities of agents by providing them
external tools and resources. Agent to agent protocol is a communication
standard that allows multiple agents to
discover each other and talk to each other directly
whether local or remote. This allows them to
collaborate, delegate tasks, and work together in a larger system across organizational and
technical boundaries. Together, they form a
communication backbone for a powerful
multi agent system, allowing agents to expand their capabilities by connecting to external tools and agents
and collaborating together. So what will you
learn in this course? We'll start with MCP
and you'll build five MCP clients and three
MCP servers from scratch, full working code included. We learn to deploy our MCP
server to Google Cloud. We'll build a UI for MCP
client in Streamlt in Python, and then we'll move on to
agent to agent protocol. We'll connect three
agents via a tow and build a host
orchestrator agent that can connect via Ata to other agents and via MCP to
other MCP servers. Finally, we'll use a
free Gemini API key so you don't need to pay for
AI models when learning, and we'll do all this on Mac OS, but I'll provide
written instructions for Windows users so
you can follow along. So if you're ready to start
building with MCP and at, let's get started, see
you in the first lesson.
2. 1.1 MCP Overview - What is Model Context Protocol?: Hi, everyone. Today we
are going to look at the basics of Model
Context protocol and understand what this is. This you can see is
a news article 0R a blog post from
Anthropic from 25th, November 2024 when they introduced the Model
Context protocol. But the best way to follow this is by clicking on this link, which will take you to
modelctextprotocol dot IO, and you have this
website over here with some documentation on getting started and some tutorials,
and this will help you. But before we do that, let's
just go through the basics. So what is Model
Context protocol? It is basically a protocol to connect large language models, which might be hosted
on some apps using this particular protocol to external data sources and tools that would be
exposed by servers. So essentially, it's a
client server model. So this is your
client over here, and the MCP client or the model context
protocol client connects to MCP servers using this
particular protocol. Now, what are the
clients over here? The clients are LLM powered
apps like Cloud desktop or IDs or tools that basically
host an LLM onto them. And the servers
are programs that expose tools,
resources, or prompts. These are the three
primitives that they basically expose and help the LLM to execute these
functions or tools, use the resources or basically
get some sample prompts. The servers might
further connect with local data sources like local data source
A, B, and so on, or through web APIs to remote services like remote
service C over here, as shown on their
documentation website. So let's talk about
the MCP server first. So this basically provides
three primitives, and you can actually go to the Quickstart over here
for server developers. And when you scroll down, you'll actually see these primitives
mentioned over here. So these resources provide
access to external data and files for the large language model to
retrieve information. So your client
application connecting to the server can then access this particular information
through these resources. There are tools, and these tools are essentially what we are used to seeing with large
language models and agents, which we provide these tools, and these are basically functions or APIs for
the LLM to invoke. And this helps this helps
the large language model perform actions. And
then there are prompts. So these are reusable
templates with instructions or context to guide the large
language model behavior, and your client app
can basically use this by connecting to the
servers using the MCP protocol. Okay, so heading back
to the introduction, there are also two primitives
that the client provides, and these are not
mentioned over here, but let's just go through them. So these are roots which are interfaces or entry
points through which the client connects to the MCP servers. And sampling. Sampling is basically
a mechanism that the server can actually request the client to generate text based on specific
inputs or context. This is what the client provides
as part of the protocol. Now, given these primitives, we can see that this
is bidirectional. So with these
primitives, MCP supports two way communication between
the server and the client. So a server can request the
LLM to generate completions, which is the sampling primitive. At the same time, the client hosting the large
language model can request a tool execution on the server and then
request for the result, or it can ask for some kind of data information or some information from
a remote service. So that is basically what
model context protocol is. It's a bidirectional protocol for clients to
connect to servers. And on the servers, you
have the resources, prompts, and tools,
and on the clients, you have the roots
and the sampling primitives that basically help the large language model and the server to communicate
with each other. There are several pre built MCP servers that
exist for Github, Google Drive, Post
cress, et cetera, still early stage
and the success of this depends on wider
adoption by a lot of people, but it has been seen, if you follow a lot
of Twitter threads, you can actually
see a lot of people working with MCP these days and mentioning it as one of
the next big things in EI. So for example, you might
have a MCP server to connect a large language model to
a local SQLI database, a Github bo, and a Slack workspace all in
the same protocol. So LLM can query the database. They can fetch the code context, and they can post updates all
within a single protocol. So that's all for MCP for now. Thanks a lot for watching.
3. 1.2. Get Code on Github: Now that we know about
the basics of MCP, let me first cover a
housekeeping item which is about the course repository where I'll be pushing
all the code, which can actually clone and use during all the
different lectures. To access the code repositories, please go to github.com
slash the AI Language, and then you can
click on repositories to visit the repositories tab. You'll see several repositories by the AI language over here, and we'll be basically
using two repositories, the MCP client repository. And the terminal
server repository. The MCP client will basically have the implementation
using Python, Lang rap, Gemini, and any other clients
that we implement. If I add another repository for any of the other lectures, I'll be mentioning that
during the course. The other repository
is for the MCP server, and I've called this terminal
server because the server that we use in the course is used to run commands
on the terminal, and this is an MCP server that can execute
terminal commands. To open a repository, just simply click on the
name of the repository. And you can see all the files over here along with
the read me over here. And if you actually
want to clone the code, you can click on code over here and use one of the specific
methods that you prefer. For example, you can use
SDDPS and copy this link and then go to your
terminal and type Git clone with this link to
actually clone this. So I request you to please
start this repository and watch this by clicking on these two items like I have
already done over here, and then you can
actually go back to the AI language and then go to the terminal
server over here. And also check out this code
again in the same way by clicking on code and using one of your preferred
methods from over here. Again, please also
star and watch this repository so that you have it ready for easy deference
on your Github account.
4. SECTION 2 START - Build Your Own MCP Server - 2.1 Introduction to MCP Server and 1 Minute Preview of: Hi everyone. Today we are
building an MCP server that can execute commands on terminal
on your local MacBook. This means you can ask
AI apps like Cloud for Desktop to run commands on your computer and
send back the output, just like using a
terminal, but through AI. Here's a preview of what we'll
actually test at the end, and it's absolutely amazing. Let me tell you that. And
what I've done is basically, can you create a directory
called Cloud output? Make a file inside it, called terminal Underscore server
underscoe test dot DHT. And then it says, I'll help you create a directory and
file as requested. So let's ask Cloud. Can you open this using code? The terminal server
test dottXDFle. And let's before
that, add some text. So can you add the text? I successfully made my first
MCP server to this file, and then can you open
this using code? The terminal underscoe
server dot DF. So let's send this message. So here you have it. So,
guys, isn't this amazing? You have made actually
an MCP server that can actually run
terminal commands, and you used a large
language model on an app, which is your MCP client,
the Cloud desktop app, and you actually controlled your computer by
creating a file, adding some text to it with the Smiley and
then opening it in
5. 2.2 Install Claude for Desktop: As a first step, let's
install this client and you can do that by searching
for Cloud desktop. Let's search for that and
let's click on Download over here and you get to this
website for downloading it, and click on MacOS
because we are on MacOS Windows folks can actually use the
Windows Link over here. Let's wait for a minute for
this to download. All right. Once it's downloaded,
let's just open this and drag Cloud two
applications over here. Now launch cloud by
typing Cloud over here and press Open.
Let's click on open. So now you need to log in. So let's login with
Google account. So if you're using Cloud
for the first time, you need to enter
your full name. All right. So once
you've signed in, you'll see a welcome
screen like this. Good afternoon, the A language, and we have the Cloud 3.7
Sonnet model over here. This is the desktop app
for Cloud for MacOS, and it will give you 40
messages for free per day, so you don't need
to actually pay initially to do this tutorial.
6. 2.3 Install Python, UV and VS Code: So the next thing
that we want to do is to install Python. So if you already have
Python installed, you can check for the
version over here. You can type Python
three dash version. And for example,
we have 3.9 0.6, but we need 3.10 or higher. So to install the latest
version of Python, we can open a new browser
window and say, install Python. And let's go to the
download Python page over here. All right. Let's click on download
Python 3.13 0.2. And let's wait for the
download to complete. So once this is completed, you can actually open this, and you get an installed
Python window like this. There's an introduction.
Let's continue. There's a read me over here. So let's continue over here. And there's a license,
I'm going to accept it. I'm going to use the
standard installation and not going to
customize for now, and let's enter our password. And this is not going to
install Python for us. All right. So we have
Python installed now. Let's move the installer to the bin this is going
to actually open this Python 3.13 folder
over here and it's going to show you
all the things that are installed,
including the IDLE. Now, let's go back
to the terminal. Let's type which Python three, and this shows you that this is installed to user local
Bin Python three, so we can now type
Python three version, and we see that our Python
three is now 3.13 0.2. This actually satisfies our
version requirement for 3.10 and higher. We're
good with this now. Now, next, we want
to install UV, which is a fast
package manager for Python that helps us install
and run dependencies. So to install that, we're
going to paste this command, and you can find this in the
description. Press Enter. So let's wait for some time for this to get
installed. All right. So there's a permission
denied error, and you might want to
then and we can see that the error is due to the ownership of local
being with root. The local directory is
inside our home directory, so we can actually change
the permission over here. So use this command for this, enter your password, and then let's verify if the
ownership has been updated. And yes, we can see that the ownership is now
updated over here. This permission
fail, you might be tempted to run
this, with pseudo. But running with sudo might
install this as root, which could cause
permission issues later. So let's not do that, and let's update this
permission like this. And let's not try to install UV again and see if it
works. Alright, great. So everything's installed. Let's source our
environment file so that we can recognize
the command UV. And let's type UV dash version. And we can see that
UV is now installed. This is the package
manager that we wanted. If you're on a Windows machine, you can actually follow the
install instructions on the UV page on Github by Astral, and if you scroll on over here, you have the
installation over here.
7. 2.4 Setup Project Directories and Files: Now we are going to create
the MCP directory structure. So MCP is our root directory, and inside this there servers, and we might make
multiple servers. For the current server
that we're making, which is what we're
calling a terminal server, we're going to make a
new folder over here, and this is that
directory structure. We also going to make
a workspace directory so that all the work that you Clouds desktop
app does for us using our MCP server is done inside
this workspace directory. So if it runs a terminal command to do something to
create a folder, for example, or directory, it actually does it inside
the workspace directory. Let's press center
now. Let's change to our terminal
server directory. So just to reiterate, MCP will save everything related to MCP, including our server, which will be inside the
servers directory. And there'll be a
dedicated workspace inside the workspace
directory over here. Each server will
have its own folder. For now, I've just created
one, which is terminal server. So now we are going
to initialize a Python project inside
terminal server. For that, we just type UV
in it, and it's done that. Now, let's create a
virtual environment. So for that we type UV, V and V, and let's activate it. Now, this creates a
virtual environment which keeps our projects dependencies separate from the system's Python
installation. Now we need to install
the MCP package which allows our server to
communicate with Claude. So for that, we'll do UV, add MCP CLI and presenter. All right, so that's
now installed. So now let's write the code
to execute terminal commands. So first, create a new file, terminal underscore
server dot PY. Now, let's head over to VS code, and let's click on
open over here. So we see the MCP folder over
here that we just created, and we see the servers over here and we open our terminal server. So let's open this. So I'm going to trust the
authors over here. And then let's go
to what terminal underscore server dot PY file.
8. 2.5 MCP Server Python Code Walkthrough: First we are going to
import the necessary code. We import the
required libraries, subprocess lets us run
terminal commands and fast MCP helps us set
up an MCP server. The workspace directory is
always set to MCP workspace. Now we are going to
define the tool that's going to help us run the
commands on the terminal. So let's break down this
function step by step. This is a decorator,
as we call it, and this registers the
function as an MCP tool. This is what allows
Cloud for desktop or any MCP client to call this
function as an external tool. This entire thing over here
is what defines the function. This basically means that the function expects
a string input, which is the shell command
that it needs to execute. And this will be provided by the Large language
model as an input, and it's going to be a string. And the function will return a string back which will contain the entire command's output or an error message which
you can see over here. This is the line that runs
the command on the terminal, and this is what
resounds the output. We use a simple try
except block over here to basically catch any
errors that might happen. So if something
unexpected happens, example, missing permissions,
invalid commands, the error message is caught and returned as a string to
your large language model, and then the large
language model can basically take
a decision on that. All right, so now
we're basically going to start the server, and this starts the MCP server allowing Cloud to
communicate with it. Let's save this file and
head back to our terminal, and let's now run this server. This command runs
our Python script inside the virtual
environment using UV. So that's all for
our MCP server, and now we need to connect
it to Cloud for Dist.
9. 2.6 Connect Server to Claude Desktop and Test Your Server: Hi, everyone. Welcome
back to the AI language. Today we are building
an MCP server that can execute commands on terminal
on your local MacBook. This means you can ask
AI apps like Cloud for Desktop to run commands on your computer and
send back the output, just like using a
terminal, but through AI. Here's a preview of what we'll
actually test at the end, and it's absolutely amazing.
Let me tell you that. And what I've done is basically, can you create a directory
called Cloud output? Make a file inside it called terminal Underscore server
underscore test dot DHT. And then it says, I'll help you create a directory and
file as requested. So let's ask Cloud. Can you open this using code? The terminal server
test doTXDFle. And let's before
that, add some text. So can you add the text? I successfully made my first
MCP server to this file, and then can you open
this using code? The terminal underscoe
server dot DF. So let's send this message. So here you have it. So,
guys, isn't this amazing? You know, you have made
actually an MCP server that can actually run
terminal commands, and you used a large
language model on an app, which is your MCP client,
the Cloud desktop app, and you actually controlled your computer by
creating a file, adding some text to
it with the Smiley, and then opening it in code. Before we begin, if you enjoy learning about AI
coding and automation, please like this video and
subscribe to our channel. It really helps us bring
more tutorials your way. Just a quick overview
of MCP again. MCP stands for model
context protocol. It is a system that lets AI, your Large language models hosted on apps like
Cloud Desktop, IDs or Tools or any other app, and you can build your own
app as well over here, which is called a MCP client. And this helps this AI interact with external tools
and fetch information. And this is done
through MCP servers. The servers do basically
three kinds of things. They can provide resources or
data and files, et cetera. So that's one primitive. There's another primitive
which is called tools, so the servers can
run or execute commands or actions on the behalf of the
Large language model. So that is called the second
primitive, which is tools. And the third is that the
server can actually provide sample prompts to the
Large language model to help guide its behavior. And that the prompts are
the third primitive. Today we are going
to focus on tools and we're going to build
an MCP server that basically exposes a tool that the LLM can call from the app, which is going to
be Cloud desktop, which is going to be MCP client, our server is going to then execute that particular
terminal command. So it'll be a shell command that is going to be executed on the terminal and send back
the output to the ****. As a first step, let's
install this client, you can do that by searching
for Cloud desktop. And let's search for that and let's click on
Download over here, and you get to this website
for downloading it, and just click on MacOS
because we are on MacOS, Windows folks can actually use
the Windows ink over here. So let's wait for a minute for
this to download. Alright. So once it's downloaded,
let's just open this and drag Cloud to
applications over here. Now launch Cloud by typing Cloud over here and press Open. Let's click on open. So
now you need to log in. So let's login with
Google account. So if you're using Cloud
for the first time, you need to enter your
full name. All right. So once you've signed in, you'll see a welcome screen like this. Good afternoon, the A language. And we have the clot 3.7
Sonic model over here. This is the desktop app
for Cloud for MacOS, and it'll give you 40
messages for free per day, so you don't need
to actually pay initially to do this tutorial. Alright, so the
next thing that we want to do is to install Python. So if you already have
Python installed, you can check for the
version over here. You can type Python
three dash version. And for example,
we have 3.9 0.6, but we need 3.10 or. So what we need to
do is we need to install Python for MAC, and you can actually follow
this video on our website, which is install
Python on a MacBook. If you're on Windows or Linux, you can follow a
similar, you know, steps from the website
for installing Python. So to install the latest
version of Python, we can open a new browser
window and say, install Python. And let's go to the download
Python page over here. All right, so let's click
on download Python 3.13 0.2 and let's wait for
the download to complete. So once this is completed, you can actually
open this and you get an installed Python
window like this. There's an introduction.
Let's click Continue. There's a Read Me over here.
So let's continue over here. And there's a license,
I'm going to accept it. I'm going to use the
standard installation and not going to
customize for now, and let's enter a password. And this is not going
to install Python for so we have Python
installed now, Let's move the
installer to the bin. So this is going to actually
open this Python 3.13 folder over here and
it's going to show you all the things that are
installed, including the IDLE. Now, let's go back
to the terminal. Let's type which Python three, and this shows you that this is installed to user local
Bin Python three, so we can now type
Python three version. And we see that AA Python
three is now 3.13 0.2. This actually satisfies our
version requirement for 3.10 and higher. We're
good with this now. Now, next, we want
to install UV, which is a fast
package manager for Python that helps us install
and run dependencies. So to install that, we're
going to paste this command. And you can find this
in the description. Press Enter. So let's wait for some time for this to
get installed. All right. So there's a permission
denied error, and you might want to then and we can see that the
error is due to, you know, the ownership
of local being with root. The local directory is
inside our home directory, so we can actually change
the permission over here. So use this command for
this, enter your password. And then let's verify if the
ownership has been updated. And yes, we can see that the ownership is now
updated over here. So on this permission fail, you might be tempted to run
this, you know, with sudo. But running with psudo
might install this as root, which could cause
permission issues later. So let's not do that, and let's update this
permission like this. And let's not try to install UV again and see if it works. All right, great, so
everything's installed. Let's source our
environment file so that we can recognize
the command UV. And let's type UV
da dash version. And we can see that
UV is now installed. This is the package
manager that we wanted. If you're on a Windows machine, you can actually follow the
install instructions on the UV page on GitHub Bastrl and if you
scrolled on over here, you have the
installation over here. All right, so now we
are going to create the MCP directory structure. So MCP is our root directory and inside this there servers, and we might make
multiple servers. For the current server
that we're making, which is what we're
calling a terminal server, we're going to make a
new folder over here, and this is that
directory structure. We also going to make
a workspace directory so that all the work that Clouds desktop app
does for us using our MCP server is done inside
this workspace directory. So if it runs a terminal command to do something to
create a folder, for example, or directory, it actually does it inside
the workspace directory. So let's press enter
now. Let's change to our terminal
server directory. So just to reiterate, MCP will save everything related to MCP, including our server, which will be inside the
servers directory. And there'll be a
dedicated workspace inside the workspace
directory over here. Each server will
have its own folder. For now, I've just created
one, which is terminal server. So now we are going
to initialize a Python project inside
terminal server. For that, we just type UV
in it, and it's done that. Now let's create a
virtual environment. So for that, we type UV, V and V, and let's activate it. Now, this creates a virtual
environment which keeps our projects
dependencies separate from the system's
Python installation. Now we need to install
the MCP package which allows our server to
communicate with Claude. So for that, we'll do UV, ad MCP CLI and presenter. All right, so that's
now installed. So now let's write the code
to execute terminal commands. So first, create a new file terminal undersource
server dot PY. Now, let's head over to VS code, and let's click on
open over here. So we see the MCP folder over
here that we just created, and we see the servers over here and we open our terminal server. So let's open this. So I'm going to trust the
authors over here. And then let's go
to our terminal underscore server dot PY file. So first, we are going to
import the necessary code. We import the
required libraries. Subprocess lets us run
terminal commands, and fast MCP helps us
set up an MCP server. The workspace directory is
always set to MCP workspace. Now we are going to
define the tool that's going to help us run the
commands on the terminal. Let's break down this
function step by step. This is a decorator,
as we call it, and this registers the
function as an MCP tool. This is what allows
Cloud for desktop or any MCP client to call this
function as an external tool. This entire thing over here
is what defines the function. This basically means that the function expects
a string input, which is the shell command
that it needs to execute, and this will be provided by the Large language
model as an input. And it's going to be a string, and the function will
return a string back, which will contain the
entire command's output or an error message which
you can see over here. This is the line that runs
the command on the terminal, and this is what
resounds the output. We use a simple try
except block over here to basically catch any
errors that might happen. So if something
unexpected happens, example, missing permissions,
invalid commands, the error message is caught and returned as a string to
your large language model, and then the large
language model can basically take a decision. All right, so now
we're basically going to start the server, and this starts the MCP server allowing Cloud to
communicate with it. Let's save this file and
head back to our terminal, and let's now run this server. This command runs
our Python script inside the virtual
environment using UV. So that's all for
our MCP server, and now we need to connect it. Now we need to connect
it to Cloud for Desktop. So let's open a new terminal, press Command N for that. So for that, we need to open the Cloud desktop
conflict dot JCN file, which is located at this path, and we'll use Visual Studio code to open that and press Enter. This is a clod desktop
configuration file. At this particular
code in this file, this basically tells
Cloud that there's a MCP server by the
name of terminal, and it's located at this
particular directory, and you can run it using this
particular file using UV. In case you get some
error in Cloud that you not able to connect
to the MCP server, just provide the full path
to the UV installation. Do that, go to the
terminal and type which UV and you'll get the
path where UV is installed. This happens because
Cloud might not be able to recognize the command
UV in its own environment, and so we provide the
absolute path over here. Similarly, we provide
the absolute path for our server
directory as well. One more change would
be to actually add a sync to the function
definition over here, which is as per the practice followed on the Cloud website. Once you have done all
this, you actually go to Cloud and quit that and
then open Cloud again. When you open it
for the first time, it might take up to 1 minute
to load one or 2 minutes, and you might see plank
screen till that much time. But now you see this hammer icon overhear and
you click on this, it basically says that these
MCP tools are available, and there's a run command tool. And when we go to our VS code, we see that this is the tool
that we actually added. And then there's a definition
over here which says run a terminal command inside the workspace directory and
the arguments of the command, the shell command to run, and it returns the command output
or an error message. And if you go over here, you can actually see
that this is what the doc string that we had provided for our
function over here. So now let's test this out. Alright. So it says, Allow
the tool from terminal Local. Do you want to allow
for this? So let's click Allow for this chat. And what I've done is basically, can you create a directory
called Cloud Output, make a file inside it called Terminal Underce Server
underscoetest dot TXT. And then it says, you
know, I'll help you create a directory and
file as requested. So let's ask Cloud. Can you open this using code? The terminal server
test dot TXT file. And let's before
that, add some text. So can you add the text? I successfully made my first
MCP server to this file, and then can you open
this using code? The terminal underscoe
server dot DF. So let's send this message. So here you have it. So,
guys, isn't this amazing? You know, you have made
actually an MCP server that can actually run
terminal commands, and you used a large
language model on an app, which is your MCP client, the Cloud desktop app, and you actually control your computer by
creating a file, adding some text to
it with a Smiley, and then opening it in code. So please do subscribe
to my channel. And like this video, if
you found this helpful, I really find it amazing what all AI and large
language models can do, and I really want to bring
this to other people around. So please do subscribe
because that helps me bring more tutorials your way.
Thanks a lot for watching.
10. SECTION 3 START - Build Your Own MCP Client (Using Python + Google Gemini API) - 3.1 Quick Recap and: Quick recap, MCP servers,
these are the folks over here. These are the back ends
that expose external tools. MCP clients are these
folks over here. These are the front
ends that interact with the server and they send queries to the large
language model over here and they
process the responses, and then they can
trigger and execute tool executions on the server. So today, we'll build a
client that will basically connect to our previously
built Python based MCP server. It will list the
available tools like our terminal command executor and it'll send user queries to the Gemini API for
process and it'll execute any tool calls through MCP server and display
the final response. Before we start, let me show
you an amazing preview of what I was able to
accomplish with this MCP client connecting
to my MCP server. And it's pretty amazing
that you can actually use a remote large language
model connected to your MCB client and your
server and then give queries to your MCB client to actually run all the
commands using your server. So here goes the preview. So
once you run the MCP client, you'll actually get the
welcome message which says connected to server with
tools run commands. This means that our
client has been able to connect to the server and
get the available tools, which is just one
tool over here, which is the Run command
function which we basically use to run
commands on the terminal. This output shows that we are in the chat loop and
we can write a query. Let's ask Gemini to create a file for us and
add some text to it. All right, so I've
asked it to create a file MCP client success
dot TXT, and at the text, I successfully created
an MCP client with the Gemini API and connected
it to my MCP server. And let's see what
Gemini does with this. Okay, so as you
see in the result, Gemini requests a tool
call, which is run command, and the arguments are echo, I successfully created
an MC client with the Gemini API and connected
it to my MCB server. And this is basically written to MCB client success dot TXT, which is the filename that
we provided over here. So this works pretty well, and then this tool
would be executed and the response from the
execution sent back to Gemini, which then reads and responds as I have successfully created the file and added the text
which we had requested. So if I go to the Finder now, I can see that I have the MCP client success dot
THD file over here, and I can just double
click to open this. And it says, I successfully
created an MCP client with the Gemini API and it
connected it to my MCP server.
11. 3.2 How to Get Free Gemini API Key: Now, why are we using
Gemini and not, you know, anthropic
or any other model? Because you can actually go to aistudi.google.com
and login through your Gail account and
they're going to give you a Gemini API key for free. So there are some
terms and conditions that you can check when
you take this key, and essentially you can actually use it for free for
testing purposes with certain lower limits
and by allowing them to use your data while
for training their mode. And this then makes it our ideal choice for this tutorial
and experimentation. So I'm on astuditgg.com, and when I visit this site, it automatically
gives me a prompt to get an API key over here. And otherwise, I think there's a button over here
also to get the APK. So I'm just going to click
on Get API key over here. Okay, so there's a code to test the Gemini API and there's a
create APK button over here. So let me just use the
API key from here. It's going to now
generate the API key for me, and, you know, you should use your API key
securely and not share them or embed them in code
that the public can view. I'm going to disable
this after the tutorial. For now, I'm just
going to copy it from here and let it be visible. So to store our
APIKeys securely, what we can do is we can go to the terminal and we can
create a dot ENV file. Then let's open
that file in Nano. And then let's paste
our API key over here. And then before that, they're
going to write Gemini, underscore, API,
underscoe key, equal to. And let me add the I over here. And now this is basically an environment variable
that I don't need to store in view in my code, and I can use this while
developing my MCP client. Let's press Control
X and save the file. And what we'll also do is
that we'll add this file to Get Ignore Bluthytecho dot ENV, which is the file
name in double codes, and then we write
it to Get Ignore. So this basically prevents
accidentally sharing our API key on places like Github or any other
remote repository.
12. 3.3 Setup Project Directories, Files & Install Google-GenAI SDK: So first, let's set
up our environment. So we need to create
a new project and a virtual
environment, right? So we go to the terminal, so
you can open your terminal. So now we are in
our home directory, and we can do a LS L and see that we had created a MCP directory over
here last time. You can create this directory
if you have not got this, and then we change
to this directory. And you can see that we
have two folders over here. One is for the workspace
where our server can run all the commands
so that we don't spoil anything outside
this workspace. And the other thing is
the server's folder where we had created our folder. So now let's create a
client's folder over here. And let's change to
the client's folder. Alright, so now
we'll use UV in it. MCP client to actually
initialize our project. So this command creates a
new project directory called MCP client using the UV command
line interface CRI tool. Now let's change to this
director This has changed our current working directory to the newly created
MCP client folder, and now we'll create a
virtual environment. For that, we do UV V
ENV and press Enter. And this basically is a
virtual environment that will isolate all our
project dependency so that they don't conflict
with other projects. Now let's activate the
virtual environment by typing source dot n BNS ACI. Activating the virtual
environment ensures that Python uses the correct
dependencies for this project. All right, guys,
now we're going to install the required packages. And one thing that
we're going to install is the Google GNI
SDK for Python. How you do that is using
PIP install Google GNAI. We're going to do UV add Google GNAI because we are
using UV as a package manager. And the other two are the Python environment variables
package python dot ENV and the MCP library as well. So what to do that, what we
do is we say UV add MCP, then Python dot ENV. And then we add Google Jen AI, and we can make sure
that we have matched the name correctly over
here and then press end. I've already added MCP during the last tutorial on
setting up MCP servers. I've already added
Python dot NV as well, and Google GNAI, so it does
that very fast for me. You can actually install
it by typing this com.
13. 3.4 MCP Client Python Code Walkthrough: Alright, so now let's write
the code for our clients. So we'll use touch
client dot py. And this is going to
create our Python file, and let's now open VS code. This is our server
code from before. So what we'll do is we'll
say File New Window. We'll go to file Open folder, and then go to MCP clients and MCP client and we'll
open this code over here. Let me say I trust the authors, and let's open our
client dot P file. All right, so this is
the client dot PI code, and we'll go through
it step by step. So first of all, on the top, we have all the
necessary libraries like ASN IO OSS and JCN and these
are used for ASN operations, environment
variables, and system specific parameters
and functions and for handling JCN data. All right, so next, we import
the MCP client components, and this basically allows
us to interact with external tools and execute
commands like the MCP server. We use client session to manage our connection
to an MCP server. We use SDD IO server parameters
to define how we connect, and we use SDD IO client to
establish communication. And now we import
Google's Gemini, AI library and some helper
types for function calling. So Gen AI is basically the main client for
interacting with Gemini AI. Types helps us structure
our AI generated responses and load ANV Loads our
API keys securely. Next, we load our dot ANV file, so our script can access
the Gemini APIKey. This ensures that sensitive
data like our APIKey is kept separate from this code and
not exposed in this script. Okay, so now let's create
our MCP client class. This will handle
the connection to the MCP server and
communicate with Gemini AI. This is the MCP session
for communication, and this is going to manage our Async resource then we have the Gemini API key
that we store in this variable and check for its value over here
for any errors. And then we configure
the Gemini AI client by providing it with
a Gemini API key. So this MCP client class
basically manages everything from connecting
to the MCP server to sending queries to Gemini. Inside the constructor,
we retrieve our API key and configure
the Gemini AI client. So next we define the
connect to server function, and this is the method
that basically connects to the MCP server and list all the available tools that are available
with the server. It basically takes in
the server script path. And if you recall
from the last video, we had created a
terminal undiscoe server dot P script for the
terminal server, and that's basically what
it'll take over here. Now, based on the extension
of this script file, we might have a
server written in Python or in JavaScript. So basically, we want
to determine whether the server script is
written in Python or JS, and this allows us to execute the correct command to
start the MCP server. So in our case, the
script path ends with PI, so we're going to take
Python as the command. Then we define the parameters for connecting to
the MCP server, which is basically the command and the server scribed path. And then we establish
communication with the MCP server using
SDD IO over here. And finally, we are going to
extract from the transport object the read and write
streams for the server. Then we initialize the MCP
client session over here, which allows interaction
with the server, and then we're going to send an initialization request
to the MCP server. Finally, we request
the list of tools from the session
from the MCP server, and we will save it in the tools variable
over here and then print a message for
the user that we connected to the server
with these tools, which is the tool names. In our case, for the
terminal server, this is going to be the
Run command tool which will allow us to run
commands on the terminal. Now, MCP tools which are received from the
server are basically in a certain format that
we need to convert to a different format so that we can actually
pass it to Gemini. And that is what we do using convert MCP tools to Gemini format, this
function over here. Let's have a quick look at
what this function does. So we have the function
defined over here, convert MCP tools to Gemini
and basically converts MCP tool definitions to the correct format for the
Gemini API function calling. This basically takes in
MCP tools as a list, which is basically a list of
MCP tool objects with name, description and input schema. And it returns the
list of Gemini tool objects with properly formatted
function declarations. So we initialize a
blank Gemini tools. And for each tool in MCP tools, we basically first, you know, run a clean schema function
on the tool dot input Schema. This is basically
going to remove some parameters which are
forbidden for Gemini, which basically Gemini
doesn't expect. And if you want to look at
the clean schema function, we can go up over here, and this is the first step. So what we do do is that
if it's a dictionary, we will basically
remove the title if that is present inside it. And then we check if there's
any nested properties, which is also a dictionary. Then for each key
in that properties, we are going to again run
the clean schema command. So basically removing
title over here. And I did this because when
I tried it with the title, I got an error which
said that, you know, this title is basically
forbidden inside this schema. So that is the
clean schema part. So for each tool, we'll
first clean the schema. Just to clarify, function
declaration and tool are both imported from
Google dot Geni dot Type. So these are basically
what Google defines for proper function calling
in Google's GEI SDK. And over here, while converting
MCP tools to Gemini, once we have cleaned the schema, we basically use function
declaration and tool, which are both provided by Google's GNAISDK and we first define a
function declaration, which basically takes
the name description and parameters and provides us with a function
declaration object, and then we create a tool object using these function
declarations, and then we append it to
a list of Gemini tools, and then we return that
Gemini tools list. That is what the convert
MCP tools to Gemini does. In case you are going to use some other large language model, you might have to convert
the tools returned from the MCP server to
a proper format, which is expected by that particular large
language model to implement function calling because
all these models might implement function
calling in a different way. All right. So let's go back to our connect to server
function. All right. So here's our connect
to server function, and we were on the last
and now we basically have defined the function
declarations for our MCP client class. So next, let's move to the
process query function, and this basically processes the user query
using Geminis API, and it will also execute
tool calls if that is what is returned in the response from the Large
language model API. And the arguments are
basically a string query, which is the user's input query, and it will return the response generated by the Gemini model. Next, we'll format
the user input as a structured content
object for Gemini and basically we define
the role as a user, and we basically define
the parts, part of it, which is basically
the query from the user and get the
user prompt content. So this is one part of the
content that we'll use. Next, we send the user
input to Gemini AI, and we'll include all
the vailable tools for function calling over here. So basically asking Gemini
AI to generate content. We are defining the model as
Gemini two point flasho one. And we are defining the contents as the
user prompt content, which we basically
defined over here. For the conflict, we
basically provide the tools. Then we store the response in the response variable over here. So then we have these two
variables, final text, which is going to store the
final formatted response and the assistant
message content, which is going to store the
assessment response and we just initializing these
two variables over here. And this is where we basically process the response
received from Gemini. So again, this
basically depends upon the response format
that Gemini uses. So we basically process
each candidate in the response and then each
candidate has content parts. And for each part, we will check if the part is a valid
Gemini response unit, and if it's a function call, this basically means
that Gemini is suggesting a function
to be called. So we want to process
that. So we'll extract the function
call details. So we'll store the
function call response in something called
function call part. This is a variable. And we'll get the name and
the arguments that Gemini requested for the function call as tool name and tool arguments. And then we'll
just tell the user that Gemini has
requested a tool call, and then we're going to execute the tool using the MCP server. So we'll call the MCP
tool with the argument. So we'll provide the tool
name and the tool arguments, and we're using the session over here that we defined early on and then we'll await the response and
store it as a result, and then we'll basically get the function response
as result dot content. If there's an error,
then we store the error. Now, again, we need to
format this response of the tool call to a way that
gemini understands, right? So basically create
something called a function response
part using types again, and we provide the tool name and we provide the
response over here. Then we structure
the tool response as a content object for
Gemini over here. And we send this tool execution result back to Gemini
for processing. So it knows what has happened over there
in the tool call. So over here, we again
provide the model. But now for contents, we provide the user
prompt content which we provide
for the first time. Then we include the Geminis
function call request, and finally, we provide the
tool execution results. So now Gemini has a complete
context of what transpired, and we again pass
the tools as well so that it can use it
for continued use. Finally, we get the
response over here, and then we extract the
final response text Gemini after processing the tool call, and this is what is appended
to final text over here. And finally, we return
all the parts of the final text separated
by a new line character, and this basically completes
our process query function. All right, so then we
have two more functions. One is the chat loop. So this is basically
going to run a loop which expects
a query from the user and it's basically going
to process the query and get a response and
print that response out. And this uses our
process query function. We also have a cleanup
function which basically cleans up the resources
before it exits the program, and then we basically go to
our main function over here. The main function basically initializes the MCP client and then connects to
the server using the connect to server
function that we went over. It then initiates the chat loop, expecting queries and processing all the queries from the user. And once the user
basically types quit, it basically then quits the
chat loop and breaks out. Finally, we run the cleanup
and then exit the program. This basically completes the entire client
dot P file that we
14. 3.5 Test Your MCP Client with Your MCP Server: Now you can actually
go to the terminal. At the terminal, you can write Uv run client dot P and then provide the
path to the server. Based on our
directory structure, it's the parent directory twice, and then the servers directory and then the terminal
server directory, and then the terminal server.p5. We've provided the
terminal server PI path over here and we can
press Enter to run this. So once you run the MCP client, you'll actually get the
welcome message which says connected to server with
tools run commands. This means that our
client has been able to connect to the server and
get the available tools, which is just one
tool over here, which is the Run command
function which we basically use to run
commands on the terminal. This output shows that we are in the chat loop and
we can write a query. So let's ask Gemini to create a file for us and
add some text to it. All right, so I've
asked it to create a file MCP client
success dot TXT, and at the text, I successfully created
an MCP client with the Gemini API and connected
it to my MCP server. And let's see what
Gemini does with this. Okay, so as you
see in the result, Gemini requests a tool
call, which is run command, and the arguments are echo, I successfully created
an MCB client with the Gemini API and connected
it to my MCB server. And this is basically written to MCB client success dot TXT, which is the filename that
we provided over here. So this works pretty well, and then this tool
would be executed and the response from the
execution sent back to Gemini, which then reads and responds as I have successfully created the file and added the text
which we had requested. So if I go to the Finder now, I can see that I have the MCP client success dot
THD file over here, and I can just double click
to open this and it says, I successfully created
an MCP client with the Gemini API and it
connected it to my MCP server. Isn't this amazing guys
that we have created our own MCP client today, and we have connected
to our MCP server. The MCP client
actually runs with a large language model
that's actually not local. It's on a remote
Google Cloud and it uses the Gemini API to
connect to the Gemini model. The Gemini model can actually
receive the user query, understand what tools are
available on the server, then request tool calls, and our client then actually
passes that to the server, runs the commands that
we need locally on our computer and gets the result and then
sends it back to Gemini, which then responds with the final response
message over here. You can quit by typing
quit and press Enter, and then we have
exited the MCP client.
15. SECTION 4 - Containerize your MCP Server Using Docker: Hi, everyone. So today, we are going to understand
how to containerize our MCP server and
deploy it using Docker and then connect
it to Cloud desktop. So before we start,
let me show you a review of what we'll
actually accomplish with the help of an
MCP server that is containerized using Docker and connected to Cloud desktop. So now let's go through the Cloud desktop
conflict dot JCNFle. So over here, you have
a terminal server, which is just one server that
we have under MCP servers, and there's a command
that is Docker, and this basically
tells Cloud to use the Docker command
line interface to launch your MCP server. Then you have a
list of arguments. So this is a full
set of arguments passed to the Docker command. So now we have Cloud opened up, and we see that we have one MCP tool available over
here, when we click this, it says that you have a run command the
run terminal command inside the workspace directory, which is the description
that we have provided in the terminal server PY file. And if a terminal command
can accomplish a task, tell the user you'll use
this tool to accomplish it, even though you cannot
directly do it. So this is basically an
instruction for Cloud, and then you have the argument, which is the command to run, the shell command to run,
and then basically you have the command output or an error message
that is returned back. So Cloud does see the Docker container with
the server available, and now let's try to
test it out. All right. So I'll ask Claude
to create a file called MCP Docker
serversuccess dot TXT, and at the text, I
successfully built a Docker container with an
MCP server and tested it. So let's see what this does. All right, so it says, Allow tool from terminal
server local to run, and I'll say allow for
this chat over here. And then says, I'll
help you create the file using Run Command tool. And then it's always run the command is asking
you to view the result. So let's check this
out. So the command it ran was echo and then the
text that we gave it, and then the file name. So
it looks good till now. And let me verify the file
was created correctly. So it says view result from the run command
from Tamil server, so it actually then
runs the cat command for this particular file
and it gets this output. And based on that, it says the file was created
successfully, and it has the specified text. So is there anything else
I can help you with? So that's all that we
wanted Claude to do. And now let's check this out
for ourself as well in in our local directory
on our MacBook to see whether that's
actually reflected in the mounted volume or not. Alright. So I have
my finder open. Let me open the workspace
directory. All right. And you can see that,
you know, I have this MCP Docker
server success dot TXT over here, and
I can open this. And it has a text that was
provided that I successfully built a Docker container with an MCP server and tested it. And you can see now that we have put our server
inside a container, and we're using that
with Cloud desktop. That basically helps us isolate our MCP server code from the environment that it's
running on. All right. So before we begin, a very brief recap of what is Model
Context protocol. So MCP or Model
Context Protocol is a new standard that enables
AI applications like Cloud desktop to interact with external data sources and tools through what we
call MCP servers. It operates on a client
server model where MCP clients embedded in
your AI app send requests, and MCP servers perform
the requested tasks. Whether that's accessing
a file system or managing repositories or
invoking specialized tools. However, deploying these
MCP servers directly on your host system can
be a real headache because you'll face issues
like dependency conflicts. Different tools require
specific versions of programming
languages or libraries. The complex setups
installing and configuring the
server environment manually is time consuming. And is a lack of isolation. So running the server
directly on your host risks exposing your system to
unwanted access or conflicts. So this is where Docker
comes into play. And if you go down, you can see this nice infographic
on their website. Docker packages these servers
into containers which encapsulate all the dependencies and isolate the environment, ensuring consistency across
different platforms. Tools such as Docker
Desktop, Docker Hub, Docker Scout and
Docker Build Cloud work together to streamline development testing
and deployment. So now, essentially,
you have the client, which is connecting with
MCP servers that are contained inside
their own container along with their tools. Alright, so let's go to
our terminal server code. And we have written
this code last time, and we see that we have a
terminal server directory inside which we have the terminal underscore
server dot p file, which basically has
a run command and this tool is an MCP tool defined using this
decorator over here. And what this basically
means is that your MCP server
recognizes this as an MCP tool that can
then run commands on the terminal as requested
by the MCP client, and it then returns the
output from running the command or the error,
whatever the case may be. If there's an exception, we cache that and then return the exception
text as a string. So that is what this
server is all about. And let's also head over to the terminal and check
our directory structure. So as you know that we created a directory called MCP
in our home directory. And if you do an LSL, you'll see that we
have three folders, clients which holds
our MCP client that we built in one
of the other videos, and the servers folder which holds the
terminal server that we built and are going to use in this particular video
and a workspace folder, which is the workspace for our server to run all
the terminal commands. And this is basically to ensure other files in our file system don't get affected
by this tutorial. Next, we need to
download Docker. So let's search for Docker Desktop download and
click on the docker.com link, which is the official
download website, and let's scroll down. And you'll find this link
to download Docker Desktop. Let's hover on this button and you'll see the
options over here. Please download it
for your system. I'm going to download it
for Mac Apple Silicon. Let's wait for a minute for this to get
downloaded. All right. So I already have it
downloaded from before, and once you get it downloaded, you'll have a DMG file, which you can just double
click and then drag the Docker application to the applications folder over here and leave
it over there. So I already have
this installed, and then I'm just going
to open this from the application go to the
applications folder from here, look for Docker, and then
double click to open this. So once you have this
open, you can sign in. Otherwise, you can actually
skip by clicking over here. And now you have your
Docker desktop open, and in case you have some
previous containers, you might see them
over here. All right. So first, we need to
create a Docker file to containerize our
custom terminal server. So let's change our directory to the terminal server folder, and let's create
this Docker file. Now let's open this Docker
file in VS code, all it. So here we have our Docker file. So this is going to
be our Docker file, and I have added commands to describe what is being
done in each of the lines, and we'll just
quickly walk through this to understand
how this works. So we are going to use
the official Python 3.11 slim image as the
base image for our Python. And this basically
ensures that we have our Python en
which is above 3.1, which is required for MCP. Then we set the working
directory inside the container to slash app, and we copy all the files from the current working directory on our host machine to this app
directory in the container. This will happen when we
run this talk of file. So our entire code will be
transferred to the container. Then we install any
required Python packages from requirements store TXT. We export the code 5,000 so that the container can
accept incoming connections, and unless you have some
reason to change it, you can leave it at this
and then we finally defined the command for the
docker to run our server, and that would be Python and then terminal underscore
server dot PI. And it basically starts our custom terminal server when the container launches.
So that's it. A simple docker file
for us. All right. So now what we need to do is we need to create a
virtual environment. And just in case you have been following from
the previous videos, you would already have
this created, but if not, you can create it by simply type CD MCP servers terminal server
to change to the directory and then typing Python
three VnV which basically creates a
virtual environment in this particular directory. And now you can activate
the virtual environment by typing source dot
nv Bins Activate. And now you can install MCP in this virtual environment
by typing PIP install MCP, and then you can actually write all the requirements
to the requirements doTXD file by typing PIP
frees requirements dot TXT. Let's head over to VS code and check out our
requirements to TXT file. And this is our
requirements TXT file, and we see that we
have MCP over here. And this is needed
by Docker to install all the requirements in the Docker container once it's run. All right, so now we are going
to build the Docker image, and for that, we'll use
the Docker build command. The Docker build commands basically builds
the Docker image. The T terminal server Docker tags the image with the name
terminal server Docker. The dot basically specifies
that the current directory containing the
Docker file is used as the build context.
Let's press Enter. This might take a
while to build. Let's wait for it to install and download
everything that's needed. It's time to integrate
it with Cloud desktop. So this configuration
tells Cloud desktop how to invoke your custom
MCP server via Docker. And this is basically the same config file that we have updated earlier for our server
without first first, you need to open your Cloud
desktop confictt JCN file, and you need to locate
this in your system, but it's generally located at this place on a MacBook
and let's press Enter. And this basically shows us the last used Cloud
desktop config dot JCN and we see that the
path is correct. Over here. Next, we need to replace the
existing terminal MCP server with the new definition
that uses the container. All right. So now
let's go through the clod desktop confit JCNFle. So over here, you have
a terminal server it's just one server that
we have under MCP servers, and there's a command
that is Docker, and this basically
tells Claude to use the Docker command
line interface to launch your MCP server. Then you have a
list of arguments. So this is a full
set of arguments passed to the Docker command. So you have run, this is going
to start a new container. You have I, and this
basically keeps SDD IN open. It is needed for Claude to send, receive messages via SDD IO. And then you have dash RM, and this basically
tells Docker to remove the container
automatically when it exits. This keeps your system clean, so no leftover containers
after each use. And then you have Dasdash in it, which basically adds a
minimal in it system. Have E Docker container
equal to true, which basically sets an
environment variable, Docker container equal to
true inside the container, and the MCP tool or server
basically could check this and behave differently
in a container environment. Then you have V, and this
basically is a volume mount. So the left side uses the AI
language MCP and workspace. This is basically a
workspace directory, and this is the host directory. And what we want is we
want to mount it to a certain directory on
the Docker container. So now for this, what
you can do is you can actually go to
terminal server dot p. And you can check
this out that we had used A tilda for
the home directory, slapAS Workspace as the path
that your script expects. This is basically going
to translate to slash root slashmCPlash Workspace in the world of Docker containers. So instead of writing
users and your username, you'll need to
specify slash root slash MCP slash Workspace. And this is then going
to basic map both of them or mount
your host volume onto the Docker container. So now the container has access to your real workspace files. So for example, when your
terminal server runs a command like shoot slash MCP
slash Workspace, it's actually listing files
from your local MAC folder. Finally, you have the
Docker image name, and it's basically
the one that we had built earlier using the
Docker build command. So this config looks great, and we've already checked that this config is located
in the Cloud directory. Now what we can do is we can actually go ahead
and launch Cloud. If you already have
Cloud launched, please do close it and
then right click on the icon to quit it so that you have
completely closed it, and then you can reopen
by clicking it over here. All right, great. So now
we have Cloud opened up and we see that we have one
MCP tool available over here. When we click this, it says
that you have a run command, which is a run terminal command inside the workspace directory, which is the description
that we have provided in the terminal server PY file. And if a terminal command
can accomplish a task, tell the user you'll use
this tool to accomplish it, even though you cannot
directly do it. So this is basically
an instruction for Cloud and then you have the argument, which
is the command to run, the shell command to run,
and then basically you have the command output or another message
that is returned back. So Claude does see the container with
the server available, and now let's try to
test it out. All right. So I'll ask Claude
to create a file called MCP Docker
serversuccess dot TXT, and at the text, I
successfully built a Docker container with an
MCP server and tested it. So let's see what this does. Alright, so it says Allow tool from terminal server
Local to run, and I'll say allow for
this chat over here. And then says, I'll
help you create the file using Run Command tool. And then it's always run the commands asking me
to view the result. So let's check this
out. So the command it ran was echo and then the
text that we gave it, and then the file name. So
it looks good till now. And let me verify the file
was created correctly. So it says view result from the run command
from Tamil server, so it actually then runs the CAT command for
this particular file, and it gets this output. And based on that, it says the file was created
successfully, and it has the specified text. So is there anything else
I can help you with? So that's all that we
wanted Claude to do. And now let's check
this out for ourself as well in our in our local directory
on our MacBook to see whether that's
actually reflected in the mounted volume or not. Alright. So I have
my finder open. Let me open the workspace
directory. All right. And you can see that I have this MCP Docker
server success dot TXT over here, and
I can open this. And it has a text that was
provided that I successfully built a Docker container with an MCP server and tested it. And you can see now that we have put our server
inside a container, and we're using that
with Cloud desktop, and that basically
helps us isolate our MCP server code from the environment
that it's running
16. SECTION 5 - Simplify client code with LangGraph & LangChain: Hi, everyone. Today
we are going to make an MCP client using
Lang Chin MCP adapters. Langchin makes it very simple to build your
own MCP client, and I'm going to cover
all the steps to make the MCP client and connect
it to the Lang graph agent. So before I start,
let me give you a 1 minute preview of
what we are going to achieve today with the
Lang chin MCP client. Trust me, it's pretty
amazing that you can use Lang chains
MCP adapters to do a very simple
implementation of a client and connect to the Gemini
API with all the tool calls. And all this can be done
for free because Gemini provides you a free API
key. So here it goes. So we're going to
ask our MCP client make a file and add
some text to it, and let's see how that works. Let's press Enter. All right. So this means now our
MCB client has started. Let's provide a query. So let the query B make
Langhin MCP client or TXT and add the text I made an MC client with Lang chin
successfully with Smiley. So let's press Enter. Right, so you can see the
response over here and we have the human message which says make file Lang
chain MCP client or DHT. And this is basically
the query that we sent. And then we have an AI
message which has no content. Then we have a tool message, and this would have
been a tool call. You can actually just directly print the response
to see more details. I'm just printing
out the content over here using my custom encoder. And then finally, you
have the AI message with the content
which says, Okay, I have created the file
Lang chain CP client dot TXT and added the text you
provided anything else. So let's just check
this out now. Let's go to find a and I have my MCB directory
open over here. I'm going to go to my workspace, and I see I have Lang
chain MCB client dot text, and you can see
that it has added the text that I asked for, and this works amazingly well. All right, so a quick
recap of what MCP is. So MCP starts for model
Context protocol, and it basically
provides a way for large language models to connect with external
tools and services. There are a few basic components
like MCP servers which provide context tools
and prompt the clients. For example, you can have an MCP server with a tool
to add or divide numbers, and it can also provide
resources like data. There are MCP clients, and the clients maintain
a connection with the servers inside a host app, and there are clients
you can make in Python. You can use the Cloud
desktop app as a client, and we are going to
build our own client in Python today using Lang
chains MCP adapters. MCP clients can actually
request the servers to call or execute tools and
provide the results client. The clients connect with the lag language
model over here, we're going to use
the Lang graph agent, and the Langraph agent can
actually use adapters to connect to this
particular client and load the available tools, head back to the
terminal and create an environment variables
file by typing touch dot NV and press Enter. I already have this
file over here, so I'm going to just open this
using NanO and as you see, I have defined APIKsGemini APIKe and Google APIKe and I have
pasted my key over here. You can actually
write this line over here and paste your
key next to it. I have used two
environment variables, Google APIKey and Gemini APIKe because in my
personal project, I like to use the environment
variable Gemini APIKey. However, Lang chin
expects the key to be present under an environment variable
called Google APIK. Just to avoid any confusion and allow both of the
variables to be used, I actually defined the key twice using two environment
variable names. And you can do the
same. For Lang chain, you need to use Google
API press Control X and save the file
before exiting it. So now you have your
Environment file setup. So now we can create
a virtual environment which basically helps us keep our project dependencies
separate from all other work that we're
doing on a computer, source dot NV slashBN
slash Activate. And this basically
makes you enter the MCP client
Virtual environment. Okay, so now we are going to add some packages that we need
to develop our MCP client. These packages are Lang chin, the Lang chin MCP
adapters, Lang graph, Lang chin Google Gen AI package, the Google Genitive AI package, and the Python Environment package for the
environment variables. After writing UV ad and the list of
packages, press Enter, and I already have
this installed, but it will take
a few seconds or a few minutes to
get this installed. And once this is installed, we are then ready
to actually start writing our angchinmcp
client dot PII. N type code and the file name, and this is basically going
to open this in VS code. All right. So let's
begin talking about the Lang chain CB client dot
PY file and how this works. So I'm going to start with
the imports over here, and we have some standard
imports for Async operations, for accessing the
environment variables, for command line argument
processing and others. Then we import the MCP
client related packages for session management and
for server setup and startup. And this is used to actually
connect to the MCB server. Now we have the ang chin
agent and LLM imports. We have the load MCP tools. This is basically used to
load the MCP tools correctly. Then we have the prebuilt
react agent from Lang graph and we have the Google Gemini
LLM wrapper from Lang chin. Finally, we load the
environment variables using load underscore dot ENV
from the dot ENV package. So these are basically
all your imports, and now we can actually
directly go down to the LLM instant we
basically use the chat Google generative
AI package that we imported and
instantiating the LM is just as simple as
using chat Google generative AI with a
specified model name, so we're using Gemini
1.5 P over here. You can actually use
two point oh as well. You have the temperature
which we have set to zero for completely
deterministic output. So basically, you
can increase it for making the output
more creative. Then you have the max retries, which is two over here, basically automatically
retries the API calls up to two times to overcome
any kind of errors. And then you have
the Google API key. So Langhin basically uses
the Google API key in one variable to load that key and provide access
to the Gemini API. Okay, so over here, we're
just checking if we have provided the right number of arguments to a Python script. Next, we have the MCP
server parameters. We configure these parameters
providing the command, which is Python and
can actually have a server script that ends
with PI for a Python script, and otherwise we assume
that it's going to be a node or Jspace and we provide the server
script path which we actually got through the
Python program call over here. So then we have a
global variable to hold the active MCP session, and we're going to use it with the tool adapter that we're going to look at when
we go down the code. Okay, then we go down to
the run agent function. This is our main function
that does all the work. It basically opens a
connection to the MCP server. It starts the MCP session, stores the session
for tool access and loads these tools
using the MCP tool. So you basically have your
server, your MCP session, and your tools all started
up over then it's going to create an agent and provide that agent
with these tools. And finally, it's
going to enter a loop requesting the user to
provide a query and then invoke the agent asynchronously and
print the response as a well formatted Jason. So just to repeat, we basically have the connection
to the server. We have a MCP session setup. We have the tools set up, and then we basically
create an agent with these tools and then process the user queries and
printer response. This is basically all the
steps we're going to do. And to do that,
here is the code. So we basically get the read
and write properties for the server using SED IO client and the server pattern
that we defined above. Using read and write,
we actually build the client's session and
store it as session. We then initialize the session using session dot initialize, and then we use the global
MCP client variable to a simple object that holds
this particular session. Now we are going to load the MCP tools using the
adapter from Lang chain. So this is basically a very
simple implementation. If you've seen the
previous video on implementation of an MCP
client with the Gemini API, would know that we did a lot of conversions over there to manage the conversion from
an MCP tools format to the Gemini format. But ang chin makes it easy by actually using
load MCP tools, and we directly have a
list of tools over here. Now, finally, we have the
tools and we have the LLM that we defined at the top
using the Google API key. We use that to create what we
call a react angrap agent, which we store as
agent over here. We print that we've
started the MCP client and we can type quid to exit, and then we go into a
loop and prompt the user for a query as soon as
we get the query input, we actually produce
a response by invoking the agent
using the Asynchrons invoked function from Lang chain and providing the
query to the agent. Since this agent
already is aware of all the tools and the
model that we are using, Lang chain provides us with the implementation of sending
this request to Gemini, understanding the tool calls, and then executing the tools
for us using these adapters, and it is that simple to
then get the response. Finally, we format
the JSON using the custom encoder we've defined on the top. You
can have a look at it. It just basically prints the output in a well
formatted fashion, and this completes our
run agent function. So that's a simple explanation
of the entire code. And again, if you've followed
from the previous videos on creating the MCP client
without using Lang chin, you can see that this
is much, much simpler, and that's one of
the key advantages of using Lang chain over here. Now we can actually test out this agent by heading
over to terminal. So we have named our client file Lang chain MCP client dot PY, so you can type UV run
and the client file name, and then you provide the Python or the Javascript
server file name. So for our situation, we actually go to our servers folder that I just showed
you at the beginning and go to the terminal server and terminal server dot PY file over the terminal server has the ability to run
commands in your terminal. So we're going to ask
our MCP client to make a file and add some text to it and let's see how that
works. Let's press Enter. All right. So this means now
our MCB client has started. Let's provide a query. So let the query B make Lang
chin MCP client or TXT, and add the text I made an MCB client with Lang chin
successfully A with Smiley. So let's press Enter Right, so you can see the
response over here and we have the human message, which says make file Lang
chain MCP client or DHT. And this is basically
the query that we sent. And then we have an AI
message which has no content. Then we have a tool message, and this would have
been a tool call. You can actually just directly print the response
to see more details. I'm just printing
out the content over here using my
custom encoder. And then finally, you
have the AI message with the content
which says, Okay, I have created the file
Lang chain CP client dot TXT and added the text you
provided anything else. So let's just check
this out now. Let's go to find a and I have my MCB directory
open over here. I'm going to go to my workspace, and I see I have Lang
chin MCB client dot text, and you can see
that it has added the text that I asked for, and this works amazingly well. So this is how you can create an MCP client with Lang chin. Basically, it provides
wrappers for all kinds of AI models and for
the MCP connections, which help us simplify the implementation
of the code a lot. And you would notice this if you have actually seen
the video of building your MCP client
without Lang chin and this particular video
of building with angin.
17. SECTION 6 START Build MCP Client with Multiple Server Support: 6.1 Introduction: Everyone. Today, we
are going to make a Lang chin based MCP client which supports a JCN config
for multiple servers. And so we can actually
provide a file like the AI language
underscore config dot JCN which can actually
specify multiple servers, similar to what you
have for clot desktop. But this time we're going
to make it on our own using Lang chin or Lang graph
for the react agent, and we are going to use MCP and Google Gemini API for the
large language model, and we're going to
do this in Python.
18. 6.2 Let's look at the config.json file: So let me just show you the sample conflict dot
JCNFle that I'll use. Over here, we'll have
two MCP servers defined. One will be the terminal server that we have previously
built in this course, and the other will be a
pre existing MCP server, which for now, I'm
using as fetch. You can choose any one
of the MCP servers, and I'm going to actually use the Docker command to
run both these servers. And we've already
looked at how to build a terminal server
and put this in a Docker container and then
use it with your own client.
19. 6.3 Demo - MCP Client with Multiple MCP Servers: Before I start, let me
show you how this works. And for that, I'll head
over to the terminal. So let me change to
our MCP directory, then to clients,
then to MCP client. Then let me run our
Lang chain MCP client with conflict PY. This is going to
start our MCP client. And as you can see, it basically connects to the MCP
server terminal server, which is our first MCP server, and it loads the
tool run command. And this basically allows
the MCP client and LLM to run commands on the terminal on the
local host machine. And then it says, I've loaded one tool from terminal server. Then it connects to
MCP server fetch. This is the pre existing
reference server from the model context
protocol Github and it loads the tool fetch, which can basically fetch
content from a URL, and then it says, I've
loaded one tool from fetch. And then it says
the MCP client is ready and then
type quid to exit. So let's ask the client first what all tools are
available to it. All right. So what tools do you have
available to use? All right. So to this query, basically the responses that I have access
to the following tools. Run command to run
terminal commands and fetch to fetch
content from a URL. Now, let's provide a query to fetch content from
a Wiki URL and then summarize it and write it to a file using a
terminal command. All right, so we'll
write the query. Can you fetch content from
the Wiki page for airplanes, first few lines, and
write a summary of the content to a file
called airplanes dot TXT. Let's press Enter and
see what happens. All right, so I explicitly specified the Run command
tool over here since Gemini seems to not recognize this while writing the content. Okay, guys, now this is the
response that I've got. So there is the
message that we sent, and then we have the
tool message which says contents of the
Wikipedia page on airplanes. And then it says that you can actually call
the fetch tool with a start index of 500 to get more content, and
then it does that. It says that the
content was truncated. I will fetch more content
to provide a summary. And this is more content. And then again, there's
a new start index, and then there's
again, more content. And then seems there
was a connection issue and it could not fetch more. So it says I was unable to
fetch the entire content of the Wikipedia page due to truncation and
connection issues. However, I have the first
few lines and here's a summary fetch for
the fetch content. And then at the end, it says that I will use the Runommand
tool to save the summary. And then I fetched the
first few lines from Wikipedia page for airplanes and created a summary
of the content, and I've written this
to a file called airplanes dot THD using
the Run Command tool. So let's go to the finder
and check this file out. So here's the file airplanes dot HD in our
Workspace directory. And if we direct
click to open this, we can see that there's
a summary of content, whatever it was able
to fetch over here. So an aeroplane is a
fixed wing aircraft propelled by a jet engine, propeller or rocket engine. They come in various sizes, shapes, and wing configurations, and are used for recreation, transportation, military,
and research, and so on. So it seems to be that it has actually written a summary
of whatever content it could fetch from the Wikipedia
page and written it to a file using both fetch
and the Run command tool. So this is pretty amazing. Now we have our own MCP client, which basically works with
the Google Gemini API, and it can actually connect
and initialize servers based on a conflict dot cN
file just like Cloud desktop, and then use the
tools to perform actions using the
react agent from LangrapT makes a client more robust with this
added functionality.
20. 6.4 MCP Client (with json config) Code Walk Through - Part 1: Alright, so now let's
walk through the code for this client with the confit. So the name of the file for this client is Lang
chain MCP client. Wconfg dot py and W Config
basically stands for W ConfiC. This code implements a
Lang chain MCP client that loads a configuration from
adjacent file specified by the AI language config
environment variable and connects to one or
more MCP servers defined in this convic. It loads the available
MCP tools from each connected
server and then uses the Google Gemini API
via Lang chain to create a react agent with
access to all the tools, and then it runs an
interactive chat loop where user queries are
processed by the agent. Now this client builds up
upon the previous client that we built on Lang
chain ncpclientPY. So in case you've not gone
through this particular code, please do that by looking at the previous
video. All right. The first thing is
that this client uses the AI Language underscore
config environment variable to specify the path for the AI language
conflict dot JCN file. If that path is not set, it uses the default file available in the
code root directory, the AI language underscore
conflict dot JS. Now if you scroll down, we have the regular imports
that we have. We have our custom encoder. Then we now define a read
conflict JSN function, and this basically reads the
MCP server config Jason. And again, it tries
to read the path from the AI language
environment variable, and if not self, it falls
back to a default file, the AI language conflict or Jason is the same
directory as the code. And it returns a dictionary
past JSN content with MCP server definitions. This is the attempt to read
the environment variable. And then if this is not set, there's a fallback to get
the script directory, and then join the file name with the script directory to
get the actual file path, then it opens the path,
whichever it gets, either the environment
variable or the default path, and it returns the JSN that
it's loaded from there, we handle exceptions to print
an error message and exit. Then we instantiate
the Google Gemini LLM. One difference over here
is that we are using Gemini two point oh flash and
there's a reason for this.
21. 6.5 Why Choose Gemini 2.0 Flash and not the Pro Models: Reason is that you
can actually go to atudiogogle.com and login
with your Gmail account, and then you can check out the different models over here. So Gemini two point oh
flash has a request per minute rate limit for
15 requests per minute. If you were to look at
some of the pro models like Gemini 2.5
Pro Experimental, it has just five
requests per minute. Now, this is also
the case with 1.5 P, which has two requests per
minute. This is too low for an agent execution which might need multiple
calls per minute, but we don't expect to go
over 15 requests per minute, which is provided by
Gemini two pinto flash, so we are good with this
particular model selection. So that's one difference
from the previous client, and then we have the Google
API key which we load.
22. 6.6 MCP Client (with json config) Code Walk Through - Part 2 (continued): Have the run agent
function which connects to all the MCP servers defined
in the configuration, loads the tools, and creates a unified react agent
from Lang graph, and then starts an interactive
loop to query the agent. This is unlike the previous client implementation
where we just had one server that we were connecting to
through a Python script. So we basically first
read the confit. Then we basically get the MCP server definitions
from the confit, and if there are no
MCP servers inside it, then we basically
print an error message and we then define
and initialize an empty list to hold
all the tools from the connected servers
because we want to unify them into
one single list. And now basically using
this ASNcEit stack to basically manage and cleanly close multiple ASYnc resources, we iterate over each MCP server defined in the configuration. So now we basically have
the server name and the server info in
mcpservers dot IM. So we have loaded the MCP
servers from the confit file, and let's look at
the confit file once again to understand this. So MCP servers has these
items, item number one, and item number two,
and each item has a server name and then it's information in terms
of command and arguments. So now, basically, you
get the server name, which basically corresponds
to either terminal server or fetch and you get
the server info, which basically corresponds to all the information that
we have noted against it, the command and the arguments. We then print that
we're connecting to MCP server with a specific
name and then we create the STDIO
server parameters using the command and arguments
specified for the server. So we'll specify the command, which in this case is going
to be Docker for both. And then we specify
the arguments like so, and for Docker, we basically use the Docker image
file over here. So this basically gives
us server params, which we are then
going to use to establish an STD IO
connection to the server. So we use the server
parameters and then we get the read and write
stream objects for this particular server. So we are looping
through each server. So for one server at a time, we do all these steps. We then use the read and write parameters to create
a client session, and then we initialize
the session, and then we load the MCP
tools into server tools. And for each server tool, we'll basically inform
the user that we have loaded the tool
with a specific name, and then we append it to
our list of unified tools. Once we are done
with all the tools for this particular server, we'll say that we have basically loaded tools from this
particular servers, and we'll use the length of server tools to denote how
many tools were loaded. Then the four loop continues
for the next server, and this way we load
all the tools into a single unified tools list that we are then going
to pass to the agent. All right. So then we have
some basic error handling, and after that, we
basically create the agent, which is a react agent from Langrap and we provided the LLM, which is the Google Gemini
LLM instantiation that we did above along with the unified
list of all the tools. Then we start an
interactive chat loop like we did for our
previous MCP client, and this expects a quick
statement to quit the loop. Otherwise, it basically
invokes the agent with the query provided by the user and then
prints the response. And then, of course, we have
the entry point over here. All right, so that's
all about the code for the ankchin MCP client
with the JSON confit file.
23. 6.7 How to use existing MCP servers from MCP Github (example uses "fetch" server): Now let's look at the
config file itself. Now, you would remember
terminal server from the previous
videos in this course, and we have used
this previously with the Lang chin client
without the config. Now we actually add more
servers to this, and for this, I have used a preexisting
server just to check out my implementation of the
MCP client with the config. For this, you can choose any
specific server you want. What I have done is I
have searched for the MCP servers list and then
gone to the Github page. And over here, you have
several MCP servers. I have chosen the fetch
server from here, which basically can fetch
web content because what I aimed at making as a
demo was to fetch content, summarize it, and
then write it to a file using my own
terminal server. So if you click on
the fetch server, you can see that they
have a Docker file, and they provide a configuration to add to Cloud using Docker, where the command
Docker is used, and then they have the run
command with the image name. So when setting this up, what you need to do is you need to first clone
these servers. So we head back to the main
Gu page for all the servers, and then we actually
copy the clone command. I have SSH setup, so I'm going to copy the SSH
clone command from here. You can open a fresh
terminal window and you can go to CD MCP, and over here, you
can actually make a directory for these
reference servers. So you can actually clone the servers into a directory
called servers Ref. I've already done that,
and these are basically all the preexisting
reference servers provided on their Github page. To do this, just type it
clone and then paste what you copied from there and provide
a name for the directory, which is servers underscore
reef and then press Enter. I've already done this, so
I'm not going to repeat it, and you can clone the directory like this to this
particular folder. Note that we are doing this
under our MCP directory, and this is going
to make the servers underscore ref
directory inside it. You don't need to
create it separately. Once you have this created, you can actually shift to
the server's ref directory. And then you'll find
a SRC directory, which is a source
directly inside it, and then you'll find the
fetch directly inside it. Now I'm inside the
fetch directory, and you can do an H L to list
all the files over here, and you see that you have
the Docker file over here. So before we can use this particular MCP server in
our MCP client confit file, we need to build
the Docker image. So for that, we can use
the command Docker build. T which basically tags the
image with a specific name. You can provide it
with some names. Let me just check the
name that I have used. I've used MCP Feed server test. I'm going to just copy it
to demonstrate how you can build it and this
is how you build it. Then you put a dot at
the end to basically provide the current directory as built context
and press ender. This is basically going to build the Docker image for you. Now your Docker image is built. And then you can actually go to the AI Language underscore
conflict dot JCNFle when you specify this
Docker image name, Docker understands that this is what you need
to actually run. Of course, if you know
from our previous videos, you need to have Docker desktop running in the background
for which you can actually open Docker
desktop by going to your applications folder and looking for Docker over there. Okay, so with this
conflict dot JCNFle and with the Lang chain MCP client
with config dot py file, we actually have our
MCP clients setup, and now we can basically
head over to the terminal to actually run this
particular client and test it.
24. SECTION 7 START - Server Sent Events - MCP Server and Clients using SSE - 7.1 Introduction: So this is going to be pretty
exciting because till now, we have been working
with MCP servers that have been running
locally on our computer. And as you can see in this
particular infographic, that is what the
general trend has been even with Claude Desktop as a client and Python or JavaScript servers which
run on our computer. Now we have a chance to use
server Sent events and build a MCP server which basically connects through
an STDP connection. So today we are going to
build our own MCP server, and we are going to use server sent events or SSE to
build it and then deploy it on a URL and use that to connect our client
to the SSE server, and we're also going
to try and deploy this SSE server over the web to the Google
Cloud platform for free, and we're going to
try and connect our MCP client to this
SSE server remotely.
25. 7.2 Quick Recap, What is STDIO and SSE?: Before we begin, let's understand the basics
of what MCP is. Model context protocol
is a way to allow structured and
consistent communication between large language models
that are hosted on clients, which we call as MCP clients. And a server which
we call MCP server. There can be multiple servers, and the servers can expose functionality like
tools where tools can provide some kind of extra functions that the server can perform for the
Large language model. Then they can provide
data sources. For example, over here, we have local data source A,
local data source B, or they can provide access to remote services
over the Internet, like we have remote
service C over here. All right. So there are two main
components as we just said. One is the MCP client, and this is the user
facing agent or interface that
sends the prompts, receives the responses from
the Large language model. And handles the
user interaction. This also integrates with
the server and then sends any kind of requests from the large language
model to the server, which then basically responds to your model inputs
with actions, tool calls, data, et cetera. Now there are two ways MCP clients can connect
to MCP servers. One is through SDD IO, which is standard
input output where each client talks to its own server through
standard input output. So there's basically one to one connection between the
client and the server, and basically the client
is given the path to the server file and it starts the server and
then starts talking to. The other way is
server sent events or SSE where multiple clients can basically connect to an already running server using a web based streaming protocol. With SSE, you can basically let your MCP clients connect to an already running server
using a weblink and port, just like connecting to an API. It supports long lived
unidirectional streams of data perfect for receiving
model outputs in real time. On the other hand,
SUD IO requires a fresh MCP server to be spawned
for every single client. That means one
server per client.
26. 7.3 Setup Directories, Clone GitHub Code (git pull only, if done at course start): All right, so let's set up our
project directories first. So in your home
folder over here, let's make the MCB directory. I already have this made. You can enter this
command to make it. Then let's change to
the MCP directory, and let's make three
directories over here, one for all our clients, the other for our servers, and the third for our workspace. So let's make the servers
directory and press Enter. Then let's make the
clients directory. And press Enter, and then let's make a
workspace directory, which we can use for
any kind of files that our server creates or any kind of data that
it needs to read. This will keep the
remaining file system separate from our workspace, which basically avoids any
kind of issues and risks. Okay, now we change to
our servers directory, and we first check out
the code for the server. Please use the link
in the description to do a Git clone
of the server code. And then this will basically
make a directory over here, which is called terminal
underscore server. I already have done that and I already have this terminal
server directory. So this will basically give you the entire code for your server. Let's go to the parent directory and shift to the
clients directory now. And then let's clone the
Github code for our client. Once you do that, you'll
have a MCP client directory, which basically has all
the code for our client.
27. 7.4 Setup Virtual Environment and Dependencies: All right, so now we have
Python and UV both installed, and we also have a Gemini
API key from Google, and we have put that
in the dot ENV file in our MCP client directory. Now we need to continue
with the remaining setup by creating a virtual environment and installing the requirements. So for that, let's do that
first for our client. So let's shift to
the MCP clients directory and go
to our MCP client. Then we write UV VENV
and this basically creates a virtual environment like we do with Python MVNV. Then we activate this By typing source dot
slash Bins Activate. Now we're going to open up Pi
projects file and overhead, we specify the module that we
actually want to build for, which is our client with SSE. We go back to the terminal
and we can simply type UV PIP install dot to then install
all the dependencies. This has installed
all the dependencies that we needed for our client. Please note that
you already have all these files and you
just need to activate your virtual environment
and then install the requirements using
UV PIP install dot. All right, so I'm back
to the home directory. Now let's do the setup
for our server code. So we first head over to
the server directory. So that directory will be
MCP servers terminal server, and then SSE server because this is the directory
that actually holds the SSE server code for
our terminal server. Just to remind you,
the terminal server is a server that
can actually run commands using the shell on
the terminal of your machine. Now we create a
virtual environment by typing UV space VE NV. Now we activate this virtual
environment by typing source dot n slash
Bnlash Activate. Now we open our Pi
projects file over here. And we just make sure
that the Pi module is set to terminal underscore
server underscore SSE. Now we head back to the
terminal and we can type UV PIP install dot, and this is going to install all the dependencies that
we need for our server. All right, great. So
with this our server and our client dependencies
are all installed. We have the code
setup and we have everything else that we need
to now start testing this.
28. 7.5 MCP SSE Server Code Walkthrough: Now let's go through the
code for our server first, and you must have checked out the terminal underscore server
code from our Github repo. And if you have not, you can do that using the link
in the description. And inside that folder, we have an SSE server folder. You might see other files over here, including
a Docker file, which is basically
the simple server that we had made in
our previous video. Now we go to the
SSE server folder, and over here we have the underscore server
underscores dot py file. This is our main server file, and this is what basically we
are going to look through. We also have a Docker file which basically helps us
continualize this server, and we'll use this to
run our server later on. First, let's look through our terminal server
underscores dot py file. I have this file open over here, and this basically
implements an MCP server using the server sent events
or SSE transport protocol. It uses fast MCP it uses fast MCP framework to expose tools that clients can call
over an SSE connection, and SSE allows real time one way communication from server
to client over STTP. This server uses starlet for the web server UCon
as the AHGI server. Fast MCP to define the tools and the SSE server
transport to handle the long lived SSE connections
that you're using. Just to explain this briefly, ASGI stands for Async
server Gateway interface. So it's basically like a
middleman that defines how a Python web server like UVCon talks to a web
app like Starlt. It allows Async input output, so your app can handle
thousands of users, use web sockets and
stream data like SSE. So this is what ASGI does. UVCon is the ASGI web server. It's the actual engine
that runs your Python app. It listens for SEDPRquests, understands the ASGIPtocol and passes incoming
requests to your app, which is Starlt or Fast MCP. It basically brings
incoming data to your app and it sends back
the responses to the browser. Starlt over here is the
lightweight web framework, so it's a minimal
ASGI web framework. And we use it to define the
roots inside our app like slash SSE and slash messages that we are
going to look at below. We use it to define the
request handling logic as well and the middleware
background tasks, et cetera. So this is the web
framework that we're using, and all the connects to MCP, which is basically the language model
communication protocol. MCP over here defines
how the clients talk to the server what
tools are available, how to send receive
structured prompts, responses, tool
calls, et cetera. And finally, we have SSE
or server sent events, and this is basically a
streaming HTP protocol. It's a one way communication
server to client, and it's used for
pushing updates, tokens, results in real time, and it's perfect for AI and
model outputs. In this case, we're using it for connecting the MCP client to our MCP server over GTP and allowing the server to stream back results
from the tool calls. So we basically have a MCP client slash agent
in the browser which connects via SSE
over SGTPT our server, which is the UVCon server, and this uses the
ASGIPtocol bridge to connect to Starlt web app. The Sarlet web app handles all the logic, roots, et cetera, and basically connects
with the MCP server that offers the MCP tools like
Run command and and numbers, which we're going
to look at below. So this is basically the
visual representation of how our flow
is going through. And now we go through the code. So we import our basic libraries and all the MCP libraries. So we have the core MCP wrapper to define tools and expose them. We have the underlying
server abstraction used by fast MCP, and we have the SAC
transport layer. We then import everything
related to Starlight. So we basically import the web
framework to define roots, import routing for SCDP
and message endpoints through root and mount, and we import the SGDPRquest
objects through request. Then we import UCon which is the ASGI server to
run the StarLt app. And then we start with step one, which is basically initializing
fast MCP instance, and this is basically
our tool server. So we call this terminal, and this is our MCP server. We define the workspace as the home directory slash
MCP slash workspace, which we've already defined
in the previous steps. We then define our first tool, which is the run command tool, and the purpose of this
tool is to execute a shell command on the terminal
in our local machine and return the out we use the mcpt tool decorator
to market as an MCP tool, and it's basically
an ASN function that takes in a string, which is the command that we
need to run on the terminal, and then responds
with a string output, which is the output
of the command or an error as the
case might be. And this function
is pretty simple. So we use subprocess to
run this on the shell, and we return the STD SDD error, whatever the case might be. If there's an exception,
we basically catch it and return the exception
string over here. We then have the second
tool which is add numbers, and it's also a simple tool to add two numbers and
return the result. We again have the decorator
to market as an MCP tool. And then this tool takes
in two arguments, A and B, which is the first and
the second number and it adds the two numbers
and returns the sum, which is a float value, which is a sum of A and B. That's a very simple
tool over there. Now we need to create
the Sarlet app to expose the tools
via STTP using SSE. We basically define this
function called create Starlet app this basically constructs a starlet app with
SSE and message endpoints. MCP server is the core
MCP server instance, and we enable Debug
mode for verbs logs, the full Sarlet app with
all the roots. All right. So then we create an
SSE transport handler to manage the SE connections. After that, we define a handle underscore
SSE function which basically is triggered whenever a client connects to slash SSE. So this is the root that
the client will connect to, and it will trigger
this function. And what this does is
basically it handles the new SSE client
connection and links it to the MCP server.
So how do we do that? We open an SC
connection and then hand off the read and
write streams to MCP. And then we return
the starlet app with the configured endpoints. We have two endpoints. One is the slash SSE, which is for initiating
the SSC connection, and then we have the
messages endpoint, which is basically for
post based communication, which basically is used when we create the SSE
transport handler over here. Now we basically start
the server using UVCon. So we first get the underlying
MCP server instance from fast MCP into MCP server. Weiput RC pass, which is
basically for passing command line arguments for
host slash Port control. So we create a parser
with a description. We then add the host
and the port over here. We're using local host and we are using 80 81 as the port, and then we basically get the
arguments from the parser. We built the Sarlet app using the create Starlet app function that we basically used earlier, providing it the MCP server, and we have the
Sarlet app over here, and then we launch
the server using UVCon by saying Uicon dot run, and we provide the
app with the host and the port using the arguments
that we just passed above. So in essence, if we go back to our visual
representation, we have created a
starlet app which when receives a connection
over slash SSE, hands it off to the
fast MCP server, which exposes the tools like
ad numbers and run command. The Sarlet app also provides another route which
is slash Messages, which is for post
initialization communication, and the UVCon server
basically runs this app using the
ASDIPtocol bridge. So this was a quick
overview of the code. But for now we are
going to head over to our client code to just have a quick walk through
of that before we start testing our client
and server together.
29. 7.6 MCP SSE Client Code Walkthrough: All right, so this is the client underscore SSE dot PY file, and this implements the MCP
client that connects to an MCP server using server
Cen events or SSE transport. This client builds up upon the previous client
that we built, which is client dot
PI that uses SDD IO, and the only difference between the two clients is the
difference between SDD IO versus SSE being
used for communication. So let's go through the
quote for the client. We first have some basic
imports over here, which are self explanatory. You can actually
read the commands over here to understand what these are being imported for. Then we import the client
session from the MCP package, and this basically manages all the communication
with the MCP server. We then import the
SSE client helper, we then import the comperens
from the Gemini SDK for AI based function calling and large language
model integration. Over here, we are
basically importing the main Gemini
SDK module, Geni. This we used to configure the Gemini client and send
prompts and get completions. So this is the core
SDK entry point. Then we import the types. The types module contains important data structures used for tool and function
declarations, generation configuration and structured prompts and
response handling. Then we import from types the tool and function
declaration. So this basically imports these specific classes which is used for function calling. The tool represents a tool that can be invoked by the AI model. Each tool tells the
model what it can do, how to call the function, and what parameters
are expected. The function declaration defines an individual functions name, description, and parameters. So we're going to use
this below in the code. Generate content
config is used to configure how the model
generates content. We basically set the temperature and other details over here, and again, we're
going to use this when we go down in the code. We then import the ENV file to load environment variables, and over here, we're basically storing
the Gemini API key. This is a good practice to not expose your API key while you're actually using it in
the code or you're pushing it to a get repository. If you remember we have added the dot NV file to get ignore. We then actually load the file after importing the
required packages. We then have our main
MCP client class with the initialization function that initializes the MCP client. So this constructor is
basically going to set up the Gemini AI client using the API key from the
environment variables, and it's basically setting up the placeholders for
the client session and the stream context, which is going to manage
the SSC connection. The Gemini client will basically be used to generate content and request any kind of tool
calls from MCP server. This is a placeholder
for the MCP session that manages the communication
with the MCP server. These will basically hold our context managers
for the SSE connection. So there will be a
SSE stream life cycle that we need to manage
the context for, which will be held over here and there will be an
MCP session life cycle, which will be held over here. Now, we use the OS dot Get ENV function to actually
get the Gemini API key. In case we don't find it, we raise a value error. And finally, we initialize the Gemini client
with the API key, and this client is basically
used for the communication. So next in the MCP class, we have the connect to
SSE server function. So the steps that we
perform are we open a SSE connection using
the provided server URL. So this is the server URL
that we're going to use. We use the connection streams to create an MCP client session. We initialize the
MCP session which sets up the protocol
for communication, and we retrieve and
display the list of available tools from
the MCP server. All right. So we first open
the SEC connection using the SSE client and
we store it in the streams context variable
that we defined above. So the SSE client
function is returning an Async context manager
that yields the stream. Or the data channels
for communication, and this is what we are going to store in the streams context. So we now get that data
channel or streams using the streams context from above by entering
the Async context. So streams is
expected to be like a tuple like reader
or reader and writer. That the client session can use. Now we create an MCP
client session over here using the streams provided
by the SSE connection. So we have the read
and write streams, we use that to now create
the MCP clients session, and the clients session object
basically handles sending and receiving messages
following the MCB protocol. Again, we get the
session context first, and then we enter this session context to
create the clients session. Now we initialize
the MCP session by calling session
dot initializes, and this basically sends an
initialization message to the server to negotiate capabilities and start
the protocol. All right. So now we are going to
finally retrieve and list the available tools
from the MCP server, and we basically print
for the user that we are initialized the SSE client
and we are listing the tools. And then we call
session dot list Tools. And when we get the response, we get the list of tools from the response as
response dot tools, and then we basically say, we are connected to
the server with tools, and we print the
tool name for each of the tools in the tools list. All right, so then we
have a function which basically converts MCP
tools to Gemini format. So the tools that we
get from MCP have some fields or properties
which Gemini does not expect, and we need to remove them. So we basically have
a convert MCP tools to Gemini function. Let's have a quick look at this to understand what it does. This is a convert MCP
tools to Gemini function, and I'm going to
just scroll down to show you exactly
what it's doing. It basically creates an
empty list of tools and for each tool in the MCP tools that it has received
as an argument, it's basically going to clean the schema for the parameters. What cleaning is done is inside the clean
schema function. And if you look at the
clean schema function, it is basically just
removing the title key if it exists inside that schema recursively throughout
all the properties. And that's it. That's
a simple removal that we need to do
because Gemini does not expect this title key
if it exists in the schema. Now, going back to the convert MCP tools
to Gemini function, after we have
cleaned the schema, we just create a
function declaration. That is how Gemini wants the
function declaration to be. And if you remember, we imported this from the Google
GNI SDK types. This is the type it expects
for how to define a function, and we provide the name which
is the tool dot name and the description
and the parameters which have been cleaned
to remove the title. Now we wrap the function
declaration into a tool, and we assign it to
a geminiTol object, and then we append this to
the list of Gemini tools that we have and return this
Gemini tools list from here. So this is all that the convert
MCP tools to Gemini does. You can have a look
at the function. It's pretty self
explanatory based on all the comments that
I have put over here. So we now get all the
function declarations for the MCP client, and this basically completes the connect to SSE
server function. Next, we have the
cleanup functions. It basically cleans
up the resources by properly closing the SSE
session and stream contexts. So we basically need to manually call their
exit functions, and this ensures that all
network connections and resources are gracefully closed
when the client finishes. And that's what we do over here. Now we have the
process query function and this is a major function that does a lot of work for us to process the query
given by the user. It basically uses the Gemini
API to process the query and the Gemini API might request a tool call via function
calling and if that happens, then the response is passed and the MCP server is invoked
to call the tool. The result from the
tool call is then sent back to Gemini for
a final response. The steps that we
follow are we format the user's query as a
structured content object. We send the query to Gemini, including the available
MCP tool declarations. And if Gemini's response contains a function
call request, we execute the tool
on the server, and then we send
the response from the tool execution to Gemini. If GeminizsRsponse
contains a function call, we send the result
of a tool execution back to Gemini and then
we get a final response, which is processed
as text from Gemini. And then we basically get the final response from
Gemini and return that the arguments are just
the query string and that final response string is then returned as the return value. All right. Let's go
through the code now. We create a gemini
content object. It represents the
user's query and this basically includes
a role which is user, which basically says
that this is coming from the user and the query is
text wrapped in a part. The role and parts are
provided to the content class, and this is basically then used to form the
user prompt content. Then we send the query
to the Gemini model. So we basically use generate
content to do that. We specify the model, we specify the contents, which is just the user
prompt content over here, and we basically generate the content configuration
which we imported above. Over there, we
provide the tools, which is based on the
function declarations that we basically got from converting the MCP tools to Gemini tool definition formats. So we get the response over here and then we basically
parse the response. Before that, we basically create an empty final text list to accumulate the
final response text. Now for the response, we look at each candidate in
response dot candidates, and then each candidate has content parts and we
look for each part. And if the part is
basically a function call, we extract the name
of the tool and the arguments and we
then tell the user that Gemini has requested a call to this particular tool with
these particular arguments. And finally, we attempt to call the specified tool
on the MCP server, and here we basically use the MCP session to call the tool with the name
and the arguments. We get the result
and this result is then put in
function response. Or if there's an exception, we put the exception string
inside the function response, and then we create a
function response part where the response
is the function response that we defined above, and we name it as the tool name. And this is basically the
function response part. Then we wrap the
function response part in a content object. Marked as coming from a tool. The role is basically
specified as tool. Then we again call the Gemini
API using generate content. We specify the model over
here and in the contents, we now include all
the three parts. We have the initial user query. We have the tool call that was requested and we have the
response from the tool call. All three are included so that Gemini has the full
context of what's happened and we give the function declarations
again for continued use. Based on the response
that we get, we basically again look at
candidates content parts and text and we append that text
to the final text over here. In case there's no function
call that was required, we basically just use the
text provided by Gemini and append it to the
final text over here and we return this
final text over here. So this basically is the
process query function. Next, we have the chat loop. Chat loop is basically an interactive chat
loop that runs in the terminal and it basically waits for the user input query. It prompts the user
for the query. If the user type quits, then it basically
breaks out of the loop. Otherwise, it uses the process query function
to process the query provided by the user and awaits the response and then prints
the response over here. Finally, we have the clean schema helper function
that we've already looked at along with the convert MCP
tools to Gemini function, which we also had
a look at earlier. The last but not the least
is the main function, which is the main entry
point for the client. And this basically checks if this is basically
invoked when we run the client Python script and it basically checks that
the server URL is provided. It creates an instance of
the MCP client over here. Then it basically
tries to connect to the SSE server using the
connect to SE server function, and then it basically
calls the client chat loop and prompts
the user for a query. And finally, it basically does all the cleanup that is
required before exiting. And over here, we simply run
this main function using the Async IO event loop by using asyncodt run and passing the
main function over there. That's all for your client
underscoresS dot PV function, and this is our
SSE based client.
30. 7.6 Dockerfile Code (for MCP Server) Walkthrough: Also look at the
Docker file that we are going to use for
our terminal server. It's a simple Docker file and we basically use from
Python 3.11 SLM, which is the
official Python 3.11 SLIM image as the base image. We set the working
directory inside the container to slash app. We copy all the files from
the current directory in the local host basically to the app directory
in the container. Then we install the
required Python packages from requirements dot TXT. We expose the port 8081 so that the container can
accept incoming connections. So basically, 881 is
the port where we'll be able to access
the MCP SSE server. And then finally, we use the Python command to
run our SSE server, which is the terminal
underscore SSE dot PY five. So that's all for
the Docker file. It's a pretty simple Docker file to actually run this server.
31. 7.7 Test your MCP SSE Server and Client Locally: Alright, so now, let's go ahead
and run our server first. So we can click here
to open the terminal. So let's use the
Docker build command, Docker Bildt to tag the image, and let's name the
image terminal underscoe server underscore SSE. And let's not forget
to put the dot for the built context
and press Enter. This is going to build our Docker image with
all the requirements, et cetera, and now this
image is actually built. Now, let's run our server. Now let's run the container
using Docker Run. The dash RM is going to help us clean up when the
container exits. The P helps us match the local port
to the container port. The V is a volume mount, and we are basically mounting the local host volume located at our workspace to root MCPs workspace inside
the container. And then we provide the name of our image and we press Enter. And now we have a server
running at local host 80 81. Now let's run the client. So we're going to click
over here to open the terminal and we're going
to use the command UV run. So we'll use UV run
client unscos dot P, which our client file, and we are going to
provide the URL for our server as local
host codon 881 with the port slash
SSE because that's the place where our server
listens for initialization. Let's press Enter. And now it says initialized SSE
client listing tools, and now it's connected
to the server with tools run command
and add numbers. So let's test this out. So let's ask Gemini to
actually add two numbers. So I'm going to say
add two numbers two and five using the tools, please, because it's
a simple operation, it might just add it on its own. I wanted to use the tool and check if the
server is running over that SSE connection or
not. So let's press Enter. All right, so now
we get the result that Gemini requested
a tool call, add numbers with
arguments A equal to two, and B equal to five, and the sum of two and
five is seven point. So that's wonderful.
Our server is now running over
a HTP connection, and now we can actually go
and deploy this server on the Google Clot platform
and try to use this over a remote connection
because there is still on our local
machine on Local host. So I'm going to deploy
this server on the web. Let's try to do that now.
32. SECTION 8 START - Deploying MCP Server to Google Cloud Platform - 8.1 Create a new Gmail Account (if: Okay, so first, let's set up a new Google account
that we are going to use to create a project on
the Google Cloud platform. So for that, I'll just
simply search for create a new Google account,
and I'll go to that. Let's click over here.
And, um, as you can see, it says you can go to
the Google account, sign in page and click
on Create Account. So what I'll do
is I'll open this in a new incognito window. Okay. And I'll con
create account. And over here, I'm going to use for my work or for my business, and you can choose
this accordingly. Since I'm going to be
using it for an app, I'll choose for my work
or for my business. Okay, and I'm going
to go ahead with a Gmail address over here, or ask me for a first
name or a last name. So just for this demo,
I'm going to put the AI language and I'll put demo as the
last name for myself. You can choose whatever
is appropriate for you. Okay, and after filling
in those, you know, details, it might provide
you with these options. And for now, I'm following
this format of using the AI Language dot Demo. So put in a Gmail
address that you prefer and then click Next. Okay, and then we can create a new password
for ourselves. And it'll verify your
phone number as well. So, um, please put in
your phone number. So I've put my phone
number over here. Okay, so I have set up my Google account that
I'm going to use. It does ask you for a
little bit more information like your phone number or
recovery email and all. So please follow those
steps to set it up. And once you have your
account set up for now, I'm going to use Not now to
set up my business, right, and continue to use the
Google account, right? So now I have this
Google account set up, and I can use this for setting up my Google Cloud project.
33. 8.2 Create a Google Cloud Project: Okay, so now let's create
a Google Cloud project. So we open a new tab and go
to Google Cloud platform. And here we have it. Okay, and let's click on Sign in first. Let's click next.
And a password. Alright, so now we have
signed into a Google account, and we can actually click
on Get Started for free. And All right. And to start for free
with $300 free in credit, you need to click on Start free, but you will need a particular, um, you know, payments profile. Okay, now, since I'm recording a demo and it's not
for an organization, I'll choose sing
individual over here, I'll actually have to put in my details, the real details, true details over here because it does require my
legal name over here. So I'm going to do that
and then click Create. Now I've completed
the payments profile, and I added a credit card, which was charged a very minimal
amount for verification, and we're presented with
this welcome screen. So a small disclaimer over
here that please make sure that you read
and understand all the things that
you are, you know, entering and accepting
as policies from Google, because this is a live
account that can be used for, you know, hosting
things that can be charged amounts, right? And you are responsible for the creation and conduct
of this account. So do give that proper attention and consideration before
proceeding, right? Now, over here, we ask some very basic stuff like what best describes your
organization needs. So let me just say it's a very, you know, small company. I guess startup, right,
and we click next. And what brought you
to Google Cloud? Let's say learn more and Explore and then what are
your interests? So let's choose web and
mobile laps for now. And then what best describes your role and you can put
in whatever suits you. I'm going to put Engineer developer over here
and click Done. Okay. And now, as
you see, you know, you have the welcome
page and you have some free credits to
use over here which expire in around
three months of time. Okay, so let's check out the project that
we have over here. So Google has already
created a project for us, which is called
the MIFirstPject, and it has a specific
ID over here, and we'll stick with this for now and use this
for further setup.
34. 8.3 Install and Setup Google Cloud Command Line Interface (gcloud CLI): All right. So let's
move further. Now we'll install
the GCloud CLI, which is the command line
interface for Google Cloud. So let's search install the
Google Cloud CLI. All right. And we get this link. Let's click here. Okay. And here we are with all
the instructions. So I'm using MCOs and
I'll choose this. So first, what we need
to do is that we need to confirm whether we have
a supported version of Python installed or not, right? And let's do that. So we'll open terminal, and here we have our
terminal window. And then let's just follow the instructions
from Google Spade. So we first need to
check, you know, whether we have
Python three or not. So let's just check
Python three. We and we get we have Python
3.12 0.2 installed, right? And in case you don't
have this installed, what we can do is
that we can install a Python version by going
to Python DelisF Macua. So let's look at that
as well, once, right? So it says, you know, the
latest one is Python 3.13 0.1. So let me just try to
install that over 3.12. So when you scroll down, you see all these files available. Now, since I'm on MacOS, I can use this particular
file over here just to confirm what MacOS
version you are running, you can actually click on
you can actually press Command Space and you can
select about this MAC, right? So what I'm going
to do is I'm going to click on this file. And allow the downloads, right? And I'm going to wait
for this to download. Once you have downloaded
this installer for Macaws, you can go to your downloads, click on this and open that
particular package over here, and you'll be presented
with the Python installer. So you can follow the
instructions in the installer. I'm going to do that
right now for myself. All right, so now I
click on Install. Okay, so now this
is going to install Python 3.13 0.1 for me. So for now, I'm
going to click Close and I'll move the
installer to the bin. And now let me go
to the terminal. And if I just do this again, I'll get the updated
Python version, which is Python 3.13 0.1. So this way, you
can install Python if you don't already
have it installed, and now we can continue with our Google Cloud command
line interface setup. So let's go back
to our browser and click on back to go
back to Google Cloud. And let's go on
to the next step. So the next step is to
download one of the following, which is the package for
Google Cloud CLI. All right. So what I'm going to
do is I'm going to use the 64 Apple M one silicon because that's what
my machine is. Let's click on this and it says we want to
allow download. And again, we allow the download and wait for the
download to complete. Okay, so once the
download completes, let's go and select a downloaded
TAR file and it should automatically expand all right. And now we see that, you know, we have downloaded
the Google Cloud SDK. It's expanded into
a folder over here. And what we're going to do is
we're going to move this to our development directory
that we had built last time. So we'll go to development. Over here, we press
Command Enter to open a new terminal
to open a new tab, and let's just try
dragging it right over here and we have
it dragged over here. So now, you know,
all our stuff is well arranged inside the
development directory. Okay, so now since we
have the SDK downloaded, we can go on to the next steps, which is to actually install it. So let's copy this
command from here to install the SDK and let's
paste it in a shell. And of course, you know,
we need to shift to the development directory
and then paste this command. This is going to
install the Google command line interface for us, Google Cloud command
line interface. And do you want to
help improve Google? Let me choose now
for now over here. Okay, and, you know, it gives me a set of components
that it has installed. Apart from the components,
it'll also update your path variable and enable
shell command completions, and it also asks for a
RC file for any kind of settings that need to
be put in in that file. So I leave this blank
to use the default one. I don't want to run the Python Installer since I've
already installed it, so I'll say no over there, then I go back and start
with the init command. So this is the innit
command for me, and let's just
paste it over here. So this will ask us to actually log into our Google account, and there that is. So you must sign in to Continue. Would you like to sign in? So I'll say yes. So sign in
using your account over here. Once you've signed in,
it asks for permission, so I'm going to click
Continue over here. Okay, and now it says you are authenticated with Google
command line interface. So I'll go back to the terminal, and it says, you know, pick
a Cloud project to use. So this is going
to be the default project that it's going to use, and I'm going to use
the first item so I'm going to press one
and enter that. So it set the
current project now, and it seems all the int
aspects have been completed.
35. 8.4 Build Docker Image for Google Cloud: Guys. So now we are going
to deploy our server to Googles Cloud
using Cloud Run. To deploy our MCP server
to Google's Cloud, we need to first build the Docker image for
that for Google Cloud. Let's change to terminal
server directory. All right, so I'm
going to change to MCP servers terminal server
SSE server directory. Then I'm going to use a
Docker build command. I'll specify the platform
as Linux slash AMD 64, which is needed for
Google's Cloud. And I'm going to tag this as per Google's Cloud
naming conventions, which is gcar dot IO for
Google's Container Registry. Then we have the AI Language Pd, which is my project name, and then we have the name
of the image and the dot for the build context.
Let's press Enter. All right. So once
the build is done, we can actually push the image to Google's
container registry from where we will
be able to actually deploy it to Google Cloud.
36. 8.5 Deploy MCP SSE Server to Google Cloud Run: All right. So once
the build is done, we can actually push the image to Google's
container registry from where we'll be
able to actually deploy it to Google Cloud Run. To push the image,
we are going to use docker pushgc dot I, slash the AI language prod, slash the MCPSSE server, and this is going to push the
image that we just created. All right. So once the
push command is completed, we'll actually run
the Deploy command. This is GCloud run Deploy. We provide the name,
which is MCP SSE server, then we provide the image tag. We basically use the
managed platform, we choose a region and
we provide a port, and we allow unauthenticated
access for this demo. Be sure that you
actually deactivate the Cloud run API
after you have tested it yourself and use some kind of security mechanism on this as leaving it unauthenticated
might be risky. Let's press Enter to deploy it. All right. So this deploys and provides us a URL
for the service
37. 8.6 Test MCP SSE Server on Google Cloud: Now let's clear this and move
to our client directory. Now we'll write UV run and
then we'll write the name of our client and we'll provide
the URL that we just got, and we'll put SSE as the endpoint because that's
needed for initialization. Now, let's press
Enter. And, guys, is this not amazing
that now we have our MCP server running on
Google's Cloud over here, and we're able to run our client with that
particular endpoint. So now we get initialized
SR client listing tools and disconnected to server with tools Run Command
and add numbers, MCP client has been
started type quid to exit. So let's type our query. So we're going to use
the AdnumbersTol, and I'm going to ask
it to add to numbers. Now, I've asked you to add two and four using
the add numbers tool. Since it's an easy operation, I specifically mentioned the
tool to be used because I want Gemini to use the tool rather than just
adding numbers itself. And now we'll check whether
our MCP server hosted on the Cloud can
actually connect with the client that's running
on our laptop right now. Let's press Enter and
see what happens. Okay, so Gemini requested Tool call add numbers with
arguments four and two. That's perfect. And then the
sum of two and four is six. And, guys, is this
not completely amazing that we have been
able to build our MCP server, host it on the Cloud
on Google Cloud, and then basically run this
using a client on our laptop. And this is the
first step towards basically making MCP servers on the web and make them easily accessible to
clients all over.
38. SECTION 9 START - STREAMABLE HTTP MCP SERVER - 9.1 Quick MCP Recap and What is Streamable HTTP: Today, we look at the transport mechanism in
model context protocol, which is called streamable STTP. It's a new mechanism compared to the two
earlier ones that we had, which were STD IO, it's still there, and
then server sent events, which is now possibly
going to be deprecated. I'll go over the client
to server interactions possible over the life
cycle of one session. So you can head over to model
context protocol dot IO slash Introduction and they give a pretty good
introduction about MCP. I'll just run through a
very few basic concepts over here and as you might
already know that it is a communication standard released by anthropic and it's designed to facilitate structured interactions
between clients, which we call as MCP clients and MCP servers, which
you can see over here. These are a bunch of
servers over here which are connecting to a host
with the MCP client. And this client is, for example, application on your computer or mobile device that
requests data or actions. And can access other
resources as well. So the clients typically
host or access an LLM, like Cloud or GPT or
Gemini or whatever. This can be an LLM based agent, and that can then use MCT. So the agent can use Model
Context protocol and have access to all these servers
and tools on the servers. So the agent can decide
that it wants to use a tool exposed by one of these servers and then use that to
access, let's say, a datasource A or a
datasource B through a different server or
access resources over the Internet or remote services like remote service C
mentioned over here. Through a web API
through this MCP server. But the communication that
happens between the host with the MCP client and the MCP server is through
the model Context protocol. The server basically processes the client requests
and sends back responses or it can send back streams of messages through
server sent events, which we look at and
even notifications. So it's a bidirectional client server
communication architecture as you can see over here. Now, since there is this
communication happening, we need to define a standard
transport mechanism for this client server
communication and soci does that and they
give us two of them. So one of these
transport mechanisms is streamable SGDP and the
other one is STD IO. Streamable SUDP enables
communication over the Internet. So this server does not
need to live locally. It can be hosted on
some cloud platform, and streamable SDDP will help your client that might
be running locally on your computer or you might
be accessing it through a website which is hosted
on some other server. So it can communicate to
this external MCP server, which is a remote server through streamable SDDP. Transport mode. So this enables the
communication over the Internet and the servers can handle multiple
clients simultaneously through continuous
message streams. So that's the advantage of SEDP communicate over
the Internet and handle multiple continuous
message streams. With multiple clients. So what is SDD IO? SDD IO is one more
transport mechanism and it is a client
server communication, which on the other hand
enables communication through standard input
output channels. You might have a MCP server B. Let's say that's running
on a local subprocess on your computer and the client server communication
can happen locally. So you can enable that using SDD IO and this basically uses the standard input
and output for communicating to and
from the client.
39. 9.2 Overview of Streamable HTTP using Sequence Diagram: So what are we
going to do today? We are going to explore
MCV communication over the Internet through streamable GDP and
we'll break down each step and we'll show
this in a sequence diagram. So let me head over to
that sequence diagram now. Great. So now let's see
this sequence diagram. This is a diagram that
shows the two participants, the client and the
server in this transport and the interactions between them over this
entire life cycle. So as we go down, we see the lifecycle events
like initialization, client request, and so on. And these are the two participants
that are interacting, and these are the
granular interactions that are happening between them, which are shown by Aosovos. This is basically what
is a sequence diagram. Client will initiate
communication by sending requests
and notifications. The server will respond
to client requests, send back data, manage
ongoing communication, and might also initiate
requests for additional data. Streamable SEDP is what allows this continuous communication
through three mechanisms. There are SEDP post requests. There can be a post request made by the client
to the server. There are also get
requests that can be made to initiate this communication and allow the
communication to happen. There are server send
event streams so the server might send
back streams of data. You might see the
mention of JSN RPC at different places or Jason
messages being sent. So JCN RPC is the
protocol for messages. So the messages being sent are JCN RPC messages
encoded in UTF eight, and JCN RPC is basically a lightweight text baase
format for exchanging data and relies on JCN which is JavaScript
Object notation, then UTF eight is an
encoding standard capable of encoding all
possible Unicode characters. So that ensures that messages sent across the network
are universally understood regardless
of language or symbol complexity, et cetera. So that is the protocol
for messages that is being used inside
of streamable SGDP. So we'll look at five
different aspects of the streamable SGDP
communication life cycle. So one is initialization, which is shown here
in the gray box. So the client does
the initialization. Then in the green box,
you can see client making requests to the servers
and how is that handled? Then in the yellow box, you can see client
notifications and responses. So how does the client send notifications or
responses to the server? Then the purple box over here, we have the fourth one, which is listening for messages
from the server. The client can actually listen to messages being
emitted from the server. And finally, we have
session handling. So how are different aspects of the session handled
in stream let?
40. 9.3 Initialisation Phase: Let's start with initialization. The client sends to the server a post request on the
slash MCP endpoint, which is a initialized request
to start the MCP session. It specifies the
accepted responses, which is application JSON for a single simple response
in Jason from the server. And the other one that it specifies is text
slash event stream, which is basically a stream of SSE messages that
it can receive. As per the streamable
SVB protocol, the clients must
accept both of these. The client cannot
say that we don't accept SSE messages as a stream. We only accept simple
JSON responses, but that can't happen. Client if you build a client, you have to build
for both of these. Secondly, this
initialization request does not specify any MCP session ID. So this MCP session ID will
be generated by the server. It is an optional ID, but in
this full sequence diagram, we assume that the
server does create that MCP session ID because that then enables management of
the session, right? And then there's the
initialized request that is included as part as a part
of this post request. The server will respond with
an initialized response. It provides a unique session ID, which is MCP session ID, and this is what we call
an initialized response. This session ID will be used for subsequent
communications, and if the server responds
with the session ID, the clients need to include this MCP session ID in the header for all
further requests, and that is a mandate
from the protocol. After this initialized response, the client sends a post
request on the MCP endpoint with a initialized notification indicating it is ready
for further interactions. I'll send the MCP session ID and the notification
in the body, and the server will respond
with a confirmation that it's received
the notification with an SDDP 202
accepted response. That is what happens
during initialization.
41. 9.4 Client Requests Phase of Streamable HTTP: Now we go on to client requests. What happens over here
is that the client, which is represented by
this vertical line sends a post request on the slash MCP endpoint
containing the session ID, and again, with
the accept header, which says it accepts
both kinds of responses and a body
with the request. Whatever the request
is whether I want to run this tool or
whatever that might be, the server can respond
and it has two pathwayse. There's one option, which
is a single response. And the server immediately sends a single JSON object response and the response is in the body and that just basically
completes the response. The second option is that the server can open
an SSE stream. So the server either responds with content type application JSON or with a text slash
event stream content type, which basically means that it is initiating an SSE stream, continuously sending data and interacting with the
client in real time, where the server
sends to the client continuously SSE messages
which are real time updates. So these are the SSE
messages from the server. And there's an
optional request for input that the server can send and then the
client can respond. It can send the
required input back. There's also an
optional notification. Jason RPC notifications that
can be sent by the server to notify the client about whatever might be the important
aspects that it wants to. And once all this is done, the server can close the SSE stream with a
final response over here. So this is how the communication happens when the client
raises a request,
42. 9.5 MCP Client Notifications and Responses: Next we have client
notifications or responses. What happens over here
is that the client sends notification
or responses via post slash MCP to the server and the server
responds in two ways. Either it sends the 202
accepted with no body. I just accepts the
notification or response or it can respond with
a 400 bad request, which indicates that
some invalid input with error has been provided
and it'll send back an optional JCN RPC error
response as well with this. So the server either accepts this notification or response or the server cannot accept it.
43. 9.6 Client Listening to Messages from the Server in Streamable HTTP: Next is the client listening
to messages from the server. The client can send
a get request to MCP endpoint and request to open
an SSE stream explicitly. I'll say it accepts texts event stream with MCP
session ID with a G request. And what this means is now that the server can respond by saying that SSE is supported and
it can open an SSE stream, or the server itself
might not support SSE. SSE the server has an
option to not support SSE, so it can say SSE
is not supported. This is a trivial case
where it says that 405 method not allowed, and that is the response
that the client gets back. But the non trivial
case is where it actually opens
the SSE stream, and then something similar
happens to what we have seen when clients
initialize requests. So the server
responds by sending SSE messages in a
continuous stream. Or optionally, it
can also request for inputs and the client
responds with a response, or it can send some kind of a JCN RPC notification
if needed. So these are optional, and
if it accepts SSE stream, it can send messages
over the SSE connection.
44. 9.7 Session handling in Streamable HTTP: Finally we have
session handling and how is this done
in streamable STP? So the first part is
resuming after disconnect. So the client can resume a
connection after getting disconnected by
sending a get request to the MCP endpoint
with the last event ID. And this is an event ID
that the server might have provided in one of the
last messages that it sent. And then the server can take in this request and then
resume from the last event, so it can replay
all the messages, post event ID so that the client can get
that information. So the server also
has an option to end the session if it wants to
for whatever reason, right? And in that case, if a client sends a request with a session ID that the
server has closed down, then the server
must respond with a SCDP four or four
not found response, and the client in turn
has to understand that this session is closed or it
might be invalid or expired, and it has to start
a new session with the initialization request
that we saw in the beginning. So this happens when a request
is made from the client with a session ID that is either closed or invalid or expired. And finally, you have the client requesting
to end the session. The client can send a delete MCP request
with the session ID. So the server has two options. It can either allow that
session termination, and this is the allowed
case over here, and the server in
this case just sends a 204 response with no content saying
that the session is successfully terminated. The server might instead also say that the clients cannot end session like this and this is something that the server
does not allow for example. In that case, it can send
a 405 method not allowed, and session termination
is not permitted, which is what is
communicated to the client.
45. 9.8 External Resource for keeping up to date: According to me, are
the major interactions that happen between
the client and the server in streamable
SGDP when it's used as a transport mode for MCP client server
communication. This covers most of the
interactions that happen. There are a few that might
not have been covered and you can actually look at
this particular website, model Context protocol
dot I to do that, you can actually go to
the menu over here, and then you can scroll down to protocol and go to transport. And once you click
on transports, you have all the details
about how SGDIO works, how streamable SGDP works. And there's a lot
of textual detail. I've tried to convert this into a diagram with most
of these things. But just as a caveat, you should also read
through this and one of the rules is that whenever you work with some
kind of protocol, you understand the ins and outs of this is something
that is updated with every version of
MCP which might not be possible 200% in the video. So whenever you see this video, please go to the latest
version over here, go to the protocol
and understand the transports from here
to see if something has changed or something has
been updated or missed so that you are abreast with all the latest updates
to this protocol. MCP is pretty new, you expect updates to happen
very, very fast as well. You can see that with streamable STDP which is now going to be the recommended
transport mechanism in place of the earlier SSE one. This is the sequence
diagram that they have. It has lesser number of
interactions mentioned over here, which just simplifies the
sequence diagram, I guess, but it leaves out a few
of the interactions that we might have covered in
the other sequence i. Keep this page handy
and go through this to look at
the latest updates and for anything that
I might have missed or maybe presented incorrectly
as this protocol is very, very new and you expect this
to change very, very fast.
46. SECTION 10 START - Streamlit User Interface for MCP Client - 10.1 Streamlit UI MCP Client Overview: So today we are going to do
something pretty amazing. We're actually going to
add a user interface, as you can see over here to our Python based MCP client that actually works
with MCP servers. We're going to do this
with Streamlt and you can see that UI on my
screen right now. So before we begin,
let's have a look at the user interface
and what we are going to build in this
particular video. So this is how the overall
user interface looks. There's a main area for the chat and a side
bar over here. This is basically the user
interface for our MCP client. The client is built using LangrapsRact agent and uses Gemini's API as the
large language model, and it's written in Python. The UI is built using Streamlt and this basically is
the entire client. Let me zoom in to show you the different components
of this particular client. We can collapse the
sidebar from here, and you have the title
over here with the logo. And you have a subtitle
where I've just put a subscribe link
to a YouTube channel. There's a section where the
chat comes in and you can see the messages sent by the user and the response
from the MCP client. Then there's a chat box to
put your query and you can press Enter to simply submit
your query for processing, or you can press
the Send button. You also have a new
conversation button to start a new conversation, and you can save
the conversation by clicking on Safe
conversation over here. You can expand the sidebar
by clicking this button, and then you have
some settings over. So you can select what kind
of MCP client do you want? So you can either
have MCD client that actually connects
to SDD IO servers, which are basically Python and JavaScript scripts that run
locally on your computer, or you can use an MCP
client that can connect to a server over the web
or the XDDB protocol, which uses server sent events
as the transport protocol. I have included the
code for both of them, but the SSE code is not
fully implemented yet. You can actually use
it as a placeholder or a starting point
if you'd like to. When we select the
SCD IO server, you can actually
upload a conflict dot JCNFle with all the
MCP servers over here, and you can click on
Browse files to do that. Otherwise, the system uses
a default confit file, which is the AI language
underscoecfict dot JSON. You can see the
historical conversations that you have saved over here. These are saved in a
conversation folder with the timestamp at
the end of the file. There's also an expandable log output block over here where you can actually see
the entire log for your MCP client and
server interaction. All right, so let's preview
how this works now.
47. 10.2 Streamlit UI Demo: So let's preview
how this works now. So we press New conversation
to start a new conversation. So let's first type
Hi and press Enter. Now, you can see that it's
basically running it in the background and we get
the response from MCP. Hi there. How can
I help you today? So I'm using user to tag the messages from
the user and MCP to tag the response messages from the MCP client
and server. All right. So now let's write a query. Create a file
streamltUnderscore success dot TXT with the text Streamlt success using
the Run command tool and press Enter and
see what happens. Again, it runs the query. Okay, and we get the response
that I've created this file Streamlt Underscoe success dot tXT with the text
streamlt success. So let's check this out
in the Finder Window. All right, so this is
the Finder window with the workspace for
our terminal server, and we see that we
have a file called streamltUndersco success
dot TXT right over here. Let me open this file now. Okay, and you can
see that it has the text streamlt
success inside the file. This means that our client and server are working well
together with the UI. All right. So you
can also basically click on Save
conversation over here, and this is basically going
to save the conversation. You can click on
new conversation by clicking this
button over here, and this is going to start a
new conversation over here. Now, when you actually click
on this sidebar over here, you have the option
to load conversation. So let me just load this
particular conversation. And this is the latest
conversation that we just saved, and you can see that you have the entire message
history over here. Now for the MCP
client Config JCNFle, you have the default file, the AI Language
underscoconfg dot JCN, which is basically in the same directory as the application. In case you want to
provide your own file, you can click on Browse
files over here. You can choose a file and
it'll basically replace the content in this AI Language underscoconfit dot JcN file, and then your client is basically
going to use that file. You also have an option to
change to a different kind of MCP client and server
combination, which is SSE. So SSE stands for
server sent events. It's a transport
method that is used by MCP clients to
work over STTP. I have provided some
placeholder code for this, but this is currently not active in this particular
client implementation. You can have a look
at that code and modify it for your
needs if you need to. I've just worked with the
SDDIO client over here.
48. 10.3 Comparing our UI with Claude Desktop!: Is basically to show how you can actually build an MCP client, and this is the first step to build an app of
a magnitude like, let's say Cloud desktop, which is also an MCP client, one would need to
actually include a lot of features to have a
wonderful user experience. But this is just meant as an educational code to
show you how you can use streamlt to build
a basic client UI for your MCP clients
and servers.
49. 10.4 Setup Directories (skip if done earlier): Once you have UV installed, you can actually set up your
MCP directory structure. So what I do is
that I actually set up an MCP directory
in my home folder, and then I set up three
directories over here, clients servers and workspace. In the clients directory, I save all my clients, and I'm going to save the
Langhain MCB client over here. In the servers directory, I have saved all my servers, all the prebuilt ones
that I have downloaded, and all the servers
that I made on my own. Then I have a workspace folder over here because if I want the MCP servers to take any actions locally
on my computer, I use the workspace folder for any kind of file creation,
deletion, et cetera. For now, we're going to
head over to clients folder and you see that I already have an MCP
client over here. You can actually use the
Github repository link to check out this
MCP client folder, once you have checked this out, you can actually go
inside the folder
50. 10.5 Setup Google Gemini API Key (verify again if done earlier): Before we do that, we
need the Gemini API key. So I'm going to head
over to Chrome, and I'm going to go
to atudiogogle.com. When you reach this site, just click on Get APIKey. You, of course, have to be
logged in using your Gmail ID. You can click Create
APIKey to create a key. I already have a key
created over here and I can just click this button
and copy the key. Do make sure that your
API keys are secure. You should not share
them or embed them anywhere in the code
or in any repository. I'm going to disable this APIKey after I finished
recording this video, so I'm going to just let it
remain visible over here. Once you have copied
this APIKey head back to the terminal and create an environment variables
file by typing touch dot NV and press Enter, I already have this
file over here, so I'm going to just open this
using NNO and as you see, I have defined
APIkysGemini APIKey and Google APIKe and I have
pasted my key over here. You can actually
write this line over here and paste your
key next to it. I have used two
environment variables, Google APIkey and Gemini APIKey because in my personal project, I like to use the environment
variable Gemini API key. However, an chin
expects the key to be present under an environment variable
called Google APIK. Just to avoid any confusion and allow both of the
variables to be used, I actually defined the key twice using two environment
variable names. You can do the same.
For Lang chain, you need to use Google
API press Control X and save the file
before exiting it. Now you have your
environment file set.
51. 10.6 Create Virtual Environment and Install Dependencies: We can create a
virtual environment which basically helps us keep our project dependencies
separate from all other work that we're
doing on a computer. So you can type U V, NV and press Enter to actually
create that environment. I already have created that, and if you're following along
from my previous videos, you would also have this directory and
environment created, so you don't need to create it. For everyone watching new, please create this environment. Once you have this
environment created, you can actually
activate it by typing source dot nvlashBN
slash Activate. And this basically
makes you enter the MCP client,
virtual environment. Okay, so now we are going
to add some packages that we need to develop
our MCP client. These packages are Lang chin, the Lang chin MCP
adapters, Lang Graph, Lang chin Google Gen AI package, Google Genitive AI package, and the Python dot
Environment package for the environment variables. After writing UV ad and the list of
packages, press Enter, and I already have
this installed, but it will take
a few seconds or a few minutes to get this
installed. All right. So once you've added
all these packages, you need to add the
other packages for specifically streamed it and
the UI that we are building. So we're going to add the
streamlt package from the file. We're also going to add Nest underscore AC IO and we're going to
install Pillow as well. So these are three additional packages that we
want to install. All right, so just type UV add Streaml Nest underscore ASN, IO and Pillow, and this is going to install these
three packages as well. Alright, so this installs all the dependencies
for your client.
52. 10.7 Get Streamlit UI Code: All right. So to get the
code for the UI, the client, and the server, you can actually refer to the links
in the description. You'll see that right
now we are referring to the MCP client
folder where we have all the clients that
we have previously implemented if you have followed through the previous videos. Over here we have three
different files of interest. One is the streamlint
client UI dot PY file. This implements the
entire streamlint UI, and this connects to the
different client implementations above this file over here. So we have two client
implementations for now. One is the SDD IO
client implementation. Again, this is basically
all the servers that you can actually specify
in your conflict file, and these run locally
on your computer. You also have SC dot
PI implementation, which is right now just
a placeholder for you to use and implement further if you actually want
to work with this. For now, I'll be covering the UI file and the
SDD IO server file.
53. 10.8 Streamlit App Imports and State Initialisation: Let's go through the
streamlt client ui dot py file code work through
and setup together. So to begin with, we have
the imports and I've added very detailed
instructions throughout the code for you to
help understand what each line means and also
the install instructions. So Streamlt is the code
library that we use to build the UI and we
import it over here. Then we have ASN Kio
for ASN processes, especially for the
tool communication and we import it over there. We have OS which basically provides file system
interactions, and we have JCN
to read and write the conversation
data to JCNFles. We then have datetime which
is used for timestamping, we have Nest Async IO, which you can actually
install by typing PIP install Nest Async IO. This basically allows nested
use of Async IO event loops, and this is useful in interactive
notebooks or streamlt. Then you have the
Python image library, and this is used to basically
style the images like the logo in our tool and you can install it by typing
PIP install Pi. Have the estimL components for basically more
advanced customization and this basically
completes all our inputs. Then you have nest
underscore asynco dot apply, and basically in Python, when you use something
called Async or await, you're telling the program
that do this task in the background while waiting
for other things to finish. But Python normally only allows
one event loop at a time. Streamlt and Jupiter, all tools like them already use an
event loop in the background. So if your code tries to
create or run a new one, Python throws an error. So this line is saying
that it's okay, let this new event loop run
inside the existing one. It's like nesting one
loop inside the other. So this basically patches
Python's Async IO, you can safely run
nested event loops, especially useful in interacting environments like the
client that we are building with streamlt
or things like Jupiter notebooks or even repels that have already running
loop inside. Now you have the app session
state initialization. We have a conversation list. If that's not already present
at the session state, we initialize that
with an empty list. We have a client
config, and again, we initialize that as SDD IO, which is the type of the client. This is extendable to
different client types. You can basically include
categorized clients over here. You have a server URL for SSE type of
clients and you have a SDD IO config file for
the SDD IO type of clients. Then you have a placeholder
for a reusable client object. So if client instance is
not in the session state, we basically assign it to none, and we'll basically
initialize this later on. There's a theme for
toggling dark mode. I was trying to include
this in the code, but I ended up not including it. But if you had to, you would store that in the session state. Then you have the
logs which we saw in the log blog and we initialize that to an
empty list. All right. Then we have a submit
triggered flag in the state, and this is just
to identify that if somebody is
submitting the query in the chat box by
pressing the ent key rather than clicking
the Send button. Then there's a flag to prevent infinite reruns of the loops
after processing a query, it's a query executed flag, which is initially false and
that's what we set it to. We'll use it later
on. And there's a pending query
string to actually hold the current query to be
executed after submission. Again, we'll look
at the usage of these state variables
as we go along.
54. 10.9 Code for Utility Functions: We have some utility
functions and the first one is run
async in event loop. So this function is
like a task manager for background jobs or also
known as routines. Normally to run an
Async task in Python, you need something
called an event loop, and it's like an engine
that handles multitasking, but streamit already
runs its own engine. So if we try to run our own separately, Python
gets confused. What this function does
is that it checks, is there already an
event loop running? If no, it basically creates one. And if yes, it politely
asks the running loop to add our task to its schedule
and waits for the result. Then we have the
add log message. So it's a logging utility
that logs the messages with a timestamp to both
console and UI logs. So this function is like
a note taker or diary. So every time
something happens like receiving a message or
processing a query, this function writes it down. So it adds a timestamp, so we know when it happened, it adds the message
to both the screen, which is in basically
the streamed state and the console which is basically printing it on the console. This basically will help you in debugging flagging events, or just seeing what
happened when.
55. 10.10 Streamlit App Sidebar Code: So then we have the sidebar for the settings
and the log view. So this line with
st dot sidebar, this creates a sidebar panel on the left side of
the streamlt app, and everything inside this block gets displayed in that sidebar. It's like saying that, whatever
you're adding over here is basically stuff added to the left hand
menu. All right. So sd header settings adds a section header labeled settings at the top
of the sidebar. So this basically
shows a radio button that lets users select
between SDD IO and SSE. The selected option is stored in the session state under the
client config as client type, so it can be used later. And we use Adog to basically log this selection which shows up in both the terminal and
the UI. All right. So then we have the
SDDiocfig dot Json or the fallback to the default option if we
don't provide a file, so what we do is we
basically upload our Cfict dot JCN for SDD IO
using a file uploader. So this basically adds a file upload option where the
user can upload at JCNFle. The JCNFle is expected to have the confit for
the MCP servers, and this is used later to
connect to the Bend tools. So if a file is uploaded, we save it to disk with the name the AI language underscore
config dot JCN. This apps the standard config file your Bend code expects. So if the file is uploaded, we basically store
it on this path, which is in our, you know,
directory over here. And we'll actually open
this file and save it. Then we'll set the client SDI on Square Config
with this path, and we'll also print
a success message and analog and add
this to the log. If a file is not uploaded, we'll basically use
the default file, the AI language
underscconfg dot JSON. And just after a basic check if that path actually exists, which it should because we already have the file over here. We basically again set it to SDDI or config in
the session state, and we add a log
statement around that. If just in case this file
is not present, you know, we'll actually provide
a warning for that and again add that warning
to the log. All right. So then we have a section on
loading the conversation. This is basically a
horizontal divider, and then we have a header with the title
load conversation. So we then set up our
conversations folder or directory, and we actually make that
if it already exists, that's okay because
this prevents any kind of errors
because of that. Then we list all the files
from the conversation folder. And if we do find any
conversation files, we basically go through each of these files and we
get a file path. Then we open the file in read mode and we load
the Json from the file. We load a review from the first 20 characters in
case you want to show it. And then we add a button to load this
particular file name. If the user clicks
this load button, the conversation is
stored into memory, and sessionstg dot conversation
is set with that value, and then a success message is
basically shown on the UI, and it's also added to the log. And if no old chats were found, we basically then, you know, provide information
that we don't have any saved
conversations at this time. We finally have, again,
a section divider, and then we have a logs viewer, and this is basically
a text area with all the logs from our
session state logs.
56. 10.11 Building the Main Chat UI for the App: So next we have
the main chat UI, and on the top of the chat, we first render the
logo and the title. So call one and call two basically splits the top of
the app into two columns, and column one is basically one by seventh of
the total width, and column two is basically six by seventh of
the total width. And this is basically
used for the title, and this is used for
the logo. All right. So for column one, we
will load the image that's present at
assetlashogo dot JPG, and you can go and see
that we have an Assets folder over here with the
logo dot JPG file over here. Then we take the smallest dimension height
or width to make a square crop and this will basically help us make
the logo circular. This creates a circular mask, a white circle on
background that will apply to the image
to make it look round. Now, this crops the original
logo into a square and applies the circle mask
as Alpha or transparency. Anything outside the circle basically becomes transparent. This then finally displays
the circular logo in the first column using full
width of its container, and that's how you see the
circular logo on the top. All right, so then you have the second column, and over there, we basically show our apps main heading in
the right column. Okay, so for a subtitle, I use a YouTube call to
action as a clickable link, and this is basically
what we do over here. The unsafe allow STML
equal to true basically allows us to use basic SML
formatting like bold text, and the markdown formatting is basically used
to style the link. Then we show the
past conversation. So basically loop through all the previous chat
messages stored in memory. We get the timestamp, we get the sender, and
we get the message. And then we display
it in bold using markdown format using
this particular line and in this particular format. Then we have a callback
function that sets the submit triggered flag
when Enter is pressed. So this function is
called automatically when the user presses
enter in the box. So we first check if
the input isn't empty. So if it's not empty, we set the submit
triggered flag equal to true to indicate that a
query needs to be processed. This just means that, and then we copy the text to
the pending query, so we can use it after
streamlt loads the app again. On it. So then we
have the input box. So this basically renders
a single line text input. The label is your query. When the users presses Enter, submit on Enter is
automatically called, and the input is
basically stored in query underscore input
inside the session state. The placeholder text that we
use is type your query here, and this is that input box. Then we have three action
buttons for sending the query new
conversation button and a save conversation button. So the Send button basically
submits the query to the MCP agent the new
conversation button clears the chat history
and starts afresh. The save conversation
button saves the current chat log as
a Todd Jason file.
57. 10.12 Core Logic For Query Handling: So now we have the code
logic for query handling, and first, we have
this function. So this is an ASN function
that sends the user query to the back end via SDD IO and returns the
response as a string. We add a log entry that
we using SDD IO mode. Then we attempt to import the run agent function from
streamlt client SDD IO. This is the implementation
that we have over here, and we'll go through
this later on. And we handle any kind of import error if it
happens over here, and then we basically
run the agent with the query that we import it over here and the query
that we received over here, and we get the result, we add a log saying that we have processed the query and
we return the result. So then we have the
handle query function and this function basically handles the full lifecycle
of a single message. The user sends it, we send it to the back end and then we
display the response. So if the user types quid, we set the conversation to an empty list over here
and we log the same. Otherwise, we get
the current time to timestamp the user message. We save the user query in the conversation history
and we log it over here. Then we send the query to the back end using the
function that we just looked at and we wait for the response and we are using the SDD
IO client over here. We save MCPs response over here. This is the assistance message basically and we log
that two over here. Then we set query executed equal to two so that
we don't keep on re running this query on
each reload and this basically helps us prevent infinite loops for
running the query.
58. 10.13 Trigger Logic for Send and Other Chat Buttons: We have the main trigger logic. So if the user has clicked on the Send button or the
user pressed Enter in the input box and the query hasn't already been
executed in this cycle, that is the query
executed flag is false, we grab the query from the pending query
in session state, and if the query exists, we basically run the life cycle for the query using
handle query, which we just looked at using this helper that we looked
at in the beginning, which runs async
code inside stream. And then we reset
this flag to false so that the next keystroke
doesn't reiterate. Then we re run streamlt app after query completes
to refresh the UI. If the query executed
is equal to true, we will reset it to
false so that we can basically set up for
the new query and we force a UI rerun
usingst dot rerun to refresh the screen and
show the new chat messages. Then we have the new
conversation button logic. Basically clicking this clears the entire chat history and logs it and then it reruns the app to reflect
the empty state. Then we have the save
conversation button. This is basically going to
help us save the chat to a file we basically
make sure that the conversations directory
exists over here. Then we cream a
timestamped file name using this particular statement, and then we provide a path with the conversation directory and the file name joined together. We open that file and
then we basically use Jasen ton dump to dump the
conversation over there. We then basically add a log regarding our
successful save, and we also show it on the UI. This is our entire
streamltUnderscore client underscore UI dot PY file.
59. 10.14 Streamlit App MCP Client Code: So now let's look at the streamt
client stdio dot PY file that we're actually using to run the agent inside the MCP client. This is basically the Lang chain MCP client with config agent. So if you've already looked at that particular
lecture or video, you can actually skip
this explanation. Otherwise, we're just
going to quickly walk through the concepts
over here. All right. So this file
basically implements a lang chain or Lang
graph based MCP client. It loads the config from
a JCN file which is specified by the AI language config variable
environment variable. And if you don't find
this, it basically falls back to the local
file over here. It connects to all
the MCP servers that are defined in the config, loads the available MCP tools
from each connected server, and it uses the Google
Gemini API via Langraph and anchin to create a react agent with access to all the tools. Then it runs an
interactive chat loop, so you can actually independently
run this file as well. But we are going to use it
with our client UI. All right. So then we have the regular
imports that we have looked at earlier when we have been designing our clients. So we have these imports
for Async operations, environment
variables, et cetera. We have the MCP client
related imports for managing the MCP client session and server and for establishing
a STD IO connection. Then we have the agent and
large language model imports. So we have an adapter to convert the MCP tools to
Lang chin format. We have the react agent that is pre built and comes
with Lang graph, and then we have the Google
generative AI zapper for the Gemini API. Then we set up our environment, so we basically load the dot ENV file, which
is our environment. You can actually
see over here that we have the dot ENV
file over here, which is this, and you have a Google and Gemini API key defined over here,
which is the same key. We just use two
different variables to, you know, handle both
options in our code. Then we have a custom encoder,
which basically, you know, manages the formatting
of our JCN content by checking for a content
attribute inside the JCN keys. Then we have the read
configuration function. So this basically reads
the MCP configuration JSON so we actually have the AI language conflict
environment variable. If that is set, we use
that conflict path. If that's not set, we basically check
the current directory and we formulate the
conflict path based on the current directory
name over here and the AI language
conflict dot JCNFlem. So this basically gives
us this file path. Then we open whichever file, we have selected
over here, we open that and we load the JCN. We of course do some
exception handling over here to handle any kind
of errors. All right. So then we have the
run agent function and this basically
takes in the query as a string and returns a string output for the
UI for our MCP client. This is very similar to
what we have done over here in lang chain MCP client
with confect or PI. The only difference
is that the query is a input over here that
comes from the UI rather than coming from the
function over here itself as a query input as a user
input from the terminal. So basically this connects to all the MCP servers defined
in the conflict file, loads their tools, and
then processes this query. Now, this is the Google
Gemini LLM instantiation. We have used this to specify
the model over here. We also use the API key that we are basically getting from
the environment variables, and we then set up
some other parameters which we have explained
over here in the comments. Then we read the conflict file
and we get the MCP servers list from the conflict
file by using the MCP servers key
in the config Jason. If there are no MCP servers, we just print that we could
not find any MCP servers. Otherwise, we create an
empty list of tools, and this is basically
in the end, going to be used
to pass to Gemini. Or the react agent that we
are using through that, it'll basically go to
the Gemini APA so that the late language model understands what tools
it has available to use. All right. So we use
an Async exit stack to basically manage multiple
async resources, and then we iterate over each server name and server info in the cp servers dot items. Let's just quickly look at our AI language
confit dot JCNFle and you can see that
inside MCP servers, we have two servers,
terminal server and fetch. Terminal server is basically the name of one server and fetch is the name
of the other server. And everything else
is the server info, which basically is the
command and the arguments. Now, going back, so we now understand we have
the server names and the server info in
all the MCP server items. Then we basically
take one server at a time, we connect to it. We then get the
server parameters using the command
and the arguments. We use the server
parameters to establish an STD connection to the server, and we get the read and write streams for
that connection. Then we use these read
and write streams from the connection and
we basically create a client session that
we store in session. Then we initialize the session, it basically is a handshake
or setup operation that happens using MCP, we then use Lang
chain's MCB adapter to load the MCP tools
from the session, and we save it in the
list of server tools. Now, for each tool
in the server tools, we basically inform the user by printing that we have loaded this particular tool
and we append it to the list of tools that
we have declared above. We then do a final print over here and catch any
exception over here, and this is basically done
for each server name. In case there were
no tools found, we basically do an appropriate
print statement over here. We finally create a
react agent using the large language
model instantiation that we did above and the list of tools that we
have basically got from the conflict json file after initializing
all the servers. This is our agent, and then we take the query and we invoke the agent with
that particular query. We get a response over here and we then print that response. We print that response
after formatting it, so we basically check for messages in the
response dictionary. And if we do find messages, we take them in reverse order. We want the last
message from the AI, and if it has the
content parameter, we basically turn that content. Otherwise, we basically return that no AI response was
found in the message. Finally, we have the
main entry point for this function defined
over here. All right. This completes the
client SDD PY file. We finally can look at the AI language conflict dot
Json file over here.
60. 10.15 theailanguage_config.json and other files: Let's look at the AI language underscore conflict dot JCNFle. Over here, we've already had a brief look at
it, and, you know, you have MCP servers
key which has a list of MCP servers defined in JCNFmat and we have
two servers over here. We have the FET server. This is a pre built
server that I got from the Github for model
context protocols prebuilt servers
that I found online, and then we have the
terminal server. So this is a server that we had created in one of
our previous videos, and this is basically used to run commands on the terminal. So that is why we call it terminal Underscoe server
and hence the name. And we use both
these servers just to demonstrate that multiple
servers can be connected to, and we can actually
load all the tools from these servers and then use
them with our MCP client. So before I move on to running this file and setting
it up on the terminal, I actually also show you the
conversation file so you can actually see that you
have a conversations folder over here with
two conversations. So let's open one of them, and this is how we actually
save conversations. So it's a list of
messages with a sender, which is the user or MCP. Then we have the
message, which is high, and in this case, it's a response to this message which is hi, how can I help you today? And then we have a
timestamp over here. At the end, also remember
that we are using a logo, you need to provide a logo
dot JPG file over here, which is actually
going to be loaded onto the client UI
and shown over there.
61. 10.16 Running the Streamlit UI: Run the UI type stream run Streamlt Underscore
client UI dot PI. This is basically going
to launch the UI for us.
62. SECTION 11 START - A2A or Agent to Agent Protocol - Lifecycle - 11.1 Introduction: Hi, everyone. Today we are
going to take an exciting look at ATA protocol or agent
to agent protocol, and how it works with two powerful tools
model context protocol and agent development kit. We'll cover what ATA
is, why we need it, and how it enables powerful agent
collaboration across organizational or
technological boundaries. We'll also look at the
key components in ATA, which you can see on
your screen right now and how are the typical
flows managed in away like discovery processing and other flows that happen
in A to a communication. So if you're a
programmer or builder or even just technically curious, you are in
the right place. All right. So let's start right at the beginning
from a blank slate. To understand A to A, we need to first understand
the concept of agents and how they work in the world of large language models or LLMs. We first have a large
language model, and LLM is basically the
abbreviation for it. So this is like GPT or Gemini, and these are trained on massive datasets that can generate natural
language responses, write code, summarize documents, and reason across many domains. But by default, this
large language model is stateless and passive. It waits for a prompt and
gives a reply, and that's it. To do more, we wrap it
inside something smarter. That's what we call an agent. Now, to do that, we have
an agent framework, which is a software
layer that gives this large language model
memory, goals, and tools. So basically, it helps us build systems where an LLM knows
what it's trying to do. It can reason step by step
and call tools like APIs, search engines, or databases. Think of it like turning a simple chat bot into a
helpful co worker with a brain, a to do list, and
access to software. Now comes multiple local agents. So inside a single
app or organization, we might have multiple agents. So these three can
be multiple agents, one for writing one for analysis and one for
database queries, et cetera. They can collaborate together internally using
a shared memory, common context, or
message passing. And then these together
basically form our agent, which basically consists
of multiple local agents collaborating
together and which is basically called
multi agent colab. Now, these agents can actually
access tools, resources, and other kinds of APIs and enterprise applications
through something that we called model
Context protocol. And we've already gone through all the details
about how MCP works. And MCP basically
helps these agents use these APIs and
enterprise applications through tools or
other resources.
63. 11.2 Why A2A Protocol - A2A vs MCP: Here's the catch.
Most agent systems are bounded by their
own infrastructure. An agent inside one company or one cloud server cannot easily talk to another
agent built elsewhere, say in another company, another data center or
another programming language. This is what we call organizational or
technological boundaries. Maybe due to API security,
network isolation, or system compatibility, these agents cannot communicate
past this boundary. What happens when agents need to collaborate
across these boundaries? This is where things break down. Two intelligent agents, even if they are both brilliant
can't help each other because there's
no common protocol or language for them
to communicate. And here is where agent to agent protocol comes
into the picture. ATA stands for agent
to agent protocol. It's a protocol that
lets agent talk to each other across these organizational or
technical boundaries. It provides a secure
and structured way for agents to discover
each other, send messages, and share artifacts and
collaborate on tasks all while staying modular and sandboxed in their
own environments. How does ATA work
with MCP and ADK? So MCP, we've already looked at is the model
context protocol, and it helps these agents
work with these APIs and enterprise applications through tool discovery or resources, and other kind of database
queries, et cetera. ADK over here is the agent
development kit that gives you the building blocks to create and deploy agents fast. Could use other agent
frameworks like Langraf and ADK is one
of them by Google. ATA is basically giving each
agent a passport to travel across systems and collaborate on global problems
with other agents. This is the what and why of ATA. Next, we'll break down the key components of
a two a and life cycle of an AA interaction from handshake to response
to artifacts shape.
64. 11.3 Discovery - A2A Client, A2A Server, Agent Card: All right. So now let's dive into the core
components of away, which are basically shown on this particular page as blocks. Also the full life cycle, which is basically denoted by these arrows and shows
the typical flows that happen in A to a
protocol communication and how all these work
together to bring eta to life. So let's start with
the ATA client. The 80 client is usually an app, script or another agent. It basically consumes
the ATS services and hence it is
called the client. The client is the one that
sends the requests like tasks SEND which we look
at to an AT servers URL. What is the ATs server? It exposes a set of standard STDP endpoints
defined by the ATA protocol, and it's responsible
for receiving requests, processing tasks, and
replying with results. Next, we have the agent card. Now, here's a really cool part. Each ATA server has a metadata file known
as the agent card. This is usually available at slash dot well known slash AgNTOTJSN which is
basically a URL. We call this the well known URL, and it describes
the agent's name and also the description, the capabilities, the skills, the endpoint URL, and any authentication that
the agent might require. So this helps the
client discover and understand what these agents can do through the ATA server. So a client can simply fetch the agent cards to discover
a server's capabilities, and this makes the A protocol self describing and
very plug and play. This is what we called
discovery where the AA client discovers what different agents are available and
what they can do. So now we are clear with what a client is and what a server is and how the client can discover all the different agents
through the server, through the discovery flow. So as an example, you might
have a client which is a personal assistant
agent that's running with every user and you might
have a server that exposes three different agents for this personal
assistant agent. These might be to
book plane tickets, to book hotels,
and to book cabs. So your personal assistant agent can basically then
discover through the AT server that there are these three agents for
helping with travel bookings, and when the user asks the client to book a
particular trip for them, this client then might
then discover and use these agents to
complete this trip booking.
65. 11.4 Initiation - Tasks, Messages, Parts: How does that work happen
after the discovery process? This happens through tasks, and let's look at that next. So the core unit of work
in A is called a task. When the client wants
something done, it sends a request to
start a task using either tasks Send or
tasks SND subscribe. Each task has a unique
ID associated with it, and it can be in
multiple states. So it can be either
submitted or it can be in working state or it can be on input required
state or completed. Failed or canceled. The client basically sends
a request to start a task, and inside the request, the client sends a message
from the user role. So messages represent
communication turns between the
client which has the role user and the agent that's doing the work
which has the role agent. And the message basically
is built using parts. A part is a fundamental
content unit within a message or an artifact, which we'll look at later. It can be of three types, so it can be either a
text part or a file part, or it can be a data part. The file part can be
basically used to represent files with inline
bytes or a URI. And the data pad can be used for structured JSON
example forms. In short, when the client
wants something to be done, it generates a unique task ID and sends the initial
user message. The task itself
contains a message and the message is
basically made of parts, and this officially starts
a task on the As server. This is what is
called as initiation, and this is one of the flows in the 80 protocol that happens
after the discovery flow.
66. 11.5 Processing - Artifacts, Streaming, Push Notifications, Non-streaming: All right. So once we have
the task that started, as the agent works, it may produce what
we call artifacts. So these artifacts are
basically structured outputs like final answers or generated
files or structured data. And they also follow the same part structure that's followed by tasks and messages. So how does the ATA server communicate its
updates to the client? First, is the streaming option. For long running tasks where the servers support streaming, clients can use tasks Sent
subscribe to enable streaming, and the server then
uses server sent events or SSE to push updates
like task status, new message or the artifacts
that are being generated. So the client sees the
progress in real time. Alternatively, eateway also
supports push notification, so the clients can
set a webhook using tasks push notification
slash set. The server will call back
when new events occur, and this is great for
integrations where polling or streaming
isn't practical. So this forms one of
the processing mode, which is called the
streaming mode. There's also another
processing mode which is called the
non streaming mode, and the server does all the work and replies with the
final result at the end. The choice depends upon the server capabilities
and the use case, and this non streaming
processing flow happens directly from the
server to the client, where the server sends back this particular
task to the client.
67. 11.6 Interaction - input-required state: Sometimes the agent may hit a point where it
needs more inputs. In that case, it
enters what we call the interaction or the
input required state. In this case, the
client can continue the conversation by
sending more messages using the same task
ID to the server to satisfy the requirement
in this interaction flow.
68. 11.7 Completion Flow & Summary of A2A: Eventually the tasks will reach a state that we
call as completion. So this is a terminal state
and the task can either be completed,
failed, or canceled. At this point, the
task is done and the artifacts and message lock can be saved or
displayed as needed. So that's a detailed look at the core components and life cycle of the
A to a protocol. There are agent cards for
discovery through the server where the client can discover what agents are available
and what they can do. There are tasks and messages, including parts for
communication where the clients can initiate a task with an
ID and send it to the server, and this is basically
the initiation flow. Then there is two
modes for processing, which is the streaming mode
and the non streaming mode, and the server can then
basically use artifacts, which are the outputs
that it generates to notify progress to
the client in real time, or it can also use
push notifications, and on the other side, the processing can happen through the non
streaming mode where the final result with the task and the same ID can be
sent back to the client. Whenever some more
information is needed, the interaction flow can basically pass on more
messages to the server, and then you'll have a
final completion state where the task is completed. Either it's completed
successfully or it gets failed or
it gets canceled. ATA, in this way is
what enables modular, decentralized and
collaborative AI agents to work together no
matter where they're hosted.