Skip to main content
Glama
Line-Up MCP server

Line-Up MCP server

by dragosh29

Line-Up MCP server

An MCP server that lets Claude, ChatGPT and other MCP clients work with a Line-Up ticketing channel: events, performances with pricing and capacity, seating, packages, visits (who is coming to a performance), transactions, the organisation's venue plans, payment, delivery and seat types, and (when enabled) creating and releasing reservations. It is built from Line-Up's public Transactional API documentation: the OpenAPI 3.1 document the API serves at https://api.line-up.tickets/api/openapi.json (Swagger UI at /api/docs).

Once it's connected, someone at the venue can ask things like:

  • "What's on in the next two weeks, and how many seats are left for each night?"

  • "How much is a concession in the stalls for Friday's Macbeth?"

  • "Which rows in Stalls Left still have seats together?"

  • "Who is coming tonight? Show me booking LU-TEST1 and its notes."

  • "What packages do we sell for Macbeth, and what payment methods does this channel take?"

  • With writes enabled: "Hold two adult stalls seats for tonight for 15 minutes under the name Box office hold."

Tools

Tool

What it does

API calls

list_events

Events with description, run time, venue and address, tags. Filters venue_id, hasPackage; sort by id, name or venue name; paged.

GET /event/

get_event

One event in full: description, booking information, organisation currency, gallery, seat-view base URL.

GET /event/{event_id}/

list_performances

Performances with start/end, time zone, total capacity and remaining, and pricing per price band and variant (including the price_id a reservation needs). Filters event_id, venue_id, start_date, start_date.gte/.lte, end_date.gte/.lte, tags, day_of_week, code; paged.

GET /performance/

get_performance

One performance with the same pricing and capacity detail.

GET /performance/{performance_id}/

get_performance_seating

Seating groups (areas, sections, blocks, rows) with capacity and capacity remaining; optionally the individual available seats with price band, seat type and group, capped and summarised per group. Filters parent_id, include_root, package_id, code.

GET /performance/{id}/seating-group/, GET /performance/{id}/seating-object/

list_packages_and_add_ons

Packages per performance for one event (paged) or for one performance, with capacity remaining and total price; with a transaction_id, also the products and add-ons available to that basket.

GET /package/, GET /performance/{id}/package/, GET /product/, GET /add-on/

list_visits

Visits: for each performance a customer holds tickets to, the event, the performance and the ticket items with their transaction (reference, lead booker name, status). Filters time_filter (upcoming/past), performance_buffer_minutes, sort_direction; paged.

GET /visit/

get_visit

One visit with every ticket item and, for shared tickets, whether the share was claimed.

GET /visit/{visit_id}/

get_transaction

One transaction: status, reference, totals, customer name, ticket items with seat, price and barcode status (paged), product, delivery, payment, add-on, package and adjuster items, coupons, staff notes (paged).

GET /transaction/{id}/, GET /transaction/{id}/ticket-item/, GET /transaction/{id}/note/

get_organisation_context

The organisation's currency, venue plans with admission type, payment methods by name and type, seat types (optionally for one performance) and, with a transaction_id, the delivery methods available to that basket.

GET /meta/, GET /venue-plan/, GET /payment-method/, GET /seat-type/, GET /delivery-method/

create_reservation

Creates a transaction, then adds one ticket item per request (reserved seat, general admission or best available). Refuses locally when seat fields do not match the reservation type. Only registered when writes are enabled.

POST /transaction/, POST /transaction/{id}/ticket-item/

release_reservation

Deletes a transaction, releasing the seats it holds. Marked destructive. Writes only.

DELETE /transaction/{id}/

Not covered on purpose: customer accounts, login and password reset, addresses, opt-ins, forms, discounts, payments and payment actions, terminal readiness, completing a transaction, product/package/delivery/add-on items on a basket, notes creation and editing, and the PUT/PATCH transaction updates. The spec's x-purchase-flow-id header and the group_preset/group_by query parameters are not sent. GET /health is not called by any tool (the test suite checks the mock serves it unauthenticated, as the live API does: an anonymous curl https://api.line-up.tickets/api/health answers {"status":"ok"}, while /meta/ answers 401; the spec itself lists the bearer scheme on /health, with no scopes, so the open access is an observation, not documentation).

Related MCP server: TicketSource MCP server

Setup

Requires Node 18 or later.

npm install
npm run build

You need a channel API key from Line-Up. Keys are not self-serve: Line-Up issues one per sales channel on request to their support (see their guide on setting up a third-party seller).

Claude Desktop: add this to claude_desktop_config.json:

{
  "mcpServers": {
    "lineup": {
      "command": "node",
      "args": ["/absolute/path/to/lineup-mcp/dist/index.js"],
      "env": { "LINEUP_API_KEY": "your-channel-key" }
    }
  }
}

Claude Code:

claude mcp add lineup -e LINEUP_API_KEY=your-channel-key -- node /absolute/path/to/lineup-mcp/dist/index.js

Variable

Required

Meaning

LINEUP_API_KEY

yes

Your channel API key, sent as Authorization: Bearer <key> by default.

LINEUP_AUTH

no

bearer (default) or basic. The OpenAPI document declares an OAuth2 password/bearer scheme and an unauthenticated call is answered 401 with WWW-Authenticate: Bearer; the older docs at docs.lineupnow.com describe HTTP Basic with the key as the username and no password. If Bearer is rejected, try basic.

LINEUP_CHANNEL

no

An integer sent as the optional x-channel header, only on the operations the spec declares it on; it is not sent to GET /meta/, /venue-plan/, /visit/ and /visit/{id}/, which do not declare it. Leave unset unless Line-Up tells you to set it.

LINEUP_ALLOW_WRITES

no

true to register create_reservation and release_reservation. Off by default.

LINEUP_BASE_URL

no

Defaults to https://api.line-up.tickets/api. Used by the tests.

Safety defaults

  • Read-only unless LINEUP_ALLOW_WRITES=true. Read tools carry the MCP readOnlyHint annotation; release_reservation is marked destructive.

  • Customer names are returned by default. The customer's email address, postal address and phone number on a transaction, and the purchaser's and recipient's email addresses on a shared ticket, are only returned when a tool is called with include_contact_details=true. The same flag governs one rule for text: every string a person typed, whether a customer or the organisation (event, venue, performance, package, product, add-on, venue plan, payment method, delivery method, seat type, price band, variant and adjuster names and descriptions, seat and seating group labels, tag names, seat type notes, coupon codes, transaction references and source references, lead booker names, customer names, staff notes, API warnings and error messages), has email addresses replaced with [email redacted] and phone-number-like sequences with [phone redacted] by default; IDs, external IDs, enums, dates, times, colours and URLs are returned as stored. Every read tool takes include_contact_details; create_reservation always returns the redacted form. The phone match is a heuristic: it covers international numbers written with + or 00 (including the +44 (0)7700 … form), UK numbers with a bracketed area code such as (020) 7946 0958, and UK-style 0… numbers of 9 to 11 digits with spaces, dots or hyphens between groups; other digit strings starting with 0 are redacted too, while public IDs such as txn_test1, numeric IDs and timestamps are left alone. Only emails and phone numbers are matched: a postcode, a date of birth or a document number typed into a staff note, a lead booker name or a reference is returned as stored. A venue's address is a business address and is returned.

  • Ticket barcode codes (what gets scanned at the door) are only returned with include_barcodes=true; their status (type, redeemed, scan status) is always returned.

  • Payment provider data is never returned: the Stripe continuation secret, Adyen session data, additional actions, payment references, the SOLT lastFour digits, and the provider keys on payment methods (Stripe publishable key and account id, Adyen client key, Square application and location ids). A payment item is reported as method type, amount, currency and status. The customer's identity-provider id (idpId) is never returned either.

  • Input is checked before any call is made: integer IDs must be positive whole numbers; transaction IDs must match the spec's txn_[a-zA-Z0-9]+ and visit IDs ^vis_\d+$; dates must be real calendar dates in YYYY-MM-DD; enum parameters (sort_by, sort_direction, time_filter, reservation_type) must be one of the documented values; page, max_results, performance_buffer_minutes, day_of_week, max_seats, max_notes and seconds_to_book must be within their documented or stated ranges.

  • create_reservation refuses locally a seating_object_id on anything but RESERVED and a seating_group_id on anything but BEST_AVAILABLE (the spec's ReservedTicketCreate and BestAvailableTicketCreate). Line-Up documents no idempotency key, so none is sent.

  • Line-Up documents no rate limit anywhere in the OpenAPI document or the older docs. Requests are spaced 250 ms apart (about four per second). A 429 is retried at most twice for any method, including the two POSTs of create_reservation, on the assumption that a rate-limited request was not processed (see Status). The retry waits for Retry-After (whole or fractional seconds, or an HTTP-date; 2 s then 4 s when the header is absent or unreadable). Each wait is capped at 10 seconds, so one request waits at most 20 seconds in all; a tool call that makes several requests (pages, lookups) can still run past the MCP client's default 60-second request timeout. Past the cap: if Line-Up asks for a longer wait the call gives up at once and the message says how long to wait.

  • 502, 503 and 504 are retried the same way for GET only; when all three attempts fail the error says the service may be unavailable and to try again in a few minutes, without the gateway's HTML. A POST or DELETE is never retried after a gateway error, because the request may already have been processed and a retry could reserve seats twice; the error tells the assistant to check with get_transaction first.

  • A 200 whose body is not JSON (a proxy or a login page in the way), or whose JSON is not the documented shape (a list without the Page envelope's metadata and data; a record or transaction without data), is reported as an error naming LINEUP_BASE_URL and the keys it did get, never as an empty list or an empty transaction.

  • A rejected API key produces a message that says which variable to fix and which scheme was used (Bearer by default, suggesting LINEUP_AUTH=basic; or Basic, suggesting the default); a 403 says the key lacks the scope (api_read, api_write, api_list, api_delete in the spec); a 422 is passed on with FastAPI's field-level messages.

Tests

npm test

The test suite:

  1. Validates every fixture record against the component schemas in Line-Up's OpenAPI document (Event, EventDetail, PerformancePricing, SeatingGroupCapacity, PerformanceSeatingObjectDetail, PerformancePackages, ProductPricing, AddOn, VisitSummary, Visit, Transaction, TicketTransactionItem, NoteDetail, Meta, VenuePlanListItem, DeliveryMethod, PaymentMethod, SeatType) with Ajv 2020 (the document is OpenAPI 3.1). The document is downloaded from api.line-up.tickets/api/openapi.json to spec.json on the first run. One format is relaxed: the spec types startTime/endTime as format: time, which ajv-formats reads as RFC 3339 full-time with a mandatory zone, while FastAPI serialises a naive time as 19:30:00; the suite accepts HH:MM:SS with an optional fraction and zone.

  2. Starts a local mock of the API under /api that serves those fixtures with the documented page/size pagination and MetaData, serves /health without auth (as the live API does; the spec lists the bearer scheme on it), answers a wrong key with the live API's unauthenticated 401 body ({"detail":"Could not validate credentials"} and WWW-Authenticate: Bearer, which anyone can observe), unknown routes and ids with {"detail":"Not Found"} (the spec documents 404 with no body schema), invalid input with the spec's HTTPValidationError, and the first GET /event/ with a 429. Every list, detail, write and error response of the mock is validated against the spec's response schemas.

  3. Starts the built server and drives it over stdio with the official MCP client: 32 checks (34 in all with the two above) covering every tool, tool annotations, page-based pagination stopping at the documented totalPages (events, performances, packages, ticket items, notes) and reporting next_page, every exposed filter passed through under its documented name (including repeated keys for array parameters), redaction of contact details by default (customer email, address and phone; share emails; lead booker names; notes; event text) and their return on request, a probe that types a phone number and an email address into every text field the fixtures carry (names, descriptions, labels, notes, tag names, coupon codes, on every record type) and checks that no tool returns them by default and every read tool returns them with include_contact_details, barcodes withheld by default, payment provider data and keys absent from every output, the 429 retry waiting for Retry-After in the seconds, fractional-seconds and HTTP-date forms, giving up after three attempts on a persistent 429 and at once on a Retry-After above the cap, a 429 on POST /transaction/ retried once, a 502 retried for GET and never for POST, a 503 on DELETE not retried, a GET failing three times with 503 reported with advice and without the gateway HTML, a 200 with a non-JSON body and a 200 whose JSON lacks the Page or data envelope reported as errors, the write gate with the variable unset and set to false, the POST /transaction/ body validated against TransactionCreate and each ticket-item body compared field by field with the documented shape (the spec's branches allow extra properties, so schema validation alone would not catch a stray seat field), the local refusal of mismatched seat fields, a refused later ticket item reported with the transaction id, the DELETE, refusal before any call of bad IDs, unknown enum values, out-of-range numbers and impossible dates, the 401 message in both auth schemes, the 403 and 404 messages, LINEUP_AUTH=basic, LINEUP_CHANNEL sent exactly on the operations the spec declares x-channel on (checked against the spec) and not on /meta/, /venue-plan/, /visit/ and /visit/{id}/, that the main session used only the Bearer key and every call template it should, and that every request of the run, later sessions included, hit a documented method and path with its trailing slash, without the x-purchase-flow-id header.

Status

This is a working prototype. It has not yet been run against the live API, because it was built without a Line-Up channel key (keys are issued by Line-Up on request, not self-serve). Everything below is taken from the published OpenAPI document and should be confirmed on a real channel:

  • Authentication: whether the channel key is accepted as a Bearer token (the OpenAPI document's scheme, and what the unauthenticated 401 advertises) or must be sent as the HTTP Basic username (the older docs' scheme, LINEUP_AUTH=basic); whether POST /purchase-channel/{key}/ (a token exchange the spec lists) is needed first; what a 403 looks like when a key lacks a scope; and whether the x-channel header is needed at all.

  • Pagination: that page is 1-based and size up to 1000 are honoured as documented, that totalPages and numberOfResults are filled (the spec allows null; the server then ends at a short page), and what a page past the end returns (the mock answers an empty data).

  • The sort order of every list. The spec documents sort_by/sort_direction only on events and sort_direction on visits; the server returns whatever order the API uses.

  • The meaning of code on performances, seats and products (typed as a list of strings; the mock records it and does not interpret it), the numbering of day_of_week, the expand parameter on GET /performance/ (not exposed), and whether hasPackage=false means "no filter" or "events without packages" (the server only sends the parameter when given).

  • Which id is priceId for a ticket item. The server reports a pricing variant's price.id as price_id because a ticket item's Price in a transaction has the same {id, value, priceBand, priceVariant} shape; the variant's own id is reported as variant_id. Confirm which one POST .../ticket-item/ expects.

  • The reservation flow: the default and maximum secondsToBook, the status a new transaction has (IN_BASKET or RESERVATION in the enum), whether ticket items can be added one request at a time as this server does (the spec also accepts ticketItems inside TransactionCreate), what a refused ticket item answers (the mock uses 422 for an unknown price and 404 for an unknown seat), and whether DELETE /transaction/{id}/ releases a basket (the spec calls it "Delete Transaction", describes it as "Patch an existing transaction" and answers with the object's id; what it does to a completed transaction is undocumented, so only release reservations you created).

  • GET /product/ without a transaction_id (the spec makes it optional; the server only calls it with one), and whether GET /add-on/ and GET /delivery-method/ really need a transaction (they are required parameters in the spec).

  • Time and date formats: startTime with or without a zone (see Tests), and timeZone values.

  • Whether GET /transaction/{id}/ticket-item/ fills barcode for reservations and completed bookings alike, and which fields the live API fills on Transaction (customer, address, phoneNumber, parentTransaction, coupons).

  • The wording of Line-Up's error messages and whether any echo request data; the server passes on FastAPI's detail strings and redacts contact details from them regardless.

  • Rate limits: nothing is documented, so the 250 ms spacing is a guess on the polite side, and a 429 on POST is retried on the assumption that a rate-limited request was not processed. Confirm on a live channel that Line-Up never creates the transaction before answering 429.

Going to production

This version runs locally over stdio, with the channel's own API key. For venues to connect from claude.ai or ChatGPT without handling keys, the next step is a remote server (Streamable HTTP) behind OAuth, hosted by Line-Up, and then a listing in the Claude and ChatGPT connector directories. Customer-facing flows (customer accounts, payment, completing a transaction) can follow once they can be tested on a real channel.

Licence

MIT. Built by Alexandru Dragoș (alexandru.dragos96@gmail.com) with an AI agent (Claude) working under his direction.

Available Tools

10 tools
get_eventGet eventA
Read-onlyIdempotent

One event in full: description, booking information, organisation and currency, venue with address, tags, gallery, seat-view base URL. Uses GET /event/{event_id}/.

Inputs: event_id (required), include_contact_details.

get_organisation_contextOrganisation contextA
Read-onlyIdempotent

What the channel is set up with: the organisation's currency (GET /meta/), venue plans with admission type (GET /venue-plan/), payment methods by name and type without any provider keys (GET /payment-method/), seat types (GET /seat-type/, optionally for one performance) and, when a transaction_id is given, the delivery methods available to that basket (GET /delivery-method/, which the API scopes to a transaction).

Inputs: performance_id, transaction_id, include_contact_details.

get_performanceGet performanceA
Read-onlyIdempotent

One performance with its pricing per price band and variant, capacity and capacity remaining, venue plan id. Uses GET /performance/{performance_id}/.

Inputs: performance_id (required), codes, include_contact_details.

get_performance_seatingPerformance seatingA
Read-onlyIdempotent

Seating availability for one performance: the seating groups (areas, sections, blocks, rows) with capacity and capacity remaining, from GET /performance/{id}/seating-group/, and optionally the individual available seats (seating objects with price band, seat type and group) from GET /performance/{id}/seating-object/. Seat lists can be large, so seats are off by default and capped by max_seats; a per-group summary is always included when seats are fetched.

Inputs: performance_id (required), codes, max_seats, package_id, +4 more.

get_transactionGet transactionA
Read-onlyIdempotent

One transaction (basket, reservation or completed booking) by its txn_ id: status, reference, totals (gross, tax, paid, balance), customer name, every ticket item with seat, price and barcode status (GET /transaction/{id}/ticket-item/, paged), product, delivery, payment, add-on, package and adjuster items, coupons, and the staff notes (GET /transaction/{id}/note/, paged). The customer's email, address and phone number only with include_contact_details; barcode codes only with include_barcodes; payment provider data is never returned.

Inputs: transaction_id (required), max_notes, include_barcodes, max_ticket_items, +1 more.

get_visitGet visitA
Read-onlyIdempotent

One visit with every ticket item, its transaction and, for tickets that were shared with someone, the share (created, claimed). The purchaser's and recipient's email addresses only with include_contact_details. Uses GET /visit/{visit_id}/.

Inputs: visit_id (required), include_contact_details.

list_eventsList eventsA
Read-onlyIdempotent

Events on this channel (shows, productions, exhibitions): name, short description, run time, venue with address, tags, image. Filter by venue and by whether the event has packages; sort by id, name or venue name. Uses GET /event/.

Inputs: page, sort_by, venue_id, has_package, +3 more.

list_packages_and_add_onsPackages, products and add-onsA
Read-onlyIdempotent

Packages (bundles priced per performance, with capacity remaining and total price) for one event (GET /package/, paged) or one performance (GET /performance/{id}/package/). With a transaction_id, also the products available to that basket (GET /product/) and its add-ons such as booking protection (GET /add-on/); the API scopes both to a transaction, so without one they are not fetched. Give event_id or performance_id.

Inputs: page, event_id, package_id, max_results, +5 more.

list_performancesList performancesA
Read-onlyIdempotent

Performances (dated showings) with start and end, time zone, total capacity and capacity remaining, and pricing per price band and variant (the price_id values a reservation needs). Filter by event, venue, date range, tags, days of the week. The spec says this endpoint 'bypasses Pydantic due to performance requirements', so the shape is documented but not validated by Line-Up on the way out. Uses GET /performance/.

Inputs: page, tags, codes, event_id, +9 more.

list_visitsList visitsA
Read-onlyIdempotent

Visits: for each performance a customer holds tickets to, the event, the performance and the ticket items with their transaction (reference, lead booker name, status). Filter to upcoming or past performances with a buffer in minutes around the start time. Uses GET /visit/.

Inputs: page, max_results, time_filter, sort_direction, +2 more.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.9/5.0

    Scored across 10 tools

    Disambiguation4/5

    The list_/get_ pattern cleanly separates collection vs. detail for events, performances, visits and transactions, and get_performance_seating and get_organisation_context are clearly distinct. The only slight ambiguity is list_packages_and_add_ons, which merges packages, products and add-ons with conditional behavior depending on event_id/performance_id/transaction_id.

    Naming Consistency5/5

    Every tool follows a consistent verb_noun snake_case convention (list_events, get_event, list_performances, get_performance, get_visit, etc.). The pattern is predictable throughout with no mixed styles or vague verbs.

    Tool Count5/5

    Ten tools is well within the ideal range and each earns its place, covering the read/query surface for events, performances, seating, packages, visits, transactions and org context without redundancy.

    Completeness4/5

    The lifecycle for querying the domain is well covered: list/detail for events, performances, seating, packages, visits and transactions plus configuration context. It is a read-only surface with no reservation/booking creation or update tools, but the descriptions imply a query-oriented purpose rather than a gap.

    Maintenance

    ActivitySlowing
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      C
      maintenance
      Enables MCP clients to search availability, read services, resources, bookings and customers, and, when writes are enabled, reserve slots, create or cancel bookings, and add or update customers.
      10
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables Claude, ChatGPT and other MCP clients to read an Amiqus ID account—clients, onboarding records and steps, check results, templates, case status counts and webhooks—and, when writes are enabled, create records.
      9
      MIT