Dizko Events MCP
OfficialAllows Hermes agents to perform autonomous ticket purchases through a ticketPurchaseProvider adapter, with safety rails requiring a locked quote and explicit written confirmation.
Integrates with LangChain MCP adapters so agents can search events, get recommendations, plan nights, and prepare ticket purchases.
Enables LangGraph agents to access event search, recommendations, night planning, and ticket preparation tools via MCP over HTTP or stdio.
Supports the OpenAI Agents SDK with live event search, recommendations, night planning, and ticket preparation tools through MCP.
Works with Pydantic AI agents via MCP to provide event search, recommendations, night planning, and ticket preparation capabilities.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Dizko Events MCPFind live music events in Brooklyn tomorrow night"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Dizko Events CLI and MCP
Quick Start
Recommended: add the hosted connector. It runs on our servers, so it never gets access to your computer (no scary local-install warning), needs no install and no auth:
https://mcp.dizko.app/mcpPaste that into your client's Settings → Connectors → Add custom connector
(claude.ai, Claude Desktop, ChatGPT developer mode, Cursor). Step-by-step per
client: https://www.dizko.app/mcp/install (https://mcp.dizko.app/install
redirects there).
For Claude Code:
claude mcp add --transport http dizko-events https://mcp.dizko.app/mcpAdvanced: run the server locally (only if you can't use the hosted connector, e.g. a free Claude plan or a client that only supports local servers). A local server runs as you, so Claude Desktop warns it "can access everything on your computer". That is inherent to any local MCP extension, not something this package over-requests. The hosted connector above avoids it.
npx -y dizko-events install claude-desktop # writes the local config
# or the raw stdio snippet for any client:
{ "command": "npx", "args": ["-y", "dizko-events", "mcp"] }Claude Desktop one-click bundle: npm run build:mcpb produces
dist/dizko-events-<version>.mcpb; the hosted server serves it at
/download/dizko-events.mcpb.
Related MCP server: touch-grass-mcp
Agent frameworks (Hermes, OpenClaw, LangGraph, OpenAI Agents SDK, custom loops)
Framework agents don't have a "Connectors" UI, so they integrate programmatically. Four paths, simplest first:
Remote MCP over HTTP: point the framework's MCP client at the hosted endpoint
https://mcp.dizko.app/mcp(streamable-http, no auth). Works with the official MCP SDKs, the OpenAI Agents SDK, LangGraph/LangChain MCP adapters, Pydantic AI, etc.stdio MCP: spawn
npx -y dizko-events mcpas a subprocess and speak MCP over stdio.Raw HTTP JSON-RPC: no MCP library needed; POST to
/mcp:curl -s https://mcp.dizko.app/mcp \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -H 'mcp-method: tools/call' \ -H 'mcp-name: search_events' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"search_events","arguments":{"city":"los angeles","when":"week"},"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'The
_metablock is what makes this a2026-07-28request: the revision is stateless, so there is noinitializehandshake and no session - every request carries its own protocol version and client capabilities. Callserver/discoverto see which revisions the endpoint speaks. Clients on2025-11-25and earlier keep working unchanged; omit_metaand the server answers them over the 2025-era path instead.In-process library: embed the tools directly (no MCP layer). The package exposes a stable API:
import { tools, prompts, callTool, getPrompt, searchEvents } from "dizko-events"; // Hand `tools` (JSON Schemas) to your model as function definitions, then: const result = await callTool("search_events", { city: "berlin", when: "weekend" });
Autonomous ticket purchase (the Hermes / OpenClaw integration point)
By default the ticket tools return a checkout handoff (a link). To enable
bounded autonomous purchase, supply a ticketPurchaseProvider adapter. This is
only injectable when you embed the package or self-host the server - the
hosted endpoint runs in our process and cannot accept your payment adapter.
import { createHttpMcpServer, callTool } from "dizko-events";
const hermesAdapter = {
canPurchase: (event, summary) => true,
purchase: async ({ quote, confirmation_text, delivery_email, add_to_calendar }) => {
// ... perform the bounded purchase, return { status, order_id, receipt_url }
}
};
// Self-host with the adapter wired in:
createHttpMcpServer({ ticketPurchaseProvider: hermesAdapter }).listen(8787);
// ...or call tools in-process with it:
await callTool("purchase_ticket_order", input, { ticketPurchaseProvider: hermesAdapter });purchase_ticket_order still requires a signed quote from quote_ticket_order
and explicit written confirmation before the adapter is invoked. The
dizko_ticket_policy prompt explains the rules. Set DIZKO_QUOTE_SIGNING_SECRET
so quote tokens survive restarts.
This package exposes Dizko's live event inventory to agents and humans:
dizko-events: a CLI for quick searches, ranked recommendations, night plans, client install, and diagnostics.dizko-events mcp(binarydizko-events-mcp): a stdio MCP server for local developer clients.dizko-events serve(binarydizko-events-http): an HTTP MCP server for hosted connectors/apps.Agentic ticket tools for ticket offers, signed quotes, written confirmation, checkout handoff, and future Hermes/OpenClaw/Dizko purchase adapters.
Why This Beats Normal Chat
General chat can describe likely events, but it does not reliably know current inventory. This tool gives an agent:
Live structured results from Dizko's
/eventsAPI across 49 cities, with every time already in the city's local timezone.Deterministic filters for city, date, genre, vibe, event type, venue, neighborhood, promoter, price, and artist.
Artist, venue and promoter lookups with upcoming dates, and city-level trend reads with evidence counts.
Explainable ranking so recommendations include reasons instead of opaque taste guesses.
Ticket links and Dizko event links for verification, plus one-click calendar and directions links.
A planning tool that returns a primary option plus fallbacks in a compact machine-readable shape.
Consent-based preference learning: onboarding questions, saved taste profiles, post-event feedback, note-derived signals, and learned ranking signals. Saved taste only ranks results; it never hides them.
Ticket purchase safety rails: agents can quote and prepare ticket orders, but autonomous purchase requires a signed quote, explicit written confirmation, and an integrated purchase provider.
The agent can still write conversational prose, but its event facts come from the tool.
CLI
Run without installing:
npm exec --yes --package dizko-events -- dizko-events search --city "Los Angeles" --when week --limit 5
npm exec --yes --package dizko-events -- dizko-events recommend --city "New York" --when tonight --vibe underground,intimate --max-price 30Or install the early-access developer package:
npm install -g dizko-events
dizko-events search --city berlin --when weekend --genres techno --limit 5From this repository:
node ./bin/dizko.js search --city berlin --when weekend --genres techno --limit 5
node ./bin/dizko.js recommend --city new-york --when tonight --vibe underground,intimate --max-price 30
node ./bin/dizko.js plan --city london --when weekend --event-types party --avoid mainstream
node ./bin/dizko.js cities
node ./bin/dizko.js doctordoctor checks DNS resolution, the API health endpoint, the hosted MCP endpoint (health, metadata, tools/list), and one small live search, reporting the underlying cause of any failure. Run it first whenever a search fails.
Transient network failures (EAI_AGAIN, ETIMEDOUT, ECONNRESET, ENOTFOUND, temporary 5xx) are retried automatically and reported as retryable: true with the underlying cause, code, hostname, and target URL when they persist. All surfaces (CLI, stdio MCP, hosted MCP, smoke test, monitor) resolve endpoints through src/config.js; see Configuration.
MCP For Local Developer Clients
Run the stdio MCP server from npm:
npm exec --yes --package dizko-events -- dizko-events-mcpOr from this repository:
node ./bin/dizko-mcp.jsExample MCP client config:
{
"mcpServers": {
"dizko": {
"command": "node",
"args": ["/absolute/path/to/dizko-mcp/bin/dizko-mcp.js"],
"env": {
"DIZKO_API_BASE_URL": "https://api.dizko.app"
}
}
}
}Example MCP client config using npm:
{
"mcpServers": {
"dizko-events": {
"command": "npm",
"args": ["exec", "--yes", "--package", "dizko-events", "--", "dizko-events-mcp"],
"env": {
"DIZKO_API_BASE_URL": "https://api.dizko.app"
}
}
}
}Tools
The server lists 30 tools (the count comes from tools.length in src/tools.js; scripts and monitors read it from there rather than hard-coding it). Every parameter carries a description and defaults are served in the schema. The names are the published connector surface, so they do not change between releases.
Discovery:
search_events: search live events in one city and timeframe. Use for any "what's on" request that names a city, a venue, an artist, or a timeframe. Filters you pass (genres,vibe,event_types,neighborhoods,venue,featuring,promoter,free,pride,price_min,price_max) are hard filters;avoidandmax_priceare ranking hints.countis the total matching; page by passingpage.next_cursorback ascursor(offset/next_offsetstill work for older clients). Withrank: "taste"it returns one ranked window of at most 50 and no cursor.recommend_events: the same search ranked by taste (rank: "taste"). Every result carriesrecommendation_reasons.result_limit(orlimit) sets how many ranked events come back, at most 50; taste ranking scores one candidate window, so there is no cursor.recommend_events_for_user: taste-ranked search driven by a saved profile (profile_id+profile_secretrequired), with the sameresult_limit/limitof at most 50. Saved and learned taste rank the results; they never hide them.plan_night: a night plan for one city and date: a primary event plus a nearby fallback (best taste fit within 6 km), a later-starting fallback, and alternates. Same filters as search;citymay be omitted when a profile with a saved home city is given.get_daily_roundup: one-day digest for a city: top picks plus sections for parties, live music, art, comedy and theatre, talks, food, and more.compact=truegives a short push-style digest. Built for "what's happening today" and scheduled briefings.get_city_pulse: aggregate read of a city's scene over 1-14 days: busiest nights, top venues, genre mix, headline events and free-event count, every stat with evidence counts. Public inventory only.get_event: full detail for one event id (local times, venue and address, price, lineup, set times, artist socials, image, coordinates, links). Search results already contain what the render template needs, so only call it for an id the user gave you.list_cities: live coverage: every city with status (live,unlocking,early), event count, timezone and freshness.
Entities:
All three name searches rank candidates by how well the name answers the query, not by upstream relevance: an exact name first, then a name that starts with the query, then a whole-word hit, then a hit buried inside a longer word. The catalog name has to contain what was typed, never the reverse, so a profile called "Honey" is not an answer for "Honey Dijon". Two rows for the same name are one answer rather than an ambiguity, since catalogs carry the same venue twice under different casing.
When several candidates answer the name equally well, the name has said all it can, so how much of an answer each one actually is decides. prominence scores that: for an artist, listed Dizko dates, upcoming dates, co-billed artists, press clips, mixes, career appearances and editorial standing; for a venue, whether the record carries a capacity, genres and a real bio rather than a one-line stub; for a promoter, its upcoming count. best_match.confident is true only when the leader wins outright on name, or is decisively ahead on prominence. So "Klock" resolves confidently to Ben Klock (nine listed dates, twelve press clips) over BJ Klock (one appearance), while two comparable artists sharing a name stay ambiguous and best_match.alternatives carries the candidates to ask about.
An exact name match holds unless the fuller record is decisively ahead. Catalogs carry the same real place twice, once as a one-line encyclopedia stub that happens to win the name and once as the record with the capacity, the genres and the bio, so "Berghain" serves "Berghain / Panorama Bar" rather than the stub named exactly Berghain. Displacement is one name tier and never reaches below a whole-word match, so a big venue called "Medlock Hall" cannot win a search for "Lock". Artist prominence costs two extra upstream calls per competing candidate and is fetched only when something is actually competing, so a query one profile answers outright still costs a single request; venue and promoter scores come from rows the search already returned. Upstream relevance cannot do this job: its score ranked Nina Kraviz last of nineteen "Nina" profiles and its authority field is 0.59 for every artist in the catalog.
get_artist: pass anidfor the full profile (bio, cities, genres, links, upcoming events, insights, mixes, press, and the published Dizko page when one exists), or a namequery. A query whosebest_matchis confident is resolved to that profile in the same call and carriesresolved_from; an ambiguous one returns the candidates and the assistant is told to ask rather than guess.get_venue: the same contract for venues: neighborhood, capacity, genres, bio, links, and upcoming events. Listings are matched room by room, so Berghain Kantine does not inherit Berghain's Klubnacht and Berghain does not claim Kantine's programme. A venue with no listing under its full name falls back to its colloquial short form, which is how "Salon zur Wilden Renate" finds events listed at "Renate".find_scene_entities: the general catalog search acrosskind=dj,venue,collectiveorpromoter, or a full profile byid. Promoters and collectives are one crew to a user, so either kind searches both: passcityto include promoters with upcoming listings (promoter ids are per city; collectives are searched worldwide).get_artist_events: upcoming shows grouped by artist for up to 8 named DJs, performers or comedians, deduplicated and date-ordered, optionally scoped to a city. With a profile and no artists named, it tracks the profile's savedfeaturinglist.get_artist_page: an artist's published Dizko page byhandle, with stable block ids for deep links to a specific mix or set.
Preferences (opt-in, protected by profile_id + profile_secret):
get_preference_onboarding: the consent-first questions to ask before saving anything.create_event_preference_profile: create a private preference profile after explicit consent (consent: true). Returnsprofile_id(dzk_...) and a one-timeprofile_secret(dzs_...); the service stores only a hash of the secret.save_event_preferences: add to (mode: merge, default) or replace saved preferences: cities, event types, genres, vibe, neighborhoods, venues, promoters, artists to track (featuring), avoid, budget, and per-weekdayday_filters.get_event_preferences: saved preferences, learned taste (with scores) and feedback count.delete_event_preferences: delete saved preferences and feedback history; requiresconfirm_delete: trueafter the user confirms. Destructive.get_event_feedback_prompt: short post-event questions for one event, asked before feedback is saved.record_event_feedback: store post-event feedback (liked, 1-5rating,notes) and update learned taste. At least one signal is required.get_event_search_followups: the missing-context questions worth asking before a broad "what's on" search, and only when the request is genuinely ambiguous.
Signed-in Dizko account (OAuth, Muse connector contract):
get_taste_profile(saved:read): the connected user's learned taste plus saved and binned events.save_event,add_to_dizko_plan,bin_event(saved:write): save an event, add it to the user's plan, or hide it and teach their taste model. Each needsconfirmed: trueafter the user agrees and anidempotency_keyof at least 16 characters, so a retried call does not act twice.
These four declare an oauth2 security scheme. Without a signed-in caller holding the scope they return authentication_required or insufficient_scope before any argument is read.
Tickets and calendar:
get_ticket_purchase_policy: current purchase modes, the hard safety rules, and what a provider needs to support autonomous purchase.get_ticket_offers: ticket options for one event: provider, checkout link, estimated price, free entry, whether autonomous purchase is supported, and the purchase policy. Call before quoting.quote_ticket_order: a signed, time-limited (10 minute) quote: quantity, ticket type, max total, currency, refund terms, delivery email and stop conditions. Returnsquote_tokenand the exact confirmation text to ask the user for.purchase_ticket_order: execute a quoted order after the user's explicit written confirmation. The server rejects a confirmation that does not name the quantity and max total, or that refuses, hedges or asks ("don't buy 2 for 120"). With a third-party link it returnsstatus: requires_external_checkoutand thecheckout_url; never claim a purchase unlessstatusispurchased. The idempotency key is derived from the signed quote, so a retried purchase cannot buy twice. Destructive and open-world.create_event_calendar_file: an importable.icsentry for one event; the per-eventcalendar_urlis the one-click alternative.
Annotations: discovery, entity, onboarding, follow-up, get_event_preferences, get_taste_profile, the ticket policy, offer and quote tools, and create_event_calendar_file are readOnlyHint: true. create_event_preference_profile, record_event_feedback, save_event and add_to_dizko_plan write private user state (readOnlyHint: false, destructiveHint: false). save_event_preferences (its replace mode overwrites the profile), delete_event_preferences and bin_event are destructiveHint: true; purchase_ticket_order is destructiveHint: true and openWorldHint: true because it is the bounded action point for ticket purchase or checkout handoff. No tool serves an outputSchema; results come back as structuredContent plus a JSON text block.
Auth note: the public hosted connector submits as noauth for event discovery. Preference tools still require the user's opaque profile_id plus private profile_secret. Tool descriptors include securitySchemes (noauth, or oauth2 with the scope for the four account tools) and mirror it in _meta.securitySchemes for ChatGPT compatibility, plus _meta["openai/toolInvocation/invoking"] / invoked status text.
Retention note: saved preference profiles are pruned after the configured inactivity window (DIZKO_PREFERENCE_RETENTION_DAYS, default 730 days), matching the published 24-month retention policy.
Prompts
The server also advertises the prompts capability with four prompts (prompts/list, prompts/get). They carry the same guidance as the onboarding, follow-up, feedback and policy tools, for clients that surface prompts rather than call tools:
dizko_onboarding: consent-first questions to ask beforecreate_event_preference_profile.dizko_search_followups(city,whenoptional): the clarifying questions worth asking before a broad "what's on" search.dizko_post_event_feedback(event_idrequired): short questions to ask beforerecord_event_feedback.dizko_ticket_policy: how quoting, confirmation, checkout handoff and autonomous purchase work, plus the hard safety rules.
Request conventions
Timeframes.
whenacceptstoday,tonight,tomorrow,weekend,next weekend,week,next week,month, a weekday name,any, or an exactYYYY-MM-DD; all are resolved in the city's timezone.date_from/date_to(inclusive, city-local) overridewhenfor custom ranges.get_daily_roundupaccepts single-day presets only.Event payloads carry (a field with no value is omitted rather than sent as null)
when(already local, e.g.Fri 11 Sep, 22:00),starts_at/ends_at(UTC),starts_at_local,timezone,venue,address,city,price,genres,vibe,event_types,lineup(capped, withlineup_countwhen truncated),set_times,pick,promoters(names),image_url,ticket_url,event_url/dizko_url,calendar_url(a downloadable.ics),directions_url, and the connector contract fields:source,source_url,source_provenance,last_updated_at,retrieved_at,availability,price_min/price_maxandprice_freshness. Theirunverifiedandunknownvalues are intentional, not missing data. Ask fordescription,coordinates,socials, or fullpromotersobjects viafields. See Data honesty forquality_flags,currency_status,ends_on_local,also_listed_byandalternate_starts_local.Ordering.
sort_by:soonest(default for single-day requests),popular(default for ranges),cost,event_type, ordistancewithorigin_lat/origin_lng(only pass coordinates the user gave you).rank:relevance(default) keeps the API order;tastere-ranks the page by genres, vibe, avoid and budget from the request and the profile.rankdefaults totastewhen a profile is given.Profiles.
profile_id+profile_secretare accepted bysearch_events,recommend_events,recommend_events_for_user,plan_night,get_daily_roundupandget_artist_events. Saved taste is a ranking hint only; it is never turned into a filter.Empty results. A search or plan that matches nothing is not an error. It returns
no_resultswithreason(filters_too_narrow,empty_timeframeorno_inventory),baseline_count(how many events exist in the same city and timeframe with every filter removed),active_filters, andsuggested_relaxations: an ordered list of{ relax, why, retry_with }whereretry_withis a directly callable argument set. Price caps are suggested first because they also exclude every event with no published price. Theassistant_instructionon an empty result tells the model to report what is on instead of rendering an empty list.Consent and confirmation flags (
consent,confirm_delete,confirmed) must be the literal boolean;"yes"or1is refused withinvalid_argument, although other flags accept those spellings.Errors are returned as
isError: truewith{ error, code, field?, allowed?, hint? }. Codes:invalid_argument,invalid_cursor,unknown_tool,unsupported_city(withnearest_covered_citycomputed by distance from the live list),event_not_found,entity_not_found,profile_not_found,profile_secret_invalid,consent_required,confirmation_required,explicit_confirmation_required,feedback_signal_required,authentication_required,insufficient_scope,invalid_quote_token,confirmation_mismatch,quote_expired,upstream_unavailable,upstream_timeout.
Data honesty
The backend fixes listing data at the root; the connector still refuses to pass on what it cannot stand behind (src/quality.js):
Currency. When the backend sends
currency_inferred(a boolean), it decides: a code the source stated is kept whatever it is (a Goa festival that really sells in EUR), and an inferred one is shown withcurrency_status: "inferred"(relabelled to the city's currency if it still disagrees). Without that field (older backends) the connector checks itself: a price whose currency disagrees with the city's country (EUR on a Goa listing) is shown as500 (currency unconfirmed), withcurrencyomitted andcurrency_status: "unverified". USD is accepted alongside the local currency in Mexico, Argentina, Colombia and Turkiye; EUR never is, because it was the old universal fallback. Amounts are never judged by size, so a high price in the right currency is not flagged. Event payloads carrycurrency_inferred(trueorfalse) whenever the provenance of the shown code is known, and omit it otherwise. Ticket offers and quotes never carry an unverified code forward.End times. A city-local 23:59 end, an end equal to the start, and an end before the start are all treated as unknown:
ends_at/ends_at_localare omitted, calendar entries use the default three-hour duration, and a multi-day listing keeps its closing date (ends_on_local,whenlikeWed 14 Oct, 12:00 → Sun 1 Nov).Duplicates. Search, recommend, plan, roundup and venue pages collapse one show listed by two sources: same city-local date, the same or an adjacent venue (300 m or shared venue words), titles that share most of their words once venue, city and lineup words are removed, and starts within 90 minutes. The better listing is kept, with
also_listed_by; differing starts setstart_time_conflictandalternate_starts_local. One source listing two nights, and sessions more than 90 minutes apart, are never merged.Quality flags. When the backend sends
quality_flags(an array, even empty), it is the authority on listing-level flags such aszero_duration_aggregatorandstart_time_assumed(a date-only source such as AgendaLX, whose stored 12:00 is made up:whenthen shows the date only,starts_on_localcarries it andstarts_at_localis omitted); a flag the connector does not know costs a small ranking penalty, never more than a known one; the connector adds only what it alone can know:currency_unverifiedorcurrency_inferredfor the price it is about to show, andstart_time_conflictfrom merging two sources in one response. Without backend flags the connector derives its ownstart_equals_endandaggregator_only(only an aggregator such as allevents lists it). The backend'sconfidencepasses through aslisting_confidence. Flagged listings rank lower in recommend, plan and roundup flows (with the reasonlisting details unverified), never hidden, and the result'sassistant_instructiongains one sentence asking the assistant to mention the uncertainty.Near duplicates. Two sources listing one party collapse only when they share the city, the local date, a start within 90 minutes, most of the title, and the venue: coordinates within 300 m, or a distinctive shared venue word. Coordinates a kilometre or more apart never merge, and generic room words (club, bar, cafe, jazz and the like) do not count as a shared venue name.
Artist page completeness. When the scene API scores an artist's page,
get_artistreturns it asentity.completenessandget_artist_pageascompleteness:{ score, max, version, missing }, heaviest missing signal first. It is omitted when the backend has none.Attribution. Dizko web links meant for the user (
dizko_url, artistpage_urland mixdeep_link, entitydizko_url,collective_url) carryutm_source=dizko_mcp&utm_medium=ai_assistant, read by the web app's existing UTM capture.refis not used because the web app treats it as a referral code.event_url, ticket, calendar and directions links stay clean.
Learning rules
A like promotes the event's genres, vibe, event types, venue and promoters.
A dislike marks the venue and promoter. Genres are only penalized when the notes blame the music or lineup.
Notes about crowds, price, timing, mainstream or alcohol-focused events become learned avoid signals.
A term the user saved as a preference is never learned negative.
Genres, vibe and event types need two negative signals before they become an avoid rule; venues and promoters need one.
Profile ids changed prefix from upg_ to dzk_ and secrets from ups_ to dzs_; profiles created before that change keep working with their existing values.
Hosted MCP For ChatGPT And Claude
For normal users, publish a hosted MCP endpoint rather than asking them to run a local command.
PORT=8787 node ./bin/dizko-http.jsEndpoints:
POST /mcp: JSON-RPC MCP endpoint (rate-limited, optional bearer token).POST /openai/mcp: the reviewed ChatGPT and Codex plugin surface (src/surfaces.js): 13 read-only discovery tools, no profile inputs, no prompts, no ticket flow, and no app promotion in results.GET /.well-known/openai-apps-challenge: the OpenAI domain-verification token fromDIZKO_OPENAI_APPS_CHALLENGE(404 when unset).GET /health: deployment health check.GET /: basic service metadata.GET /install: redirects to the per-client install guide athttps://www.dizko.app/mcp/install.GET /e/<event-id>/cal,/map,/ics: short links used in tool payloads: Google Calendar template, Google Maps directions, and a downloadable.icsfile. They share the/mcprate limiter.GET /download/dizko-events.mcpb: the Claude Desktop bundle whendist/contains one.GET /privacy-policy.html,/support.html,/terms.html,/user-guide.html,/install.html: public pages.GET /.well-known/security.txt(also/security.txt): vulnerability-reporting contact metadata.GET /logo-512.png: 512px connector logo for submission metadata.
Current deployed endpoint:
https://mcp.dizko.app/mcpTo become a ChatGPT app/plugin:
Deploy this HTTP server on a public HTTPS domain.
Test it in ChatGPT Developer Mode by creating a connector with that URL.
Validate tool calls on web and mobile.
Submit the app through the OpenAI dashboard with the public MCP URL, privacy/support URLs, logo, screenshots, test prompts, and tool descriptions.
Claude users can add the same public MCP URL as a custom connector where their plan/workspace supports remote MCP.
See plugin-submission.md and submission-fields.json for the working submission copy.
See OPENAI_PLUGIN_SUBMISSION.md for the ChatGPT and Codex plugin package, which uses the read-only /openai/mcp endpoint.
See SCREENSHOT_CHECKLIST.md for exact ChatGPT Developer Mode screenshot prompts and filenames.
See SUBMISSION_AUDIT.md for requirement-to-evidence mapping and remaining external review gates.
See USER_GUIDE.md for normal-user prompts, preference-memory behavior, and support/privacy links.
See DEPLOYMENT.md for Railway, Docker, custom-domain, and review steps.
See OPERATIONS.md for health checks, logs, rollback, preference-data handling, and resubmission triggers.
See golden-prompts.md for direct, indirect, and negative prompt tests for connector discovery and tool routing.
See SECURITY.md for vulnerability reporting, the security posture, and operational safeguards.
See REGISTRY_PUBLISHING.md and DIRECTORY_SUBMISSIONS.md for registry and directory listings.
Live smoke test:
npm run monitor:live
npm run smoke:livemonitor:live is read-only and suitable for uptime checks; it expects the tool count from the package's tool registry. smoke:live also exercises the prompts and temporary preference profile creation and deletion.
Submission evidence:
npm run verify:submission
npm run verify:submission:write
npm run verify:submission:bundle
npm run verify:submission:fields
npm run submission:status
npm run review:demo
npm run preflight:submissionThe bundle command saves submission-evidence/latest.json and submission-evidence/latest-summary.md for dashboard review prep while keeping generated evidence out of git. The evidence includes the verified endpoint, current Railway deployment id, image digest, public-page checks, tool metadata, live search, prompt checks, and the preference-memory flow.
The fields command validates submission-fields.json, the stable dashboard-copy artifact, against the latest live evidence, including deployment metadata and the served tool count.
The preflight command runs local tests, live smoke, live submission evidence generation, and dashboard-field validation in one pass.
The status command gives a final go/no-go for OpenAI dashboard submission and names the single endpoint to submit.
The review-demo command writes a sanitized submission-evidence/review-demo.md transcript from live MCP calls for dashboard prompt/response prep.
Support deletion utility:
npm run preferences:delete -- --profile-id dzk_... --profile-secret dzs_... --preferences-path /data/preferences.jsonThis is for support deletion requests when the user cannot call delete_event_preferences through their MCP client.
Configuration
DIZKO_* names are preferred. EVENTCHAT_* (and, for the four URLs, UPLAYGROUND_*) are legacy aliases the code still accepts; the HTTP server's own settings are read under EVENTCHAT_* only.
Shared by every surface (src/config.js):
Variable | Default | Legacy aliases | Purpose |
|
|
| Events API. |
|
|
| Public event links. |
|
|
| Hosted endpoint; also the base for |
|
|
|
|
|
|
| Per-request upstream timeout. |
|
|
| Retries for transient network/5xx failures (backoff + jitter). |
|
|
| Backoff base delay. |
|
|
| Upstream response cache; |
|
|
| How long an expired cache entry may be served while the upstream fails. |
|
|
| User agent sent upstream. |
| unset |
| Shared secret the hosted deployment sends to the Dizko API as the |
Tickets, preferences (src/tickets.js, src/preferences.js):
Variable | Default | Legacy aliases | Purpose |
| random per process |
| HMAC key for quote tokens. Set it on hosted deployments so quotes survive restarts; the per-process fallback is only safe for a single instance. |
|
| none | How many spent-quote claims are held at once. Sized for concurrent live quotes: expired claims are pruned first, so the ceiling is only reached by that many unexpired claims. At the ceiling a purchase is refused ( |
|
|
| Preference store file. |
|
|
| Inactive profiles are pruned after this many days. |
|
|
| Ceiling on stored profiles. At the ceiling, profiles that were created but never used or consented to are evicted oldest-first; only if that frees nothing does creation fail with |
|
|
| Ceiling on one serialized profile. Over it, the oldest feedback entries are dropped to fit (their signal already lives in |
| unset | none |
|
HTTP server (src/httpServer.js, EVENTCHAT_* only):
Variable | Default | Purpose |
|
| Listen port ( |
|
| Listen host. |
| unset (public) | When set, |
|
| Comma list for |
|
| Requests per window per client IP on |
|
| Rate-limit window. |
| empty | Comma list of IP prefixes exempt from the limiter (hosted assistants call from shared egress addresses). Matched against the client IP resolved through |
|
| Disable the limiter entirely. |
|
| Maximum JSON-RPC body. |
|
| Maximum requests in one JSON-RPC batch. A larger batch is rejected with |
|
| How many proxy hops sit in front of the server. The rate-limit client IP is taken this many entries from the right of |
Flows
Personalization:
User asks for personalized event help.
Assistant asks the onboarding questions (prompt
dizko_onboarding): event types, genres, vibe, budget, locations, avoidances.Assistant asks whether Dizko may save those preferences.
If yes and there is no existing profile, assistant calls
create_event_preference_profilewithconsent: trueand privately remembers the returnedprofile_idandprofile_secret. The response also includesaccess_instructions, a user-facing access card for clients that cannot persist connector state across sessions.If a profile already exists, assistant calls
save_event_preferenceswith bothprofile_idandprofile_secret.A request that names a city and a timeframe is one
search_eventscall with the profile attached. Clarifying questions (type, vibe, budget, area) are asked conversationally and only when the request is genuinely ambiguous.Preferences can include
day_filters, per-weekday rules such as techno Fridays but chill Sundays. Array fields add to the general taste andmax_price/free/nightlifeoverride it, but only on that weekday: single-day searches (tonight, tomorrow, an explicit date) and daily roundups apply the matching day automatically.For a daily digest ("what's happening today?", a scheduled morning briefing), assistant calls
get_daily_roundupwith the city plus the profile credentials and renders top picks followed by category sections.After the event, assistant asks whether the user went and liked it (prompt
dizko_post_event_feedback), then callsrecord_event_feedbackonly after the user provides liked/disliked, rating, or notes.
Ticket purchase:
User asks to buy or reserve tickets for an event.
Assistant calls
get_ticket_offerswith the event id.Assistant explains whether the offer supports autonomous purchase or only external checkout.
Assistant calls
quote_ticket_orderwith quantity, ticket type, max total, currency, and refund constraints, and receives a signedquote_tokenplus the confirmation prompt.Assistant asks for explicit written confirmation, for example:
Yes, buy 2 ticket(s) for Ostbahnhof XL, max total EUR240. Stop if price, date, venue, ticket type, quantity, or refund terms change.The confirmation must containbuyorpurchaseas a whole word plus the quantity and the max total as whole numbers;confirmation_mismatchlists what is missing.Assistant calls
purchase_ticket_orderwith the unchangedquote_tokenonly after that confirmation. Quotes expire after 10 minutes (quote_expired); edited tokens fail signature verification (invalid_quote_token).If the quote uses
external_checkout(handoff_only: true,next_step: hand_off_to_checkout), the tool returnsrequires_external_checkoutand a checkout URL: a hand off to checkout, where the user pays on the provider's site. The assistant must not claim it purchased or reserved the ticket. The confirmation check runs on the server before either path, so a call without matching written confirmation returnsconfirmation_mismatchand never reaches a provider.If Hermes, OpenClaw, Dizko Checkout, or another integrated provider is configured, the provider can execute the bounded purchase and return order/receipt/ticket delivery status.
supported_modes:external_checkout,partner_api_purchase,dizko_checkout,delegated_payment_future.
Provider adapter contract:
{
canPurchase(event, summary) {
return true;
},
async purchase({ quote, confirmation_text, user_payment_profile_id, idempotency_key }) {
return {
purchased: true,
status: "purchased",
order_id: "...",
receipt_url: "...",
ticket_delivery_status: "pending_delivery",
provider_response: {}
};
}
}Distribution Notes
The public ChatGPT user surface is the hosted MCP endpoint plus OpenAI app submission. The npm package is the developer distribution path for local agents, Claude Desktop-style MCP clients, Cursor and Windsurf setups, and technical testers who can run a command. The source is available under the MIT License for inspection, reuse, and issue reporting.
Before publishing a new npm version:
npm test
npm pack --dry-run --json
npm publish --access publicThis server cannot be deployed
Maintenance
Related MCP Connectors
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
Capability registry for the agentic economy. Semantic search over verified MCP server listings.
MCP server for progressive tool usage at any scale (see https://klavis.ai)
MCP server for Boson Protocol — on-chain agentic commerce for physical & digital goods.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceAn MCP Server that enables interaction with Ticketmaster's Discovery API for accessing event, venue, and artist information through natural language commands.-
- AlicenseNot gradedqualityDmaintenancePreference-aware events discovery MCP server that aggregates events, restaurants, and cultural activities across multiple sources and re-ranks them against your personal taste profile to surface things you'd actually want to do.1MIT
- FlicenseNot gradedqualityCmaintenanceMCP server for managing events on Meetup.com and Luma via AI assistants like Claude.2-
- AlicenseNot gradedqualityDmaintenanceMCP server for eventos event management platform, enabling AI assistants to manage events and tickets via API integration. Supports authentication, ticket CRUD operations, and listing with pagination.1MIT