Skip to main content
Glama
mrwulf

kitinerary-mcp

by mrwulf

kitinerary-mcp

An MCP server that wraps KDE's kitinerary-extractor CLI: give it a travel confirmation email, PDF ticket, or Apple/Google Wallet .pkpass file, and it returns whatever structured schema.org Reservation JSON-LD the file contains — deterministically, with no LLM in the loop.

Most real airline/hotel confirmation emails already embed this JSON-LD (it's the same markup Gmail parses for its own "smart" trip cards). kitinerary-extractor reads it straight out, along with structured PDF tickets and Wallet passes that plain text/regex extraction can't touch. This server is a thin, stateless shim around that binary so any MCP client can call it.

What this is (and isn't)

  • Is: a single tool, extract_booking, that takes a file and returns raw JSON-LD (or nothing, if the extractor finds nothing). No outbound network calls, no secrets, no state — every call is an isolated subprocess against a temp file, cleaned up immediately after.

  • Isn't: a booking-type mapper. It does not translate the JSON-LD into any particular app's reservation schema — that's a deliberate boundary, so this stays reusable across whatever consumer calls it rather than coupled to one caller's data model.

Related MCP server: root-ext-docs

How it's used

This server powers the deterministic extraction stage of a self-hosted TripIt replacement: forward a booking confirmation email, an n8n workflow calls extract_booking on it, and a matched result gets mapped and written into Trek as a trip — no LLM involved for the common case, with an LLM fallback only for emails this server finds nothing in. See tripit_replacement.md in that pipeline's repo for the full architecture, the JSON-LD-to-booking mapping table this server's output feeds into, and the gotchas found integrating it (some datetime fields come back as {"@type":"QDateTime",...} objects rather than plain strings, multi-leg/multi-passenger handling, and more).

That pipeline is one consumer, not a dependency this server has on it — nothing in this repo is coupled to it, and any MCP client can use extract_booking the same way. If you build something else on top of this server, opening a PR to list it here is welcome.

Tool interface

extract_booking(file_base64: str, filename: str, context_date: str | None = None) -> {
  items: object[],    # the raw JSON-LD array kitinerary-extractor emitted (possibly empty)
  warnings: string[], # e.g. "unsupported file type", "no reservation data found in file"
}
  • file_base64 — the file's raw bytes, base64-encoded (MCP tool args are JSON, so there's no binary/multipart transport at this layer).

  • filename — used to infer format from the extension. Accepted: .eml, .pdf, .pkpass, .html, .txt. Anything else is rejected with a warning, not silently passed through.

  • context_date — optional ISO date/time, forwarded to kitinerary-extractor --context-date. Helps resolve dates that don't state a year or are relative to "today" (pass the email's own Date: header if you have it).

  • No match is not an error — you get back items: [] with a warning. Tool errors are reserved for genuine failures: the binary is missing, the process crashed, or the file exceeds the size cap.

  • Limits: 10 MB decoded file size, 60s extraction timeout. Both fail cleanly (a warning, or a tool error) rather than hanging or crashing the caller.

Running it

docker run --rm -i ghcr.io/mrwulf/kitinerary-mcp:v0.1.0

It speaks MCP over stdio. Wire it into any MCP client's stdio server config, e.g. Claude Desktop's claude_desktop_config.json:

{
  "mcpServers": {
    "kitinerary": {
      "command": "docker",
      "args": ["run", "--rm", "-i", "ghcr.io/mrwulf/kitinerary-mcp:v0.1.0"]
    }
  }
}

Or point a ToolHive MCPServer at the same image with transport: stdio — no secrets, no volumes needed.

Always pin an exact tag; latest is published alongside each release for convenience but isn't meant to be what you deploy against.

Tags and version strings

Every release publishes three tags:

  • vX.Y.Z — this server's own version, e.g. v0.1.0. Pin this in normal use.

  • vX.Y.Z-kitineraryA.B.C — the same build, with the bundled kitinerary-extractor release folded into the tag (e.g. v0.1.0-kitinerary24.12.3), so you can tell which upstream KDE release a given build carries without pulling it or reading the Dockerfile. Pin this instead if your own compatibility depends on a specific kitinerary behavior.

  • latest — convenience only, not for pinning.

The same two version numbers are also on the image itself, so you don't have to trust the tag alone: as OCI labels (org.opencontainers.image.version, io.github.mrwulf.kitinerary-mcp.kitinerary-version) readable via docker inspect, and in the running server's own MCP version field (0.1.0+kitinerary.24.12.3, semver build-metadata form) that any MCP client can read after connecting.

Development

python3 -m venv .venv && .venv/bin/pip install -r requirements.txt pytest

# Fast unit tests (mock the CLI subprocess, no image build needed)
.venv/bin/pytest tests/test_server.py -v

# Real end-to-end tests against the actual kitinerary-extractor binary
docker build -t kitinerary-mcp:test .
IMAGE_TAG=kitinerary-mcp:test .venv/bin/pytest tests/test_integration.py -v

Dependency footprint

libkitinerary-bin pulls in a genuine KDE Frameworks 6 + Qt 6 chain (yes, including libqt6gui6/libqt6qml6, even for this headless CLI use) — there's no slimmer package upstream, so expect a multi-hundred-MB image. This is a tradeoff of using the reference KDE implementation rather than reimplementing extraction logic from scratch, which would mean re-deriving and maintaining parsers for every airline/hotel/rail JSON-LD dialect ourselves.

License

This repository's own code (server.py and supporting files) is MIT-licensed. The built image additionally includes KDE's kitinerary library, which is LGPL-2.0-or-later (confirmed from /usr/share/doc/libkitinerary-bin/copyright in the Debian trixie package, version 24.12.3-1). This server only invokes kitinerary-extractor as a subprocess — it does not link against libkitinerary — so using this image imposes no LGPL obligations on your own application code; the obligations that do apply (source availability for the library itself, etc.) are already satisfied by Debian's own package distribution.

Keeping this current as kitinerary grows

Two independently-versioned things need tracking, and Renovate handles them differently:

  1. This image's own published tag — a normal container reference; any downstream consumer's tooling (Renovate, Flux, etc.) tracks it exactly like any other pinned image.

  2. The libkitinerary-bin/libkitinerary-data apt package versions pinned in the Dockerfile — Renovate has no native Debian-apt datasource, so these are pinned exactly (never a bare apt-get install with no version) and annotated for a customManagers regex against KDE/kitinerary's GitHub releases as a freshness signal:

    # renovate: depName=KDE/kitinerary datasource=github-releases
    ARG KITINERARY_VERSION=24.12.3

    Debian's version string tracks upstream KDE Gear releases closely but appends its own revision suffix (-1, -2, ...) that a Renovate bump can't verify still resolves in the trixie archive on build day. If it doesn't, the CI build simply fails loudly — an accepted, visible failure mode rather than trying to fully automate around Debian's package cadence.

  3. The Python mcp SDK version — pinned in requirements.txt; any pip-aware dependency bot (Renovate, Dependabot) tracks this natively.

Acceptance test

The fixtures in tests/fixtures/ and tests/test_integration.py cover the smoke test this server is expected to pass before any release: a synthetic hotel confirmation with embedded LodgingReservation JSON-LD extracts correctly, a plain marketing email with no structured data returns an empty result with a warning (not an error), and an oversized file is rejected cleanly.

Maintenance

ActivityMaintained
ResponsivenessUnresponsive

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    B
    maintenance
    Document-engineering MCP tool server providing tools for PDF, Office, Images, and Archives extraction and conversion. It never mutates source files and requires local tesseract for OCR.
    12
  • F
    license
    Not graded
    quality
    C
    maintenance
    Classifies PDFs (text-based vs scanned vs image vs mixed), extracts text, and converts to clean Markdown over Streamable HTTP MCP. Supports remote PDFs via URL or base64, with optional page restriction and OCR fallback detection.

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/mrwulf/kitinerary-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server