Wellington Transport Assistant
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Wellington Transport AssistantWhen is the next bus to Karori from Lambton Quay?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.
Prerequisites
You need the following before you start:
What | Why | Where to get it |
Azure subscription | Hosts the MCP server and the language model | |
Azure CLI installed | Used to deploy resources | |
Python 3.11 or newer | Runs the agent and the MCP server locally | |
| Manages Python dependencies | |
Metlink API key | Lets the MCP server call the Metlink data API. Free. | |
Docker (optional) | Only needed if you want to build the image locally. The deploy script builds in the cloud, so you can skip this. |
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.
Quick start
This is the fastest path from cloning the repository to a working chat window on your laptop.
# 1. Clone the repository
git clone https://github.com/sdhilip200/metlink-mcp.git
cd metlink-mcp
# 2. Install Python dependencies
uv sync --group foundry --prerelease=allow
# 3. Set up your local .env file
cp .env.example .env
# Open .env in your editor and fill in:
# METLINK_API_KEY (from opendata.metlink.org.nz)
# FOUNDRY_ENDPOINT (your Foundry account endpoint)
# FOUNDRY_API_KEY (your Foundry account key)
# FOUNDRY_DEPLOYMENT (your model deployment name)
# MCP_URL (your deployed MCP server URL, see Deployment below)
# 4. Start the chat UI
uv run --group foundry --prerelease=allow streamlit run examples/streamlit_app.pyThe chat window opens at http://localhost:8501. Ask a transport question and watch the agent answer.
If you do not want to deploy the MCP server to Azure yet, you can run it locally instead:
uv run python -m src.serverThen set MCP_URL=http://localhost:8000/mcp in your .env and start the chat window.
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 diagramDeployment
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.shThe 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 1Step 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/mcpFoundry 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" >> .envA 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 |
| Your Metlink Open Data API key | (40 character string) |
| The endpoint URL of your Foundry account |
|
| The API key for your Foundry account | (long string) |
| The name of your model deployment in Foundry |
|
| The public URL of your deployed MCP server |
|
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 liveTroubleshooting
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.ContainerRegistryContributing
This is a personal project but pull requests are welcome. Open an issue first if you want to discuss a larger change.
License
MIT. See the LICENSE file for the full text.
This server cannot be installed
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/sdhilip200/metlink-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server