diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..7a51c63 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,15 @@ +{ + "name": "freeticket", + "description": "Official FreeTicket plugin: run and grow ticketed events from your agent — CLI, MCP server and brand-voice copy.", + "owner": { + "name": "FreeTicket", + "url": "https://github.com/AppFreeticket" + }, + "plugins": [ + { + "name": "freeticket", + "source": "./", + "description": "Skills for the ft CLI, the MCP server and event copy, plus the freeticket MCP server wired in." + } + ] +} diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..3cb7de0 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,20 @@ +{ + "name": "freeticket", + "displayName": "FreeTicket", + "version": "0.1.1", + "description": "Operate FreeTicket from your agent: skills for the ft CLI, the MCP server and event copy, plus the freeticket MCP server (B2B, superadmin and public contracts as tools).", + "author": { + "name": "FreeTicket", + "url": "https://freeticket.co" + }, + "homepage": "https://freeticket.co", + "repository": "https://github.com/AppFreeticket/agent-skills", + "license": "MIT", + "keywords": ["freeticket", "ticketing", "events", "latam", "mcp", "cli"], + "mcpServers": { + "freeticket": { + "type": "http", + "url": "https://mcp.appfreeticket.com/mcp" + } + } +} diff --git a/README.md b/README.md index 4c7c734..20786cb 100644 --- a/README.md +++ b/README.md @@ -21,12 +21,27 @@ Claude Code, Cursor, and any agent that reads markdown skills. ```bash # Install a specific skill (recommended) npx skills add AppFreeticket/agent-skills@freeticket-cli +npx skills add AppFreeticket/agent-skills@freeticket-mcp npx skills add AppFreeticket/agent-skills@freeticket-eventos # List what's in the repo npx skills add AppFreeticket/agent-skills -l ``` +Or install **all three skills plus the MCP server at once** — this repo is also a +plugin under the [Agent Plugins 1.0.0](https://agent-plugins.org) standard +(`plugin.json` + `skills/` + `mcp.json` at the root): + +```bash +# Claude Code +/plugin marketplace add AppFreeticket/agent-skills +/plugin install freeticket@freeticket +``` + +Any Agent-Plugins-compatible client can read the same manifests. The plugin +wires the `freeticket` MCP server over its hosted endpoint and authorizes in the +browser on first use — no keys to paste, nothing to install. + Skills install into `~/.claude/skills/` (global) or the project's `.claude/skills/`. The agent loads each skill's `name` + `description` and opens the body only when your prompt matches it ([progressive disclosure](https://agentskills.io/specification)). @@ -63,12 +78,15 @@ The only requirement is a terminal where the agent (or you) can run `npx`. ## Skills Overview -**2 skills.** They compose: `freeticket-eventos` calls `freeticket-cli` to pull -live data before it audits. +**3 skills.** They compose: `freeticket-eventos` pulls live data before it +audits, through `freeticket-cli` in a terminal or `freeticket-mcp` in a chat +client. `freeticket-cli` and `freeticket-mcp` are the same B2B contract from two +directions — the CLI for terminals and scripts, the MCP server for chat clients. | Skill | Scope | Install | |---|---|---| | [`freeticket-cli`](./skills/freeticket-cli) | Drive the official `ft` CLI (`@freeticket/cli`): log in (browser device flow), list/inspect **and** create/update/delete events, dates, ticket types, sales, membership plans, venues, staff; publish events; cancel/refund sales; run CFO reconciliation; export anything to CSV. Superadmin via `ft admin …`. `--json`/`--csv` for automation. | `npx skills add AppFreeticket/agent-skills@freeticket-cli` | +| [`freeticket-mcp`](./skills/freeticket-mcp) | Connect to and operate the official MCP server (`@freeticket/mcp`): local stdio setup, remote connectors on claude.ai via the embedded OAuth 2.1 server, the three credential layers (anonymous B2C → workspace B2B → superadmin), all 87 tools, and the MCP Apps view that renders lists and reports inside the host. | `npx skills add AppFreeticket/agent-skills@freeticket-mcp` | | [`freeticket-eventos`](./skills/freeticket-eventos) | Event & community advisor: applies FreeTicket's brand voice and real product rules (visibility, member-gated presales, platform fee, time zone, required ticket fields), and **audits events with live data** (via `ft`) to recommend sales and retention improvements. | `npx skills add AppFreeticket/agent-skills@freeticket-eventos` | --- @@ -83,6 +101,8 @@ Ask your agent in natural language — it picks the right skill. Examples: | "Create an event" / "Publish it" / "Refund sale X" / "Raise a ticket price" | `freeticket-cli` | | "Run the CFO reconciliation" / "Suspend a tenant" / "Set a feature flag" | `freeticket-cli` (incl. `ft admin …`) | | "Automate this report" / "Give me `--json` for `jq`" / "Configure my API key" | `freeticket-cli` | +| "Add FreeTicket to Claude Desktop" / "Set up the MCP connector on claude.ai" | `freeticket-mcp` | +| "Why do I only see the public tools?" / "Which MCP tool refunds a sale?" | `freeticket-mcp` | | "Write the description for this event" / "Improve my event copy" | `freeticket-eventos` | | "Why isn't this event selling?" / "Audit my event" | `freeticket-eventos` (pulls data via `ft`) | | "How do I grow and retain my audience?" / "Recommend a presale strategy" | `freeticket-eventos` | @@ -106,6 +126,9 @@ agent-skills/ ├── freeticket-cli/ │ ├── SKILL.md │ └── references/commands.md + ├── freeticket-mcp/ + │ ├── SKILL.md + │ └── references/tools.md # 87 tools + what is deliberately absent └── freeticket-eventos/ ├── SKILL.md └── references/ @@ -118,6 +141,7 @@ agent-skills/ ## Related - CLI: [`@freeticket/cli`](https://github.com/AppFreeticket/freeticket-cli) (binary `ft`) +- MCP server: [`@freeticket/mcp`](https://github.com/AppFreeticket/freeticket-mcp) - B2B API v1: OpenAPI 3.1 contract at `GET /api/v1/openapi.json` ## License diff --git a/mcp.json b/mcp.json new file mode 100644 index 0000000..5b3e957 --- /dev/null +++ b/mcp.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", + "mcpServers": { + "freeticket": { + "type": "streamable-http", + "url": "https://mcp.appfreeticket.com/mcp" + } + } +} diff --git a/plugin.json b/plugin.json new file mode 100644 index 0000000..5831647 --- /dev/null +++ b/plugin.json @@ -0,0 +1,14 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "freeticket", + "version": "0.1.1", + "description": "Operate FreeTicket from any agent: skills for the ft CLI, event copy, and the MCP server that exposes the B2B, superadmin and public contracts as tools.", + "author": { + "name": "FreeTicket", + "url": "https://freeticket.co" + }, + "homepage": "https://freeticket.co", + "repository": "https://github.com/AppFreeticket/agent-skills", + "license": "MIT", + "keywords": ["freeticket", "ticketing", "events", "latam", "mcp", "cli"] +} diff --git a/skills/freeticket-cli/SKILL.md b/skills/freeticket-cli/SKILL.md index 4a2a09d..0cb1bdd 100644 --- a/skills/freeticket-cli/SKILL.md +++ b/skills/freeticket-cli/SKILL.md @@ -1,6 +1,6 @@ --- name: freeticket-cli -description: Drive the official FreeTicket CLI (binary `ft`, npm `@freeticket/cli`) to operate a workspace from the terminal — log in through the browser (device flow), list/inspect AND create/update/delete events, dates, ticket types, sales, membership plans, venues, staff, discount codes and webhooks; publish events; create/cancel/refund sales; check tickets in at the door and resend QRs; list a sale's tickets, a plan's subscribers, cancel subscriptions; run reports (summary, by-event, timeseries, inventory, CFO reconciliation); and export buyers/attendees/subscribers to CSV. Superadmin (`ft admin …`) manages tenants, users, platform plans, feature flags and impersonation. Use it when the user wants to read OR mutate their FreeTicket account from the terminal, run `ft `, automate with `--json`/`jq` or `--csv`, manage the session/workspace, send feedback/suggestions (filed as GitHub issues on the right repo), or when another skill needs live data or actions on the B2B v1 backend. +description: Drive the official FreeTicket CLI (binary `ft`, npm `@freeticket/cli`) to operate a workspace from the terminal — log in through the browser (device flow), list/inspect AND create/update/delete events, dates, ticket types, sales, membership plans, venues, staff, discount codes and webhooks; publish events; create/cancel/refund sales; check tickets in at the door and resend QRs; list a sale's tickets, a plan's subscribers, cancel subscriptions; run reports (summary, by-event, timeseries, inventory, per-function financials, CFO reconciliation); read settlements paid to the organizer; mint headless service API keys; and export buyers/attendees/subscribers to CSV. Superadmin (`ft admin …`) manages tenants, users, platform plans, feature flags and impersonation. Use it when the user wants to read OR mutate their FreeTicket account from the terminal, run `ft `, automate with `--json`/`jq` or `--csv`, manage the session/workspace, send feedback/suggestions (filed as GitHub issues on the right repo), or when another skill needs live data or actions on the B2B v1 backend. --- # FreeTicket CLI (`ft`) @@ -39,9 +39,20 @@ npm install -g @freeticket/cli@latest && ft whoami > the CLI is installed globally. Pinning `@latest` matters: a user on an older > version (e.g. before the device-flow login) breaks otherwise. > -> This skill documents **`ft` ≥ 0.7.0**. A globally-installed `ft` that's behind -> prints an `⚠ Update available` line on *stderr*; when you see it, tell the user -> to run `npm i -g @freeticket/cli@latest`. `npx …@latest` always runs the newest. +> This skill documents **`ft` ≥ 0.9.0** (skill revision 2026-08-03). A +> globally-installed `ft` that's behind prints an `⚠ Update available` line on +> *stderr*; when you see it, tell the user to run +> `npm i -g @freeticket/cli@latest`. `npx …@latest` always runs the newest. +> +> **If the skill is the stale one** — `ft --help` lists a command this file does +> not, or a documented flag is rejected — trust the CLI, not this file, and +> update the installed copy: +> +> ```bash +> npx skills add AppFreeticket/agent-skills # reinstalls from source +> ``` +> +> Then say so, so the drift gets fixed upstream instead of silently repeating. `ft login` uses the OAuth 2.0 Device Authorization Grant: it shows a short code and a URL, opens your browser, and once you approve it stores the session in @@ -97,6 +108,9 @@ Config lives in `~/.freeticket/config.json` (mode `0600`). Precedence: | `ft staff list\|create\|set-role` | Workspace staff (`set-role --data '{"role":"…"}'`) | ADMIN | | `ft reports summary` | KPIs (`--period 7d\|30d\|90d\|1y`) | VIEWER | | `ft reports by-event\|timeseries\|inventory` | Revenue/tickets by event · over time (`--interval`) · capacity/availability | VIEWER | +| `ft reports financials` | Per-function P&L: gross, platform fee, facial, payment fee, 4x1000, net to settle (`--event`, `--past`) | ADMIN | +| `ft settlements list` | What FreeTicket pays the organizer: amount, status, event (`--event`, `--status`) | ADMIN | +| `ft api-keys create\|list\|revoke` | Headless service credentials — plaintext shown once on `create` (`--scope read\|write`) | VIEWER | | `ft reports reconciliation` | CFO: cross-check Mercado Pago ↔ sale ↔ Siigo invoice (`--from` `--to`, `--match`, `--provider`) | ADMIN | | `ft reports export buyers\|attendees\|subscribers\|reconciliation` | Export to CSV (buyers/attendees take `--event` `--event-date` `--from` `--to` `--status`) | ADMIN | @@ -106,12 +120,24 @@ Common flags on all: `--json` (raw output for `jq`), `--workspace ` **`--data `** (inline JSON or `@file.json`); `delete` asks for confirmation unless `--yes`. +**Confirmation and exit codes.** A refused confirmation exits **1**, and without +a TTY a destructive command fails immediately instead of prompting — so +`ft … delete && next-step` never runs `next-step` on an abort. In scripts pass +`--yes` deliberately, not as a habit. + +**Money questions go to `reports financials`, not to arithmetic over `sales`.** +It returns the authoritative breakdown FreeTicket already computed (the same +numbers as the Liquidaciones dashboard). `settlements list` then says what was +actually paid out. The proof PDF is panel-only — the API exposes `hasDocument` +and file names, not a download URL. + ### Superadmin (`ft admin …`) — cross-tenant, separate contract Superadmin commands hit a **different contract** (`/api/admin`) with **different auth**: a SUPER_ADMIN better-auth session, *not* an API key. Save it once with -`ft admin login` — it validates against `/api/admin/me` before storing (MVP — a -service token is coming, see free-admin#157): +`ft admin login` — it validates against `/api/admin/me` before storing. For CI, +mint a platform service token from that session with `ft admin tokens create` +instead of shipping the cookie around: ```bash # copy the `better-auth.session_token` cookie from an authenticated admin browser session @@ -132,6 +158,7 @@ ft admin me # confirm identity | `ft admin feature-flags list` · `set --data '{"scope":"…","enabled":true}'` | Feature flags | | `ft admin audit-log list` | Audit log (`--actor`, `--action`, `--from`, `--to`) | | `ft admin impersonate --data '{"targetUserId":"…"}'` · `impersonate-stop` | Impersonation | +| `ft admin tokens list\|create\|revoke` | Platform service tokens (PAT) for headless `ft admin` — plaintext shown once | Admin lists also take `--csv`. Writes take `--data `. diff --git a/skills/freeticket-cli/references/commands.md b/skills/freeticket-cli/references/commands.md index f57c455..312968a 100644 --- a/skills/freeticket-cli/references/commands.md +++ b/skills/freeticket-cli/references/commands.md @@ -31,7 +31,7 @@ minimum role; insufficient → `403`. | Command | Own flags | Role | |---|---|---| -| `ft events list` | `--limit` `--cursor` | VIEWER | +| `ft events list` | `--q ` (search) `--limit` `--cursor` | VIEWER | | `ft events get ` | — | VIEWER | | `ft ticket-types list` | `--event-date-id ` `--limit` `--cursor` | VIEWER | | `ft ticket-types get ` | — | VIEWER | @@ -45,7 +45,10 @@ minimum role; insufficient → `403`. | `ft webhooks list` | `--limit` `--cursor` | ADMIN | | `ft venues list` · `get ` | `--limit` `--cursor` | VIEWER | | `ft staff list` | `--limit` `--cursor` | ADMIN | +| `ft settlements list` | `--event` `--status SENT\|AWAITING_PAYMENT\|PAID` `--limit` `--cursor` | ADMIN | +| `ft api-keys list` | `--limit` `--cursor` | VIEWER | | `ft reports summary` | `--period 7d\|30d\|90d\|1y` | VIEWER | +| `ft reports financials` | `--event` `--past`, `--csv` \| `--json` | ADMIN | | `ft reports by-event` | `--from` `--to` `--status` | VIEWER | | `ft reports timeseries` | `--interval day\|week\|month` (req), `--from` `--to` `--event` | VIEWER | | `ft reports inventory` | `--event-id` `--event-date-id` `--from` `--to` `--include-drafts` `--group-by ticketType\|date\|event` | VIEWER | @@ -54,6 +57,18 @@ minimum role; insufficient → `403`. | `ft reports export subscribers` | `--csv` \| `--json` | ADMIN | | `ft reports export reconciliation` | `--from` `--to` (req), `--match` `--provider`, `--csv` \| `--json` | ADMIN | +`settlements` = what FreeTicket pays the organizer (authoritative, already +computed): `reference`, `status`, `amount`, `currency`, event/function, +`hasDocument`, `paymentProofs[].fileName`, `requestedAt`/`paidAt`. The proof PDF +is **not** downloadable through the API — it lives in the panel. + +`reports financials` = one row per function with the full breakdown: `gross`, +`platformFee`, `facial`, `paymentFee`, `gmf` (4x1000), `net`, plus the linked +`settlementStatus`/`settlementId`/`settlementAmount`. Use it instead of +recomputing margins from `sales list` + payment-provider data — same numbers as +the Liquidaciones dashboard. `--past` narrows to functions that already happened +(the settleable ones). + `reconciliation` `--match` / `match_status` enum: `OK` · `MISSING_INVOICE` (payment without invoice) · `MISSING_CUFE` (invoice without DIAN stamp) · `AMOUNT_MISMATCH` (MP amount ≠ sale) · `MISSING_PAYMENT`. @@ -94,6 +109,20 @@ matching endpoint in the OpenAPI contract — when unsure, check the spec, don't | `ft staff create` | `--data` (`{"email","role"}`) | ADMIN | | `ft staff set-role ` | `--data` (`{"role"}`) | ADMIN | +## Service credentials (`ft api-keys …`) + +Headless credential for CI / cron — no browser, no device flow. The plaintext +(`ft_live_…`) is shown **once**, in the `create` response. + +```bash +ft api-keys create --name "finance-sync" --scope read # scope: read (default) | write +ft api-keys list +ft api-keys revoke --yes +``` + +`read` allows GET/HEAD only; `write` enables mutations subject to the user's +role. Use it with `--key ft_live_…` or `FT_API_KEY`. + ## Admin (`ft admin …`) — separate contract `/api/admin` A **second contract**, not `/api/v1`. It is **cross-tenant** (not workspace-scoped) @@ -127,6 +156,9 @@ ft admin login --session # validates vs /api/admin | `ft admin audit-log list` | `--actor` `--action` `--from` `--to` `--limit` `--cursor` | SUPER_ADMIN | | `ft admin impersonate` | `--data` (`{"targetUserId","workspaceId?"}`) | SUPER_ADMIN | | `ft admin impersonate-stop` | — | SUPER_ADMIN | +| `ft admin tokens list` | — (no pagination) | SUPER_ADMIN | +| `ft admin tokens create` | `--data` (`{"name","expiresAt?"}`) — plaintext shown once | SUPER_ADMIN | +| `ft admin tokens revoke ` | `--yes` to skip confirm | SUPER_ADMIN | Without an admin session (`ft admin login` or `FT_ADMIN_SESSION`) the command fails fast with a clear message. A missing/expired session returns `401`; a diff --git a/skills/freeticket-mcp/SKILL.md b/skills/freeticket-mcp/SKILL.md new file mode 100644 index 0000000..fdebf77 --- /dev/null +++ b/skills/freeticket-mcp/SKILL.md @@ -0,0 +1,162 @@ +--- +name: freeticket-mcp +description: Connect to and operate the official FreeTicket MCP server (`@freeticket/mcp`) — the same B2B domain the `ft` CLI exposes, but as MCP tools for any MCP client. Covers the three credential layers (anonymous B2C `public_*`, workspace B2B, superadmin `admin_*`), local stdio setup, remote HTTP connectors on claude.ai via the embedded OAuth 2.1 server, the full tool inventory (87 tools), and the MCP Apps view that renders lists and reports as tables and KPI tiles inside the host. Use it when the user wants to add FreeTicket to Claude Desktop / claude.ai / Cursor, is already calling `freeticket` MCP tools and needs to know which one to pick, is debugging why tools are missing or a connector won't authorize, or is deciding between the MCP server and the `ft` CLI for a task. +--- + +# 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. + +**87 tools** across three contracts: B2B `/api/v1` (61), superadmin `/api/admin` +(20), public B2C `/api/public` (6). Full inventory with signatures: +[`references/tools.md`](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`](../freeticket-cli/SKILL.md)) | +|---|---| +| 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/mcp` **is not on npm yet** — `npx -y @freeticket/mcp` fails +> 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`). + +```jsonc +{ + "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:///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 (61) | +| Superadmin | `FT_ADMIN_SESSION` | `admin_*` (20) — 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. + +**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`): 25 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`, plus + `structuredContent` for 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`](../freeticket-cli/SKILL.md) — same contract, terminal-side +- Copy & event advice: [`freeticket-eventos`](../freeticket-eventos/SKILL.md) +- Server source: [`AppFreeticket/freeticket-mcp`](https://github.com/AppFreeticket/freeticket-mcp) diff --git a/skills/freeticket-mcp/references/tools.md b/skills/freeticket-mcp/references/tools.md new file mode 100644 index 0000000..4697165 --- /dev/null +++ b/skills/freeticket-mcp/references/tools.md @@ -0,0 +1,183 @@ +# Tool inventory — `@freeticket/mcp` v0.13.0 + +87 tools, one per contract operation. `?` marks an optional argument. +**▣** = renders through the MCP Apps view (table or KPI tiles in the host). +**⚠** = `destructiveHint`: confirm with the human and quote what will be +affected before calling. + +Every B2B list also accepts `workspace` (`"all"` or an array of ids) to +aggregate across workspaces; rows come back tagged with `workspaceId` / +`workspaceName`. Lists page with `limit` (1–100, default 20) and `cursor`. + +--- + +## Public B2C — `/api/public` (no credentials, always available) + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `public_events_list` | `q? city? from? to? page? pageSize? sort?` | Published catalogue. `sort`: `date_asc\|price_asc\|price_desc` | +| `public_events_get` | `slug` | Public event detail | +| `public_events_availability` | `slug` | Dates, ticket types, prices, live stock | +| `public_orders_create` | `buyerEmail buyerName buyerPhone? items` | Returns `checkoutUrl` (Mercado Pago). Idempotency key generated for you. General admission only | +| `public_orders_get` | `id` | `pending\|paid\|expired\|cancelled` + tickets once paid | +| `public_tickets_resend` | `code email?` | Resends the QR to the buyer's own address, rate-limited | + +The agent never touches payment data: hand the human the `checkoutUrl`. + +--- + +## B2B `/api/v1` — needs an API key or an `ft login` session + +### Session + +| Tool | Arguments | +|---|---| +| `whoami` | — — user + accessible workspaces | + +### Events and dates + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `events_list` | `limit? cursor? workspace?` | | +| `events_get` | `id` | | +| `events_create` | `name slug description? venueId? dates` | `dates`: at least one `{startsAt, endsAt?, timezone}` | +| `events_update` | `id name? description? venueId? coverImageUrl?` | | +| `events_publish` | `id` | Makes it visible in the public catalogue | +| ⚠ `events_delete` | `id` | | +| ▣ `event_dates_list` | `eventId` | | +| `event_dates_create` | `eventId startsAt timezone? label? endsAt? doorsOpenAt? venueId?` | `timezone` defaults to `America/Bogota` | +| `event_dates_update` | `eventId dateId startsAt? endsAt? doorsOpenAt? timezone? label? venueId?` | | +| ⚠ `event_dates_delete` | `eventId dateId` | | + +### Ticket types and tickets + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `ticket_types_list` | `eventDateId? limit? cursor? workspace?` | | +| `ticket_types_get` | `id` | | +| `ticket_types_create` | `eventDateId name description? price currency capacity maxPerOrder isVisible organizerAbsorbsFee` | | +| `ticket_types_update` | `id name? description? price? currency? capacity? maxPerOrder? isVisible? organizerAbsorbsFee?` | Price and stock changes | +| ⚠ `ticket_types_delete` | `id` | | +| `tickets_access` | `code` | Read-only door check — does **not** admit | +| `tickets_checkin` | `code` | Admits at the door, idempotent | +| `tickets_resend` | `code` | Re-issues the QR by email | + +### Sales + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `sales_list` | `status? channel? event? eventDate? reference? buyer? from? to? limit? cursor? workspace?` | | +| `sales_get` | `id` | | +| `sales_tickets` | `id` | Individual tickets/attendees of a sale | +| `sales_create` | `buyer items channel comp notes?` | Comps and programmatic orders | +| ⚠ `sales_cancel` | `id` | | +| ⚠ `sales_refund` | `id` | | + +### Memberships + +| Tool | Arguments | +|---|---| +| ▣ `plans_list` | `limit? cursor? workspace?` | +| `plans_get` | `id` | +| `plans_subscribers` | `id` | +| `plans_create` | `name description? price currency billingCycle benefitPresale benefitFreeTicket benefitDiscount benefitExclusiveContent benefitMerch isActive sortOrder?` | +| `plans_update` | `id` + any of the above | +| ⚠ `plans_delete` | `id` | +| ⚠ `subscriptions_cancel` | `id` | + +`billingCycle`: `MONTHLY\|QUARTERLY\|ANNUAL\|LIFETIME`. + +### Commercial + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `discounts_list` | `event? active? limit? cursor? workspace?` | | +| `discounts_create` | `code type value eventId? maxUses? startsAt? endsAt?` | | +| `discounts_update` | `id active? value? maxUses? startsAt? endsAt?` | | +| ⚠ `discounts_delete` | `id` | | +| ▣ `webhooks_list` | `limit? cursor? workspace?` | | +| `webhooks_create` | `url events secret?` | HMAC-signed delivery | +| ⚠ `webhooks_delete` | `id` | | +| ▣ `venues_list` | `limit? cursor? workspace?` | | +| `venues_get` | `id` | | +| `venues_create` | `name address city country capacity? latitude? longitude?` | | +| `venues_update` | `id name? address? city? country? capacity? latitude? longitude? portalVisible?` | | +| ⚠ `venues_delete` | `id` | | +| ▣ `staff_list` | `limit? cursor? workspace?` | | +| `staff_create` | `name email` | | +| `staff_update_role` | `id role` | | + +### Reports and money + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `reports_summary` | `period?` | `7d\|30d\|90d\|1y` | +| ▣ `reports_by_event` | `from? to? status?` | Revenue / tickets / availability per event | +| ▣ `reports_timeseries` | `interval from? to? event?` | `interval`: `day\|week\|month` | +| ▣ `reports_inventory` | `eventId? eventDateId? from? to? includeDrafts? groupBy?` | Capacity / sold / reserved / available. `groupBy`: `ticketType\|date\|event` | +| ▣ `reports_financials` | `event? past?` | Per-function P&L: gross, platform fee, facial value, gateway fee, 4x1000, net to settle. **These are the authoritative numbers** — do not recompute them from `sales_list` | +| ▣ `reconciliation` | `date_from date_to match_status? provider? page? page_size?` | CFO view: sale ↔ Mercado Pago ↔ Siigo invoice. `match_status`: `OK\|MISSING_INVOICE\|MISSING_CUFE\|AMOUNT_MISMATCH\|MISSING_PAYMENT` | +| ▣ `settlements_list` | `event? status? limit? cursor?` | What FreeTicket pays the organizer. `status`: `SENT\|AWAITING_PAYMENT\|PAID`. The receipt PDF is downloaded from the panel — the API carries `hasDocument` and filenames, not a URL | +| `reports_export_buyers` | `event? eventDate? from? to? status?` | One row per sale | +| `reports_export_attendees` | `event? eventDate? from? to? status?` | One row per ticket | +| `reports_export_subscribers` | — | | +| `reports_export_reconciliation` | `date_from date_to match_status? provider?` | | + +### Credentials (read-only by design) + +| Tool | Arguments | Notes | +|---|---|---| +| ▣ `api_keys_list` | `limit? cursor?` | Audit which service keys exist and when they were used. Never returns the secret. Minting and revoking are CLI-only (`ft api-keys`) | + +### Headless SSO (enterprise integrations) + +Both require an **enterprise service API key** *and* the buyer's session token +from the headless SSO exchange. With a normal workspace key the API returns 403. + +| Tool | Arguments | +|---|---| +| `customer_me` | `customerSession` | +| ▣ `customer_tickets` | `customerSession limit? cursor?` | + +--- + +## Superadmin `/api/admin` — needs `FT_ADMIN_SESSION` + +Cross-tenant. Everything here affects other people's workspaces. + +| Tool | Arguments | Notes | +|---|---|---| +| `admin_whoami` | — | | +| ▣ `admin_audit_log` | `action? from? to? limit? cursor?` | | +| ▣ `admin_tokens` | — | Platform PATs; minting/revoking is CLI-only (`ft admin tokens`) | +| ▣ `admin_workspaces` | `q? status? limit? cursor?` | | +| `admin_workspaces_get` | `id` | | +| `admin_workspaces_create` | `name slug type country email?` | `type`: `ARTIST\|VENUE\|ORGANIZER` | +| `admin_workspaces_update` | `id name? slug? type? isPublished?` | | +| ⚠ `admin_workspaces_suspend` | `id` | | +| `admin_workspaces_restore` | `id` | | +| ▣ `admin_users` | `q? role? limit? cursor?` | | +| `admin_users_get` | `id` | | +| `admin_users_update` | `id role? banned?` | `role`: `SUPER_ADMIN\|ADMIN\|STAFF\|VIEWER\|MINCULTURA` | +| ⚠ `admin_impersonate` | `targetUserId? workspaceId?` | Returns a token | +| `admin_impersonate_stop` | — | | +| ▣ `admin_platform_plans_list` | — | | +| `admin_platform_plans_get` | `id` | | +| `admin_platform_plans_create` | `name slug priceMonthly priceYearly priceBiannual? isActive maxEvents?` + feature flags | `maxEvents: null` = unlimited | +| `admin_platform_plans_update` | `id` + any of the above, `sortOrder?` | | +| ▣ `admin_feature_flags_list` | `key?` | | +| `admin_feature_flags_set` | `key scope scopeId? enabled` | | + +--- + +## Not exposed, on purpose + +| Operation | Why | +|---|---| +| `POST /auth/device/code`, `POST /auth/device/token` | Device-flow mechanics — driven by the server's own authorization server, not by an agent | +| `POST /api-keys`, `DELETE /api-keys/{id}` | Minting and revoking credentials belongs in the CLI, with a human at the keyboard | +| `POST /tokens`, `DELETE /tokens/{id}` | Same, for platform PATs | +| `POST /api/customer-auth/enterprise-exchange` | Mints third-party buyer sessions — server-to-server between free-admin and the integrator | + +A guard test in the server (`src/coverage.test.ts`) fails if the contract grows a +new operation and nobody gives it a tool, so this list stays honest.