Skip to main content
Model Context Protocol (MCP) is an open standard for connecting AI applications to external data sources and tools. Tyler has first-class support for MCP through declarative configuration—no boilerplate code needed!

Quick Start

Python API

from tyler import Agent

# Create agent with MCP config
agent = Agent(
    name="Tyler",
    model_name="gpt-4.1",
    tools=["web"],
    mcp={
        "servers": [{
            "name": "slide_docs",
            "transport": "streamablehttp",  # Mintlify uses streamablehttp
            "url": "https://slide.mintlify.app/mcp"
        }]
    }
)

# Connect to MCP servers (fail fast!)
await agent.connect_mcp()

# Use normally - MCP tools are now available
thread = Thread()
thread.add_message(Message(role="user", content="How do I create a Tyler agent?"))
result = await agent.run(thread)

# Optional: Cleanup MCP connections (not needed for scripts)
# Useful for long-running apps or when creating many agents
# await agent.cleanup()

CLI (tyler-chat)

Add this to your tyler-chat-config.yaml:
name: "Tyler"
model_name: "gpt-4.1"

tools:
  - "web"

mcp:
  servers:
    - name: slide_docs
      transport: streamablehttp  # Mintlify uses streamablehttp
      url: https://slide.mintlify.app/mcp
Then run:
tyler chat  # MCP servers connect automatically on startup!

How It Works

  1. Agent Creation: Agent(mcp={...}) validates config schema immediately (fail fast!)
  2. Connection: await agent.connect_mcp() connects to servers and discovers tools
  3. Tool Registration: Discovered tools are namespaced (servername_toolname) and merged with built-in tools
  4. Usage: Tools are available in agent.run() like any other tool
  5. Cleanup: await agent.cleanup() disconnects MCP servers

Configuration Reference

Server Configuration

Each MCP server requires:
{
    "name": str,        # Required: Unique server identifier
    "transport": str,   # Required: "stdio", "streamablehttp", "sse", or "websocket"
    
    # Transport-specific fields (choose based on transport):
    "url": str,         # Required for streamablehttp/sse/websocket
    "command": str,     # Required for stdio
    "args": List[str],  # Optional for stdio
    "env": Dict,        # Optional for stdio
    
    # Optional fields:
    "headers": Dict,           # Custom HTTP headers (for streamablehttp/sse/websocket)
    "include_tools": List[str],  # Whitelist specific tools
    "exclude_tools": List[str],  # Blacklist specific tools
    "prefix": str,             # Custom namespace (default: server name)
    "fail_silent": bool        # Continue if connection fails (default: true)
}

Transport Types

Streamable HTTP - For HTTP-based MCP servers (Mintlify, hosted servers):
{
    "name": "slide_docs",
    "transport": "streamablehttp",
    "url": "https://slide.mintlify.app/mcp"
}
stdio - For local process MCP servers:
{
    "name": "filesystem",
    "transport": "stdio",
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
SSE (Server-Sent Events) - Legacy HTTP transport (for backward compatibility):
{
    "name": "legacy_server",
    "transport": "sse",
    "url": "https://legacy.example.com/mcp"
}
WebSocket - For WebSocket-based servers:
{
    "name": "api",
    "transport": "websocket",
    "url": "wss://api.example.com/mcp",
    "headers": {
        "Authorization": "Bearer ${API_TOKEN}"  # Use env vars!
    }
}

Advanced Usage

Multiple Servers

Connect to multiple MCP servers simultaneously:
agent = Agent(
    mcp={
        "servers": [
            {"name": "docs", "transport": "sse", "url": "https://docs.example.com/mcp"},
            {"name": "db", "transport": "sse", "url": "https://db.example.com/mcp"},
            {"name": "files", "transport": "stdio", "command": "mcp-server-files"}
        ]
    }
)

await agent.connect_mcp()  # Connects to all servers in parallel

Tool Filtering

Control which tools are registered:
{
    "name": "filesystem",
    "transport": "stdio",
    "command": "mcp-server-filesystem",
    "include_tools": ["read_file", "list_directory"],  # Whitelist
    "exclude_tools": ["write_file", "delete_file"]   # Blacklist (applied after include)
}

Custom Namespace

Override the default namespace prefix:
{
    "name": "wandb_documentation_server",  # Long name
    "prefix": "docs",  # Short, clean prefix
    "transport": "sse",
    "url": "https://docs.wandb.ai/mcp"
}
# Tools: docs_search, docs_query (instead of wandb_documentation_server_search)

Environment Variables

Use environment variables for secrets (recommended!):
mcp:
  servers:
    - name: api
      transport: websocket
      url: wss://api.example.com/mcp
      headers:
        Authorization: "Bearer ${API_TOKEN}"  # Substituted from env
Set the environment variable:
export API_TOKEN=your_secret_token
tyler chat

Graceful Degradation

Control failure behavior per server:
{
    "servers": [
        {
            "name": "critical_server",
            "url": "...",
            "fail_silent": False  # Fail startup if unavailable
        },
        {
            "name": "optional_server",
            "url": "...",
            "fail_silent": True  # Continue if unavailable (default)
        }
    ]
}

Resource Management

When to Use cleanup()

MCP connections are automatically cleaned up when your process ends. You only need explicit cleanup in: Long-running applications:
# Web server creating agents per request
@app.post("/chat")
async def chat_endpoint(request):
    agent = Agent(mcp={...})
    await agent.connect_mcp()
    result = await agent.run(thread)
    await agent.cleanup()  # ← Important! Free connection
    return result
Testing or batch processing:
# Creating/destroying many agents
for task in tasks:
    agent = Agent(mcp={...})
    await agent.connect_mcp()
    await agent.run(thread)
    await agent.cleanup()  # ← Prevent connection leak
Short scripts DON’T need cleanup (except streamablehttp):
async def main():
    agent = Agent(
        mcp={"servers": [{"transport": "stdio", ...}]}  # stdio, sse, websocket
    )
    await agent.connect_mcp()
    result = await agent.run(thread)
    # Done - no cleanup needed!

asyncio.run(main())  # Connections close when process ends
Exception: streamablehttp transport requires cleanup:
async def main():
    agent = Agent(
        mcp={"servers": [{"transport": "streamablehttp", ...}]}  # Mintlify
    )
    await agent.connect_mcp()
    result = await agent.run(thread)
    await agent.cleanup()  # ← Required for streamablehttp to avoid asyncio errors

asyncio.run(main())
The streamablehttp transport uses asyncio task groups that require explicit cleanup, even in simple scripts. Other transports (stdio, sse, websocket) clean up automatically.

Security Best Practices

Never hardcode API keys or secrets in config files! Always use environment variable substitution (${VAR}).
DO:
mcp:
  servers:
    - name: api
      url: https://api.example.com/mcp
      headers:
        Authorization: "Bearer ${API_TOKEN}"  # ✓ Good
DON’T:
mcp:
  servers:
    - name: api
      url: https://api.example.com/mcp
      headers:
        Authorization: "Bearer sk-1234567890"  # ✗ Bad - secret exposed!
Trust only verified MCP servers:
  • Only connect to MCP servers you trust
  • MCP tools execute with your agent’s permissions
  • Review tool capabilities before enabling

Troubleshooting

Connection refused

Error: Failed to connect to MCP server 'xyz': Connection refused Solutions:
  1. Verify the server URL is correct
  2. Check if the MCP server is running
  3. For stdio servers, verify the command path is correct
  4. Check firewall settings

Invalid config schema

Error: Server 'xyz' with transport 'sse' requires 'url' field Solution: Ensure required fields are present for the transport type:
  • SSE/WebSocket: url required
  • stdio: command required

Tools not discovered

Error: MCP connects but no tools available Solutions:
  1. Verify the MCP server is functioning (check server logs)
  2. Check if tools are being filtered out (remove include_tools/exclude_tools)
  3. Try connecting to the server manually to verify it exposes tools

Environment variable not substituted

Error: Connection fails with literal ${VAR} in URL Solution: Ensure the environment variable is set before running:
export MY_VAR=value
python my_script.py
Advanced Users: Need more control? The low-level MCPAdapter API is available for advanced use cases, but the config approach is recommended for 99% of users. See the API Reference for details.

Real-World Examples

Next Steps