Skip to main content
Glama
sdhilip200

Wellington Transport Assistant

by sdhilip200

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.


Related MCP server: Auckland Transport MCP Server

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.

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.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

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