{
  "openapi": "3.1.2",
  "info": {
    "title": "Journey Planner MCP",
    "description": "Journey Planner MCP is a [Model Context Protocol](https://modelcontextprotocol.io)\n(MCP) server in front of Entur's public travel APIs: the\n[Journey Planner](https://developer.entur.org/pages-journeyplanner-journeyplanner)\n(OpenTripPlanner transmodel GraphQL), the Geocoder and the Mobility\nAPI. Connect it to an MCP-capable AI client (Claude, ChatGPT, GitHub\nCopilot, Cursor, …) and the assistant can plan trips, show live\ndeparture boards, find stops and rental vehicles near a place, and\nreport service disruptions across Norway.\n\n### Connecting\n\nPoint your MCP client at `https://api.entur.io/journey-planner-mcp/v1/mcp`.\nThe endpoint is public and needs no authentication. It serves Entur's\nopen travel data only, holds no user data, and reads nothing on the\ncaller's behalf.\n\n### Interactive UIs (MCP Apps)\n\n`trip`, `departures`, `nearby-stops` and `nearby-mobility` are\n[MCP Apps](https://modelcontextprotocol.io/docs/extensions/apps)\ntools: each names a `ui://` HTML resource in `_meta.ui.resourceUri`\n(a trip map, a departure board, a nearby-stops map and a\nshared-mobility map). Hosts that support MCP Apps render it in a\nsandboxed iframe; other hosts simply show the tool's text result.\n\nThe UIs call two app-only tools, `poll-departures` and `poll-trip`, to\nrefresh a departure board or re-plan a trip. Their `_meta.ui.visibility`\nis `[\"app\"]`, so hosts do not offer them to the model.\n\nThe apps load no third-party script: their libraries (Leaflet and the\nMCP Apps SDK) are inlined into the HTML. Each resource declares a\ncontent-security policy in `_meta.ui.csp` that admits only\nOpenStreetMap tiles and the Entur API hosts (plus their WebSocket\nendpoints for live vehicle positions on the trip map).\n\n### Tool results\n\nThe UI tools return a slim JSON document as `content[0].text` for the\nmodel and carry the fields only the UI needs (geometry, styling data\nand the like) under `_meta[\"org.entur/ui\"]`, so they do not consume the\nmodel's context. A UI degrades gracefully when a host strips `_meta`.\n`geocode` and `alerts` return plain JSON text.\n\n`trip`, `departures`, `nearby-stops` and `nearby-mobility` take a\nrequired `language` parameter (`en`, `nb` or `nn`; anything else falls\nback to `en`), which the model detects from the conversation and which\nsets the UI language.\n\nAn empty `trip` result carries a top-level `noTripsFound` advisory: the\nquery was valid and repeating it will not help. When a search with a\n`departureTime` finds nothing, the server asks once more with a\nday-long search window and reports the next available\n`nextDepartureTime`.\n\nTool errors are returned as a JSON error document in the tool result\n(`error`, `message`, `type`, `details`), never as internal stack\ntraces.\n\nSee the [opentripplanner-mcp repository](https://github.com/entur/opentripplanner-mcp)\nfor the source, configuration and local development.\n",
    "termsOfService": "https://developer.entur.no/terms-of-service",
    "contact": {
      "name": "Team RoR",
      "url": "https://github.com/entur/opentripplanner-mcp"
    },
    "version": "2026.10.0",
    "summary": "MCP server for Norwegian/Nordic public transport — trip planning, real-time departures, nearby stops, shared mobility, service alerts and geocoding, exposed as Model Context Protocol tools with embedded interactive UIs."
  },
  "servers": [
    {
      "url": "https://api.entur.io/journey-planner-mcp/v1",
      "description": "Production"
    }
  ],
  "tags": [
    {
      "name": "MCP",
      "description": "The Model Context Protocol endpoint."
    }
  ],
  "paths": {
    "/mcp": {
      "post": {
        "tags": [
          "MCP"
        ],
        "summary": "Journey Planner MCP server",
        "description": "Streamable HTTP MCP endpoint in stateless mode: each POST carries\none JSON-RPC message. A request (a message with an `id`) receives a\nsynchronous `application/json` response; a notification (no `id`,\ne.g. `notifications/initialized`) is acknowledged with `202 Accepted`\nand an empty body. There is no session state and no SSE stream —\nsafe behind load balancing and scale-out.\n\nAs the Streamable HTTP transport requires, every POST must send\n`Accept: application/json, text/event-stream`, even though this\nserver only ever answers with JSON. A request that accepts only\n`application/json` is refused with `400`.\n\nNo authentication is required. Request bodies are capped at 64 KiB;\nreal MCP messages are a few kilobytes at most.\n",
        "operationId": "mcp",
        "parameters": [
          {
            "name": "Accept",
            "in": "header",
            "description": "Must list both `application/json` and `text/event-stream`, as the MCP Streamable HTTP transport requires.",
            "required": true,
            "style": "simple",
            "explode": false,
            "schema": {
              "type": "string"
            },
            "example": "application/json, text/event-stream"
          }
        ],
        "requestBody": {
          "description": "A single JSON-RPC 2.0 message per the MCP specification.",
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonRpcRequest"
              },
              "examples": {
                "toolsList": {
                  "summary": "tools/list",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "tools/list"
                  }
                },
                "toolsCall": {
                  "summary": "tools/call — departures from Oslo S",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "departures",
                      "arguments": {
                        "stop": "Oslo S",
                        "numberOfDepartures": 5,
                        "language": "en"
                      }
                    }
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "JSON-RPC response.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/JsonRpcResponse"
                },
                "examples": {
                  "toolsList": {
                    "summary": "tools/list — the tool catalog",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 1,
                      "result": {
                        "tools": [
                          {
                            "name": "departures",
                            "description": "Get real-time departures from a single stop or station in Norway.",
                            "inputSchema": {
                              "type": "object",
                              "properties": {
                                "stop": {
                                  "type": "string"
                                },
                                "language": {
                                  "type": "string"
                                }
                              },
                              "required": [
                                "stop",
                                "language"
                              ]
                            },
                            "_meta": {
                              "ui": {
                                "resourceUri": "ui://departures/departures-board.html"
                              }
                            }
                          }
                        ]
                      }
                    }
                  },
                  "toolsCall": {
                    "summary": "tools/call — slim model text plus UI-only _meta",
                    "value": {
                      "jsonrpc": "2.0",
                      "id": 2,
                      "result": {
                        "content": [
                          {
                            "type": "text",
                            "text": "{\"stopPlace\":{\"id\":\"NSR:StopPlace:337\",\"name\":\"Oslo S\",\"departures\":[{\"expectedDepartureTime\":\"2026-10-20T12:04:00+02:00\",\"realtime\":true,\"destinationDisplay\":{\"frontText\":\"Lillestrøm\"},\"quay\":{\"publicCode\":\"4\"},\"serviceJourney\":{\"line\":{\"publicCode\":\"L1\",\"transportMode\":\"rail\"}}}]}}"
                          }
                        ],
                        "_meta": {
                          "org.entur/ui": {}
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "The message was a JSON-RPC notification (no `id`) and has been accepted. The response has no body."
          },
          "400": {
            "description": "The `Accept` header does not list both `application/json` and `text/event-stream`, or the body is not a valid JSON-RPC message. The response has no body."
          }
        },
        "security": [],
        "x-mcp-server": {
          "name": "entur-journey-planner",
          "version": "1.0.0",
          "tools": [
            {
              "name": "trip",
              "description": "Plan multi-leg public transport routes between two locations across Norway and the Nordic region, with departure/arrival times, transfers, occupancy, duration and service disruptions per leg. Accepts addresses, place names or coordinates, an optional departure or arrival time, and an optional transport-mode filter. Renders the trip map UI."
            },
            {
              "name": "departures",
              "description": "Real-time departures from a single stop or station in Norway, by name (e.g. \"Oslo S\") or NSR ID (e.g. NSR:StopPlace:337): line numbers, destinations, platforms, occupancy and live delays. Renders the departure board UI."
            },
            {
              "name": "nearby-stops",
              "description": "Find public transport stops near an address, place name or coordinates, sorted by distance, with transport modes and live next departures. Renders the nearby-stops map UI."
            },
            {
              "name": "nearby-mobility",
              "description": "Find the closest shared-mobility vehicles (e-scooters, city bikes, car-share, mopeds) and rental stations near a location in Norway, filterable by form factor, propulsion type and operator. Renders the shared-mobility map UI."
            },
            {
              "name": "alerts",
              "description": "Active service disruptions and alerts for Norwegian public transport: severity, affected lines and stops, validity period and descriptions in Norwegian and English, optionally filtered by severity."
            },
            {
              "name": "geocode",
              "description": "Convert place names or addresses into geographic coordinates."
            },
            {
              "name": "poll-departures",
              "description": "App-only. Refreshes departure data for a stop; called by the departure board UI."
            },
            {
              "name": "poll-trip",
              "description": "App-only. Re-plans a trip with updated parameters; called by the trip map UI."
            }
          ]
        }
      },
      "parameters": [
        {
          "$ref": "#/components/parameters/ET-Client-Name"
        },
        {
          "$ref": "#/components/parameters/X-Correlation-Id"
        }
      ]
    }
  },
  "components": {
    "schemas": {
      "JsonRpcRequest": {
        "type": "object",
        "description": "A JSON-RPC 2.0 request as defined by the MCP specification.",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ]
          },
          "method": {
            "type": "string",
            "examples": [
              "initialize",
              "tools/list",
              "tools/call",
              "resources/read"
            ]
          },
          "params": {
            "type": "object"
          }
        },
        "required": [
          "jsonrpc",
          "method"
        ]
      },
      "JsonRpcResponse": {
        "type": "object",
        "description": "A JSON-RPC 2.0 response as defined by the MCP specification.",
        "properties": {
          "jsonrpc": {
            "type": "string",
            "const": "2.0"
          },
          "id": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "integer"
              }
            ]
          },
          "result": {
            "type": "object"
          },
          "error": {
            "type": "object",
            "properties": {
              "code": {
                "type": "integer"
              },
              "message": {
                "type": "string"
              }
            }
          }
        },
        "required": [
          "jsonrpc"
        ]
      }
    },
    "parameters": {
      "ET-Client-Name": {
        "name": "ET-Client-Name",
        "in": "header",
        "description": "Entur Client Header.\nIt is required that all consumers identify themselves by using this header.\nEntur will deploy strict rate-limiting policies on API-consumers who do not identify with a header and reserves the right to block unidentified consumers.\nThe structure of ET-Client-Name should be: `<company>-<application>`.",
        "required": false,
        "style": "simple",
        "explode": false,
        "schema": {
          "type": "string"
        }
      },
      "X-Correlation-Id": {
        "name": "X-Correlation-Id",
        "in": "header",
        "description": "Correlation id",
        "required": false,
        "style": "simple",
        "explode": false,
        "schema": {
          "type": "string"
        }
      }
    }
  },
  "x-mcp": {
    "protocolVersion": "2025-11-25",
    "servers": [
      {
        "url": "https://api.entur.io/journey-planner-mcp/v1/mcp"
      }
    ],
    "capabilities": {
      "tools": {},
      "resources": {}
    },
    "tools": [
      {
        "name": "trip",
        "description": "Plan multi-leg public transport routes between two locations across Norway and the Nordic region, with departure/arrival times, transfers, occupancy, duration and service disruptions per leg. Accepts addresses, place names or coordinates, an optional departure or arrival time, and an optional transport-mode filter. Renders the trip map UI."
      },
      {
        "name": "departures",
        "description": "Real-time departures from a single stop or station in Norway, by name (e.g. \"Oslo S\") or NSR ID (e.g. NSR:StopPlace:337): line numbers, destinations, platforms, occupancy and live delays. Renders the departure board UI."
      },
      {
        "name": "nearby-stops",
        "description": "Find public transport stops near an address, place name or coordinates, sorted by distance, with transport modes and live next departures. Renders the nearby-stops map UI."
      },
      {
        "name": "nearby-mobility",
        "description": "Find the closest shared-mobility vehicles (e-scooters, city bikes, car-share, mopeds) and rental stations near a location in Norway, filterable by form factor, propulsion type and operator. Renders the shared-mobility map UI."
      },
      {
        "name": "alerts",
        "description": "Active service disruptions and alerts for Norwegian public transport: severity, affected lines and stops, validity period and descriptions in Norwegian and English, optionally filtered by severity."
      },
      {
        "name": "geocode",
        "description": "Convert place names or addresses into geographic coordinates."
      },
      {
        "name": "poll-departures",
        "description": "App-only. Refreshes departure data for a stop; called by the departure board UI."
      },
      {
        "name": "poll-trip",
        "description": "App-only. Re-plans a trip with updated parameters; called by the trip map UI."
      }
    ],
    "resources": [
      {
        "uri": "ui://trip/trip-map.html",
        "name": "Trip Map",
        "mimeType": "text/html;profile=mcp-app"
      },
      {
        "uri": "ui://departures/departures-board.html",
        "name": "Departures Board",
        "mimeType": "text/html;profile=mcp-app"
      },
      {
        "uri": "ui://nearby/nearby-stops-map.html",
        "name": "Nearby Stops Map",
        "mimeType": "text/html;profile=mcp-app"
      },
      {
        "uri": "ui://mobility/nearby-mobility-map.html",
        "name": "Nearby Mobility Map",
        "mimeType": "text/html;profile=mcp-app"
      }
    ]
  }
}