Wellington Transport Assistant
Provides tools to search stops, get next departures, and retrieve service alerts from Metlink's real-time public transport data, enabling natural language queries about Wellington transit.
README
Wellington Transport Assistant
A model-powered agent that answers real-time questions about Wellington's public transport. The agent reads live data from Metlink (Wellington's public transport authority), reasons over multiple tool calls, and replies in plain English.
Built with FastMCP, Microsoft Agent Framework, Microsoft Foundry, and Azure Container Apps.
This is a personal project. The code is open source under the MIT license and is designed so anyone can clone the repository and deploy the same setup in their own Azure subscription.
What it does
Ask questions in plain English and get answers from live Metlink data. Some examples:
- "When is the next bus to Karori from Lambton Quay?"
- "Is the Johnsonville train line running normally?"
- "Any disruptions on the Eastbourne ferry today?"
- "I have a meeting at Te Papa in 25 minutes. Walk or bus from Wellington Station?"
The agent decides which tools to call, in what order, and combines the results into a single readable answer.
Architecture

The system has three parts:
- An MCP server running on Azure Container Apps. It wraps the Metlink Open Data API and exposes three tools over HTTPS:
search_stops,next_departures, andservice_alerts. - A language model deployed in Microsoft Foundry. It does the reasoning and the conversation.
- An agent that runs on your local machine. It connects to both the model and the MCP server, orchestrates the tool calls, and presents the answer in a chat window or a command line script.
The agent and the MCP server only talk to each other over HTTPS. The MCP server has no knowledge of the model. The model has no knowledge of the underlying Metlink API. That separation means you can swap any piece without touching the others.
Data source
The agent reads its data from Metlink's Open Data API, the official public API for Wellington's bus, train, ferry, and cable car services. The API is free, requires no payment details, and is rate limited rather than billed.
To use it, sign up at https://opendata.metlink.org.nz, fill in a short form describing your use case, and your API key arrives in your dashboard within a few minutes.
The MCP server in this project uses four Metlink endpoints:
| Endpoint | What it provides |
|---|---|
GET /gtfs/stops |
All stops in the Wellington network (around 3,000 records) |
GET /gtfs/routes |
All routes across bus, train, ferry, and cable car |
GET /stop-predictions?stop_id=... |
Real-time predicted departures for a specific stop |
GET /gtfs-rt/servicealerts |
Current service disruptions and alerts |
The static reference data (stops and routes) is cached in memory for 24 hours because it changes rarely. Real-time data (predictions and alerts) is fetched on every request to keep it accurate.
Prerequisites
You need the following before you start:
| What | Why | Where to get it |
|---|---|---|
| Azure subscription | Hosts the MCP server and the language model | https://azure.microsoft.com |
| Azure CLI installed | Used to deploy resources | https://learn.microsoft.com/cli/azure/install-azure-cli |
| Python 3.11 or newer | Runs the agent and the MCP server locally | https://www.python.org/downloads |
uv package manager |
Manages Python dependencies | https://docs.astral.sh/uv/ |
| Metlink API key | Lets the MCP server call the Metlink data API. Free. | https://opendata.metlink.org.nz |
| Docker (optional) | Only needed if you want to build the image locally. The deploy script builds in the cloud, so you can skip this. | https://www.docker.com |
You also need a Foundry account with a language model deployed. See the Foundry setup section.
Running the commands
Every command in this README runs in a terminal. If you use Visual Studio Code, open one by clicking Terminal > New Terminal in the top menu (or press Ctrl+`). Any other terminal works too: the macOS Terminal app, Windows Terminal, PowerShell, or your favourite. The Azure CLI behaves the same way in all of them.
Getting started
This is the full step by step walkthrough. Follow the seven steps below in order and you will have a working chat window on your laptop. The whole process takes about 30 minutes the first time, most of which is waiting for Azure to provision resources.
Step 1: Install the prerequisites
Install everything listed in the Prerequisites table above. Then sign in to Azure from your terminal:
az login
Step 2: Clone the repository
git clone https://github.com/sdhilip200/metlink-mcp.git
cd metlink-mcp
All the commands below run from the metlink-mcp folder.
Step 3: Deploy the MCP server to Azure
The repository includes a deploy.sh script that runs the four Azure CLI commands needed to create the resource group, build the Docker image in the cloud, deploy the Container App, and store your Metlink API key as a secret.
# Set your Metlink API key as an environment variable so deploy.sh can read it
export METLINK_API_KEY=your_metlink_key_here
# Run the deploy script
./deploy.sh
This takes about five minutes the first time. When it finishes, it prints a URL similar to:
https://ca-metlink-mcp.<random>.australiaeast.azurecontainerapps.io/mcp
Copy this URL. You will need it in Step 5.
Step 4: Set up the model in Microsoft Foundry
The agent uses a language model deployed in Foundry. Follow the three commands in the Foundry setup section below. They create a Foundry account, deploy a model, and write the endpoint and key into your .env file automatically.
Step 5: Configure your local .env file
cp .env.example .env
Open .env in your editor and fill in the five values:
| Variable | What to put |
|---|---|
METLINK_API_KEY |
The Metlink API key from Step 1 |
FOUNDRY_ENDPOINT |
Filled in automatically by Step 4 |
FOUNDRY_API_KEY |
Filled in automatically by Step 4 |
FOUNDRY_DEPLOYMENT |
The deployment name you chose in Step 4 |
MCP_URL |
The URL you copied at the end of Step 3 |
Step 6: Install Python dependencies
uv sync --group foundry --prerelease=allow
The --prerelease=allow flag is needed because the Anthropic provider package is still in beta.
Step 7: Run the agent
You have two ways to run the agent.
Option A: Browser chat window (recommended for hand testing)
uv run --group foundry --prerelease=allow streamlit run examples/streamlit_app.py
The chat window opens at http://localhost:8501. Type a question and watch the agent answer.
Option B: Command line with the six demo queries
uv run --group foundry --prerelease=allow python examples/agent.py
The agent runs through the six demo queries one after another and saves the transcript to examples/demo_transcript.md.
That is it. The agent is now answering Wellington transport questions on your laptop using a model deployed in your Azure subscription.
Running the MCP server locally without Azure
If you want to try the agent before deploying anything to Azure, you can run the MCP server on your own laptop:
uv run python -m src.server
Then set MCP_URL=http://localhost:8000/mcp in your .env instead of the Azure URL, and run Step 7. Everything else works the same.
Repository structure
metlink-mcp/
├── README.md Main documentation, this file
├── LICENSE MIT license
├── .gitignore Files excluded from version control
├── .env.example Template for your local environment file
├── pyproject.toml Python dependencies and project metadata
├── uv.lock Locked dependency versions for reproducibility
├── Dockerfile Builds the MCP server container image
├── .dockerignore Files excluded from the Docker build context
├── deploy.sh One-command Azure deployment script
│
├── src/ MCP server source code
│ ├── __init__.py
│ ├── server.py FastMCP entry point and tool definitions
│ ├── metlink_client.py HTTP client wrapping the Metlink API
│ ├── models.py Pydantic models for parsing Metlink responses
│ ├── cache.py In-memory cache for static reference data
│ └── formatters.py Converts parsed data into readable text
│
├── examples/ Client examples
│ ├── agent.py Command-line agent that runs six demo queries
│ └── streamlit_app.py Local chat user interface
│
├── tests/ Test suite
│ ├── conftest.py Shared pytest configuration
│ ├── test_smoke.py Live API tests
│ ├── test_formatters.py Unit tests for the formatting functions
│ └── fixtures/ Sample Metlink API responses for testing
│
└── docs/ Documentation assets
└── architecture.jpg Architecture diagram
Deployment
The MCP server runs in Azure Container Apps and scales to zero when idle. It costs nothing when nobody is using it.
Option 1: Use the deploy script (recommended)
The repository includes a deploy.sh script that runs the four required Azure CLI commands in order. To use it:
# Sign in to Azure
az login
# Export your Metlink API key
export METLINK_API_KEY=your_metlink_key_here
# Run the script
./deploy.sh
The script takes about five minutes the first time. When it finishes, it prints the public URL of your MCP server. Copy this URL into your local .env file as the MCP_URL value.
Option 2: Run the commands manually
If you prefer to run each step yourself, the four commands are:
# 1. Create the resource group
az group create --name rg-metlink-mcp --location australiaeast
# 2. Build the image and deploy the container app
az containerapp up \
--name ca-metlink-mcp \
--resource-group rg-metlink-mcp \
--location australiaeast \
--environment cae-metlink-mcp \
--source . \
--ingress external \
--target-port 8000
# 3. Store the Metlink API key as a secret
az containerapp secret set \
--name ca-metlink-mcp \
--resource-group rg-metlink-mcp \
--secrets metlink-api-key=$METLINK_API_KEY
# 4. Bind the secret and set the scaling rules
az containerapp update \
--name ca-metlink-mcp \
--resource-group rg-metlink-mcp \
--set-env-vars METLINK_API_KEY=secretref:metlink-api-key \
--min-replicas 0 --max-replicas 1
Step 2 takes about five minutes the first time. It creates an Azure Container Registry, builds the Docker image in the cloud, pushes the image, creates a Container Apps environment, and deploys the app with a public HTTPS URL.
After deployment your MCP server is reachable at a URL similar to:
https://ca-metlink-mcp.<random>.australiaeast.azurecontainerapps.io/mcp
Foundry setup
The agent connects to a language model deployed in Microsoft Foundry. Foundry hosts model families from Anthropic, OpenAI, Meta, Mistral, and others behind a single Azure interface.
To set up a model:
# 1. Create a Foundry account (a Cognitive Services account of kind AIServices)
az cognitiveservices account create \
--name my-foundry-account \
--resource-group rg-metlink-mcp \
--kind AIServices \
--sku S0 \
--location eastus2
# 2. Deploy a model on the account. Replace the model details below with the
# model family you want to use. The Foundry catalogue lists every model
# available in your region, with their model-name, model-version, and
# model-format values.
az cognitiveservices account deployment create \
--name my-foundry-account \
--resource-group rg-metlink-mcp \
--deployment-name my-model \
--model-name <model-name-from-catalogue> \
--model-version <model-version-from-catalogue> \
--model-format <model-format-from-catalogue> \
--sku-name GlobalStandard \
--sku-capacity 250
# 3. Get the endpoint and key and add them to your .env
ENDPOINT=$(az cognitiveservices account show \
--name my-foundry-account \
--resource-group rg-metlink-mcp \
--query properties.endpoint -o tsv)
KEY=$(az cognitiveservices account keys list \
--name my-foundry-account \
--resource-group rg-metlink-mcp \
--query key1 -o tsv)
echo "FOUNDRY_ENDPOINT=$ENDPOINT" >> .env
echo "FOUNDRY_API_KEY=$KEY" >> .env
echo "FOUNDRY_DEPLOYMENT=my-model" >> .env
A quick note on the bash inside step 3 for anyone new to shell scripting. The command az cognitiveservices account show normally returns a big JSON object. The --query properties.endpoint part tells the CLI to return only that one field, and -o tsv strips the surrounding quotes so you get a clean string. The $(...) wrapper runs the command and captures the result into a variable. The same pattern grabs the API key. The >> then appends each value to your .env file, where the agent reads them later.
The --deployment-name is a name you choose. The --model-name, --model-version, and --model-format values must match an entry in the Foundry catalogue exactly. To see what is available in your region, run az cognitiveservices model list --location eastus2 -o table and pick a model.
Configuration
All configuration lives in your local .env file. The repository includes a .env.example template you can copy.
| Variable | What it is | Example |
|---|---|---|
METLINK_API_KEY |
Your Metlink Open Data API key | (40 character string) |
FOUNDRY_ENDPOINT |
The endpoint URL of your Foundry account | https://my-account.cognitiveservices.azure.com |
FOUNDRY_API_KEY |
The API key for your Foundry account | (long string) |
FOUNDRY_DEPLOYMENT |
The name of your model deployment in Foundry | my-model |
MCP_URL |
The public URL of your deployed MCP server | https://ca-metlink-mcp.<random>.australiaeast.azurecontainerapps.io/mcp |
The .env file is excluded from version control. It only exists on your local machine.
Testing
The repository includes a small test suite. To run it:
# Unit tests against the fixture data
uv run pytest
# Live tests against the real Metlink API
# (requires METLINK_API_KEY to be set)
uv run pytest -m live
Troubleshooting
Cold start is slow on the first request. Azure Container Apps scales the MCP server to zero when nobody is calling it. The first request after an idle period takes around 10 to 30 seconds to wake the container up. Every request after that is fast.
Streamlit shows a long blank page on first load. The chat window itself takes a few seconds to initialise. If you do not see the input box after 30 seconds, check the terminal for errors.
Agent does not respond and returns a 529 error. This is an "overloaded" response from the upstream model provider. It usually clears within a few minutes. Try again, or switch to a different model in Foundry.
Tool calls fail with a 401 or 403. Your Metlink API key is missing or incorrect. Check the value in your Container App secret and in your local .env.
Deployment fails with "provider not registered". Run these two commands once per subscription to enable the required Azure providers, then try the deployment again:
az provider register -n Microsoft.App
az provider register -n Microsoft.ContainerRegistry
License
MIT. See the LICENSE file for the full text.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。
mcp-server-qdrant
这个仓库展示了如何为向量搜索引擎 Qdrant 创建一个 MCP (Managed Control Plane) 服务器的示例。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。