Edinburgh Fringe 2026 for machines — MCP server and JSON API
Both interfaces are temporarily withdrawn. Temporarily withdrawn while we review data use with the festival organisers.
The MCP endpoint and every /api/v1 route answer 503 with a JSON
body explaining why. Nothing has been deleted and no URL has moved, so a client
pointed at either will work again when they return without being reconfigured.
The documentation below is left in place so integrations can be written against
it in the meantime.
The live Edinburgh Festival Fringe 2026 programme, two ways. Connect it to Claude, ChatGPT, Grok or Cursor and ask “what’s on at Monkey Barrel on the 14th, under £10, still on sale?”, or call the same data as JSON from your own code. Both answer from live ticket data, not from whatever a model remembers about last year’s Fringe.
No account, no API key, no sign-up. Read-only. Same underlying queries behind both, so they cannot disagree with each other or with the website.
MCP server — use the Fringe inside ChatGPT, Claude or Grok
https://edfringeguide.com/mcp
Add it to ChatGPT, Claude, Grok or Cursor
Every client takes the same thing: the URL above. Pick yours.
Claude (web, desktop, mobile)
On Free, Pro or Max: Settings → Connectors, click + Add custom connector, paste the URL, click Add. Free accounts can have one custom connector.
On Team or Enterprise an owner adds it first, under Organization settings → Connectors → Add → Custom → Web; members then hit Connect.
To use it in a chat, click + at the lower left, choose Connectors, and toggle Ed Fringe Guide on.
ChatGPT
Needs a paid plan (Plus, Pro, Team, Enterprise or Edu) and Developer Mode switched on — custom connectors are not available on free accounts. Then Settings → Connectors → Create, paste the URL, leave authentication set to none, and enable it in the composer.
Grok
Click New Connector, choose Custom, and paste the URL. Leave authentication empty.
Claude Code and other CLIs
claude mcp add --transport http ed-fringe-guide https://edfringeguide.com/mcp
Restart the session afterwards — MCP servers are loaded at startup.
Cursor, VS Code, Zed, Windsurf, Qwen Code and anything config-driven
Add this to the client's MCP config file (.cursor/mcp.json,
.vscode/mcp.json, Qwen Code's settings.json, or the equivalent):
{
"mcpServers": {
"ed-fringe-guide": {
"type": "http",
"url": "https://edfringeguide.com/mcp"
}
}
}
No account, no key, no OAuth — if a client asks for credentials, choose “no
authentication”. Anything that speaks the Streamable HTTP transport will work; a client
that only supports stdio needs a bridge such as mcp-remote.
Not currently possible in the consumer Gemini app or Qwen Chat, neither of which offers a custom-connector screen. Gemini CLI and Qwen Code both do, via the config file above.
What you can ask about the Fringe
Once connected, just ask normally — the assistant decides when to call the tools:
- “What’s on at the Fringe on the evening of 14 August, under £12, near Bristo Square?”
- “Find me free wheelchair-accessible comedy that’s still on sale.”
- “Build me a Saturday — four shows, nothing before noon, walking distance apart.”
- “Is there any way into [show] if it says sold out?”
Tools
| Tool | What it does | Ask it… |
|---|---|---|
search_shows |
Search the programme by text, date, venue, genre, price, accessibility and availability. | “Find me wheelchair-accessible comedy under £12 that’s still on sale.” |
whats_on |
Everything performing on one date, in start-time order. | “What’s on the evening of the 14th near Bristo Square?” |
get_show |
One show in full: every performance date, its ticket status and price, accessibility, booking link. | “Which dates of that one are still available?” |
list_venues |
Venues with addresses, coordinates and how many shows each is hosting. | “Which venues are near the Pleasance?” |
ticket_advice |
What to do when a show has no tickets left, including that venue’s own stated door policy. | “It says sold out — is there any way in?” |
Technical details
- Transport: Streamable HTTP, stateless. POST JSON-RPC to the endpoint; no session id needed.
- Tool results are plain text, written to be read by a model rather than parsed. Want JSON instead? That is what the REST API is for.
- All times returned are UK local. Prices are GBP.
- Rate-limited per IP, same as the rest of the site. Read-only: no tool writes anything.
- Machine-readable card: /.well-known/mcp/server-card.json
REST API — the same data as JSON
https://edfringeguide.com/api/v1
Read-only JSON over HTTPS. No key, no sign-up, CORS open to any origin. Every endpoint is a
GET.
curl 'https://edfringeguide.com/api/v1/whats-on?date=2026-08-14&time_of_day=evening&max_price=12'
Endpoints
| Endpoint | What it returns |
|---|---|
GET /api/v1/shows |
Search the programme by text, date, venue, genre, price, accessibility, rating and availability. |
GET /api/v1/whats-on |
Everything performing on one date, in start-time order. Prices and availability describe that date. |
GET /api/v1/shows/{slug} |
One show in full: every performance with its ticket status, price and allocation remaining. |
GET /api/v1/venues |
Venues with addresses, coordinates and how many shows each is hosting. |
GET /api/v1/ticket-advice |
What to do when a show has no tickets left, including the venue's own stated door policy. |
/api/v1 lists the endpoints, the valid values for every enumerated filter and the current limits. /api/v1/openapi.json is the OpenAPI 3.1 document, generated from the same constants the API validates against — so it cannot document one vocabulary and enforce another.
Filters
/shows and /whats-on take the same filters, combinable:
query,venue,genre— text, partial match on the first two.date,time_of_day,start_after,start_before— UK local. Usestart_afterfor “after 5pm”: it spans both the evening and late bands, whichtime_of_daycannot express in one call.max_price,free_only,two_for_one— money.available_only— at least one date still on sale centrally.rating,access_needs— repeat the parameter or comma-separate. Results carry anaccessibilityobject naming which features each show has, split into permanent venue facilities and one-off access performances (captioned, signed, relaxed, audio described, touch tour). On a date query the access performances are those of THAT date.limit— 1–50, default 20.fields=descriptionswaps snippets for full text.
Links
Every show carries show_url (this guide’s page) and
booking_url (edfringe.com). Use them as given — the booking slug is
not the show slug. A trailing -2026 is stripped for booking, and roughly
a quarter of the catalogue carries one, so a URL built from slug is a
plausible-looking 404:
show_url: https://edfringeguide.com/show/sam-nicoresti-baby-doomer-2026.0
booking_url: https://www.edfringe.com/tickets/whats-on/sam-nicoresti-baby-doomer
The MCP tools carry the same pair as Page: and Book: lines on
every result.
Accessibility
Eight tags, from the Fringe’s own vocabulary:
WHEELCHAIR_ACCESS, WHEELCHAIR_ACCESSIBLE_TOILETS,
AUDIO_ENHANCEMENT_SYSTEM, CAPTIONING, AUDIO_DESCRIPTION,
SIGNED_PERFORMANCE, RELAXED_PERFORMANCE,
TOUCH_TOUR_PERFORMANCE. Filter with access_needs (all listed must be
met); read back which ones apply from accessibility.facilities and
accessibility.access_performances.
max_steps (integer 0–99) filters on the stated number of steps into the
performance space, which WHEELCHAIR_ACCESS cannot express — it is only
yes/no. max_steps=0 is step-free. Only venues with a stated step count
match: where the listing does not say, the show is excluded rather than assumed step-free, so
a capped search is narrower than the programme rather than an exhaustive answer.
Show detail (/shows/{slug}) also carries access_guide when the
show appears in the Fringe’s Enhanced Performance Guide: how audio description is
delivered, the caption method, the interpreter and their position, touch-tour detail, and the
relaxed-performance stimuli. It is published once per festival, so it describes how
each provision works — which dates carry one stays each performance’s
access tags, which are refreshed continuously.
In access_guide.stimuli, null is not
false. true means the show stated the stimulus is present,
false that it stated it is absent, and null that the guide did not
ask or the show did not answer. Rendering null as “no” tells a
photosensitive reader a show is strobe-free on the strength of an unanswered question.
An absent tag is not a denial. It means the Fringe listing recorded none,
which is not the same as the feature being unavailable — check with the venue. And an
access need this API does not recognise is a 400, never a silently dropped
filter: a dropped access filter returns the unfiltered programme, which is how someone who
needs step-free access ends up at a room up two flights of stairs.
Things it will not do quietly
An unrecognised parameter, an unknown rating or an unknown access tag is a
400, never a silently dropped filter. The difference matters: a dropped filter
returns a plausible list you cannot tell apart from a real result, and for
access_needs that would mean sending someone who needs step-free access the
unfiltered programme.
{
"error": {
"code": "invalid_parameter",
"message": "Unknown access need \"STEP_FREE\". Valid values: WHEELCHAIR_ACCESS, ..."
}
}
Which availability field to read
Every performance carries five, and only one of them is the raw upstream value:
on_sale— the one to trust. True when the date can be booked through the central box office: a bookable ticket status, not sold out, allocation remaining, and not cancelled.cancelled— the date is not happening. Reported separately fromon_salebecause a caller filtering on that alone cannot tell a cancelled date from a sold-out one. True on either signal the feed uses: itscancelledflag, or aCANCELLEDticket status — the two lag each other in both directions.allocation_exhausted— the central allocation is gone. False for aFREE_NON_TICKETEDdrop-in, whosepercent_remainingof 0 means it never had an allocation rather than that it sold out.status_text— the same answer as a sentence, including the “contact the venue” case that a raw status enum reads as simply unavailable.sold_out— the raw Fringe feed field, not authoritative. In practice the feed leaves it unset, so"sold_out": falsemeans “upstream said nothing”, not “tickets remain”. It is passed through unchanged rather than corrected, so that it keeps meaning exactly one thing. Do not branch on it.
percent_remaining of null means not yet measured, which is not the same as
zero: such a date is still on_sale.
Where a show actually is
venue is the operator (“Monkey Barrel Comedy”); space is the
room inside it (“Monkey Barrel 1”), which is the door an audience member has to
walk into and the thing capacity measures. Big operators run several rooms, so
the venue alone does not locate a show.
space is already resolved: it is null where the room name would only
restate the venue (“The Vault” inside “Paradise in The Vault”), so
you can render venue and space together without a redundancy check
of your own.
Comparing availability between shows
A percentage is a share of each show's own allocation, so it is not comparable across
rooms: 10% left is three tickets in a 30-seat room and seventy-five in a 750-seat house.
Every show therefore carries capacity.
capacityis the size of the room, from the Fringe spaces data. It is a per-show field, not a property ofvenue: two shows billed at the same venue are usually in different rooms, so a venue name tells you nothing about room size.nullmeans the room size has not been researched (about 7% of shows). It never means small, and never means zero — skip those shows rather than defaulting them.percent_remaining×capacityis an upper bound on tickets left, not inventory. The central allocation is the room minus press seats, comps, holds and restricted-view kills, so the true figure is always lower. A show computing to “2 seats” may have none.
The API deliberately publishes the room size rather than a seat count: the count is an estimate, and an estimate in a machine payload gets quoted as stock.
Technical details
- All times are UTC with an explicit
Z; the festival runs entirely in BST, so UK local is +1. Prices are GBP. dates_on_sale,available_onlyand the per-dateon_saleflag all use one definition, so a filtered list can never contain a date the response then reports as not on sale.- Responses are cached at the edge for 5 minutes (1 hour for venues). Errors are never cached.
- Rate limit: 300 requests per minute per IP, separate from the website's own budget.
- On a date query, everything under
performancedescribes THAT date and everything underrundescribes the whole run. They are never merged into one field. - Prefer plain files? /llms.txt and /llms-full.txt
(the whole programme in one fetch), and every page on this site serves markdown to
Accept: text/markdown.
Two things worth knowing, whichever you use
“On sale” means the central Fringe box office only. A show with nothing on sale
is not necessarily unattendable — venues keep their own separate allocation, run door queues
and hold returns lists. That is what ticket_advice and
/api/v1/ticket-advice are for, and the MCP server tells connected clients to
call it before ever declaring a show unavailable.
Ratings are opinions. Great, Good, Ok and Wild Cards are the subjective views of this guide’s authors — not critic reviews, not audience scores, nothing official. Both interfaces say so on every response that carries one.