FreeTicket MCP server (@freeticket/mcp)
The official MCP server for FreeTicket. It exposes the same OpenAPI contract
the ft CLI consumes — one tool per contract operation, generated from the spec,
never hand-written. If the API can do it, there is a tool; if there is no tool,
the API cannot do it yet.
103 tools across three contracts: B2B /api/v1 (76), superadmin
/api/admin (21), public B2C /api/public (6). Contracts at 1.7.0 / 1.3.0 /
0.4.0 — the same surface the website operates on: events and sales, the
members area (memberships, profile, own tickets), content (videos, feed,
live streams), and settlement receipts. Full inventory with signatures:
references/tools.md.
MCP server or ft CLI?
Both hit the same backend. Pick by where the work happens:
| Use the MCP server | Use the ft CLI (freeticket-cli) |
|---|---|
| The user is in a chat client (claude.ai, Claude Desktop, Cursor) | The user is in a terminal, or you are writing a script |
| You want lists and reports rendered as tables/KPIs in the host | You need CSV, --json piped to jq, or cron |
A buyer-side agent with no credentials at all (public_*) |
Minting or revoking credentials (deliberately not in MCP) |
They share one session: ft login writes ~/.freeticket/config.json, and the
local MCP server reads it. Log in once, both work.
Setup
Fastest: the plugin (Claude Code)
/plugin marketplace add AppFreeticket/agent-skills
/plugin install freeticket@freeticket
Installs the three skills and wires the server (remote transport, OAuth in the browser on first use). Nothing to configure by hand.
Local (Claude Code, Claude Desktop, Cursor)
⚠️
@freeticket/mcpis not on npm yet —npx -y @freeticket/mcpfails with "Connection closed". Until it ships, use the plugin or the remote connector below, or run it from a clone (pnpm build→node dist/index.js).
{
"mcpServers": {
"freeticket": {
"command": "npx",
"args": ["-y", "@freeticket/mcp"]
// No env needed: it reuses the `ft login` session.
// Headless/CI only: "env": { "FT_API_KEY": "ft_live_…", "FT_WORKSPACE_ID": "ws_…" }
}
}
}
Config precedence: env > ~/.freeticket/config.json > defaults.
| Variable | Effect |
|---|---|
FT_API_URL |
API base without /api/v1 (default https://admin.appfreeticket.com) |
FT_API_KEY |
B2B credential — unlocks the workspace tools |
FT_WORKSPACE_ID |
Active workspace (X-Workspace-Id) |
FT_ADMIN_SESSION |
SUPER_ADMIN session cookie — unlocks admin_* |
Remote (claude.ai custom connector)
claude.ai cannot send API keys or custom headers, so the server ships its own
OAuth 2.1 authorization server. Settings → Connectors → Add custom connector
→ URL https://<deploy>/mcp, leave Client ID/Secret empty (dynamic client
registration, RFC 7591). The consent page offers "Continuar con FreeTicket":
the user signs into free-admin with their normal account and approves — same
device flow as ft login, nothing to paste. Advanced options still accept a raw
API key (CI) or a superadmin cookie.
POST /mcp/public needs no auth at all and serves only public_* — that is the
endpoint for a buyer's agent.
The three credential layers
Tools are registered per session by what the credential carries. A missing tool is not a bug — it is a missing credential.
| Layer | Requires | Tools |
|---|---|---|
| Public B2C | nothing | public_* (6) — always registered |
| B2B workspace | API key / ft login session |
events, sales, tickets, plans, venues, staff, reports, settlements, content (76) |
| Members area | enterprise key + buyer session (X-Customer-Session) |
customer_* — speak for a buyer, not for the workspace |
| Superadmin | FT_ADMIN_SESSION |
admin_* (21) — cross-tenant |
If the user asks for something and the tool isn't there, check the layer before
anything else: no events_list means no API key; no admin_users means no
superadmin session.
Working rules
Reads are free, writes are not. Destructive tools (*_delete,
sales_refund, sales_cancel, admin_workspaces_suspend, admin_impersonate)
carry destructiveHint and say so in their description. Confirm with the human
before calling one, and quote what will be affected — id, name, amount.
Never invent an endpoint. These tools are generated from the contract. If a
capability is missing, it is missing upstream in free-admin, and the fix is to
request it there — not to compose a workaround out of other tools that mutates
data in a way the API didn't intend.
Credentials are read-only. api_keys_list and admin_tokens show what
exists so it can be audited; minting and revoking are CLI-only, on purpose. Do
not ask the user for an ft_live_… key — send them to ft login or the
connector consent page.
Payments stay with the human. public_orders_create returns a Mercado Pago
checkoutUrl. Hand the user the link. Never ask for card data, and never claim
an order is paid until public_orders_get says so.
Multi-workspace reads. Every B2B list tool takes an optional workspace
argument: "all" aggregates every workspace the session can reach, or pass an
array of ids. Each row comes back tagged with workspaceId/workspaceName.
Omit it for the active workspace alone. Writes have no global mode — a mutation
is always explicitly scoped to one workspace.
Permissions are per workspace, and the backend enforces them. whoami
returns, for every workspace, the effective role and the enabled sections
(null = unrestricted, [] = expired or revoked). The old top-level role is
deprecated — read the row, not the global field. A user capped in the panel is
capped here too: a 403 on one workspace of a global read is that cap working,
not a bug to route around.
Files come back as links, not bytes. settlements_document and
settlements_proof return a signed URL that expires in 5 minutes. Hand it to
the user; do not try to fetch or transcribe the PDF.
Content playback needs a token. The content_* listings never carry the
playback id. Mint one with content_playback_token (30 min live, 1 h video);
memberOnly items also need the buyer session of someone with an active
membership.
Money is in COP and lists are cursor-paginated (limit 1–100, default 20,
plus cursor). Dates are ISO 8601; events carry their own IANA timezone.
The view (MCP Apps)
Lists and reports do not arrive as a wall of JSON. The server implements the
official io.modelcontextprotocol/ui extension (MCP Apps, spec
2026-01-26): 29 tools declare _meta.ui.resourceUri pointing at
ui://freeticket/view.html, and a supporting host renders them — array →
table, object → KPI tiles — with FreeTicket's mark and accent, adopting the
host's own palette and locale for everything else.
What this means for you:
- The result you receive is unchanged: JSON in
content, plusstructuredContentfor the view. Reason over the JSON as always. - Do not re-render the table in your reply. The user already sees it. Say what it means — the outlier, the trend, the number they asked for.
- Hosts without the extension (terminals, older clients) simply get the text. Nothing degrades, so never branch your behaviour on whether a view exists.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
Only public_* tools listed |
No B2B credential in the session | ft login, or set FT_API_KEY |
No admin_* tools |
No superadmin session | Set FT_ADMIN_SESSION (cookie better-auth.session_token) |
| 401 on every B2B tool | Expired session or wrong FT_API_URL |
Re-run ft login; check the base URL has no /api/v1 |
| 403 on one resource | Role too low, or resource in another workspace | Check whoami; pass the right workspace |
| Connector won't authorize on claude.ai | Client ID/Secret filled in | Leave both empty — registration is dynamic |
| Tokens die after every deploy | MCP_TOKEN_SECRET unset on the host |
Set it (openssl rand -hex 32); without it the secret is ephemeral |
| Tool exists in the docs, not in the client | Client cached an old tool list | Reconnect the server |
Related
- CLI:
freeticket-cli— same contract, terminal-side - Copy & event advice:
freeticket-eventos - Server source:
AppFreeticket/freeticket-mcp