Monday, August 11, 2025

Building a Local FASTMCP Server with an ADK Agent

 In this tutorial, we’ll walk through how to create a local FASTMCP server that integrates with the Google ADK (Agent Development Kit). Instead of a streaming HTTP server, we’ll set up a local Python file to manage our server logic. The main goal is to expose a RESTful API and an ADK agent that can fetch data and respond to a separate FASTMCP client.

1. The FASTMCP Server with ADK Integration

First, let’s create the restapi-mcp-adk-server.py file. This script will define our server, an ADK agent, and the tools it uses.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# restapi-mcp-adk-server.py
from fastmcp import FastMCP, Context
import httpx
import asyncio
from fastmcp import Client
import google.genai as genai
from typing import Any
from google.genai import types
from dotenv import load_dotenv
 
from google.adk.agents import LlmAgent
from pydantic import BaseModel, Field
 
from google.adk.agents import Agent
from google.adk.events import Event
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
 
# Load environment variables (e.g., your Gemini API key)
load_dotenv()
 
# Initialize the FastMCP server
mcp = FastMCP(
    name="RESTful API Wrapper <img draggable="false" role="img" class="emoji" alt="🌐" src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/svg/1f310.svg">",
)
 
# Define the base URL for our external API
 
# Define session details for the ADK agent
APP_NAME = "MCP_SERVER_WITH_ADK_AGENT"
USER_ID = "1234"
SESSION_ID = "session1234"
 
# 1. Define a function that the ADK agent can use as a tool
# This function fetches a single object from the REST API
async def get_object_by_id(object_id: str):
    async with httpx.AsyncClient() as client:
        resp = await client.get(f"{BASE_URL}/{object_id}")
        print("get_object_by_id", resp.json())
        return resp.json()
 
# 2. Configure the ADK LlmAgent
# The agent's instruction tells it to use our tool when appropriate
call_mcp_server_agent = LlmAgent(
    model="gemini-2.0-flash",
    name="assistant",
    description="This agent is used to send data to the FASTMCP client.",
    instruction="""Help the user fetch data from the REST API and send it to the FASTMCP client.
    When the user asks to fetch data for a specific object ID, use the `get_object_by_id` tool and pass the ID to it.""",
    tools=[get_object_by_id],
)
 
# 3. Setup the ADK session and runner
# This is required to run the agent
async def setup_session_and_runner():
    session_service = InMemorySessionService()
    session = await session_service.create_session(app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID)
    if call_mcp_server_agent:
        runner = Runner(agent=call_mcp_server_agent, app_name=APP_NAME, session_service=session_service)
    return session, runner
 
# 4. Define the core async function that runs the ADK agent
async def get_agent_async(query):
    content = types.Content(role='user', parts=[types.Part(text=query)])
    session, runner = await setup_session_and_runner()
    events = runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content)
 
    final_response = "Agent did not produce a final response."
    async for event in events:
        if event.is_final_response() and event.content and event.content.parts:
            print(f"Potential final response from [{event.author}]: {event.content.parts[0].text}")
            final_response = event.content.parts[0].text
            print(f"final_response", final_response)
    return final_response
 
# 5. Expose the ADK agent as a FastMCP tool
# This is the key step that allows the MCP client to call our ADK agent
@mcp.tool()
async def get_objects_by_id_using_adk_agent(object_id: str,ctx: Context):
    print(f"object_id:::::::::::::::::",object_id)
    # The ADK agent will process this query and call its internal `get_object_by_id` tool
    final_result = await get_agent_async(f"Fetch the data for object_id {object_id}, pass the id to get_object_by_id tool")
    return final_result
 
if __name__ == "__main__":
    mcp.run()

This server file is the core of our setup. It defines a tool, get_objects_by_id_using_adk_agent, which itself acts as a wrapper. When called, this tool triggers our ADK agent with a specific query, and the agent, in turn, uses its own internal tool (get_object_by_id) to interact with the external REST API.


2. Configure Your Environment

To ensure your code can access the Google Gemini model, you’ll need to create a .env file in the same directory as your server script.

1
2
# .env
GOOGLE_API_KEY="YOUR_API_KEY_HERE"

Replace "YOUR_API_KEY_HERE" with your actual Google API key. This key will be loaded by the dotenv library.


3. The FASTMCP Client

Finally, let’s create the client script, agent.py, which will interact with our newly created server. This client will define its own tool that calls the server’s tool.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# agent.py
# This script will call the restapi-mcp-adk-server.py
import asyncio
from fastmcp import Client
from typing import Any
from google.genai import types
from dotenv import load_dotenv
 
from google.adk.agents import LlmAgent
from pydantic import BaseModel, Field
 
from google.adk.agents import Agent
from google.adk.events import Event
from google.adk.runners import Runner
from google.adk.sessions import InMemorySessionService
 
# Define a tool for our client agent
# This tool uses the FastMCP Client to connect to our server file
async def get_mcp_adk_data(object_id: str) -> dict:
    """Fetches an object by its ID from the MCP server."""
    print(f"Tool 'get_mcp_data' called with object_id: {object_id}")
    # The client connects to the server and calls its tool
    async with Client("restapi-mcp-adk-server.py") as client:
        single = await client.call_tool("get_objects_by_id_using_adk_agent", {"object_id": object_id})
        print("Fetched single:", single)
        return single
 
# Configure the client-side ADK agent
call_local_mcp_adk_server_agent = LlmAgent(
    model="gemini-2.0-flash",
    name="assistant",
    description="This agent uses the FASTMCP client to call the FASTMCP server.",
    instruction="""Help the user fetch data from the FASTMCP Server using the FASTMCP Client.
    When the user asks to fetch data for a specific object ID, use the `get_mcp_adk_data` tool and pass the ID to it.""",
    tools=[get_mcp_adk_data],
)
 
root_agent=call_local_mcp_adk_server_agent

This client script defines its own ADK agent, call_local_mcp_adk_server_agent. This agent’s primary tool, get_mcp_adk_data, uses the FASTMCP client to connect directly to our server (restapi-mcp-adk-server.py). It then calls the server’s exposed tool, get_objects_by_id_using_adk_agent, with the provided object_id.

This setup demonstrates a powerful pattern: an ADK agent can serve as a central orchestrator, with its tools acting as bridges to other systems, including other ADK agents running on different servers via FASTMCP. The chain of command looks like this:

Client ADK Agent ➡️ FASTMCP Client ➡️ FASTMCP Server ➡️ Server ADK Agent ➡️ External REST API

This layered approach allows for modularity and scalability in building complex AI-driven applications.

source code :- https://github.com/shdhumale/app.git


No comments: