agno-agi / agno

Multi-agent framework, runtime and control plane. Built for speed, privacy, and scale.

Home Page:https://docs.agno.com

Repository from Github https://github.comagno-agi/agnoRepository from Github https://github.comagno-agi/agno

[Feature Request] MCP in Playground

lmh87883819 opened this issue · comments

Problem Description

In the context of using Playground, what is the procedure for creating multiple agents that utilize MCP within it?

app = Playground(
        agents=agents
    )

Proposed Solution

/

Alternatives Considered

/

Additional context

/

Would you like to work on this?

/

Hey @lmh87883819,
Thank you for using Agno and for reaching out to us!
We appreciate you taking the time to raise this feature request. We'll discuss it internally and get back to you shortly.

Thank you for your reply and look forward to your team's discussion @monali7-d

This is also a problem for me. An easy way to unblock people is to give up on auto reload functionality and let people call the async endpoint directly to run the playground endpoint.

https://github.com/oraios/serena seems to have worked around the problem by just not plugging their MCP into agno at all, and instead writing their code so they can target Agno Tool directly. But IMO this is bad, if MCP is the standard better to only publish MCP...

So for example, this works:

import asyncio
from pathlib import Path
from textwrap import dedent

from agno.agent import Agent
from agno.playground import Playground
from agno.models.anthropic import Claude
from agno.tools.mcp import MCPTools
from mcp import StdioServerParameters

from typing import Union
from urllib.parse import quote

from fastapi import FastAPI
from rich import box
from rich.panel import Panel

from agno.api.playground import PlaygroundEndpointCreate, create_playground_endpoint
from agno.cli.console import console
from agno.cli.settings import agno_cli_settings
from agno.utils.log import logger


async def serve_playground_app_async(
    app: Union[str, FastAPI],
    *,
    scheme: str = "http",
    host: str = "localhost",
    port: int = 7777,
    reload: bool = False,
    prefix="/v1",
    **kwargs,
):
    import uvicorn

    try:
        create_playground_endpoint(
            playground=PlaygroundEndpointCreate(
                endpoint=f"{scheme}://{host}:{port}", playground_data={"prefix": prefix}
            ),
        )
    except Exception as e:
        logger.error(f"Could not create playground endpoint: {e}")
        logger.error("Please try again.")
        return

    logger.info(f"Starting playground on {scheme}://{host}:{port}")
    # Encode the full endpoint (host:port)
    encoded_endpoint = quote(f"{host}:{port}")

    # Create a panel with the playground URL
    url = f"{agno_cli_settings.playground_url}?endpoint={encoded_endpoint}"
    panel = Panel(
        f"[bold green]Playground URL:[/bold green] [link={url}]{url}[/link]",
        title="Agent Playground",
        expand=False,
        border_style="cyan",
        box=box.HEAVY,
        padding=(2, 2),
    )

    # Print the panel
    console.print(panel)

    config = uvicorn.Config(app=app, host=host, port=port, reload=reload, **kwargs)
    server = uvicorn.Server(config)
    await server.serve()

async def main():
    async with MCPTools(f"/Users/ezyang/Dev/codemcp-prod/.venv/bin/python -m codemcp.hot_reload_entry") as codemcp:
        agent = Agent(
            model=Claude(id="claude-3-7-sonnet-20250219"),
            tools=[codemcp],
            instructions="init codemcp /Users/ezyang/Dev/refined-claude",
            markdown=True,
            show_tool_calls=True,
        )
        playground = Playground(agents=[agent]).get_app()
        await serve_playground_app_async(playground)

if __name__ == "__main__":
    asyncio.run(main())

Hey @lmh87883819,
Thank you for reaching out and for using Agno!

We’ve recently added an example that might be helpful for you:
https://github.com/agno-agi/agno/blob/main/cookbook/playground/mcp_demo.py

Feel free to check it out, and let us know if you have any questions or need further clarification — happy to help!

I don't see how the example can work, won't you deadlock the outer async loop

commented

Hey @ezyang, the example seems to work fine, although I see it uses our (sync) version of serve_playground_app

  1. Can that setup work for you?
  2. Else, what exactly is the problem? would love to dive into this and help you

Hey.. i am trying to use the playground feature..with "sse" mcp, and I am running into issues.
Used the same sample as this
https://github.com/agno-agi/agno/blob/main/cookbook/playground/mcp_demo.py

below is the code that i am trying to execute

import asyncio
from os import getenv
from textwrap import dedent

import nest_asyncio
from agno.agent import Agent
from agno.models.google import Gemini
from agno.playground import Playground, serve_playground_app
from agno.storage.agent.sqlite import SqliteAgentStorage
from agno.tools.mcp import MCPTools

# Allow nested event loops
nest_asyncio.apply()

agent_storage_file: str = "tmp/agents.db"
server_url = "http://remote_server:8001/sse"

async def run_server() -> None:
    """Run the GitHub agent server."""
    async with MCPTools(url=server_url, transport="sse") as mcp_tools:
        agent = Agent(
            name="MCP Agent",
            tools=[mcp_tools],
            instructions=dedent("""\
                You are a SQL assistant. Help users explore SQL databases.

                - Use headings to organize your responses
                - Be concise and focus on relevant information
                - Use markdown formatting for your responses
            """),
            model=Gemini(id="gemini-2.5-flash-preview-05-20"),
            storage=SqliteAgentStorage(
                table_name="basic_agent",
                db_file=agent_storage_file,
                auto_upgrade_schema=True,
            ),
            add_history_to_messages=True,
            num_history_responses=3,
            add_datetime_to_instructions=True,
            markdown=True,
        )

        playground = Playground(
            agents=[agent],
            name="MCP Demo",
            description="A playground for MCP",
            app_id="mcp-demo",
        )
        playground.get_app()

        # Serve the app while keeping the MCPTools context manager alive
        playground.serve(app="mcp_demo:app")


if __name__ == "__main__":
    asyncio.run(run_server())

error i run into

 memory git:(main) ✗ python mcp_demo.py
INFO Starting playground on http://localhost:7777                                                                              
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Agent Playground ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃                                                                                ┃
┃                                                                                ┃
┃  Playground URL: https://app.agno.com/playground?endpoint=localhost%3A7777/v1  ┃
┃                                                                                ┃
┃                                                                                ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
ERROR:    Error loading ASGI app. Attribute "app" not found in module "mcp_demo".

could you please help me understand if I am doing it incorrectly.

Thanks in advance

In your specific case, the problem lies in your:

playground.serve(app="mcp_demo:app")

The mcp_demo must be your module name and "app" must be the variable inside your module that must be (I believe) available outside of the if __name__ == '__main__' part.

How to solve this exactly in your case I don't know, but the following works for a normal agent:

#filename: my_agents

<your agent>


app = Playground(agents=[<your agent>]).get_app()

if __name__ == "__main__":
    from agno.playground import serve_playground_app
    
    serve_playground_app('my_agents:app', reload=True)

Thanks a ton for pointing this out! this helped me fix the issue!!!

@rajuptvs Can you please share how you managed to fix this issue?

@ezyang the script below works . @rajuptvs is it what you're thinking about?

import os
from contextlib import asynccontextmanager
from textwrap import dedent

import nest_asyncio
from agno.agent import Agent
from agno.models.openai import OpenAIChat
from agno.playground import Playground
from agno.tools.mcp import MCPTools

nest_asyncio.apply()

COMPANY_MCP_URL = "http://192.168.16.244:8080/mcp/company"

app = None
mcp_tools = None

@asynccontextmanager
async def lifespan(app):

    global mcp_tools
    
    print("Initializing MCP tools...")
    mcp_tools = MCPTools(
        url=COMPANY_MCP_URL, 
        transport='streamable-http',
        timeout_seconds=5*60
    )
    await mcp_tools.__aenter__()
    
    simple_agent.set_tools([mcp_tools])
    print("MCP tools initialized successfully")
    
    try:
        yield
    finally:
        print("Cleaning up MCP tools...")
        if mcp_tools:
            await mcp_tools.__aexit__(None, None, None)
            mcp_tools = None

simple_agent = Agent(
    name="MCP Company Agent",
    instructions=dedent("""\
        You are a Company assistant. Help users explore company information.
        - Use headings to organize your responses
        - Be concise and focus on relevant information\
    """),
    model=OpenAIChat(
        id="deepseek-v3",
        api_key=os.getenv("OPENAI_API_KEY"),
        base_url=os.getenv("OPENAI_BASE_URL")
    ),
    add_history_to_messages=True,
    num_history_responses=3,
    add_datetime_to_instructions=True,
    markdown=True,
)

playground = Playground(
    agents=[simple_agent],
    name="MCP Demo",
    description="A playground for MCP",
    app_id="mcp-demo",
)

app = playground.get_app()
app.router.lifespan_context = lifespan

if __name__ == "__main__":
    playground.serve(app="mcp_demo:app", reload=True)

Hey @lmh87883819, Thank you for reaching out and for using Agno!

We’ve recently added an example that might be helpful for you: https://github.com/agno-agi/agno/blob/main/cookbook/playground/mcp_demo.py

Feel free to check it out, and let us know if you have any questions or need further clarification — happy to help!

URL is outdated.

New URL: https://github.com/agno-agi/agno/blob/main/cookbook/apps/playground/mcp_demo.py

Permalink: https://github.com/agno-agi/agno/blob/e68a59ea77d6db73818f55c14b310dc51bedf869/cookbook/apps/playground/mcp_demo.py