Wellington Transport Assistant

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.

Category
Visit Server

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

Architecture diagram

The system has three parts:

  1. 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, and service_alerts.
  2. A language model deployed in Microsoft Foundry. It does the reasoning and the conversation.
  3. 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.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured