WARP.md
Documents a monorepo MCP server for transit data, covering setup, architecture, tool creation, and API integration.
What this file does
Documents a monorepo MCP server for transit data, covering setup, architecture, tool creation, and API integration.
When to use it
- Onboarding new developers to an MCP server project with modular subservers
- Adding new MCP tools or subservers to an existing FastMCP codebase
- Understanding the error handling and API request patterns used in the project
- Setting up a development environment for a Python MCP server
Assumes this stack
WARP.md
This file provides guidance to WARP (warp.dev) when working with code in this repository.
Project Overview
Infobús MCP is a Model Context Protocol (MCP) server that enables AI assistants and chatbots to access transit information through standardized interfaces. It provides tools for trip planning, real-time information queries, and natural language transit assistance.
Repository Structure
This is a monorepo with two main components:
server/: Python-based MCP server implementation using FastMCP (primary focus)client/: Client implementation (currently minimal)
All Python code and dependencies are in the server/ directory.
Server Directory Structure
server/
├── transit.py # Main entry point - imports and composes subservers
├── topics/ # Domain-specific subservers (modular architecture)
│ ├── agencies.py # Agency listing tools
│ ├── routes.py # Route information tools
│ ├── stops.py # Stop info and next trips (fully implemented)
│ ├── trips.py # Trip planning (planned)
│ ├── alerts.py # Service alerts (planned)
│ └── shapes.py # Route shapes (planned)
└── utils/
└── infobus_requests.py # Shared API request utilities
Development Environment
Prerequisites
- Python 3.14 (specified in
server/.python-version) uvpackage manager for Python dependency management
Initial Setup
cd server
uv venv
source .venv/bin/activate # On macOS/Linux
uv sync # Install dependencies from uv.lock
Running the MCP Server
The server runs using stdio transport for MCP communication:
# From project root
uv --directory server run transit.py
# Or from server directory
cd server
python transit.py
The server must be run with stdio transport as it's designed to be called by MCP clients (Claude Desktop, etc.), not standalone.
VS Code MCP Configuration
The project includes VS Code MCP configuration in .vscode/mcp.json that runs the server using:
uv --directory server run transit.py
Testing
No automated tests are currently implemented. To test changes:
- Run the server using one of the methods above
- Connect via an MCP client (Claude Desktop, VS Code MCP extension)
- Test tools manually through the client interface
- Verify API responses using the Infobús API directly:
https://infobus.bucr.digital/api
Architecture
MCP Server Implementation
The project uses the FastMCP framework with a modular subserver architecture.
Architecture Overview:
server/transit.py: Main server entry point that imports and composes domain-specific subserversserver/topics/: Directory containing modular subservers, each focused on a specific domain (agencies, routes, stops, trips, etc.)- Each subserver is imported with a prefix (e.g.,
agencies,routes,stops) intransit.pyusingmcp.import_server() - Subservers are independent FastMCP instances that can be developed and tested in isolation
Key Pattern:
# In transit.py
async def setup():
await mcp.import_server(agencies_mcp, prefix="agencies")
await mcp.import_server(routes_mcp, prefix="routes")
await mcp.import_server(stops_mcp, prefix="stops")
This allows tools from each subserver to be namespaced (e.g., agencies_info, routes_info, stops_next_trips).
MCP Tools
Tools are exposed via the @<subserver>.tool() decorator in their respective topic modules.
Fully Implemented:
-
stops_next_trips(stop_id: str, timestamp: str): Fetches next arriving buses at a given stop- Returns up to 4 next arrivals
- Timestamp format: ISO 8601 (YYYY-MM-DDTHH:MM:SS)
- Uses GTFS stop IDs
- Implemented in
server/topics/stops.py
-
stops_info(stop_id: str): Get information about a specific bus stop (placeholder) -
routes_info(route_id: str): Get information about a route (placeholder) -
agencies_info(): Get list of transit agencies (placeholder)
Partially Implemented/Planned:
- Trips, alerts, calendar, fares, shapes modules exist but are empty/placeholders
Infobús API Integration
The server integrates with the Infobús API at https://infobus.bucr.digital/api
API Communication:
- Uses
httpxfor async HTTP requests make_infobus_request()inserver/utils/infobus_requests.pyhandles all API calls with error handling- Returns error dictionaries on failure rather than raising exceptions
- User-Agent configured via
USER_AGENTenvironment variable (defaults to "transit-app/1.0") - 30-second timeout on requests
- Follows redirects automatically
API Endpoints Used:
/next-trips/: Get next trips for a stop at a given time
Error Handling Pattern
The codebase uses a consistent error handling pattern:
- API requests return
{"error": "..."}on failure - Tools check for "error" key in responses
- Tools return user-friendly error messages as strings
- Format functions (like
format_next_arrival) handle missing data gracefully
Adding New MCP Tools
When adding new tools, decide whether to add to an existing topic subserver or create a new one.
To add to existing subserver:
- Open the appropriate file in
server/topics/(e.g.,stops.py,routes.py) - Use the
@<subserver>.tool()decorator (e.g.,@stops_mcp.tool()) - Provide clear docstrings with Args documentation
- Use
make_infobus_request()fromutils.infobus_requestsfor API calls - Return formatted strings, not raw JSON
- Handle error cases explicitly by checking for "error" key
- Limit data returned to users (e.g., first 4 arrivals)
To create a new subserver:
- Create new file in
server/topics/(e.g.,calendar.py) - Initialize a new FastMCP instance:
calendar_mcp = FastMCP(name="CalendarServer") - Add tools using
@calendar_mcp.tool()decorator - Import and register in
server/transit.py:from topics.calendar import calendar_mcp async def setup(): # ... await mcp.import_server(calendar_mcp, prefix="calendar")
Example tool structure:
from fastmcp import FastMCP
from utils.infobus_requests import make_infobus_request
from decouple import config
stops_mcp = FastMCP(name="StopsServer")
INFOBUS_API_BASE = config("INFOBUS_API_BASE", default="http://localhost:8000/api")
@stops_mcp.tool()
async def your_tool_name(param: str) -> str:
"""Tool description.
Args:
param: Parameter description
"""
url = f"{INFOBUS_API_BASE}/endpoint/"
params = {"param": param}
data = await make_infobus_request(url, params)
if "error" in data:
return f"Error: {data['error']}"
# Format and return data
return formatted_result
Dependencies
Core dependencies (from pyproject.toml):
fastmcp>=2.12.5: FastMCP framework for building MCP servershttpx>=0.28.1: Async HTTP client for API requestspython-decouple>=3.8: Configuration management via environment variables
Configuration
Configuration is handled via python-decouple which reads from environment variables or .env files:
INFOBUS_API_BASE: Base URL for Infobús API (default:http://localhost:8000/api)- Each topic module that needs API access should define this constant
- Production value should be
https://infobus.bucr.digital/api
USER_AGENT: User agent string for API requests (default:transit-app/1.0)- Defined in
server/utils/infobus_requests.py
- Defined in
- Python version pinned to 3.14 in
server/.python-version
What's inside
10 sections covering project overview, repo structure, setup, architecture, tools, API integration, error handling, adding tools, dependencies, and configuration.
Change this for your project
- Replace
INFOBUS_API_BASEdefaulthttp://localhost:8000/apiwith your own API base URL - Replace
USER_AGENTdefaulttransit-app/1.0with your own user agent string - Replace
https://infobus.bucr.digital/apiwith your own production API endpoint
Where it goes
Save in docs/ or the repository root. Gives agents and new contributors a map of the codebase.
Worth borrowing
- Modular subserver architecture with namespaced tools via
mcp.import_server() - Consistent error handling pattern returning error dictionaries instead of raising exceptions
- Formatting API responses into user-friendly strings before returning from tools
Related Documents
Design Document: BharatSeva AI
Describes a 10-agent AWS system that helps India's informal workers access government schemes via voice-first, serverless architecture.
OpenClaw Enterprise Transformation Plan
Transforms a single-user AI agent into a dual-mode platform supporting both viral open-source and Fortune 500 enterprise deployments through phased security, IAM, audit, multi-tenancy, and Kubernetes features.
University of Guelph Rocketry Club - Complete Tech Stack
Documents the full tech stack of a university rocketry club website with AI chatbot, member management, and project showcases.
Qwen Image and Edit: Open-sourcing and Local GGUF Generations with Lightning
Documents the Qwen-Image and Qwen-Image-Edit models, covering architecture, training, benchmarks, ComfyUI setup, and prompting techniques for local GGUF deployment.