DEV Community

Cover image for Running ADK agents with MCP tools from a REST API
David Elom GNAGLO
David Elom GNAGLO

Posted on

Running ADK agents with MCP tools from a REST API

Google's Agent Development Kit (ADK) is a code-first framework: you write a Python module, declare an Agent, give it tools, and run it. That is great when one developer owns one agent. It gets harder when a team needs dozens of agents, changes their instructions every week, and wants to know who ran what, on which model, for how many tokens.

apowerb is an open source (Apache-2.0) platform we built for that second case. Agents are stored as database records, created from a web UI or a REST API, and apowerb builds the ADK objects for you. This post walks through creating an ADK agent with an MCP server attached, using nothing but HTTP calls, and shows what came back on a real run.

From a record to an ADK agent

Each agent is a row: a name, an instruction, a model string, a list of tools, optional sub-agents and MCP servers. A function called to_agent() turns that row into an ADK object:

  • llm agents become an LlmAgent; sequential, parallel and loop agents become a SequentialAgent, ParallelAgent or LoopAgent, with their sub-agents built recursively.
  • The model string is wrapped in ADK's LiteLlm model, so any LiteLLM provider works. Gemini models take the gemini/ prefix.
  • Each entry in mcp_servers becomes an ADK McpToolset.

When an agent changes, apowerb regenerates its ADK module and the ADK FastAPI app mounted inside apowerb picks it up. Runs go through the standard ADK runner, so sessions, events and traces are plain ADK.

1. Get a token

import requests

BASE_URL = "http://localhost:8000"
EMAIL = "you@example.com"

response = requests.post(
    f"{BASE_URL}/api/auth/token",
    data={"username": EMAIL, "password": "YOUR_PASSWORD"},
)
response.raise_for_status()
HEADERS = {"Authorization": f"Bearer {response.json()['access_token']}"}
Enter fullscreen mode Exit fullscreen mode

2. Create an agent with an MCP server

For the test I used DeepWiki's public MCP server, which needs no account and answers questions about GitHub repositories.

agent = requests.post(
    f"{BASE_URL}/api/agents",
    headers=HEADERS,
    json={
        "agent_name": "docs_assistant",
        "agent_description": "Answers questions with the tools of an MCP server",
        "agent_instruction": "You are a helpful assistant. Use the MCP tools when they fit the request.",
        "agent_model": "gemini/gemini-flash-latest",
        "agent_model_params": {"model_api_key": "YOUR_GEMINI_API_KEY"},
        "agent_type": "llm",
        "agent_tools": [],
        "mcp_servers": [
            {"name": "my_mcp_server", "transport": "http", "url": "https://mcp.deepwiki.com/mcp"}
        ],
    },
)
agent.raise_for_status()
Enter fullscreen mode Exit fullscreen mode

Remote servers use the http transport by default. The stdio transport launches a local command, so it is off unless the operator sets MCP_STDIO_ENABLED=1, and only administrators can use it.

3. Run it

result = requests.post(
    f"{BASE_URL}/api/adk/run",
    headers=HEADERS,
    json={
        "agent_name": "docs_assistant",
        "user_id": EMAIL,
        "session_id": "session_001",
        "new_message": {
            "role": "user",
            "parts": [{"text": "Use your tools: what is the GitHub repository google/adk-python about?"}],
        },
    },
)
events = result.json()
Enter fullscreen mode Exit fullscreen mode

The response is the list of ADK events for the turn. On the real run, the events contained a functionCall to the MCP tool ask_wiki_question, followed by the model's answer:

The google/adk-python repository contains the Agent Development Kit (ADK), an open-source, code-first Python framework designed for building, evaluating, and deploying sophisticated AI agents.

Asked "Which tools do you have access to?", the same agent listed the three DeepWiki tools (ask_wiki_question, read_wiki_structure, read_wiki_contents) next to apowerb's own built-in tools.

The same body posted to /api/adk/run_sse streams the events as server-sent events. The full conversation and its execution trace are then available at /api/adk/sessions/{agent}/{user}/{session} and .../trace.

What the platform adds around ADK

ADK stays in charge of the agent loop. apowerb adds what a team needs around it:

  • a web UI and REST API to create and edit agents without redeploying code;
  • built-in tools for RAG, Text-to-SQL, BI charts, Google Workspace and Microsoft 365;
  • scheduled runs and runs triggered by email;
  • per-user token quotas and a per-session audit trail;
  • self-hosting with Docker Compose or a Helm chart.

Try it

Feedback and issues are welcome on GitHub.

Top comments (3)

Collapse
 
anis_meziani_52aab42304a8 profile image
Anis Meziani •

Great walkthrough, David! Creating an agent with an MCP toolset and running it with nothing but HTTP calls is exactly the demo people need 🤩.

One improvement that would make it even more useful for real deployments: an example with an authenticated MCP server. DeepWiki is public, but most teams will plug in internal servers that need a token. The mcp_servers config already accepts headers, so a short snippet with an Authorization header, and a word on where that token is stored, would answer the first question readers will have. Same for the model key: the shared default key in .env keeps it out of the request body.

Two ideas for a follow-up: what the agent sees when the MCP server is slow or down (plus a raise_for_status() on the /run call), and the toolbox-db type, which connects agents to databases through MCP Toolbox. That one deserves its own post.

Collapse
 
david_elomgnaglo_27fb318 profile image
David Elom GNAGLO •

Thanks Anis, good points.

Authenticated MCP server. Yes, headers is passed as-is to the MCP client (HTTP and SSE transports):

"mcp_servers": [
    {
        "name": "internal_mcp",
        "transport": "http",
        "url": "https://mcp.internal.example.com/mcp",
        "headers": {"Authorization": f"Bearer {MCP_TOKEN}"},
    }
],
Enter fullscreen mode Exit fullscreen mode

Read MCP_TOKEN from your environment or secret manager on the client side, never hard-code it. On where apowerb keeps it: it is stored with the agent's MCP config, and I'd rather cover storage and rotation properly in a follow-up than hand-wave it here.

Model key. Right: with "agent_model": "thaink2/default", the agent uses the server's DEFAULT_LLM_MODEL / DEFAULT_LLM_API_KEY, so there is no key in the request body and none stored on the agent.

raise_for_status() on /run: fair, I'll add it. A slow or unavailable MCP server and the toolbox-db type are noted for the next post.

Collapse
 
anis_meziani_52aab42304a8 profile image
Anis Meziani •

Looking forward to the follow-up on token storage and rotation. That’s the first thing a security team will ask about, and showing how to rotate an MCP token across every agent that uses it would make a great post.