← все видео

MCP + A2A Coding Masterclass: Build multi-agent orchestration from scratch (Beginner to Pro)

theailanguage · 2025-07-23 · 2ч 42м · 12 508 просмотров · YouTube ↗

Топики: ai-loop-engineering

🎧 Аудио

📝 Summary

model=deepseek-v4-flash · prompt=summary-v7 · 36 342→2 449 tokens · 2026-07-20 12:06:15

🎯 Главная суть

В рамках многочасовой live‑coding сессии с нуля построена рабочая мультиагентная система, объединяющая два подхода: MCP (Model Context Protocol) для локальных инструментов и A2A (Agent‑to‑Agent) для удалённых агентов. Созданы два MCP‑сервера (stdio‑терминал и streamable HTTP‑калькулятор), A2A‑агент на Google ADK, который генерирует одностраничные сайты, а также оркестратор (host agent), который через MCP‑коннектор подгружает инструменты, через A2A‑коннектор делегирует задачи веб‑билдеру и передаёт результат обратно пользователю. Система запускается в четырёх терминалах и демонстрируется на реальном примере: пользователь просит построить лендинг, оркестратор вызывает A2A‑агента, тот создаёт HTML, а MCP‑терминальный сервер записывает файл на диск.

✨ Структура системы и порядок сборки

Сессия построена последовательно: сначала создаются MCP‑компоненты (сервера, конфиг, discovery, connector), затем A2A‑компоненты (агент, executor, server, клиентский discovery/connector), поверх них строится host‑оркестратор, и наконец запускается CLI‑интерфейс. Все части соединяются в единую цепочку: пользователь → host agent → MCP/A2A.

🧩 MCP‑сервер на stdio: terminal_server.py

Первый MCP‑сервер использует FastMCP и позволяет выполнять shell‑команды на локальной машине. Определён workspace (~/mcp/workspace), инструмент run_command принимает строку команды и возвращает stdout/stderr через subprocess.run. Для работы с Claude Desktop сервер регистрируется в claude_desktop_config.json с командой uv run terminal_server.py – после перезапуска Claude видит новый инструмент и может создавать файлы, запускать команды.
Проверка: Claude успешно создаёт текстовый файл с описанием MCP в папке workspace.

🌐 MCP‑сервер на streamable HTTP: streamable_http_server.py

Второй сервер реализует протокол Streamable HTTP – инструмент add_numbers складывает два числа. Используются Pydantic‑модели ArithmeticInput и ArithmeticOutput, сервер объявлен как stateless (параметр stateless_http=True) и запускается на порту 3000. Транспорт указывается как streamable-http. Claude Desktop подключается к нему через npx‑прокси (mcp-remote), так как на момент видео Claude не поддерживает Streamable HTTP напрямую.
Проверка: Claude складывает 32 и 2, получает корректный результат и строку выражения.

⚙️ MCP Discovery и Connector для своей системы

Создан конфигурационный файл mcp_config.json, где для stdio‑сервера указан путь к uv, а для streamable‑сервера команда streamable_http и один аргумент – URL (http://localhost:3000/mcp).
Класс MCBDiscovery читает JSON, возвращает словарь серверов.
Класс MCPConnector итерирует серверы, определяет тип по команде:

Для каждого сервера создаётся MCPToolset (из Google ADK), который возвращает список инструментов через get_tools. Все toolset’ы кэшируются. Метод get_tools стал асинхронным, чтобы можно было дождаться загрузки всех инструментов.

🤖 A2A‑агент: WebsiteBuilderSimple

Агент построен на Google ADK (модель gemini-2.0-flash). Он принимает текстовый запрос пользователя и генерирует complete HTML‑файл с встроенными CSS и JavaScript по принципу «всё в одном файле, без фреймворков». Инструкции и описание хранятся в отдельных .txt‑файлах и загружаются утилитой load_instructions_file.
Ключевые файлы агента:

🔌 A2A Discovery и Connector для удалённых агентов

Реестр агентов: файл agent_registry.json содержит список базовых URL (например, ["http://localhost:10000"]).
AgentDiscovery загружает список, затем для каждого URL создаёт A2ACardResolver (из библиотеки a2a) и запрашивает get_agent_card() – извлекает карточку агента.
AgentConnector инкапсулирует отправку задач: принимает AgentCard, создаёт A2AClient через httpx, формирует SendMessageRequest с правильным payload (роль user, текстовые части) и отправляет запрос. Ответ разбирается – извлекается текст из result.message.parts[0].text.

🧠 Host Agent (оркестратор)

Host agent – это A2A‑агент, работающий на порту 10001. В его build_agent():

Таким образом host agent «видит» как локальные MCP‑инструменты (run_command, add_numbers), так и удалённые A2A‑агенты (веб‑билдер). Он получает запрос, выбирает нужный инструмент или делегирует задачу, а затем возвращает результат.

🖥️ CLI‑клиент и запуск системы

app/cmd/cmd.py – асинхронная командная строка (через click). В цикле:

Порядок запуска четырёх терминалов:

  1. python3 streamable_http_server.py
  2. python3 -m agents.website_builder_simple (порт 10000)
  3. python3 -m agents.host_agent (порт 10001, загружает MCP и A2A)
  4. python3 -m app.cmd.cmd

Итоговый тест: пользователь просит построить лендинг и сохранить его в файл. Оркестратор вызывает delegate_task для website_builder_simple, тот генерирует HTML; затем оркестратор вызывает MCP‑инструмент run_command для записи файла. Конечный HTML открывается в браузере. Также отдельно проверяется арифметический сервер – команда add_numbers отрабатывает корректно.

🔧 Важные технические нюансы

📜 Transcript

en · 29 440 слов · 364 сегментов · clean

Показать текст транскрипта
Hi everyone, welcome back to the AI language. So today I am going to do a live coding session where I'm actually going to code MCP servers, A2A servers, then A2A clients and MCP clients along with the host agent and connect them all together in this multi-agent system. right so you might have taken all the lectures before this in the MCP and A2A course and how is this going to benefit you this is going to consolidate all that learning together so it's going to be a consolidation and practice exercise for everyone who has done all the lectures and if in case you've not done those lectures it's going to be one lecture that will help you a lot because it covers almost 80 to 90 percent of whatever we have covered in the course so you should definitely take this the only one requirement that i have from all the students or all the learners who are taking this particular lecture is that please code along as we code as i put this right because uh that's going to help you uh start everything from scratch rather than pulling a code from a github repo and then just running that so let's do this let's code it on a blank slate and let's write each and everything as we go along and i think that will help us consolidate and you know solidify all our learning that we have on mcp and a2a I have numbered the sections over here in the order that I'm going to take them. So I'm going to first cover MCP servers. We're going to build MCP servers and then do the client bit over here. Then we're going to go ahead to A2A. We're going to go the, we're going to do the A2A remote agent and we're going to do the A2A client side. After that, we're going to connect them together to a host agent. And then finally we'll move on to the app front end bit. And before I begin, you know, just one request is that since this is a live coding session, there might be some mistakes or might not be as polished. I'll try to keep it as, you know, direct and straightforward as possible. But in case of issues, please bear with me. Just put out whatever questions you have and I'll try to answer them as we go along or after the video. So before starting this lecture, the major requirements for this are that you should have Python installed because that's the language we'll use to code in. You should also have UV installed. So UV is a package manager. We have used it in our previous lectures. So I think most of... you would already have this installed. So you should have UV installed for your system. You also need to have VS code installed for this to work because we'll be using that as our IDE or integrated development environment. You can use whatever IDE you want. That doesn't matter. But we are going to use VS code. And it's best if you follow along in the same way and replicate all the actions that I am taking so that everything is easy and direct for you to do. All right. So let's start with the MCP. server so i'm going to open a vs code window over here so let's do that all right so i've opened a new vs code window over here let me go to the terminal and let's make a directory so let's make mcp underscore a2a underscore master as our directory and let's shift to this directly and then let's make a new directory for mcp And then let's change to this directory. And let's begin with writing our terminal server code, which is the first server that we're going to make. So let me make a directory for that as servers. And then let's change to the servers directory. And then let's make a directory for terminal underscore server. So that can be our directory for terminal server. Let's change to that. And then let's make the terminal server.py file. So that makes the file that we want, right? And what we can also do is we can go over here and click on open folder. And in our home folder, we'll have mcp underscore a2a master. Let's open this particular folder, right? Now, before we start coding, let's make our virtual environment, right? So I'm going to go to the terminal and I'm inside the mcp a2a master folder over here. Let's do a uv init over here. You should have uv installed for this. And if you followed from the previous lecture, you would already have it installed. Otherwise, please do install it for your system and type uv space init. And it's going to create a Python project MCP A2A master over here. Now let's do uv space venv. This is going to create a virtual environment. Use the source command that it shows on your screen. This will be different for Windows folks. And I'm going to... type the source command over here and this activates my virtual environment. So I'm done with the setup of my virtual environment. I have a basic folder structure with my terminal server code over here. And now I can start writing that. Before that, just one more step that we need to do is we need to add the MCP package. So for that, I'm going to follow what's given in the documentation and I'll add this particular package. So we have uv add mcpcli. So that's added now. And We need to just make sure that the interpreter is correctly selected. So the interpreter points to virtual environment bin python, which is our venv. So that's fine. And it will now detect the MCP directory that we have just added. All right, cool. So now we can start writing our terminal underscore server.pdf. code so if you remember from our previous lectures uh this server is used to run commands on the shell or the terminal of our local machine and it can do file io operations and file system operations using terminal commands and pretty much anything that you can do through a terminal command right so let me actually make that server now so for the first thing that we need to do is we need to actually import mcp so let's do that first so import and let's do from mcp dot server dot fastmcp let's import the import fastmcp all right so we'll type fastmcp all right so this is done now we can define an mcp equal to fastmcp and we can give it a name so let's call this terminal underscore server i think that's fine and so this basically defines our mcp server right using fast mcp so it's a minimal definition that we need to provide and everything else is handled by fast mcp right now we can define a workspace that we want our terminal server to create and you know edit files in so for that what we'll do is we'll call this a default underscore workspace and uh sorry this is sorry for the autocomplete over here let me just define it over here as the home folder slash mcp slash workspace. All right. And that's all that we want over here. We need to provide some kind of a path expansion over here so that it resolves the correct path. So I'm going to import something called OS, which is a Python module that provides this functionality. And I'll write over here OS dot path dot expand user. And then I'll provide it this particular path. All right. So now this is going to be the default workspace that our terminal server works in. And just to see where this points to on your machine, you can actually go to the terminal type Python and press enter. Let me just zoom this in a bit. All right. And let's close this file navigator. Let's go completely on the terminal. Okay. That's fine. So we can do this. And what this does is that it opens the... a python interpreter for us and what we can actually do is we can type import os over here and then we can just copy this command and paste it over here to see what this resolves to. So on my machine, this resolves to users slash the AI language slash MCP slash workspace. So you can check what it resolves to on your machine just to be sure that this directory exists. So for me, this exists. I know because I've used this before. You can just make sure that actually exists in the workspace and it actually exists in your file system. You can create this particular folder. All right. So that is that. Now I'm going to define an MCP tool. So the way we define an MCP tool is to type MCP.tool and then we provide it a tool name. So I'm going to provide the tool name as terminal server. And then I'm going to define the function that is actually going to be the MCP tool. So I'm going to name the function run underscore command just to be descriptive as to what it does. And this is going to run a command in the terminal. And what we're going to do over here is we're going to type command, colon str, and this is going to be the input. And then the return type is going to be in a string. and this is going to have whatever is you know um there's whatever has been determined by the run of the command on the terminal as the output right and um then i can actually see it trying to complete a lot of text for me below um it's actually now gone so i'm just going to type it in myself so the first thing is that i have to provide a doc string so let me just press tab and start with whatever it has provided right so this is a doc string that is there And just to let you know, you know, this autocomplete is due to Gemini's code assist. I had installed that recently and it provides all this code assist in real time based on, you know, whatever you are typing. And this might not be 100% correct. So you have to see what it's writing and then decide whether you want to keep it, keep a part of it and so on. But I just press tab so that I have a basic thing to work with and then I can, you know, actually. change whatever I don't want. Right. So all right. So moving ahead, I just provide the doc string over here. So it's run a command in the terminal and return the output. That seems fine. I can specify the arguments over here. And this is basically a command which is a string. And this is a command to run in the terminal. And then I can provide the return type string. And what is the return object going to be? So it's going to be the output of the command or an error message if the command fails. That's perfect. Okay, that's that. I don't need to import subprocess over here. So I'm just going to remove this. So I'm going to now run this command using subprocess. So that import goes over here. And I write that command over there. So now we're going to use subprocess.run. We're going to provide it the command that we want to run. We're going to run it in the default shell. I'm not going to say check equal to true over here. I'll just say capture the output and as. text and i'm going to provide a current working directory so i want to run the command in a particular directory and that's going to be the default workspace all right so now this simply means that i want to run the command provided by the string in the default shell with this particular in this particular current working directory and i need to capture the output as text which will be the output from hd out or the hdderr error All right. So this is the result. And now I'll return result.std out. And if, you know, it failed, it might, there might be an error. So in that case, I can return std. All right. That's that. And then I'm going to have an accept block with any exception. I'm going to specifically capture this exception. So I'm going to just have accept as exception. And then let me just type return and error running command. It's an autocomplete that's provided. So I'm just going to accept that. So now it will return. error running command sdre and that's completely fine so i can just save this file and i think this completes the entire terminal underscore server so to test this what we can do is we can actually open claude and um all right so this is one of my old terminal server implementations which is not running so let me go to claude over here and go to settings so you can click on the menu uh button for claude on the macbook um you know uh menu bar or on windows and click on settings right and over here you have developer and over here you can actually click on edit config so this is going to give you the cloud desktop config.json file so let's copy this path all right and let's close this and we actually close this as well and we are in our vs code again let's go to our terminal and let's clear all this up and type code and provide a double quote with that file path all right so and press enter all right so so this is what i have originally in my cloud desktop config json file and i'm going to just clear this and type it again just to show you how i'm actually building this all right so i have mcp server so we need three basic keys so first key that we need to provide is the name of the servers i'm just going to call it terminal underscore server all right so that is my key for the server And then I need two more keys, which is command. And I need arguments and args, right? So these are the two keys that I need to provide. Now, for the command, I need to run the uv command. So the terminal underscore server file has to be run using uv space run and then the file path. So I need to provide uv. So what I'll do is I'll type which uv on my terminal to see where uv is installed so i have this path and i'll just copy this and i'll paste it over here so let me paste this command over here so this is my command right and now i have to provide it some arguments so the first argument that i have to provide and i'm sorry about the autocomplete this sometimes becomes frustrating because it adds in a lot of stuff that you don't need so you have arguments and i'll write it is directory and then I'll provide the directory path for this terminal server.pvf file. So this is the file, right? And I can copy the path over here and just paste it over here. But I just need the directory. So I'm just going to remove the last bit. All right. So this is the directory. And then I need to run uv space run. So I'll specify run as the next bit. and then i need the file name so let's call it terminal underscore server dot py which is actually the file name so you don't call that terminal underscore server it's the actual file name that we provide over here and uh that completes the list of arguments all right so that's all my arguments i removed that comma at the end i have the command in place and this is just telling claude desktop that there's a server called terminal underscore server to run that you need to run the command uv in the directory specified by this and after uv you type run so uv spaced run and then terminal server.p so that's what this does and now to test this out i can actually open cloud desktop all right so let's open this and uh to to re you need to restart cloud desktop so you need to go over here and actually click on quit so click on the cloud menu um button in windows or in mac os and actually press quit because that's going to properly shut down cloud If you just close it using the cross button on the top, that doesn't generally work because it keeps running in the background. So we open Claude again afresh now, and this should actually then run our terminal server. There's some kind of error over here. Just check this once. Okay, so we have actually forgotten the main block over here. All right. So we do need to provide a main block, which is actually specified with this. And then I need to say mcp.run and then transport equal to HVIO. So that's the bit that is missing. All right. And now let's do that bit with Claude again. So let's quit Claude using the menu button or actually properly closing it. And then let's open Claude again. The main block helps specify to run this as an MCP server with STD IO transport when it is run as a script, not when it is imported. And so that's why this is important over here. And now you can see that when we started Claude, it did not give us an error for the terminal server, right? And you can actually select and see this that there's a terminal server file over here. All right. And what I can do is I can ask Claude to run some terminal command. So can you create a new file? for me named the ai language underscore cloud underscore test or txt and add a summary about model context protocol to it use the run command tool so uh and specifying that you should use the run command tool because um sometimes you know cloud might come back and say that i cannot create a file and uh for that we'll need to then improve our tool doc string etc but i just specified it directly and in case you face this issue you should iterate on the tool description so let's press enter all right so i'm going to allow the tool it's terminal underscore server and it's going to use the run command tool right so let me just click on allow always All right. So it's created the file, right? And it's checking the present working directory. Let me try a different approach. Okay. So let me go and check the workspace again. There's a typo over here. Sorry about that. So let's put the correct workspace file name over here. Let's go back to Claude. Let's quit this and let's start Claude again. And let's try this again once. Can you create? All right, so... Asked you to create the AI language underscore MCP TXT in a similar fashion. So let's press enter again. It's writing everything about model context protocol. Switch the end of file. Scrolling terminal underscore server. So all right. So perfectly perfect. I have successfully created the file with a comprehensive summary about MCP. All right. Great. So let's open our finder window. All right. So this is my finder window and I'm going to go to MCP and then workspace and I'm going to look for my file over here. All right. So I have this file over here and I can actually then go in VS code and I can say code. Let me just copy the path for this particular file and then let's provide a double code and provide the full file path. All right. So this is the file that it has created, the ai language underscore mcp dot txt. And this has all the text around it and we are basically you know concerned about the tool working and cloud detecting the tool and being able to use it so that works completely fine so i'm going to close this now and i'm also going to close the cloud desktop config.json file so this basically completes our stdio based server so we have successfully created that and we live coded this and you can actually try it and create it we're next going to create a streamable http server so let's do that now Alright, so now let's make a streamable HTTP server. So I'm going to go over here and go to mcp slash servers and let's add a new file over here and let's call that streamable underscore HTTP underscore server dot py. Alright, so that's our streamable HTTP server file and now we are going to start making that server. So let's first import from mcp dot server dot fastmcp. Let's import fastmcp. that's that and then to begin with let's actually define two models over here and these are going to be pedantic models for input and output for our mcp tool so we're just going to structure the input and output over here and this particular tool is going to add two numbers together so the first will be an arithmetic input model so let's call that class arithmetic input and let me provide the base model over here And I need to import from Pydantic base model. And let's also add some field data over here. So let's also import field. All right. And over here, I can say this is importing. This is a class arithmetic input. And let's define two fields over here. So one will be A. And let's provide the type as float. And let me press tab over here to complete this as a suggestion that's showing. So it's a field which is required and it's the first number. And B is a float which is the required field which is the second number. So that looks good. And that's all that we need for this. Let's remove this line. Now let's add a second class over here. So let's call this arithmetic output. And over here what we're going to do is we're going to say result is one of the fields. It's going to be float. So that's right. And then we'll call this as result of the arithmetic operation. That's the description of the field. And we'll provide one more field which will be the expression. So let's call this expression. And this will be a string. Yeah. And it will be the expression that was evaluated. So let's keep it as it is right now. So these are the two classes that will define the structure of our input and output. And fast MCP based on these type hints that we have provided can actually structure the input and output automatically. All right. So now since this is done, we can actually define our MCP over here. Let's put this equal to fast MCP. And we need to now make a stateless streamable HTTP server. So we are going to provide a name over here let's call that arithmetic server and let's provide a host let's call that host equal to local host and then let's provide a port as 3000 so i'm going to write over here 300 and then let's provide stateless underscore http equal to two all right So this is going to make it a stateless server in the sense that the events and all the communication that's happening, it is not going to persist across sessions for it. And it's not going to persist even in the same session. So that's going to be our stateless HTTP server. All right. So once we have the input and output models defined, we have our MCP server defined. We can actually mark an MCP tool. using this mcp variable that we created and we can call this tool add underscore numbers so just remember that you cannot provide any spaces in this name and so that's why i have put add underscore numbers and let me define the function over here so it will be async def add underscore numbers and then the input will be an arithmetic input and the output will be an arithmetic output all right so that that works and um we can actually then just actually press tab to complete whatever it's showing over here and let's read through this and make sure that it's correct so it's it will add two numbers and return the results that looks good and the arguments are arithmetic input containing two numbers to add so that also looks good so i'm going to leave it there and then returns arithmetic output output containing the result and the expression evaluated so all that also looks good all right and what we're doing over here is we're saying result is equal to input a plus input b and expression is a string with the a plus b equal to a result kind of a format so that's also great and then finally we are returning an arithmetic output with the result and the expression all right so this looks great and we're going to keep keep this as it is and this basically completes the entire server now the only one thing that's left is to put the main block over here so we'll say if name equal to main and then we'll say mcp dot run and we need to provide the transport this time and let's put transport equal to and we're going to say streamable http so streamable dash http so that's how you put this and now this should be the complete definition of our streamable http server all right so now what we can do is we can test this out so just before i try it in cloud i'm just going to try to run this over here so i'll say uv space run and then mcp so this is our current directory and then we're going to say servers where we put this and then we're going to put streamable And let's put a tab over here and this is the file name. So we're just going to run it directly with the file name as a script. And let's press enter. This should run our main block and run the MCP server. And we can see that it's running on localhost 3000. And just to show you, you can actually go to localhost 3000 slash MCP and it gives an error like this for now. It says that the client must accept text slash event stream. So of course, you know, it's not supposed to function in this way. We need to send it a proper JSON RPC request. Now we're going to test this with Cloud Desktop. So let me open Cloud Desktop. All right. So here's Cloud Desktop and you can click on tools over here and see that we have the terminal server tool over here. So let's click on the Cloud menu and click on settings. And once you click on settings, you have this developer option that we went to earlier and then you can click on edit config. So let's click on this and this shows this cloud desktop config JSON file. So let's right click and press our alt key and copy the path name for this particular file because I'm going to open this in VS code. All right. All right. So here's VS code. Let me open a new terminal over here and let's write code and then provide double quotes and then let's paste that path inside this and let's press enter. So this is our file. so this is our file and let's add a new server over here so let's press a comma over here and let's call this arithmetic server and let's put the command over here inside double quotes so that's our command and let's then provide a list of arguments we close this list over here and this is going to have two things one is going to be mcp remote and this is the bridging library that we use because cloud desktop on its own cannot connect to streamable http servers as of now and then we are going to provide the url that's going to be http colon slash local host and this is going to be 3000 and then we remember that we need to provide the mcp endpoint with an ending slash over here and this is our mcp servers url so this then tells claude that there's an arithmetic server available with this particular url and it uses the mcp remote within px to run this So that's it for this definition. Now we can restart Claude. So let me just restart Claude over here. Remember to close Claude from the menu by selecting quit. Otherwise, it just keeps running in the background and does not detect the changes. So here's Claude and even click on search and tools. And now you have the arithmetic server over here. Let's quickly test this out. So let's say, can you add two numbers, 32 and 2 using the tool? All right. And let's press enter. All right. So it asks for access, allowing access to add numbers from arithmetic server. So let's say allow always. And you can see that it has, you know, structured its input as a and b and the result is 34. The tool calculated this expression. So this also has the expression. And you can see this over here. This is the input that it sent in the form of arithmetic input. And this is the output it got in the form of arithmetic output. So all right. So this works great. And now we have our streamable http server done all right so now let's go back to our system design and look where we are so we have actually now created two mcp servers a streamable http server and a stdio server so now we have these two servers and each of them has one tool each and what we want to do now is to create an mcpconfig.json for our own system not the same as what we did for cloud desktop And we then need to list these servers. So there will be something called a discovery logic that we need to do. And then we need to create an MCP connector that can, after discovering these servers, connect to them and make these tools available as tools that are compatible with our system. If you remember from our previous lectures, we have used Google's agent development kit for creating a lot of agents. And we're going to use that now as well. So we're going to create... these tool sets that are compatible with the agent development kit using our MCP connector this time. So let's start with this aspect now, which is marked as number two over here. And for that, I'm going to go back to VS Code. All right. So let's go over here. And then first of all, we are going to create a directory called utilities. So let me click on MCPA2AMaster and let's create a new folder and let's call this utilities. Alright, so now we have the MCP folder over here. And then inside the MCP folder, let's create three files. And we'll start creating these files after that. The first file is going to be a config file. So I'm going to call it mcp underscore config dot JSON. And then we'll have one file which is going to be the discovery file. So let's call it mcp underscore discovery. And this is going to be a Python file. And then let's also create the MCP connector file. So I'm going to call this MCP underscore connect dot py. So these are the three files that we want to make. And we can actually see, you know, we can actually go and copy our cloud desktop to a JSON file and create a similar config file for our system over here. So let's do that now. So again, I'm going to open cloud and just click on the cloud menu on the top and go to settings. And let's go to developer and let's click on edit config. And let's copy this path name by pressing the alt key on Mac on Windows. Also, you can actually go to properties and try to copy the path from here. Let's go back to VS code. All right. And we have mcpconfig.json over here. And let's paste. I'm sorry. Let's actually open this file first. We'll type code space and in double quotes, provide the file path. And this is going to open the file over here. So let's copy the content from this file and go to our own config.json and paste it here. Now what we're going to do is we are going to let the terminal server command and arguments be the same as they are over here. But for arithmetic server now we don't need to run mcp remote using npx because we're building our own client and we can make it directly run this particular url. So for command let's put the command as streamable HTTP. All right. And you can actually, you know, follow whatever structure you want to follow over here. I'm going to just say that instead of the command being, you know, the path to UV, I'm going to put streamable underscore HTTP. And whenever our connector finds that command, it's going to then understand that this is a streamable HTTP server. And it's then going to use the URL over here. All right. And this should have only one argument which is a url and let's save this file so now we have this logic that we can actually check for the command if it's streamable http we can actually then make a connection using this transport and if not we can make a connection using stdio so those are the those that is the logic that we're going to follow for our config.json file there are other structures you could follow and as this protocol develops there might be standardization around it so let's just follow this for now and i'll leave it up to you to edit this later on you know if you if you want to based on any kind of standard that is established all right so now we can close these extra files over here and let's just open our mcp underscore discovery file and let's start coding this all right so let's start with building a class over here and let's call this mcp discovery and let's just press enter over here All right. So then the purpose of this class is going to be to read the JSON config file, which defines the MCP servers and provide access to the server definitions under the MCP servers key. All right. So let's put that definition over here. Let's just correct this. All right. So we have reads the JSON config file defining MCP servers and provides access to the server definitions under the MCP servers key. All right. So this is the file that we're going to make. This is the class that we're going to make. And it can have two attributes. So let's put these attributes over here. So the first attribute will be the config file. So let's do that. And this is going to be a string. And this is basically going to be the path to the JSON configuration file. All right. And then we're going to have the other variable, which is config. That's going to be a dictionary. And this is going to be the past JSON content and it's expected to contain the MCP servers key. So we can actually write it like that. So past JSON content and expected to contain the MCP servers key. All right. Great. So we have the doc string for this class done. And now let's go below this and start writing the code for the class. All right. So first I'm going to define the constructor and okay. So it automatically gives me a completion over here and I can actually put the default as none for the config file. All right. And this will basically initialize our MCB discovery client with the config file. So I'm going to initialize the MCB discovery with the config file over here. So let's just accept this for now and arguments are going to be a config file string and this is going to be actually optional. So let's put optional over here. And this is the path to the JSON configuration file. And if none, it defaults to mcp underscore config.json. So let's default to config.json located in the same directory as this module. So let's also put that. Okay. And let's just move this to the next line to make it readable. All right. And we can actually see that, you know, we have the mcp config.json over here. All right. All right. So now we can say that if config file is none, we can then say that self.config underscore file and this is equal to os and we don't have os imported so let's do that over here so we'll have os.path.join and then let's provide the directory name first so that will be os.path.di name with the file and that seems to be correct so i'm going to put that and then we'll put the file name which is mcpconfig.json all right so great so this then assigns the file in the same directory if the config file is not provided to this class during initialization and we have an else condition and if the config file is provided we'll actually just use that so that's as simple as that and this basically sets our config file all right so now we can actually remove this line we don't need this and we can actually then have the con the loaded config saved inside the config variable And for that, we can use a load config function. So I'm going to continue with what's suggested over here. And then let's just define this function, which is a load config. And this is now going to return the dictionary with all the key value pairs from the config. All right. So this is going to be this. And then we probably need to import all these aspects. All right. So now these are imported properly and let's go down below. And over here, what we're going to try and do is we'll first open the config file. So let's say with open and then let's put self.config file. And yes, actually let's open it in read mode. And then we'll say data equal to json.loadf. We can do a simple check over here that if not, if it's not a dictionary, Then we can raise some kind of an error. We can raise a value error that in valid configuration format in self.config file. All right. So it needs to be a dictionary. And we, of course, need to import JSON over here. So let's add import JSON on the top. And this is loaded. This is imported then. All right. And now we just simply return this data that we have loaded. So as simple as that. Right. And this is just going to return this data to the config variable over here. Let's also put a try catch block over here. So let's say try and the kind of issues that might happen is that we might not find the file. So there can be a file not found error and we can raise a file not found error that the configuration file self.config file was not found. So that looks good. And apart from that, let's just capture any other exception. So there could be a JSON decode error as well. There could be a value error, but I'm just going to say accept. exception as E and then I can raise a runtime error over here and let's indent this to make this correct and now this seems to be fine for now. So let's leave this over here and this basically completes our MCP discovery logic. So let's go back to our system design and we can see that now we have the config.json and we can list all the servers. Just to have a function that can actually list all the servers let's go back to vs code and let's add a last function over here and this is going to be the list fun list server function so let's write it as like this list underscore servers and let's provide self and it's going to return a dictionary of all the server names and the config so uh it's suggesting this uh returns the mcp servers defined in the configuration file so that seems correct And this is dictionary string any and the content of the MCP servers key from the config. And there is a key error if MCP servers key is not found in the config. So that's great. And I'm just going to accept this over here. And I'll say if MCP servers not in self.config, which is the data that we loaded from the file, we can raise a key error. Otherwise, we can return self.config. And I'm going to use .get over here and provide MCP servers key over here. So I think this then completes our MCP discovery. So this was a simple file. And now we actually have to go to the more complex logic, which is for the MCP connector. So let's go to that. All right. So now for the MCP connect file, we're going to define a class called the MCP connector. And the purpose of this particular class is that it will discover the MCP servers from config. Then it will list each server's tools and cache them as MCP tool set instances for your ADK agent that's going to be the host agent. It just depends upon what agent framework you are using as to how you want to load the tools. We are using Google's agent development kit and we're going to do it in a way that's compatible, in a way that ADK understands that this is the tool. And for this, generally, all the agent frameworks will provide you with one kind of an adapter or connector class. So for example, Langchain provides MCP adapters, and Google's ADK provides MCP tool sets. So we're going to use tool sets over here. So let's add this, you know, doc string over here for this particular class. And we're going to say that it discovers the MCP servers from the config. And the config will be loaded by the MCP discovery class. And then the tools, then it lists basically, and then it lists each server's tools and then caches them as MCP tool sets that are compatible with Google's agent development kit. All right. So that's in a nutshell what our MCP connector is going to do. So what we're going to do now is we are going to define the constructor first. So let's write def init. And I just loaded this with the tab key. So let me remove that. And this def init and self. And then we have a config file. So we need to provide the config file to the MCP discovery tool. And that will basically come through the MCP connector. So this is going to be a string and the default is none. First, we need to initialize the MCP discovery class. So let's write self.discovery and this will be equal to MCP discovery and we are going to provide the config file over here to MCP discovery. All right. So that's that and then let's import this. Let's press command dot and then let's import this. All right. And now we have self.tools. So we're going to put self.tools. and this is going to be a list that's compatible with adk so before we can add this over here let's go to the terminal and let's add the google adk package to our project so let's click on terminal over here let's expand this and i am right now in mcp a2a master so i need to first activate our activate the virtual environment so i'm going to write source and then dot venv so we already created this so you can just directly source it over here this activates that and then I'm going to say UV add and then write Google dash ADK so this should add the latest version of Google ADK let's see what this does alright so it's added Google ADK let me go to my PI projects file and I can see that ADK version 1.7 this should probably be the latest version that's added over here and let's go back over here I'll just quickly also verify if my interpreter is correctly set so it's dot It's not set correctly. So I'm just going to do it for my virtual environment over here. So that's that. And now I need to provide a list of MCP tool sets over here. So let's do that. So let's first import that. So we'll go right from google.adk.tools.mcptool and then .mcp underscore tool set. We are going to import MCP tool set. All right. So we have this. And now we can say that this is a list of MCP toolset and we can provide an empty list for now. All right. So we have our tools now and then we are going to provide a function that is going to load all the tools. All right. So let's write self dot underscore load underscore all underscore tools. All right. So this is going to load all our tools in the tools variable over here. And now let's go and define this function. So let's go down and let's write def and load all tools itself. All right. And then I'm going to just press tab over here. So it says loads all tools from the discovered MCP servers and caches them as MCP tool sets. All right. So let me remove this preset code from here. Let me make this async because it involves connecting to servers. And then let's start writing this function. So we already have the discovery class. So we can write for name. commerce server and then we can write in self.discovery and then we can use the list servers tool that we had so this is going to give us all the servers as a name and then value pairs with all the configuration data so let's do that All right. So now we have all the servers with names and the configuration data. So let's go to the config JSON file and just revise that, you know, we have the name and a command over here and we can check the command. If it's streamable HTTP, we can handle it with the URL. Otherwise we can handle it as an STD IO server. So let's go back over here and we can just type a simple if condition over here. So we can write if server dot get and we can write the key command. And if this is equal to streamable HTTP, which is when it's a streamable HTTP server, let me remove all this from here. And now what we need to do is we need to define connection parameters. So server connection parameters. So let's name that as CONN. This is the variable I'm going to use. And let's define streamable HTTP server params. So I'm going to go above and first import that. So again, Google ADK offers these connection parameters for us as an adapter library. So we can write from google.adk.tools.mcp underscore tool. We can actually import CDIO connection parameters. So this is one. And then we also import the streamable HTTP server params. So we can write from google.adk.tools.mcp underscore tool. dot mcpsessionmanager we can actually import streamable http server params. So we have these two aspects imported over here and now we are going to use it below over here. So if we have a streamable http server we're just simply going to use a streamable http server params and over here we're going to provide the url property and this is going to be server arguments and then the first argument. So let's go and see over here. So we have the args list over here and this is the first argument over here. So that's the format that is expected and we're going to put it like this over here and let's also put an else condition over here. the else condition is going to basically say that it's an stdio server so that has to be the case otherwise it's going to be an error over here so let's put a connection equal to stdio connection parameters so then that seems correct and then we have to provide the server connection parameters over here so we're going to write server underscore params then we'll provide stdio server params and then we're going to provide the command and the arguments as is over here so let's actually press tab to accept this So we have command which is equal to server and then the key command and the arguments are server and the key arguments. And we can actually then close the server params over here. So this closes this and then we need to provide the last closing parenthesis for our stdio connection params. And below this we can actually add a timeout. So let's add a timeout for five seconds because it sometimes might take time for stdio servers to start up and connect. So let's do that. And one of the packages that we've not imported is the STDIO server params. So let's do that. We can actually just directly import it from MCP because that's what Google's ADK expects. So we can write this and we don't need STDIO. So we have imported the STDIO server parameters over here and let's correct this over here. So it's going to be server parameters. So now what we are doing is that we are going to in the constructor load all the tools and the load tools will basically go through the servers list from the discovery class and it's going to iterate through each server it's going to get the command so either it's going to be a streamable http server or an htdio server and it's going to load the connection parameters over here all right so now what we can do is we can go down and create the tool set so we'll write tool set equal to mcp tool set and we just need to provide the connection parameters that's the simplest thing that we can do over here so we do that and we close this so this is going to be our tool set now it's great to use the get tools function to get all the tools that are available so we'll write tools equal to and we'll write away toolset dot get underscore tools so that's correct and what we can then do is we can get all the tool names and print them to the screen so we can write tool names equal to and then tool.name for tool in tool. So yeah, so that's that seems correct. So now we have a list of tool names that we can actually print out. And then we can actually print. So we'll write print. And then we'll format this. And let's put this in bold green. And then let's just press tab over here. And so we have loaded tools from and this is the server server name. So we can actually format this as Cyan. All right. So this just makes it a little bit more readable. So we'll say loaded tools from this particular server. And let me add that word over here. So loaded tools from server and the server name. And then it's going to join all the tool names over here. And I'll just move this to over here. So we just have the tool names in standard color. So, okay. So now I have loaded tools from server. with the name and then we have the tool names all right so now we have all the tools loaded and now we can actually append this to the tools list so we can write over here that tools dot append and we can provide the tool set over here all right so now this tools which is a list of mcp tool sets will have the tool sets from this particular server that we've just gone through and then it's going to iterate over the next server and so on and add them to all these tools All right, great. So we now have the list of tools done and we need to now return that. So let's just also put a simple try catch over here. So let me just write try colon and let's indent this properly. All right. And let's provide an accept over here. And let's provide a generic exception catcher over here. And let's print this kind of an error over here. So print bold red error loading tools. And we can actually write the server name over here. Let's not raise it over here. So let's just catch this exception, error loading tools, and let's provide the server name. Let's just say it skips the server over here. So, okay. So now we have a message over here which says that there's an error loading tools from this particular server. And then let's just end this function by returning the tools. So now this is going to return the tools. You'll also need to add an empty list over here. So this is going to be the tools list when this function starts, which is going to be an empty list to begin with. All right. So now we have the load all tools function completed. We start with an empty tools list. Then we go over through all the names and server configurations using the discovery class that we made. And we get the connection parameters depending upon the command variable. If it's streamable HTTP, we treat it as streamable HTTP server params. Otherwise, we get the hdio connection params. And then we use the mcptoolset class with the connection params over here and we get the toolset. We list all the tools using getTools and just get all the names and print it out on the screen. And otherwise, we just append the toolset that we got over here to the list of empty tools in the beginning. And then we return these tools. And we'll be using this mcp connector outside of this code. So we also should define a function which is available outside. it's not a private function which can get all the tools so right let's write def get underscore tools and this is going to be a simple function that's going to return a list of mcp tool sets so it returns the cached list of mcp tool sets and it returns self.tools.copy so uh just making sure that uh you know we cannot modify our internal tool set so that's it and this completes the get tools function as well and i think this completes our mcp connector class We're going to leave it over here and then go ahead and code the other aspects in our design. And if any issues come up, we are going to come back to this class and modify it accordingly. All right. So now let's go back to our design over here and let's start building the next component. So we have built the MCP servers and the discovery logic with the MCP connector. And now the next thing to look at is to build an A2A agent. So we're going to build this particular agent using Google's agent development kit. And we are going to make it compatible with A2A. So I'm going to tell you how to do that. And if you're using some other agent framework, this is going to be pretty modular. agent code is going to be something that is modular, you can actually build it with any framework that you want. And then we'll have something called a main dot PY file that will expose the agent card and the agent skills, which will be common to any other framework that you might use. and there will be something called an agent executor that will be the interface from your agent to the A2A framework. So that's going to expose two functions one is going to be execute and the other is going to be cancel. And those are the two functions that A2A requires. And once we expose those two functions, we can make any agent compatible with the A2A framework. So let's try it out. And I'm going to use the Google's agent development kit as the framework to build our A2A agent. So let's start with that. Alright, so I'm back in VS code in my MCPA2AMaster folder. So in our MCPA2AMaster, we are going to create a folder and we are going to call this Agents. So this can be a folder that holds all your future agents. And inside this, we are going to create a folder for the first agent that we are going to make. So we're just going to make one agent. But since you might want to add more agents, you can have multiple folders for all your agents. So we're going to call this a simple website. builder. And this is going to be an agent that takes in a query and builds a single page website for us. Alright. So let's press enter over here. And now inside this folder to completely define this agent, which is compatible with A2A, we need to make four files. So first will be an agent.py file. Alright. And this is going to have your core agent logic. The second file that you need is going to be an agent executor file. And this is going to be the bridge file between your agent and the A2A framework. So it is going to define two functions called execute and cancel that will help us connect this particular agent to the A2A framework. Next we are going to create two more files which are going to be the init.py file. for the for marking it as a module. And then we're going to have the main dot PY file. So we can directly head to agent dot PY. And before we start writing this code, what I'll do is we'll set up the environment. So we'll go to our terminal over here. I already have my virtual environment activated and you can just activate it if not, and then just do UV add and you can write a to a dash SDK. And you can also add Google ADK. So this ensures that these two libraries are there. I've already added them before. So it says that, you know, you have them already. And then you can actually go to your pyproject.tml file over here. This is the file over here. And you can see that you have A2A SDK and Google ADK. present over here. Just remember that A2 SDK is still less than version 1. Version 1 is generally considered the first stable version after which you don't see a lot of breaking changes. So A2 SDK has still not reached there and that will take its own time but just remember and be open to breaking changes in A2 SDK that might happen very frequently and you should just be aware of them and then you know you can modify the code according to that particular change. Alright, cool. So we have this setup. Now let's go to the agent and start writing our code. So we're just going to write the class directly and we'll handle the imports as we go along. Let's let's make a class over here and call this website underscore builder. And let's call it probably simple and the end over here just to demarcate it as being the simplest version that we're building. I'm actually going to change the file name over here as well. And I'm going to mark it as website. underscore builder underscore simple. So this is just to make sure that I make it compatible with the format that I followed for my previous code basis. Alright, and now let's put a doc string for this. And this is going to be a simple website builder agent that can create basic web pages and is built using put this in the next line, Google's agent development framework. Alright, so that's my website builder simple over here. And let's press enter now. And now I need to provide some instructions to the on it. So let me provide the instructions in a file over here. So let's click over here and add a file called instructions dot txt. And now I'm going to add the instructions over here. So I have these instructions written down. Let me just copy them over here. Alright, so I'm going to add these instructions that you're an expert AI web developer, your job is to take a user query describing a simple a website and generate a complete HTML file containing HTML, CSS and JavaScript. All right. The entire content should be wrapped in a valid HTML file structure. The design should be clean and modern. Avoid frameworks. Keep everything in a single file and only return the full HTML content as a string. All right. Great. So that seems to be good enough for our instructions dot txt file. And now I'm also going to add something called an agent description. So let me add it to a file. description.txt. And this just helps to keep the code clean without a lot of, you know, large strings describing the agent and its instructions. So again, I'm going to copy this from a file that I have over here. And I'm just going to paste it over here. So it says this agent takes a user query for a website and generates a single self-contained HTML file with embedded HTML file with embedded CSS in JavaScript. Alright, so let me turn on Word Wrap. And if you are typing this out with me, you can pause here and copy the instructions from the screen. And if you want to copy the description, you can copy the description from here after pausing the video. I'm going to go ahead now and I've saved both these files and I'll go back to my agent class over here. Now I want to load these instructions from a file. So I need to create that as a utility. So I'm going to use this utilities folder that I already have. And inside the utilities folder, I can probably make a folder called common. These are like common utilities. And I can have a file over here, which can actually load this for me. Alright, so let me make a file over here, which is called file underscore loader dot py. And then I'm going to add the code for loading the file over here. So I just create a function over here. Let me first import the OS module for file handling. And then let's write def load underscore instructions underscore file. And this function is going to take a file name, which I can mark as being a string. And I can provide a default, which is a string. And that is equal to an empty string. All right. So this is going to be my function header. let me just add a simple um you know doc string over here and let me press tab over here and accept what it has so i'll say load instructions from a file if the file does not exist let me just do a word wrap return the default instructions. So these are going to be your default instructions. And there's a file name with a string and there's a default instruction return if the file does not exist, right? And it returns the string, the content of the instructions file or the default instructions. Alright, so this looks good already. It says if the path exists, you return the file, else you return default. So what I can do is I can add an encoding over here. And let me specify that as UTF-8. All right. And what this helps in is that it allows for supporting all non-ASCAI characters as well. So that is that. And then I read this as a file and I return file.read. All right. So that's great. And if it does not exist, the default is returned. So I think that looks good. So let me just save this file as file underscore loader.py. And I have my load instructions file function over here. Let me go back to our agent.py file. Alright, so over here, I'm going to provide system underscore instruction. And I'm going to say this is equal to load. And let me import this particular file over here. So I'm going to say from utilities dot common dot file underscore loader import. And I'll say load instructions file. So I have this function now. And I can write load instructions file. And then actually I can write the path. for the file. So it will be agents website builder simple. That's correct. And then we have system underscore instruction dot txt. So it's not when it's not that it's instructions dot txt only. So let me correct that. So this is my system instruction. And then I need a description, right? So let me put a description over here. And this is going to load the instruction file from website builder simple, which is description dot txt. So that looks good already. And I think this works. I think there's a typo over here. All right. So now I need to build the build agent function. So this is the function that is going to return the agent. So let me do that. All right. So I have this function now and I need to import LLM agent on the top. So let me import that from ADK agent. We have to write from google.adk.agents import LLM agent. All right, this gets my LLM agent and this is what I'm going to return over here and let's press enter. And now I'm just going to simply return an LLM agent. So what I can do is I can say return LLM agent and let me just actually accept whatever, you know, I'm getting over here. Now this is the autocomplete, of course, and I'm just going to check this up once. So the name is going to be website builder simple. So that seems good. Then we need a model over here. So I'm going to put a model. field over here and let that be equal to the Gemini 2.0 flash model. Let's say Gemini dash 2.0 dash dash. Let's put a comma at the end and then we need the instructions. So for the instruction, I need to just provide what I have loaded on the top and I'm going to put that into a constructor. So I'm going to say self dot system instruction and let me correct this. Let me just put in it over here. And let's just put self.systeminstruction and self.description. All right. And let me also correct the case over here. All right. So you can press function F2 or just F2 directly if that works for you to refactor the particular variable. So I now have instruction is self.system underscore instruction. So that looks good. And then I need the description. So let me put that as well. So description is equal to self.description. Alright, I already had this over here. So let me remove this. And apart from that, I don't think I need anything else for my agent over here. So I have provided it the instructions that it needs to create the web page. I have the description ready, I have the model and I have the name. So that's it. That's a simple agent file for me over here. We might need to add more functions over here, but we'll do that after doing the remaining part of the code. So next, let's go to main.py. And this is going to have two things. One is the agent skill and one is the agent card. So let me first define the skill. So for the skill, I need the agent skill class from A to A. So let me first import that over here. So let me say from A to A. Yeah, that comes up. All right. So I have agent skill over here. So you can go to the agent skill class and check it out once by pressing command or control and clicking the agent skill that you just imported. You'll see that there's a description over here which is a detailed description of the skill. There's a list of examples you can provide. So example prompts scenarios that the skill can handle and this provides a hint to the client on how to use this particular skill. There's an ID over here which is a unique identifier for the agent skill. Then there are input modes and this is a set of supported input MIME types for the skill. So it overrides the agent's defaults if you provide them over here. Then you have the name string. So a human readable name for the skill. And then you have output mode. So the set of supported output MIME types for the skill. And then finally you have some tags. So these are a set of keywords describing the skills capabilities. So you can actually then close the types file over here and we have the agent skill. So let's define this now. So let's provide a name. So let's actually provide an ID first. So I'm just going to say website builder simple and add skill at the end of it. So I know that this is the skill description. Then again, for the name, I'll write website builder simple skill. For description, I can write this is a simple website builder agent that can create basic web pages. So I think that works for me. And let me just close this string over here and put a comma at the end. The next thing I can put is some kind of tags, right? So I can say, and you know, again, there's suggestions from Google's code assist over here, and I'm just going to accept those. So I think these are good enough tags, website, builder, HTML, CSS, JavaScript, then we can actually provide some examples. All right. So let's actually say that this is a list. And let me provide an example over here. So create a simple web page with a header and footer. All right. So this seems to be a good example. And then let's say create a landing page. So create a landing page for a product with a call to action button. Alright. So I think those are two good examples over here. And let me keep it at that. So this is my agent skill over here. And now I'm going to define an agent card. Alright. So let's go below and write agent underscore card. And again, I need to import this. So let me import it over here. Alright, this is my agent card over here. Again, I'm going to control click or command click this and go to the definition over here. And the class definition actually shows you all the different things that you can put inside the agent card, right. But I'm just going to go back now to the main dot PY file and define this agent card. Alright, so let's write agent card equal to agent card. And then we need to provide a name. So let's write name is equal to website builder simple so that that works well. And let's provide a description. a simple website builder agent that can create basic web pages and is built using Google, Google's agent development framework. So I think that works well over here, then we are going to provide the skills. And after the skills, we're going to provide capabilities and let me import agent capabilities over here. Alright, so we have agent capabilities over here. Now this should work. Alright. And over here, I'm just going to provide streaming equal to true. Alright, so also let's put a URL over here. So let's say URL equal to and then we'll get a host and a port from the main function that we're going to just create. So let's do that. Alright, so that's going to be our host and port over there with the URL. And then we're going to define the version. So let's call this our own version number. So I think 1.0.0 is fine. And then we're going to put default input modes. And let's put text over here. And then we're going to put default output modes as text as well. Then we have the capabilities and the skills. Now let's put all this inside the main function. So we're going to write def and then main. And then we're going to have a host, which is of type string and a port, which is of type int. Let's put tab over here. Let me move this extra text from there. And then let me put this inside the function. Alright, so now I'm going to make this main function into a command line interface CLI command. And for that, we're going to import something called click. So let's write that. So we'll say import and then click and press tab. Then let's mark this as a command. So we'll use add the rate of click dot command. And I'll press tab over here to autocomplete this. Then let's put an option for a host. So I'm just going to put tab over here and we're going to use the host option with the default is local host. And this is the host for the agent servers. That's that's great. Then actually let's put a port. So add the rate of click and port and the default over here is 8000. I'm going to change it to 10,000. And this will be the port for the agent server. All right, great. So now we have these two things, they're going into the main function, we have the skills and we have the agent card setup. So this is the part which basically now identifies the agent. Now I'm going to go to the agent executor and we're going to code this over here. Alright, so let me make a class which is called website builder simple agent executors. That's a long name, but I'm going to keep it like that because it's the agent name and then agent executor at the end. So that's not the definition that we want over here. So let's put a proper doc string over here. And over here, we'll say that this implements the agent executor interface. And just to make this correct, we're going to use the exact interface name. And we're going to add this over here as well, of course. So let's put that. All right. And so we'll say that this implements the agent executor interface to integrate the website builder simple agent with the A2A framework. All right. So I think that is a simple doc string that works well. Let me remove this from here. Now let's import agent executor. So I'm going to write from A2A.server. agent execution. And we're going to import the agent executor from here. So now at least whatever we have written, this code is fine. Let's delete this. I'm going to define the constructor now. So let me delete these two for now. So I'm going to define the constructor over here. So let me remove the agent from over here. And I'm just going to say self dot agent. And then I'm going to actually use the website builder simple agent over here. So let me import it over here. So I'll say from agents.websitebuildersimple.agent and I'm going to import the websitebuildersimple. Let's go to this file over here and we have not followed the correct naming convention over here. So let me change this. All right. So that will be upper camel case for the class names. And let me go back to our file over here. It's changed over here as well. And let me say this is websitebuildersimple with a pair of parentheses at the end. So this is now going to assign the an object of website builder simple to self dot agent and we will need to use the build agent function whenever we actually use this agent. Alright, now we need to make the execute function. So the way we do that is by using an async def execute. And this execute function will have self and then it will actually have two things which will be the context and the event queue. So let me put that. So there will be the context and this will be a request context and there will be an event queue and this will be an event queue. All right. So and this will not return anything as such. So now I need to import request context and event queue. So agent execution provides request context. So let me import that from here. And the event queue is not correctly identified by the autocomplete. So for that, we actually need to use server events. So let's do that. So from a2a.server.events. And then from here, I can actually import event queue. Alright, so I have the event queue imported now along with the request context. And this is now identified over here. And this is my execute function. Now, of course, I need to actually define the execute function. I can't use what the autocomplete is provided below. So let me do that. Alright, so I'm going to remove this because this is not what we want to do over here. And I'm going to first extract the user query. So I'll say query equal to and then I'm going to use the context over here and then call get user input on that. So this is going to give me the user query, you can actually go and check this function. So this returns a string. And it extracts the text content from the user's message parts. Alright, let's go back. And now I have gotten the query over here. Now I'm going to get the task. So I'll say task equal to context dot current task. So this is going to give me the task. So I can actually go over here. And you can see that this is a property of the request context. So you can get the current task from here. Now what might happen is that this might be a new query and there might not be any task available. So you can say if not task and you can then create a new task, you can say task equal to and you'll say new task. and then you need to provide the context message so i'll say context dot message and that's it now for this new task method we need to import some a2a utilities so i'm going to do that over here a2a all right so i'll import from a2a dot utils and i might need to import multiple things so let me put a pair of parentheses over here and just write new task for now. And we'll import more things from the utilities as we go along. So I'm doing this, as I'm going along in the code, I'm not putting all the imports beforehand, because this just helps us to see from what A2A modules or packages, what all is being imported and how it is being used below. Alright, so now if we don't have a task already, it's a new task, we will create this task by using new underscore task and the new task method, it basically needs a message. So that's what we provide over here. And we can actually click on this. So you can see that there's a property called message, we can further click on message over here and see what all properties it has the important property over here is parts. So this is a property of the message class. And this is an array of content parts that form the message body and a message body can be composed of multiple parts of different types, example, text and files. Alright. So this is what we have used over here in context dot message. And if you look at the new task method over here, it creates a new task object from an initial user message. So that's what we're doing. We're using the context. for the request and we're getting the message out of it. And then we are creating a new task from it. So this is the initial message object from the user. Alright, so we can now close these files and go back to our agent executor and we have the task over here. And now what we're going to do is we're going to associate this task with an event queue. So what we'll say is that await and then we'll say event underscore queue. And then we're going to call enqueue event on this. And we're going to provide this task. So this queue is then going to hold this task and all the related updates for this task. So we can actually then add all these updates to this event queue and this can then be transmitted as updates to the user. So let's click on event queue over here and you can see this class it's defined in A2A server events and it's an event queue for A2A responses from the agent. So we're just going to leave it at that and we'll see how we use this in the code as we go along. So let's close this for now. And now we just know that there's an event queue that handles the responses from the agent and we've just added this new task into that queue. Alright, so now we have created a task, it doesn't exist, then we created it. Otherwise, we have the current task. And what we can now do is we can create something called a task updater. So we'll initialize an updater over here. So let's call that updater and let's use task updater over here. Alright, and we need to import task updater. So we'll use something called from A to A dot server dot tasks. And we'll import the task. updater from here. Alright, so this is going to be our task updater and this task updater needs the event queue. So we're going to provide that over here and then it needs a task ID that we're going to provide over here and then it needs a task context ID. So we can go and look at the task updater over here. And this is a helper class for agents to publish updates to a tasks event queue. So we've already provided the event queue. Whenever we call updater dot update or something like that, it's going to send an update event to the tasks event queue and notify the client. And it simplifies the process of creating and enqueuing standard task events. All right. And it takes in the task ID. So this is the ID for this specific task. And the context ID is a more wider server generated identifier for maintaining context across multiple related tasks. So it needs both of them and both of them are provided using the task over here. All right. So next, let's define the agent invoke function. So we are going to call this in rather over here. And the definition of it will actually go in agent.py. So we'll define it later in agent.py. But let's just put the call for that function over here right now. And then we are going to put these parentheses and pass in the query. And we're going to pass in the task context ID to this. And let's also put this in a try except block like this. Now, the invoke function is going to generate a stream of events and we can use a for async for to handle that. So let me put that over here. So I'll say async for item in this and this is going to iterate over all the items, right? And this is the start of my async for. Now, the first thing we'll check is something called is task complete. So I'm going to check that and for that we'll say item dot get is task complete. This is the key. And if this is present. And it's false, it means that it is not completed. And if it's true, it means that the task is completed. So that event will then represent the completion of the task. And the default value is false, right. And when we define the invoke function, we're going to make sure that the format is such that we get this kind of a structure with this particular key. All right. So let's go ahead. Now we will put an if condition and check that if the task is not complete, so the task is not complete, we're going to get a message. from the task and we say item dot get and we'll put updates over here. So again, we'll send this key from our invoke function. So this is going to be our message. And the default is that the agent is still working on your request. Now we're going to await the updater dot update status. So we're going to call updater dot update status. Then we're going to provide a task state and we need to import this. Let me go to the top over here and let me put from a to a dot types import task state might need to import other things. So let me put it like this. Alright. And now we can go below and we can put task state dot working. Alright. So this is the status of the task that it's still working. And then we'll put the major the message over here as a new agent text message. So we're going to pass the message, the task context ID and the task dot ID. This function itself is a utility. So let me import this from a to a dot utils. Alright, so I've imported that. And now this is resolved correctly. And this is my update status event that I have sent back through the A2A protocol. Alright, so let's go ahead and code the else condition over here. So over here, I'm going to have the final result. And because this is the condition where the task is actually complete, and then I'm going to get item dot get and then I'll get the content. And again, this is what I'm going to code in. If the content is not there, I'll say no result received, right. And then I can say await and then we'll call updated update status. And this time we are going to put a task state completed and new agent text message with the final result. Let me also add the task context ID and task ID over here. Alright, now we can break outside of the for loop. And just so that there's enough status, there's enough time for the message to be processed, we're going to put a async IO dot sleep for point one seconds. Let also import this async IO on the top. Alright, so now we can go back to our exception and add an error message over here, which is just a parsing of the exception e. And once we have this error message, we can write something like this, which is update updated or update status. The task state will be failed and we'll have a new agent text message using the utility and we'll provide the error message with the task context ID and the task ID. Now we just raise the same exception. So this completes the execute function. Now we need to add a cancel function. So let me add it here. This function is required by the agent executor and it's going to take self and it's going to take the request context and it's going to take the event queue just like we did for the execute function. All right. It's going to return a task or none. We can import task from a28.types and then we can go down and implement the cancel function which will just raise an unsupported operation error. All right. So what we'll do is we'll raise a server error and this is again provided by a28 to us. And for that, I'm just going to import this over here. So from a2a.utils.errors, I'm going to import server error. And I'll also import unsupported operation error from a2a.types. So now I can actually say error equal to unsupported operation error with the double parenthesis at the end. And this is going to complete my cancel function over here. So that's your agent executor file. Alright, so we're back to the agent.py file. And before we can define the agent, agents invoke function over here, we need to define some things around it, like the runner and the agent object over here. So let me start with the agent. So let's write self dot underscore agent. And that is equal to self dot build agent. So that's going to build the agent and and assign it to the agent variable over here. Now I'm going to define self dot user ID. And let me call this website builder simple agent. user. Then I'm going to define the runner so I can say runner and put the parentheses and then I need to import this. So let me go to the top over here and say from Google dot ADK import runner. So now that part is resolved. And now I can provide an app name over here, which can be self dot underscore agent dot name. And then there can be an agent which can just be self dot underscore agent. And then we have to provide three services. So one is the artifact service. and then there's a memory and a session service. So for all three we are going to use in-memory services. So in-memory artifact service, then in-memory session service and then we have in-memory memory service. The in-memory over here means that all the artifacts or session data or memory that is handled by these three services are lost when the app stops to work. In a production system you might use a persistent storage rather than an in-memory service. All right. So let's import these on the top. So I'm going to write from.google.adk.artefacts import in-memory artifact service. Then we are going to import from google.adk.sessions import in-memory session service. And then from google.adk.memory.in-memory memory service we are going to import. in memory memory service. Alright, now I can start defining my async invoke function. So I'll write async def invoke and then I'm going to pass self and the query which is a string and then the session ID. So this is going to be the context ID that I passed in from the agent executor and this is going to return an async iterable which is going to be of type dict over here and that's basically completes my function over here. We need to import async iterable. So I've imported that over here from collections.abc import async iterable. Now we define a simple doc string. So I'll say invoke the agent and then I'll say return a stream of updates back to the caller as the agent processes the request, the query. So now we can define the format for the return. So is one we need to have the is task complete. So that will be a bool and this indicates if the task is complete or not. And then we have the updates if this is a task in progress. So it's going to be a string. And we also have the content if the task is completed. And this is the final result of the task if complete. All right. All right. So now we can define the we can get the session. So what I'll do is I'll say await self dot underscore runner dot session. and session service and then dot get session. So this is going to get the session if it exists. All right. I'm going to provide my app name over here. This will be self dot underscore agent dot name. I'm also going to provide my session ID which is going to be simply with a session ID and a user ID which will be self dot user ID. All right. Let me put an equal to sign over here. All right. So this is my session. If the session does not exist, right? So I can say if not session, then I can create this session. So what I can say is session equal to await self dot runner dot session service dot create underscore session. And I can again provide the app name, the session ID and the user ID over here. Then I need to define the user content. So the message that we are going to send to ADK's agent has to be of a certain format and this is going to be used through GenAI types. So what I need to do is that I need to first write user content equal to types and dot content and this types I have to import over here as google.genai and import types. Now I can say types dot content and I can provide a role which is going to be user. and I can provide parts. So this is going to be a list of parts. And for this, I'm going to use types.part.fromText is going to be done like this. And then for the text, I'll provide the user query. Alright, so this is the user content that I'm sending to the agent. Now we'll say self.runner.async. And we are going to provide the user ID, which will be the self dot user ID, I'm going to provide the session ID, and we are going to provide a new message, which is going to be this user content. Alright. So this is now actually going to run our agent. And now this is what is going to return the stream of events. So I need to handle those events. And I'll use an async for for that. So I'll put an async for event in this, right. And I'll put a colon over here. And then I can write if event dot is final response. Now, this is an event that is discerned by Google ADK. And this is a functionality that ADK provides to check whether it was the final response or not. So if it is a final response, I can know what I need to return. So I just first need to get the final response. So I'll say final response equal to blank. And then I'll check whether the event has content, whether the event content has parts and whether the event content parts has text in the last part. All right. So I'll say and event.content.parts and if event.contents.parts-1.text if all that exists, then final response is simply the text. And now I'm going to yield. So what this does is that it basically sends a dictionary to the caller and this is going to be the dictionary that I'm actually trying to, you know, get over there. So it's it has a isTask complete equal to true because this is the final response. So it does not need to have the content. So it does not need to have the updates over here. So I'm going to remove this. And the content is going to be the final response, right. And then I can put in a if else condition over here. And then I can yield the other part of this, which is the task is not complete. And there's an updates, update property over here. And for the update, I can just simply put a text that agent is processing your request. Alright, so these are the two things that we yield based on whether or not it's a final response. Alright, so that completes my agent file. Alright, so I have agent executed and agent.py completed. Now I'm going to go to the main.py file and complete this. So now you can see over here that we had the agent skill, the agent card, and we had a main function which we run as a command line app. And we provide options for host and port, right, default being localhost and 10,000. Great. So that basically is like a public identity for our agent. Now what will happen is that this is going to run on a server. So we have to write the logic for that server over here. The server is going to use some kind of an app. So we're going to use a Starlet app for that. And then that needs some kind of a request handler so that when an incoming request comes, we can handle that request. So let's start by defining a request handler. And for this, I'm going to import the default request handler from A to A. dot server dot request handlers. So we can do it like this. And this is our default request handler. The default request handler needs two things. It needs the agent executor. So where does it handle that request? It passes it on to the agent executor. So that is our website builder simple agent executor, but I need to import this. Okay, so I can just press command dot and select the default option over there to import it. All right, the quick import. and a quick fix. And then we have the task store. So it needs a task store to manage all the information. So we're just going to use an in-memory task store. So let's do that. First import that. So a2a.server.tasks and then we write import task store. So that is the task that we actually need the in-memory task store. So that is the in-memory task store. And we're going to use this over here. So this is similar to what we did for ADK, where we had a memory service and a session service and an artifact service. Great. So now this completes our request handler. Right. Now we are going to use this request handler and let's first import the A2A Starlet application over here. So now we're going to define the A2A Starlet application over here. This needs two things. One is the agent card and one is the request handler. So let's do that. So we'll put the agent card equal to sorry for the autocomplete over here. Let me remove this and we have the HTTP handler where we provide the request handler. So that's our A2A Starlet application. And now we just need to start it. So what we'll do is uvicon.run. Let me also import uvicon over here. All right. And we'll say uvicon.run and server.build. All right. And then we provide the host and we provide the port. So now we just provide the main block and we just simply run the main function over here, which is going to take the host and port and run this application. So this will be run whenever we call this as a script. So now we can actually try to run this. So what we can do is we can go to the terminal over here. and we can write uv run and then python 3 dash m and then we can provide the path so that will be agents dot and then website underscore builder underscore symbol all right so this is going to run our uvcon server so there's some kind of a warning so we look at that but we can see that that the application has started and it's running well right all right so heading back to our system design document, we see that now we have defined a remote agent that can work over an A2A and expose itself through an A2A server. Alright. So now we need to define the host agent. So what we are going to do is we're going to define this host agent, which is going to use this agent registry mechanism over here to discover this particular agent and then connect to it. It's also going to use an MCP connector to connect to these MCP tools. So let's work on this host agent now. Alright, so now let's code our agent discovery logic. Alright, so the first thing that we're going to do is we're going to make a utility file which is an agent registry JSON file. Alright, so I'm going to go to the utilities folder over here, create a new folder over here which will be called A2A and then let me add agent underscore discovery agent underscore registry file over here. So agent underscore registry dot JSON. And what we're going to do is we're going to put the base URLs of our agents over here. So for now, I'm just going to put one which is HTTP colon slash local host. And then I'm going to put colon 10,000, which is for my agent. All right. And this is going to be our agent base URL. And what we'll do is we'll provide this as a list. So I'm going to put this inside a list. And this will be a string. So let me also put this in double quotes and this will look like this. And if I need to add more of them, I can actually then copy this and add more of them like this over here. And this can be 1001 and so on. All right. But for now, I'll just have one, which is localhost 10,000. All right. So this is our agent underscore registry dot JSON. Now I'm going to make a file called agent underscore discovery. So let's make that. And the purpose of this file is to read this particular JSON file and discover all the clients and return a list of agent cards. So let's make a class over here and let's call this agent discovery. Now let me put a dog string over here. So let me call this as discovers A2A agents by reading a registry file of URLs and varying each one's slash dot well known slash agent dot JSON endpoint to retrieve an agent card. All right. And then let's put some attributes over here. So there is going to be a registry underscore file, which is going to be a string. And this is path to the agent registry file. And then we'll have a list of base URLs. All right. So let's call this base underscore URLs. And this will be a list of string. And this is going to the list of base URLs for the A2A agents. All right. Now let's define a constructor for this. So we'll say def init. And now this can take a registry file. So let's put that over here. All right. And now let's put a doc string for our constructor. So let's say this will initialize the agent discovery and the arguments will be a registry file, which is a string and path to the agent registry file defaults to. utility is a2a agent underscore registry.file. That seems correct. And I'm going to leave it at that. All right. Now, what we'll just simply do is we'll say if registry underscore file. So if this is provided, we'll say self.registry underscore file is equal to registry underscore file. Then we'll put an else condition. And this is going to be self.registry underscore file. And over here, I'm not going to provide this. I'm going to use OS module over here. So let me import that. okay so this is my os module all right and now i can press enter over here and then i can say os.path.directory name for file so this could looks good and then i'll write agent underscore registry dot json solid this also looks cool so this is my default value if the registry file was not provided all right and now i can say self dot base urls is equal to self dot underscore load underscore registry and this is a function that i'm going to make next all right So let me make this function over here and this is going to return a list of string. All right. So that is that. And let me define the doc string for this. So this will load and pass the registry JSON file into a list of URLs. All right. And this returns and let's put a list of string over here, the list of URLs for A2A agents. All right. And let's go below and then let's try to. now open the registry file and load this list so we'll say with open and then we'll put self.registry file and yes let's open it in read mode as file okay and i'm going to say data equal to json.load and then put f over here let's import json over here let's press command dot and import json so that's my data and then i'm going to say that if not is instance data and i'm going to put a list over here then i can raise a value error that the registry file must contain a list of urls all right and otherwise i just return the data and they can then be a try and catch block to capture some basic exceptions over here so i'll put a try over here and let's indent this and then let's put an exception except block over here and we can put a file not found error and let me just put the print statement as it comes over here ideally you should use a logger but for this demo i'm just going to use a print statement that the registry file is not found and then i can return an empty list all right and then they can be a json decode error or they can also be a value error so let's put both of them over here json decode error and then a value error and then add as e and then we'll print that this error passing the registry file and that's the error return an empty list all right great so that's that and then we can actually define a function to list all the agent cards so whenever we create this agent discovery client the constructor is already going to load all the urls into the base url but we'll need some method to return the list of agent cards so we need to make an async function so let's call that async def and let's call that list underscore agent underscore cards and now this is going to provide a list of agent cards all right so um this is autocomplete over here let me just leave it for now and let me put an agent card over here and let's import this all right so i'm going to say from a to a dot types and let's import agent card over here all right and i can put parentheses over here so that in case i need to import something later on which i would need in the future i can just add it over here all right so this is uh what i'm going to import over here now let me go back to the list agent cards function so this function header seems to be fine so let me just say asynchronously fetches agent cards from each base url in the registry all right so that's my doc string and then i can put the returns section over here and just say it list of agent cards received retrieved from the agents all right so that's what it's going to return all right so now what i'll do is i'll just remove all this from here so let me just first make an empty list of agent cards so this will be made like this all right and this is empty to begin with all right and now i'm going to use an http x async client so i'm going to write async with httpx dot async client and let me provide a timeout over here so i'm going to give a large timeout of 300 seconds over here and then let's call this as httpx underscore client all right and so this is going to be my starting point and the timeout is long so that you know if there's a potentially long agent task that happens i can handle that now let me import httpx over here So let's do import HTTPX. So that's done. And so this is my starting point now. All right. And now let's code this up. All right. So now I need to go through each of the base URL. So I'll say for base URL in self dot base URLs. All right. So this is what I'm going to put as a loop over here. So now I'm going to use something called a A2A card resolver. So what I'm going to do is I'll say resolver equal to A2A card. resolver and I need to provide it with the HTTPX client which is correctly provided in the hint over here and I need to provide it with the base URL which is also provided correctly so let me just do that over here and I'm going to go and import this on the top so this A2A card resolver comes from A2A.client so let me say from A2A.client and I'm going to import the A2A card resolver and I'm also going to import the A2A.client all right Okay, cool. So I have the A2A card resolver and A2A client, which I'm going to use later on. And this is what's done over here. Let me click on this and show you what this is. Alright, so this is the agent card resolver. And as you can see, this has an initialization method over here, where it takes the client and the base URL and an agent card path. So this is the path to the agent card endpoint relative to the base URL. And there's a default value over here, which is the dot well known slash agent dot JSON. So we can actually keep our base URL without any ending slashes over there. And this will then resolve to the correct particular path, right? And if you go down, you have a get agent card function over here, this is what we're going to use. And this will fetch the agent card from a specified path relative to the base URL. Alright. And we're not going to provide the relative card path. And if you go down and see, it's going to use the self dot um if the relative card card path is none it's going to use the path segment self dot agent underscore card path and then the target url is just simply going to be the base url slash this particular path segment and which we have already seen is the well-known endpoint that that we generally use all right so uh this this will uh probably work correct uh because we're just using the base url the only thing that we can do over here is that we can make sure that the base URL does not have an ending slash. So we can use our strip over here and we can put this over here. So now we're removing the last slash because that's what A2A card is already gets to put in. And then we get this base URL and the HTTPX client. So now we can call the get.agentcard method. And now this is going to return the agent card for this particular base URL. So let's call this card equal to this, right? And now what we need to do is we need to append this card into the list of cards. All right. So what we'll do is we'll say cards dot append and we'll append this card over here. So now this is very simple, right? And just to recap, we have just used an HTTPX async client. And based on that, we have provided that to something called an A2A card resolver that's provided by the A2A dot client library over here. And what we do is we use the resolver to then call get agent card. This returns the card to us. And then we append this card to the list of cards. The other thing is that, you know, this get agent card would probably be an async function. So we can await this over here just to make sure that we get the card. Right. All right. So this is going to now go through each of the URLs and get us all the cards. And this is what we have achieved over here. Alright, and then at the end, we can just return the cards list. So we are inside the list agent cards function and we're just going to return the cards over here. Alright, so this is after we are done with getting all the cards. Great. So that completes our agent discovery file. all right so the next thing that we'll do is we'll create an agent connector and the purpose of the agent connector will be to provide a simple wrapper around the a2a client to send tasks to any remote agent identified by the base url or the agent card right so this will decouple our host agent from all the low level communication that happens with the remote agent okay So we're going to make that file over here. So let me add a file over here and let's call this agent underscore connect dot py. And I'm going to directly start writing this class over here. I'll call this agent connector. Let's put a colon over here and then let's add a simple doc string. For the doc string, we are going to mention that it connects to a remote A2A agent and provides a uniform method to delegate tasks. All right. So we can now define the constructor over here. and and so how do we define this so we know that we will have a host agent which will orchestrate the task that it gets and it's going to first list all the available agents that it has it's going to get the agent cards and then understand what are the skills that the agents have and then it might decide to call one of the agents so it'll call this with the agent card right so the agent connector is what's then going to connect that call with the actual agent so let's pass in the agent card over here so i'm going to call this agent underscore card and this will be of type agent card and this is the agent that we want to connect to all right now let's import this on the top so i'll say from a to a dot types and i will import agent card all right so i have agent card over here and given an agent's card we want to now connect to that agent so we'll say self dot agent underscore card is equal to agent underscore card and now we have the agent card assigned to this particular agent connector object now we need to define a function to send the task to a particular agent all right so what we'll do is we'll call this function as send task so we'll define async def send underscore task and this will have self over here followed by the message that we want to send and let this be a string and then we possibly have a session id which is also a string and this will return a task let's import this over here all right so i have that now all right so now let's add a doc string for this so i'm going to add a simple doc string which says that so it will be send a task to the agent and return the task object And then we have the arguments. So this is going to be message, which is a string and this is the message to send to the agent. So that seems right. And then session ID is a string, the session ID for tracking the task. All right. So all this looks good. And then we can say returns and put a colon and it will be task, the task object containing the response from the agent. All right. Great. So this looks good. And we're going to accept this directly. Now to send the task. to the remote agent that's working over a url we need an async client an http async client so what we'll do is we'll say async with and then http x dot async underscore client all right and i'm going to provide a timeout over here for 300 seconds like like we did for the other client that we used and we're going to call this http x underscore client all right so that's our client and let me correct this to httpx all right and we can import httpx over here so we have our httpx client now okay so now we can use the a2a library to define an a2a client all right so how do we do that we use a2a client from the library so let's say a2a underscore client so this is what we'll name our client and we'll call this and we'll define this like a2a client like this and this needs two things it needs the httpx client so we'll assign that and it needs the agent card so we'll assign that like this all right and this is my a2a client so i'm going to import this over here so i can import this from a2a.client and i'll import from this so from a2a.client i'll import the a2a client class and i'll describe define an a2a client over here all right so this is my a2a client now i need to send a message so we need to define a send message payload all right so we'll define it like this and we'll call this send message underscore payload this will be a dict all right and it will be a dictionary of string comma any and let's import this and let's press equal to over here and now what we'll do is we'll define this like this we'll first put a key for the message and this key is further going to have some key value pairs now one of the things that we need to send is the role all right so this just says that this is the user sending the message and not the agent and the other thing that we need are the parts so these are the message parts and this is going to be a list of parts and just press tab to auto complete this so this is going to have a kind first and that's going to be text so let me change this from type to kind and that works and then you have text which is the message and this is the message that we have gotten over here and send task all right so this is the correct payload then and this is the format that is required by a2a now what we can do is we can create a request object so this is called a send message request all right and to import this i am going to go to the top over here and i already have an import from 808.types so let me just add it over here and let's put a comma let's close this and then let's put a send message request over here all right so this is my send message request and this is now going to be the request that i'm going to send all right now in the send message request i first need to provide an id so i'll use uuid4 and this is going to create a unique id for me for this let's import this and then i need to provide params so i'm going to say params equal to and then message send params Now again, these are default types that A2A provides to us and it expects that we use these types when sending the requests. So I have just imported this and now I have a request object with an ID and params and now I want to complete the definition of the params. So for the params, I'm going to use my payload. All right. So let me just accept this and let's remove this. So now my payload is assigned as send message payload. I'm going to remove this assignment over here and just put a double star. So what this does is that it basically takes the message and assigns it to the message argument in this particular function in this particular object. All right. And you can see that over here that this needs a message over here. So that is what we have provided. So now this completes my send message request. Let me assign it to a variable called request. Let's press equal to. Now I'm going to say that response equal to await. i'm basically going to send the request all right and i'm going to use the a2a client for that so i'll say a2a client dot and then send message and let's put the request equal to request over here and let's remove this all right so now this will basically send my message to the agent that is connected using this a2a client so what we can see is that this is the request that's being sent right this has a send message request with the message send params that have my that has my payload and the payload has my message that i have got uh gotten in the send task call over here so that is what my agent connector helps in and while i have defined this agent connector i have specified what agent i want to send this to which i have assigned to my client over here so we know which agent to send the message to using this and we know what message has to be sent using the argument over here all right so this is my entire agent connector and of course we do need to handle the response right so what we'll do is we'll get the data from the response so let's call this response underscore data and what we'll do over here is we'll use the response and a function called model underscore dump and i'll use this model dump function that's available with the response right and this will then take a mode so let me put that equal to json and i will have a flag which is called exclude underscore none and i'll put that equal to true which means that you know all the values which have a none type they will not be included in the dump now let's look at this response right so you have a2aclient.send message and the send message function over here gives you a response which is a a coroutine type with a send message response so this is the type of this response this is a send message response and if you go to the send message response right so let me go to the send message function over here so this is my send message and this is my response so let me come and click on this and now here i am on the send message response class and you can see that there's a send message success response so let's go down into that again And here is my send message success response. Now this inherits from the base class which is A to A base model. So let's go to this. Right. So this is my A to A base model. And let me now further go to base model. So we have gone back to like the highest level class over here. which is called base model and if you search for the function model underscore dump you'll see that this provides this model dump function with a mode which you can say is json or python and there's the exclude none flag over here and this is what we are using so all right so this is something that a2a provides to us and we can just now close all these files that we have opened and go back to our agent connector so so this is the send message success response that we'll get hopefully and uh over here we'll have the model dump in json and exclude none equal to true all right so now what we'll do is we'll get the response text all right so let's call this agent underscore response and i'm going to put a equal to over here and i'll use the response underscore data and then what we'll do is we'll check for the result key over here and in the result we'll check for status all right and then we'll have the message and then we'll have parts zero and text all right now this is assuming that there is the structure for a successful completed task is going to be like this and if we don't get this we can capture a key error all right so we'll say this is we'll put this into a try block and we're going to put accept over here and we're going to put key error and index error all right so key error and index error and we're going to put a default response over here that agent response is no response from the agent all right so this is this and this is your try accept block and now what we can do is we can return the agent response so i'm going to change my response return type over here from send task it's going to be just a string and this is my agent response over here and i'm just coding this you know live over here so in case there is some issue that happens we might come back to this function and make some changes all right all right so now our agent connector is complete and whenever we use the agent connector with the agent to send a task we'll get a text response back using send task and that logic is handled over here all right Alright, so we are done with our agent discovery and connector logic. Now let's go back to our system design. And we can see that we're complete with number one over here, which is the MCP servers and tools. We are done with the MCP connectors and the MCP config dot JSON and discovery earlier on. Then we have made our agent and we have made an A2A server and we have connected to an A2A client. which is available through the agent connector. So we have done all this as well. We have done the agent registry bit. We have listed the agents and now we need to join this to something called a host agent. And this host agent will have the functionality to list the agents and then delegate tasks to it. We're just going to take the agent that we already built, which is the website builder simple. Let's just copy this folder and let's click on the agents folder and paste it here. Alright, so now we have two website builder folders over here and we're just going to modify one of them and call them the host agent. Alright, so let's call this host underscore agent. And now we have all these files for our host agent. So let's go to agent.py. Let's rename this to host agent again. We can also call this the orchestrator or some other relevant name, but I'm going to call it the host agent. Alright, and what we're going to do is we're going to update the doc string over here. So we just want to say that this is orchestrator right so orchestrator agent then this can discover A2A agents via agent discovery and then it can discover the MCP servers via MCP connector and load the MCP tools and then we can say that it routes the user query by picking the correct agent slash tool right so that's our host agent and let's go to the constructor now Alright, so now we are going to update the description file to the host agent file. So I'm going to say host underscore agent over here, right. And let's update these files as well. So for the description, I'm going to write that it routes the requests to A2A agents or MCP tools. For the instructions, I'm going to write you are an orchestrator with two tool categories, A2A agent tools, list agents and delegate task, agent name and message and MCP tools, one function tool per tool name. So pick exactly the right tool by its name and call it with the correct arguments and do not hallucinate. Alright. So these are the instructions that I'm going to put over here. I've used this previously for one of my orchestrator agents and this seems to work well. So I'll just stick with that. And let me press Command Alt S to save all these files. and let's close this and let's go back to our agent over here. I think I've switched these. So let me just correct the name over here. Now let's change the name over here. So we'll call this host underscore agent underscore user. We'll use three of these services. We have the agent and we have the agent name. Alright, so now one thing that we need to do is we first need to load the MCP tools. Alright, so that's a difference between your host agent and a greeting agent. Let me add it over here. So I'll write self dot mcp connector equal to and then let's initialize that mcp connector so let me put this as mcp connector over here and let's import this so from utilities dot mcp connect as we're going to import mcp connector all right all right so after i've added the mcp connector tools i'm also going to define the agent discovery over here so self dot agent discovery and this is equal to agent discovery right a new object of agent discovery let's initialize this and let's import this from here all right and now we can go over here and check that agent discovery is now going to load all the base urls from the registry and now i can define the two functions that i need for my host agent which will list and delegate the tasks to the other remote agents right so i'm going to call this delegate task and list agent so let's first define list agents function and this is going to take in self and return a list of agent cards all right so i'm going to put a list over here and let me put agent card over here let me import this from a2a types and import agent card all right so this is my list agent function let me put a small doc string to make sure that my agent understands what this tool does And I can improve this function by just not returning the agent cards, but a dump of the content of the cards. All right. So I can call this cards equal to this. And then instead of returning the list of cards, I'll just put a wait over here. I can return a list and I can say card dot. And I know that there's a model underscore dump function for the card as well. And let me put exclude underscore none. equal to true. Alright. And this will be for card and cards. Alright. So now what I'm doing is I'm returning a list of dictionaries with the model all the data inside the card object. Alright. And instead of list of agent card, this is going to be a list of dictionaries. So let me put it like this. And it's returns a list of dictionaries. Alright, so I've updated the dog string as well. So I think that should be enough for the agent to understand that this is the list agents tool. And now we are going to build a call agent or a delegate task agent tool. All right. So let's call it delegate task. And let's create it over here. I'm going to call this async def and then let's call it underscore delegate underscore task. And now this needs to take two things. One is the agent name. So let me put it as agent underscore name. And the other is a message. Alright. So let me call this message colon str. And this will return a string, which is the response. Right. And now I can actually define this over here. I'm going to just use a brute force kind of a method over here. So I'll just use you know myself dot agent discovery dot list agent cards again, you might avoid this by only doing it once. It's also include self over here. And what this is just doing is that it's giving me the agent cards again. And now I can match the name. So I'll just say for card in cards, I'll save card dot name dot and let's do a lowercase comparison over here. This equals agent name again in lowercase. And then what I can say is I'll say matched underscore card equal to card. All right. And let me define this above over here. Let's put an else if over here. And the else if can be that I can try to match the ID. So in just in case you know, this runs by ID, I can check the ID in the agent card and match it with the agent name provided by my host agent. All right. And if this is true, I can again then say matched card equal to card. And if matched card is none, I can return agent not found. And now I need to use my agent connector. So I can define a connector with agent connector over here. Let's import this. And let's go over here. And let's review this again once. So I need the agent card, I have got that card. And I have defined my agent connector. And just based on this, I should be able to call send task. And I need to provide the message and a session ID. So let's go back over here. I have my connector with the matched card. And now I can call connector dot send card, send task. And I can provide over here the two things which is the message. Now the message is simply the message that's available to me. And then we have a session ID, right? So I need to choose something for my session ID over here. I can actually create a new session ID or I can use the user ID. So I'm just going to create a new session ID over here like so. And now this is my connector dot send task. And now I can actually await this. So I'll say await this. Alright. And I know that the send task method should return a string. And I can confirm that here. So it's a string. And I can just say return this string. Alright. So now this is my delegate task function. And I need to provide these two functions as tools to my build agent function over here. So what I can do is I can just provide this as a list over here. So I can say self dot list underscore agents and self dot delegate task. And I can also wrap this in something called a function tool. Let me import this on the top over here from google.adk.tools.function tool. And I'm going to import function tool. And if you check this, this is going to wrap the user defined Python function as a tool. So it's a tool that wraps a user defined Python function. All right. And let me just go down back again over here to my build agent function. And now I have provided this as a tool. and I'll also define the list agents as a function tool over here and let me just unpack this over here as a list of tools that is going to be added to this list all right so now I have all this together as a list of tools Alright, so now we have the build agent function completed with the two tools to delegate tasks and list agents. And we have the MCP connector with all the tools unpacked over here. Now we have the invoke function and we're going to let the invoke function be because it's just going to get the session service and then create the session then pass on the user query to the runner over here. And it's going to then get back all the events. And then as we had done earlier for the other agent, we are going to process these events, if it's a final response or not. Depending on that, we're going to yield these particular dictionaries, right. So this invoke function remains the same over here. I'm just going to check the runner once and we're just going to confirm that we have the correct settings over here. So yeah, so that's correct. Now, the only thing is that we've still not changed the name over here of the LLM agent. So I'm going to update this to host underscore agent. All right. And I don't have any other reference to website over here. So I think this should be good. So essentially, we are reusing the same code. The only thing is we have used the MCP connector and agent connectors to give the agent some tools. Now we have the agent executor. And I think this should remain the same. I'm just going to update the naming over here. So we're going to call this the host agent executor. Let's update the import over here for a host agent. All right. So we have the agent assigned correctly. Then we have the request content event queue and self in the execute function. We'll get the user query and then we'll enqueue that as a task. Right. And then we have the task updater. Alright, so I think this all remains same as well. And again, it's a live coding exercise. I'm going to come back to this in case there's some failure over here. But this agent executor basically makes my host agent A2A compatible, right? Alright, so now this completes my host agent code. Now let me go back to the system design. And we can see over here now that we have completed our host agent. Now this host agent also has its own A2A server, right? So what we're going to do is we are going to now connect it to an A2A client and we are going to define the port that this runs on and the A2A client can then connect to this with a simple command line app that can then send a query over here. Alright, so let's define that and let's also define the host and port for this A2A server. So let's go back to our code. Alright, so the one file that we've not updated is the main.py file. Let's go over here and let's make these corrections over here. So this is going to be host underscore agent. And we're going to import the host agent executed. Alright, let's update the port for this to 1001. And let this also run on localhost. Now let's update the ID name description, etc. over here. Alright, so I've made some updates over here and I'm calling this host agent skill and a simple orchestrator for orchestrating tasks with A2A agents and MCP tools. I've updated the tags and an example over here. Now let's do the same for agent card. Alright, so now we updated the agent card as well and now we are going to update the executor over here. So let's make this the agent executor, the host agent executor and now we have the Starlet application that takes the agent card and the request handler. And then we run the server. All right. So this looks good for the host agent. And now we need to build our command line app, right? So I'm going to make a folder called app over here. So let me minimize these. And let's select a root file. And let's click on new folder. And let's call this app, right? And you can have multiple front end apps for this. I'm just going to make a simple one, which is called a command line app. So I'm just going to call this CMD. And over here, I'm going to make a file which is cmd.py. So this is going to be my command line app. and let's start writing the code for this. So the first thing is that we'll define a CLI function. All right. So this is the command line interface. This is going to be our entry point. And let's assume that this needs an agent, which is a string and a session. And let's just rename this to agent underscore URL to make sure that we are expecting the URL over here. And then we have the session. So let this be a string. All right. So this is my simple command line interface entry point. And let me add a doc string over here. So this is a CLI. This is going to be a CLI to send user messages to an A2A agent using an A2A client and display the responses. Okay. Okay. And over here, this is as simple as delegating a task to an agent. So we've already gotten that logic down in agent.py inside our host agent over here, right? So we have this delegate task logic. So given an agent name or an agent card, we know how to delegate a message to it. That's a simple part. I just need to build the app aspect over here. All right. So let's start with creating a session ID. And what we can do is that we can actually check if the session is provided. So if not, we can actually create one. So we can say UUID 4. And let's import this dot hex. If not session, else session. All right. Then we're going to make a loop and this is going to be our chat loop. So we'll say while true and we'll prompt the user. So to prompt the user, let's define this as a click app. So what we can say is that add the rate of click dot command. Let's import click over here and then we are going to add options. So click dot option. So we can specify what agent do we want to connect to. And the default can be the one that we have just created on port 1002. So let's write HTTP colon slash slash local host colon 1002. All right. So that can be the orchestrator agent. Then let's also make. So let's also add an option for the session and the default can be. equal to something that's not a session id right so let's let it be equal to zero and this can be a session id so let me put that in the help string over here all right so now we have provided the agent url and the session over here and the default is zero and what we can do is we can actually put this check over here that if string session equal to equal to zero so let's do that All right. And if that is true, then we create a new session ID else we use the provided session ID. All right. And now we can use the prompt over here. So we can say click dot prompt and let's put over here, which is what do you want to send to the agent? And then let's say type colon Q or quit to exit. All right. So that's an option to exit our chat loop. So that's our prompt. And then we can check for the exit condition. So if prompt dot strip dot lower, and then I think this completion is correct that if it's either colon queue or quit, then we need to break. Otherwise, we can now delegate our task. Alright. So the user would have provided some kind of a prompt over here. I'm just going to go to my agent dot PY file over here. And this is my logic under agent discovery for getting the agent card. So let me just copy this from here. The first thing is that I need to get the agent card for the host agent. All right. So I'll go to my command line app and I'll just paste this logic over here. And what I need to do over here is to actually get the card first. So the card is going to be of type agent card. And this is going to be none right now. Let's import this. So from A to A dot types. I'm going to import the agent card. All right. So now I have this. So this is my card, then I'm going to make an HTTPX async client. So let me also import HTTPX. Okay. And now I have this resolved. And this is my HTTPX client. And now I don't need all this. So what I can just simply do is that I can actually pass in the URL that I already have, which is the agent URL. So I'm going to say A2A card resolver. Again, I need to import this. So let's go over here. All right. So this actually is not present in, let's actually go over here and write from a2a.client import a2a card resolver. All right. So now I have the a2a card resolver. So with my HTTPX client and my agent URL. So I'm just going to say agent URL over here. And if you see, we have this provided over here. So the default value is this. And otherwise I get this with the agent option. And of course, I need to match this. So this isn't matching over here. So let me just fix this. I just call this agent. And this is also going to be called agent. So now I have my agent URL and HTTPX client. So I have my A2A card resolver. And now what I can do is I can say card is equal to and this is going to be card over here. So card is equal to await resolver dot get agent card. And so now I have my agent card. All right. So now I can delegate this task. And for that, I'm going to go to my agent code. I have the delegate task function over here. Once I have the agent card, I just need to make a connector and then await the send task over here. All right. Let's copy this. Let's go back to our command line app over here. And now we have the agent connector. Let's press command dot over here. So we have our agent connector. We need the card over here, which is going to be provided by the card variable above. That then connects my command line app to the host agent. And now I can use connector dot send task. And instead of message, I'm going to write prompt over here because that's the prompt I want to send. And for the session ID, I'm going to provide the session ID I have from above. All right. And then I put the main block over here. It's come through autocomplete. I just update this to use async io.run. And I'm going to run the command line function over here. All right. And let's import this. All right. So this then completes my command line app. So just to briefly review this. we have a command line app over here and we get a prompt from the user in a loop so we can send multiple prompts and for each prompt that we want to send we will actually first get the agent card for that agent using our a2a card resolver which is provided by the a2a library and we'll get this agent card over here and then we'll use our agent connector and we will send the task to the agent right and this can of course we you know made more efficient because um in this loop every time we have to fetch the agent card and then you know we actually send the we define the agent connector again and again and send it so this could probably go outside the loop but i'm not going to uh do that right now i'll just i just try to go ahead with what we have and you know if in case this has some issues we'll correct them as we go along all right so now we're almost near running our system and to do this we need to do a few more steps and i'm going to go through those steps before so the first thing is that we need an environment file right dot env file so i have created one over here you can see that in the root directory so let me just minimize all of this and we have created a dot env file over here you can create this file in the root directory of your mcp a2a master and then just go to AI studio dot google dot com and click on get API key. So you'll have to log in through your Gmail account and it will give you a API key and you can create an API key by clicking on create API key over here. And once you do that, you'll see API key over here. You can click on this button to copy that key and then you need to go to your dot env file and paste this API key over here just as you get it from Google's AI studio. All right. And once you do that, you will have your environment variables set up correctly. Now to use this API key, we need our agents to load this while they are running. So you go to agents and host agent and agent.py and at the top over here, please add these lines. So you'll add from.env import load underscore dot env. And this is a module that helps us load the dot env file. And then you run load underscore dot env. And then this Google API key is going to be available as an environment variable. And the Google ADK will automatically use it. All right. Similarly, you need to do the same thing in website builder simple. Go to agent.py file. Let's go to the top over here. And then we need to add these two lines before we begin the class. All right. So this will make your agents be able to use the Gemini models. All right. So that's one thing. The other thing is that we want to update the models to the latest one. So if you go to chat. Google AI studio, right? So if you go to the chat over here, we can actually see the models that are available. So 2.5 pro is like the latest model, but it does not have a free limit. If it has a free limit, you'll see that come up over here. If you go to 2.5, you see that there is a free limit of 10 requests per minute and 500 requests per day. So this is probably the best model available for us right now. And we are going to use that. So wherever I have mentioned a 2.0 flash, I'm going to change it to 2.5. And there are only two agents that we have. So this is one. And the other one is this over here. Okay, so I have 2.0. So I'm going to make it 2.5. All right. All right. So that is one thing that you need to do before you can run your agents. And then after that, I'm going to actually make the CMD folder a module by... putting an empty init file over here. So this is an empty file that I put over here. And you can do the same over here, right? And this will just mark it as a module for app.cmd.cmd. Alright, now we're ready to run this. And when you run this, you will notice a few errors because of which you need to make some changes to your code. Alright. And this is completely expected in a live coded application. So I'm going to go through these changes first. So first of all, in cmd.py, which is our command line app, please make sure that the default is set to the host agent URL over here. Otherwise, it will try to connect to 1002 or something else. And unless you specify the agent. So this should be by default the host agent over here. And at the bottom, we need to print the response from the agent rather than return it. So this has to be changed over here that we have to actually print something of the sort that agent says and a response where the response is what we get after sending the task. So this will help us see the output from the agent on our screen. So that's the change over here. The other important thing is that we need to import improve the logic for the creation and building of our host agent because it has to discover MCP tools and other agents. So that process has to be made async, which is different from the website builder agent. All right. So if you go to agent.py file, you'll notice that I have made the build agent function async and I've created an async create function and we do not create the agent or the runner object inside the constructor because these are both async processes that need to wait for discovery of the agents. the clients so you have a very simple create function and this function will then await the build of the agent and create the runner object and if you go inside the build agent function you can see that we can now await this mcp connector get tools command and this is different from what we had done earlier so it's a simple change but we need to do that and this allows us to actually then properly discover all the mcp tools and all the agents Once you do that, the same change will actually happen in agent executor. So inside the agent executor, when you actually initialize, you'll initialize the host agent, but not create it. You'll not call it the create function because that is async. So we'll instead provide a create method over here as well and call the self.agent, which is the host agent create function that we had just created inside this create function. So now what happens is that you first create the object of this agent executor. and then you call the dot create function so that the agent can actually discover all the tools in an async fashion and that dot create can then be awaited so this is the difference if we did the same thing inside the constructor we cannot wait for the discovery to happen because that is how python constructors are they cannot be awaited that's the change in the agent executor And now for this function, we have the main file and this main file also needs to initialize and wait for all that to happen. So the main function is made async over here following in the same direction as we did for agent and agent executor. And we use async io.run main over here. All right. Now inside this function, you can see that we can create an agent executor like so. We can call the create function over here. which will then call the agents create function and wait for the discovery to happen and assign all the tools, etc. Whatever it has found inside the correct variables inside the agent and agent executor. All right. So this just works on the same variable over here, the same object over here. And finally, you assign this agent executor to your default request handler. So that is how the host agent has to be changed so that the the asynchronous nature of discovery can be handled, which was not required for the website builder. Just to finish this off, you would also notice that the click functionality over here is an async click so that it can actually work with the async nature of main over here. All right. So these are the changes you need to do to the host agent so that it can run properly. Alright, so then I'm going to go to the website builder and there was a small typo over here which I fixed. And in case you were coding it live with me, you would need to do the same. So there's an agent executor over here and this was actually the host agent executor that was getting imported over here. And so if you ask what the website builder agent can do for you, it will say that I'm a host agent because the executor was wrong, right? So we need to correct this and that's what I have done over here. Alright. Apart from that, let's make some improvements to how we print the responses from the agents so that we can see what's happening in the background. Otherwise, we don't know what's happening, what the agent is working on. So our agents can actually stream events. So what I've done over here is that I have made a simple change in the event management inside agent.py. So when we invoke the agent we get a stream of events we handle them using this async for and what i've added over here is i've added a print json response this is a customized pretty print function which is going to print the response i get in a nice way on the screen and it just prints out the event for us and also prints out whether the is response is a final value or true or false. Right. So if this is a final value, it will be true, else false. So this just gives us more information about the events that are happening, the ones that are not final and for the one that is final. All right. And then we will check over here that if the event is final response, please make sure that you have the parenthesis over here because this is a method call over here. And then we actually have the same thing over here. This is the print JSON response function. You can actually go through it from the code that I have put online. If you want to type it out live, you can actually see this code over here. All right. So for this, we have imported rich and from that we've imported print as rprint and we have imported syntax from rich.syntax. All right. So this is this. And then we need to go to agent underscore connect. So let's go to the utilities over here and let's go to A2A and agent underscore connect. And the change over here is that you need to put a message ID inside your payload. And earlier this was session ID, but message ID is required. And instead of session ID, we just write message ID over here. That's a simple change over here. All right. So that's your agent underscore connect dot py. all right and similarly there's a small change in mcp connect so we go over here and the change over here is that there's a try catch that we used over here in load all tools so load all tools was having a try catch outside the for loop we have shifted this inside the for loop so we say that for name comma server in self.discovery.listservers.items and please remember to have this over here dot items with the parenthesis you have the try and the catch inside. So this will happen for each name server combo over here. And what we'll do is that we'll start with an empty list of tools over here. So we start with an empty tools list. And what we do is that we get this tool set. And we just append it to this tool. So if we have a tool set, we will append it to the tools for each name server combination. And at the end, we'll just assign this to the tools property of our class MCB connector. So this just ensures that you have each and every tool set appended to the empty tools list. And that is then correctly assigned to the MCB connector updating that. So that is that and load all tools is involving connection to MCP servers. This has to be made async. So that has to be made sure. And because of this, we cannot call it in the constructor. So earlier, we were actually calling it inside over here, load all tools, but we cannot await this inside the constructor. And that just makes it problematic for us to load all the tools inside the constructor. And that's just how Python is. So Python does not allow awaiting inside the constructor. So what we need to then instead do is we need to use our get tools function, make that async and then await the load all tools function inside this. All right. So this just makes sure that, you know, you properly load all the tool sets and you return this list of MCP tool set. Then inside our build agent function for our host agent, we actually say MCP tools is equal to and then await self.mcpconnector.gettools. All right. So this is then going to give us all the tools. So that's the change in MCP connect. So those are all the changes that we needed to do. We have set up our environment correctly and I have tested this and for whatever issues were found, I have made these corrections. And now we can try to run this. Note that you can also get this code in case you're not live coding it with me. You can get this code directly from the link for the repo. And if you do that, you'll already have these changes present, right? Because I'm going to push this particular code. Alright, so let's go to the terminal over here. And what we're going to do is we're going to have four terminals. So I've already opened these four terminals and activated our virtual environment. Alright, so the virtual environment over here is just activated by typing source space dot venv slash bin slash activate. And you might be on a Windows machine, which will then be different. So please activate it for your machine. And after that, they're going to run the four entities over here. So we're going to run the streamable HTTP server for MCP over here. That is the add numbers server. We're also going to run the other server, but that's not explicitly required to be run, right? Because that's a SDIO server. So our MCP client will dynamically invoke it whenever needed. All right. Then you have your first agent, which is the website builder agent. So we are going to run this in this terminal. And we have our host orchestrator agent. So we're going to run this in the third terminal over here. And finally, we have our command line app, which we are going to run in the last terminal over here. So let's go ahead and run that. So first, we're going to run the MCP. streamable hvdp server so we'll just simply run it as a script all right so we'll just go to the mcp directory and then go to servers and then go to streamable hvdp server.py and run this so now you see that this is running on localhost 3000 now we can shift to the other tab let's go and run our first agent over here so we're going to run it as a python module so we'll say uv run and then python 3-m and then we can write agents dot website underscore builder underscore simple. All right. And we can run it like this. And we've already provided the default ports, right? So it's already going to choose that. And that's why these defaults are useful. So it's running on localhost 10,000. Now we can go and run our host agent. So for that, again, we run it like the way we ran the website builder. We'll just say uvrun python 3 dash m. And then we'll say agents dot. And then we'll write host underscore. agent and this also has its own defaults specified so it will run 1001 all right and you can see that it has loaded tools from the server terminal underscore server and it has loaded tools from the other server which is the arithmetic server and it's the terminal server tool and the add numbers tool so i think that we have named the tool terminal server that's why the tool name is that and we have the add numbers tool over here all right so these are the tools that are successfully loaded. So now is the time to actually then start our client. So we'll say uv run python3-m and then app.cmd.cmd. So this is our client and it straight away asks us what do you want to send to the agent. So we can simply ask us what can you do for me? What's your name? So now this is going to hit the host agent. So we can go over here and we can see that There's a final response. And this is the thing that it hit it with. And it says, my name is host agent. I can route the requests to A2Agent. So this is how your host agent is going to show you all the events that happened. And on the client side, we should see the final response. So this is what it says. Agent says, my name is host agent. I can route requests to A2 agents or directly use MCP tools. Here are some of the things I can do. Delegate tasks for the agents, run commands in terminal using terminal server and add numbers using add numbers. So would you like to know more about a specific capability or see a list of available agents? All right. So let's say, can you give me a list of available agents? All right. So let's go to our host agent and see what the issue is. Alright, so I think the issue is that you know, in the instructions, we have provided the name without the leading underscore. So we're going to put that underscore list underscore agents and underscore delegate underscore task. And then I'm just going to simply start this again and test it again. Alright, so let's close down the server over here. And let's start it again. And we have our client and let's just say again, you know, can you list the available? Alright, alright. So this gives me the list of tools and lets me also ask can You give me the list of available agents. All right. So now it correctly finds out that there's a website builder simple agent. This agent can create basic web pages. All right. So let me just ask it a question that uses both the tools and the website builder agent together. All right. So you need to specify, you know, that build a landing page template using the website builder agent because it will only list the agents as a tool, right? So if in case you don't say this, it might or might not list the agents and try to find out whether it has a specific website builder available because the host agent itself is an agent. It can also make a website for you. All right. So I've explicitly specified that use the website builder agent and then write this to a file. I've given the file name and I've specified that you need to use a terminal command to make the file, right? So let's press enter and see what happens. Alright, so we can see that the host agent is working over here and there are some events that have come in. And if you go back over here, it's still working actually, let's see this. So this has given a final response over here and we should have HTML content over here. Alright. So great, we do have a web page, right? And then this is our host agent. So let's see if we have a final response over here. So it's gotten the value of the HTML page from there. And then let's see if it is able to write this out to a file. Alright, so now it's actually calling the function has received a function response from the terminal server. There's a syntax error. Alright, so I hope it retries. And there are only 10 requests per minute available. So you know, you might we might break that free limit that's available and get an API key error. Alright, so it says finally, I have successfully built a landing page template using this tool, right? So let me go back over here and read this. I've successfully built a landing page template using the website builder agent and saved it to a file named landing page template dot HTML. Alright, so what do you want to send? Okay, so now let's just check it out. You know what we have over here. So let's press colon queue. And then let's try to open this file. So I'll say code, and then I'll go to the MCP workspace. So if you have the MCP terminal server, you would have made a workspace folder for yourself. For me, it's MCP slash workspace. And then I need to look for landing page template dot HTML audit. Great. So I have this file over here. So let me open this file. This is the file it has made and I can actually click show in browser. All right. So this is what I have, you know, awesome company, awesome core and home features, pricing, contact. And these are. and not actually clickable there's no action on that unlock your potential with an amazing product and then there's a sub heading get started now why choose us ready to transform your workflow and so on all right and then there's a photo over here so that's great so we have this you know working and to get to this we used one remote agent which was actually running locally but you could very well deploy it to google cloud and that agent built this website for us And there was a local MCP HD IO based server that wrote it to a file. There's also a streamable HTTP server. Let's also just check that out, you know, just in case there's some kind of an error, we can actually fix it before we close off this masterclass. All right. So let me start the client again. And let's say, can you add two numbers? Let's say 65 and nine using the add underscore numbers tool. All right. Let's write this out and then we have our server over here so this has got a list tools request over here it's also a call tool request that happened before all right so the agent says the sum of 65 and 9 is 74 all right so so we get this and you can actually check your host agent over here so um so it did make this a function call for add underscore numbers. All right. So the MCB streamable HTTP server also works. And this actually then completes all the testing that we needed to do. So I think I'll end the class over here. And if you face any issues, please put out a question and we'll try to solve the problems that might come up. I'm going to push this code to our class repository. And you'll find that mentioned in some of the written instructions somewhere. And you can please refer to that link. All right. Thanks a lot for watching. And I'll see you in the next lecture.

⚙️ Pipeline jobs

StageStatusAtt.UpdatedError
download done 1/3 2026-07-20 12:03:58
transcribe done 1/3 2026-07-20 12:05:49
summarize done 1/3 2026-07-20 12:06:15
embed done 1/3 2026-07-20 12:06:16

📄 Описание YouTube

Показать
Build a production-ready multi-agent orhestration system using the Model Context Protocol (MCP) and Agent-to-Agent (A2A) communication.

---------------------------------------------------------------------------------------------
Discounted Udemy Course Link (get completion certificate, practice questions, Q&A) 
https://theailanguage.com/best_price?course=mcp_a2a

Discounted Google ADK Udemy Course - Udemy Course (get completion certificate, Q&A, bite-sized lectures) 
https://theailanguage.com/best_price?course=adk
---------------------------------------------------------------------------------------------

This is a coding master class. Please refer to the videos for overview - 
MCP Overview - https://www.youtube.com/watch?v=lUs2wrlazwM&list=PL6tW9BrhiPTCDteflzehKS6Cn3a79-iCs

A2A Overview - https://www.youtube.com/watch?v=0bgrPco8Wfw&list=PL6tW9BrhiPTCKTXXJAwigi7QDNpA7t4Ip

Learning about A2A and MCP? JOIN THE COMMUNITY - https://www.youtube.com/channel/UCF7fVZmwNUgL7BFH56PnigQ/join

------------------------------------------------------------

Python version - please use Python version 3.11 for this code as there is a breaking change in Python version 3.12, 3.13 and 3.14 that causes errors in the MCP Toolset class of ADK. I will post an update about this as soon as the root cause for the issue is found and fixed in ADK or some other way.

To fix the Python version, please update it inside

FILE -  a2a_samples/version_7_mcp_a2a_master/mcp_a2a_master/.python-version

VALUE - 3.11

------------------------------------------------------------

🎓 UDEMY COURSES WITH EXCLUSIVE DISCOUNTS  
Sign in at 👉 https://theailanguage.com to access special discounted links with coupon codes.  

📘 Course 1: MCP & A2A Udemy Course  
✔ Completion certificate  
✔ Practice questions  
✔ Q&A support  

📗 Course 2: Google ADK Udemy Course  
✔ Completion certificate  
✔ Q&A support  
✔ Bite-sized, easy-to-follow lectures  

------------------------------------------------------------

CODE
Please Subscribe, allow pop-ups, then log in to The AI Language site to access our GitHub repos. This link works only for subscribers. Thanks!

https://theailanguage.com/onlySubscribers?id=a2a_samples_vid_utF6leQwcts_list_PL6tW9BrhiPTCKTXXJAwigi7QDNpA7t4Ip&site=github

This video uses the version_7_mcp_a2a_master/mcp_a2a_master folder of our sample repo.

This hands-on live coding project walks you through everything - from setting up MCP servers (stdio and streamable http) to designing A2A-compatible agents and building a host orchestrator. By the end, you’ll have a fully working system with service discovery, remote agent invocation, and a CLI-based command interface.

📦 Project Features:
- STDIO and Streamable HTTP MCP servers
- Agent discovery and registration
- Task orchestration with A2A-compatible agents
- A working host agent delegating to remote agents
- Command-line interface for interacting with the system
- Clear folder structure and reusable utilities

🛠️ Tech Requirements:
- Python 3.11+
- `uv` (Universal Virtualenv tool)

📚 Chapters:
00:00 Introduction to Masterclass
01:58 Requirements
02:44 Project Setup
05:09 1.1 MCP Server - STDIO
19:46 1.2 MCP Server - Streamable HTTP
29:01 2.1 MCP Client - Design & Config
33:27 2.2 MCP Client - Discovery
41:47 2.3 MCP Client - Connector
56:44 3.1 A2A Setup
01:00:15 3.2 A2A Remote Agent - base code
01:08:45 3.3 A2A Remote Agent - main code
01:14:37 3.4 A2A Remote Agent - executor code
01:28:14 3.5 A2A Remote Agent - invoke Function
01:35:08 3.6 A2A Server - code
01:38:12 4.1 A2A - Registry
01:39:51 4.2 A2A - Discovery
01:50:18 4.3 A2A - Connector
02:02:42 5.1 A2A Host Agent - Orchestrator
02:06:32 5.2 A2A Host Agent - List agents and delegate tasks
02:12:58 5.3 A2A Host Agent - executor code
02:13:41 5.4 A2A Host Agent - main code
02:15:14 6 A2A Command Line App and Client
02:22:55 API Key and Environment Setup
02:25:22 Other Code Changes
02:33:39 GET THE CODE
02:34:03 DEMO
02:40:16 OUTPUT 
02:41:53 Ouro

🙌 Like, Share & Subscribe if you found this helpful!

#Orchestration #MCP #A2A #MultiAgent #AI #Python #OpenSource #Discovery #AgentRegistry #MCPServer #MCPClient #A2AServer #A2AClient #Tools #RemoteAgent #List_agents #delegate_task