Series "AI agents in AET" · part 9 of 10 · October 2026
Building on the AnyEventTool MCP server: a developer reference — transport, OAuth, 26 tools, annotations, errors, idempotency and discovery
Updated: · Alex Ryk, founder of AnyEventTool
Short answer
AnyEventTool's MCP server is a public, stateless Streamable HTTP endpoint at https://aet.events/api/mcp. It accepts POST only and answers in JSON; there is no session id, and a request may batch up to 5 messages within 256 KB. It runs on the official TypeScript MCP SDK 1.32 and negotiates protocol revisions up to 2025-11-25. It lists 26 tools. Fifteen are read-only and eleven write, and every tool carries a title and all four MCP annotations. Sixteen tools work without signing in, including guest drafts. The ten account tools return HTTP 401 with a WWW-Authenticate header pointing at the protected-resource metadata, so MCP clients start OAuth 2.1 only when needed. Sign-in uses PKCE S256, public clients, Dynamic Client Registration or Client ID Metadata Documents, a 1-hour access token and a rotating 30-day refresh token. A pasted aetk_ key, passed as the key argument, works as an alternative. Tool results carry structured content plus the same JSON as text. Errors share one object, with code, message, hint, field and retryAfter, and the server's instructions tell agents how to react to each code. New drafts and events need a fresh UUID v4 requestId, which deduplicates retries for 10 minutes. Irreversible actions return needs_confirmation and a one-time token that only the user's yes should release. Discovery files: /.well-known/mcp.json, the OAuth metadata, llms.txt, pricing.md and a Markdown version of every Learn page.
Key facts
- Disclosure: published by AnyEventTool (AET). Facts come from AET's code on 25 October 2026 and from requests to the production endpoint made the same day.
- Endpoint: https://aet.events/api/mcp — Streamable HTTP, stateless (no Mcp-Session-Id), JSON responses, POST only; GET returns 405. Body up to 256 KB, batches up to 5 messages, about 120 requests a minute per IP.
- Server: official TypeScript SDK 1.32 (server name anyeventtool, version 2.0.0); protocol revisions negotiated up to 2025-11-25. An initialize with 2025-06-18 was answered with 2025-06-18 in production.
- 26 tools: 15 read-only, 11 write; 16 without sign-in, 10 account tools. Each declares title and readOnlyHint, destructiveHint, idempotentHint, openWorldHint.
- Lazy authentication: an account tool without credentials returns HTTP 401 with WWW-Authenticate: Bearer resource_metadata="https://aet.events/.well-known/oauth-protected-resource", scope="events".
- OAuth 2.1: PKCE S256 only, public clients, DCR at /api/oauth/register or Client ID Metadata Documents; scopes events and offline_access; access 1 h, refresh 30 days rotated.
- Errors: a common object {code, message, hint?, field?, retryAfter?} with isError: true; codes include validation, forbidden, not_found, conflict, partial_apply, needs_confirmation, rate_limited, plan_limit, moderation, idempotency_replay, idempotency_conflict, claimed, not_available and failed.
Step by step
- 1Initialize
POST a JSON-RPC initialize to https://aet.events/api/mcp with Accept: application/json, text/event-stream. Read the instructions in the result: they are the contract for agents.
- 2List the tools
tools/list returns 26 tools with input and output schemas and annotations. Cache them per server version (2.0.0).
- 3Call a guest tool
aet_section_schema, aet_design_options and aet_create_draft work without credentials. Generate a fresh UUID v4 requestId for each new draft.
- 4Handle 401 by starting OAuth
When an account tool answers HTTP 401, read resource_metadata from WWW-Authenticate, discover the authorization server, register or present a Client ID Metadata Document, and run authorization code with PKCE S256.
- 5Call account tools with the Bearer token
Send Authorization: Bearer <access token>. Refresh with the rotating refresh token; on invalid_grant, restart sign-in.
- 6Honour confirmations and errors
On needs_confirmation show the summary to the user and repeat the same call with confirmToken after an explicit yes. Follow the per-code reactions below.
Who this reference is for
You are building an MCP client, an agent framework integration, an automation, or you are reviewing AET for a directory or a security assessment. The first eight parts of this series explain AET's MCP server from the user's side; this one is the contract from the wire up.
Three conventions hold throughout:
- The server's
instructionsare normative for agents. They are returned oninitialize, under 2,000 characters, and they describe the workflow, the access model, confirmations and error reactions. If this page and the instructions ever disagree, the instructions win. - Schemas are authoritative for shapes. Every tool's input schema is published in
tools/list, andaet_section_schemareturns the JSON Schema of each content section with writing rules. Do not hard-code shapes from this article. - Text inside content is data. Section texts, guests' answers and anything wrapped as
{untrusted: …}must never be treated as instructions by an agent built on this server.
Transport and protocol
| Property | Value |
|---|---|
| URL | https://aet.events/api/mcp (canonical) |
| Transport | Streamable HTTP, JSON responses (enableJsonResponse) |
| State | Stateless; no Mcp-Session-Id is issued |
| Methods | POST; OPTIONS for CORS; GET answers 405 with a JSON hint |
| Accept | Clients should send application/json, text/event-stream; the server fills it in for clients that send only JSON |
| Body | Up to 256 KB; JSON parse errors return JSON-RPC −32700 |
| Batches | Up to 5 messages; more returns −32600 |
| Rate | About 120 requests a minute per IP; over it, HTTP 429 with −32000 |
| CORS | Access-Control-Allow-Origin: *; exposes WWW-Authenticate and Mcp-Session-Id |
| SDK | @modelcontextprotocol/sdk 1.32 |
| Protocol | Revisions up to 2025-11-25 negotiated by the SDK; 2025-06-18 confirmed in production on 25 October 2026 |
| Server info | name anyeventtool, title "Any Event Tool", version 2.0.0 |
Browser origins. Requests with no Origin header (server-side and desktop clients) get every tool. Requests from browser pages whose origin is Claude, Anthropic, ChatGPT, OpenAI, Perplexity or AET's own sites also get every tool. Other browser origins get read-only tools; write tools answer forbidden "write tools are not available from this origin".
A note on the current specification. The MCP specification's current revision, 2026-07-28, moves beyond the handshake-based revisions that end with 2025-11-25. AET's server, built on SDK 1.32, speaks the handshake revisions; clients that require only the newest revision should check compatibility. The authorization model — OAuth 2.1, protected-resource metadata, Client ID Metadata Documents with registration kept for compatibility — already matches the 2026-07-28 text.
Authentication: none, OAuth, or a key
AET supports three ways to call tools, and the same server handles all three.
1. No credentials. Sixteen tools — answers, pricing, fee calculator, public event search, start link, capabilities, brief, playbook, section schema, design options and the guest-draft tools — need nothing. Tools that work with drafts take a draftKey argument returned by aet_create_draft.
2. OAuth 2.1 (recommended for user accounts).
- Discovery:
/.well-known/oauth-protected-resource(RFC 9728) returnsresource: https://aet.events/api/mcp,authorization_servers: [https://aet.events], scopeseventsandoffline_access, bearer in header only./.well-known/oauth-authorization-server(RFC 8414) lists the authorization endpointhttps://aet.events/oauth/authorize, token endpoint/api/oauth/token, registration endpoint/api/oauth/register,code_challenge_methods_supported: [S256],token_endpoint_auth_methods_supported: [none],client_id_metadata_document_supported: trueand UI locales uk and en. - Registration: Dynamic Client Registration with
redirect_uris(https, loopback on any port, a known editor's app scheme or a reverse-domain scheme), public clients only; or a Client ID Metadata Document — an https URL asclient_id, fetched with a 5-second timeout, 64 KB cap and no redirects, itsclient_idmatching the URL. - Authorization: code flow, PKCE S256 required,
stateup to 4,096 characters andredirect_uriup to 2,048, scopeseventsand optionallyoffline_access. The user signs in to AET and chooses the access level on AET's consent screen. - Tokens: authorization codes 10 minutes, single use; access tokens (
aeto_…) 1 hour; refresh tokens (aetr_…) 30 days, rotated on every use; a reused refresh token revokes the token family; reusing a code revokes tokens issued for that grant. Errors follow RFC 6749 (invalid_grant,invalid_client,invalid_request,unsupported_grant_type). - Calls:
Authorization: Bearer aeto_…. The server substitutes the token as thekeyof account tools; you do not pass it as an argument.
3. A pasted key. Users can create keys (aetk_d_, aetk_s_, aetk_o_, aetk_a_ by access level) in AET and paste them into a chat. Pass the key as the key argument of account tools. Do not put it in Authorization: that header accepts only AET's OAuth access tokens, and anything else is treated as no credential.
Lazy authentication. An account tool called without a key or a valid Bearer token returns HTTP 401 with WWW-Authenticate: Bearer resource_metadata="https://aet.events/.well-known/oauth-protected-resource", scope="events" and a JSON-RPC error −32001. An expired or revoked token gets the same 401 with the message "The AET connection token is expired or revoked — reconnect AET." Tools also declare securitySchemes in _meta: noauth for guest tools, noauth plus oauth2 with scope events for account tools.
The 26 tools and their annotations
RO = readOnlyHint, D = destructiveHint, I = idempotentHint, OW = openWorldHint (true means the tool changes something the internet can see).
| Tool | Auth | RO | D | I | OW | Purpose |
|---|---|---|---|---|---|---|
aet_answer | none | ✓ | – | ✓ | – | Answers from AET Learn with sources |
aet_pricing | none | ✓ | – | ✓ | – | Plans and fees |
aet_fee_calc | none | ✓ | – | ✓ | – | Ticket-fee comparison (USD or UAH market) |
aet_search_events | none | ✓ | – | ✓ | – | Public upcoming events; drafts excluded |
aet_start_event | none | ✓ | – | ✓ | – | Link to create an event by hand |
aet_capabilities | none | ✓ | – | ✓ | – | What AET can and cannot do |
aet_brief | none | ✓ | – | ✓ | – | What to ask before building (≤2 questions) |
aet_page_playbook | none | ✓ | – | ✓ | – | Playbook for an event type |
aet_section_schema | none | ✓ | – | ✓ | – | JSON Schema and writing rules per section |
aet_design_options | none | ✓ | – | ✓ | – | Three fitting layouts with previews |
aet_create_draft | none | – | – | – | ✓ | Create a guest draft |
aet_set_design | draftKey | – | – | ✓ | ✓ | Draft layout, style, colours |
aet_claim_link | draftKey | – | ✓ | – | – | Preview link; rotate issues a new claim link |
aet_delete_draft | draftKey | – | ✓ | – | – | Delete a draft (confirmation) |
aet_get_event | draftKey or account | ✓ | – | ✓ | – | Read a draft or a live event |
aet_update_page | draftKey or account | – | ✓ | – | ✓ | Edit sections (up to 12 or 40 ops) |
aet_access | account | ✓ | – | ✓ | – | This connection's level and events |
aet_list_events | account | ✓ | – | ✓ | – | All events with access true/false, 50 per page |
aet_create_event | account | – | – | – | – | Create a hidden event in the account |
aet_publish | account | – | ✓ | – | ✓ | Publish or hide (confirmation) |
aet_set_event_design | account | – | ✓ | ✓ | ✓ | Any design setting, images by URL |
aet_undo | account | – | ✓ | – | ✓ | Undo the last section edit |
aet_event_stats | account | ✓ | – | ✓ | – | 22 metrics without personal data |
aet_dashboard_save | account | – | ✓ | ✓ | – | Create or update a dashboard |
aet_dashboard_list | account | ✓ | – | ✓ | – | Dashboards of an event |
aet_dashboard_delete | account | – | ✓ | – | – | Delete a dashboard (confirmation) |
The annotations were aligned with the ChatGPT directory's scanner on 20 October 2026: openWorldHint marks tools that change what visitors see; destructiveHint marks tools that can erase or overwrite data, including a claim-link rotation and a dashboard overwrite. Most tools also declare an outputSchema; the five public read tools that predate structured output return text.
Results and the error model
Success. Tools with an output schema return structuredContent and the same JSON serialised in content[0].text, so clients that read either get the same data.
Errors. A tool-level failure returns isError: true and an error object:
{ "error": { "code": "rate_limited", "message": "too many changes with this connection", "hint": "Limit: 30 changes per minute…", "retryAfter": 60 } }
| Code | Meaning | Client reaction |
|---|---|---|
validation | A value does not match the schema; field names it | Fix the value; read hint |
moderation | A text failed moderation; field names it | Rewrite that text |
forbidden | No credentials for this tool, wrong key format, or the access level does not cover it | Call aet_access; ask the user to change the level |
not_found | No such draft or event for this caller, or a draft expired | List events; offer a new draft |
conflict | The event or draft changed since it was read | Re-read and redo on fresh data |
partial_apply | Some operations of a batch were saved; stoppedAt gives the failure | Resend only the failing op and those after it |
needs_confirmation | Irreversible or outward-facing action; summary and confirmToken included | Show the summary; repeat the same call with the token after an explicit yes |
rate_limited | A limit was hit; retryAfter in seconds | Retry once if ≤120 s; otherwise stop and tell the user |
plan_limit | Monthly event or dashboard limit | Tell the user; the hint links pricing |
idempotency_replay | The same requestId and content within 10 minutes (drafts) | Use the returned preview link |
idempotency_conflict | The same requestId with different content | Generate a new UUID v4 |
claimed | The user claimed the draft into their account | Switch to account tools |
not_available | An operation the event cannot support, or an undo that is no longer safe | Make the change with a new edit |
failed | Temporary error; retryAfter often included | Wait and retry once |
Transport-level errors are JSON-RPC: −32700 parse error, −32600 batch too large, −32000 rate limited (HTTP 429), −32001 authentication required (HTTP 401), −32603 internal error.
Idempotency, versions and atomic batches
Creating. aet_create_draft and aet_create_event require a requestId that is a UUID v4. A retry within 10 minutes with the same id and the same content does not create a second object: for events, the answer is the existing event marked replay: true; for drafts, an idempotency_replay error carrying the preview link. The draft key is not repeated in a replay, so a client that lost it should create a new draft with a new id. Draft deduplication is scoped to the caller's address; event deduplication to the connection.
Versions. Live events carry rev; drafts carry version. Pass the last value you read as ifVersion to aet_update_page. If the object has changed, the answer is conflict and nothing is written. Without ifVersion, AET still writes only if the fields it touches are unchanged since its own read, retrying internally a few times.
Batches. aet_update_page takes 1–40 operations on live events and up to 12 on drafts. On live events each operation is atomic and the batch stops at the first failure, keeping earlier operations; moderation runs on all texts before anything is applied; confirmations are evaluated before writing; the landing page is validated after the batch. Operations are {section, op, id?, after?, value?} with op one of set, clear, addItem, updateItem, removeItem, move. New list items get ids on first write, returned in applied.
Confirmations. needs_confirmation returns confirmToken — an HMAC-SHA256 over the expiry, tool, event, a fingerprint of the exact operations and the connection — valid 10 minutes and usable once. Repeat the identical call with the token. The token is consumed only when the irreversible change is written, so retrying after a conflict with the same operations works.
Limits a client must respect
| Limit | Value |
|---|---|
| Account writes | 30 per minute and 1,000 per day per connection (fail-closed) |
| Operations per call | 40 live, 12 draft |
| Guest drafts | 20 per day per person or IP by default; 500 per verified platform; 2,000 total |
| Draft edits | 30 per minute, 200 per day per draft |
| Endpoint | about 120 requests per minute per IP |
| Dashboards | 20 per event, 12 widgets each |
| Events list | 50 per page with nextOffset |
| Client registrations | 30 per hour per IP outside verified platforms |
| Token endpoint | 120 per minute per IP outside verified platforms |
| Keys | 10 active, 20 created per day per account |
When a daily limit is reached, retryAfter counts down to midnight UTC. Build backoff on retryAfter, not on a fixed schedule, and never loop on a daily limit.
Machine-readable discovery
| Resource | What it contains |
|---|---|
/.well-known/mcp.json | Server card in the official MCP registry format: name com.anyeventtool/aet, title, description, version 2.0.0, remote streamable-http at the endpoint |
/.well-known/oauth-protected-resource | RFC 9728 metadata |
/.well-known/oauth-authorization-server | RFC 8414 metadata |
/llms.txt | Product summary, plans, when AET fits, and a section for AI assistants with the MCP endpoint |
/llms-full.txt, /llms-full-uk.txt | Full Learn content as text, English and Ukrainian |
/pricing.md | Plans, limits and fees as Markdown, with an update date |
/{en,uk}/learn/{slug}.md | Every Learn page, including this series, as Markdown with its canonical URL and update date |
The server is not yet published in the official MCP registry; the server card is ready for it. AET was submitted to Claude's connector directory on 19 October 2026 and to ChatGPT's app review on 20 October 2026; both were awaiting review on the date of this article.
A minimal client, step by step
In plain HTTP terms, the sequence an MCP client runs against AET:
POST /api/mcpwith{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-client","version":"1"}}}andAccept: application/json, text/event-stream. Storeinstructions.tools/list. Cache the 26 tools.tools/callaet_section_schemawith{"section":"all"}; thenaet_design_optionswith{"kind":"learning","mood":"calm warm handmade","language":"en"}.tools/callaet_create_draftwith{"requestId":"<uuid v4>","content":{…}}. KeepeventIdanddraftKey; showpreviewUrl; giveclaimUrlonly to the user.- When the user wants their account: call
aet_access. Expect HTTP 401 withWWW-Authenticate. Fetch the resource metadata, then the authorization server metadata; register via/api/oauth/registeror use your Client ID Metadata Document; open/oauth/authorizewith PKCE S256; exchange the code at/api/oauth/token. - Repeat
aet_accesswithAuthorization: Bearer aeto_…; continue withaet_list_events,aet_get_event,aet_update_page. - On
needs_confirmation, showsummary, wait for an explicit yes, repeat the identical call withconfirmToken. - Refresh the access token before it expires with
grant_type=refresh_token; store the new refresh token each time.
The official MCP SDKs do steps 1, 2 and 5 for you; Claude, ChatGPT, Claude Code, Cursor and Gemini CLI handle sign-in themselves (part 2).
Design decisions worth copying
Some choices in AET's server are useful beyond events:
- The platform does not generate content. The assistant writes; the server validates, moderates and lays out. The vendor's marginal cost per page is a moderation call and some writes, which is what makes guest drafts affordable (part 3).
- Lazy authentication. Most value is reachable without sign-in; the 401 appears only at the first account tool, which keeps onboarding to one URL.
- Server-written confirmations. The summary a person approves is produced by the server from the actual change, and the token is bound to the exact operations, so a client cannot paraphrase a dangerous action into a harmless-sounding one.
- Item-level operations with versions. Lists are edited by id with
ifVersion, so agents and humans can edit the same page without overwriting each other. - Untrusted marking in data, not just in prose. Guest-written values arrive wrapped, so a model sees the boundary in the structure, not only in a system prompt.
- One error object, one reaction per code. The instructions map each code to a single correct reaction, which turns error handling into a lookup rather than guesswork.
- Everything the client needs is in
tools/listandinstructions. No out-of-band documentation is required to build a correct agent.
Known gaps and roadmap signals
Stated honestly, as of 25 October 2026:
- No webhooks or event subscriptions; clients poll with
aet_get_eventoraet_event_stats. - No MCP prompts or resources are exposed; everything is a tool.
- No streaming progress; responses are single JSON objects.
- No end time for events;
datetime.endreturns a warning. - Images are accepted by URL only; there is no upload tool.
- Paid tickets, seat maps, quizzes, shops, communities and calendars are configured in AET by the organizer, not through tools.
- No server-side cross-event analytics; statistics are per event.
aet_claim_linkwithrotatekeeps the three most recent claim links valid rather than only the newest.- Not yet listed in the official MCP registry or in the Claude and ChatGPT directories (submissions pending).
Part 10 of this series covers what AET plans next and the first numbers from real use.
Checklist for client developers
- Send
Accept: application/json, text/event-stream; expect JSON, not SSE. - Do not rely on
Mcp-Session-Id; the server is stateless. - Read and obey
instructions; refresh them when the server version changes. - Generate a fresh UUID v4
requestIdper creation; reuse it only to retry the same content. - Treat HTTP 401 on account tools as the cue for OAuth, not as a fatal error.
- Keep keys out of
Authorization; pass them askey. - Pass
ifVersion; handleconflictby re-reading. - Handle
partial_applyby resending only from the failing operation. - Never auto-confirm
needs_confirmation; surface the server'ssummaryverbatim. - Back off on
retryAfter; never loop on daily limits. - Render
{untrusted: …}values as quoted user content. - Respect annotations: ask before tools with
destructiveHintoropenWorldHintif your client has a confirmation layer.
Sources (checked 25 October 2026)
- AnyEventTool code on 25 October 2026:
pages/api/mcp.ts(transport, instructions, tools, annotations, origins, lazy authentication),pages/api/oauth/metadata.ts,register.ts,authorize.ts,token.ts(authorization server),lib/aiops/access.ts(connections, redirect rules, tokens),lib/aiops/actor.tsandlib/aiops/limits.ts(limits),lib/aiops/draft.ts,account.ts,live.ts,sections.ts(idempotency, versions, batches, confirmations),public/.well-known/mcp.json(server card),node_modules/@modelcontextprotocol/sdk1.32.1 (protocol versions). - Production requests on 25 October 2026:
initializewith protocol 2025-06-18;tools/call aet_list_eventswithout credentials → HTTP 401 withWWW-Authenticate;/.well-known/oauth-protected-resource. - Model Context Protocol: specification versioning and authorization, checked 20 October 2026.
- MCP endpoint, server card, llms.txt, pricing.md.
Found a mismatch or a bug? Write to support@anyeventtool.com — we correct with the date.
This series: ten articles on AI agents in AnyEventTool
This is part 9 of 10. The last part's title may change.
- The event manager who never sleeps — the landscape, three zeros, guardrails (19 October) · 2. Connecting your assistant to AET — Claude, ChatGPT, Claude Code, Cursor, Gemini (20 October) · 3. Guest drafts in depth (22 October) · 4. Writing a page the agent is proud of (22 October) · 5. Design by agent (23 October) · 6. Running a live event by key (24 October) · 7. Trust by design (25 October) · 8. Numbers without names (25 October) · 9. For developers: building on the AET MCP server — this article (25 October) · 10. Playbooks for ten event types and the road ahead (25 October).
Questions and answers
What is AnyEventTool's MCP server URL and transport?
https://aet.events/api/mcp — Streamable HTTP with JSON responses, stateless (no session id), POST only. Requests may batch up to 5 messages within 256 KB.
Which MCP protocol versions does AET support?
The server runs on the official TypeScript SDK 1.32 and negotiates handshake revisions up to 2025-11-25; 2025-06-18 was confirmed in production on 25 October 2026.
How does authentication work on the AET MCP server?
Guest tools need none. Account tools use OAuth 2.1 with PKCE S256, public clients, Dynamic Client Registration or Client ID Metadata Documents, or a pasted aetk_ key passed as the key argument. Without credentials they return HTTP 401 with WWW-Authenticate pointing at the protected-resource metadata.
How many tools does the AET MCP server expose?
26: 15 read-only and 11 write. 16 work without sign-in (including guest drafts); 10 need the user's account. Every tool declares a title and all four MCP annotations.
How do I avoid creating duplicate drafts or events on retries?
Send a fresh UUID v4 requestId with each creation. A retry with the same id and content within 10 minutes returns the existing object instead of creating another.
How should a client handle needs_confirmation?
Show the server's summary to the user, wait for an explicit yes, and repeat the identical call with the returned confirmToken. The token is bound to the exact operations, valid 10 minutes and usable once.
Does the AET MCP server support webhooks or streaming?
Not as of 25 October 2026. Responses are single JSON objects, and clients poll with aet_get_event or aet_event_stats. There are no MCP prompts or resources; everything is a tool.
Is AET listed in the official MCP registry?
Not yet. The server card in registry format is published at /.well-known/mcp.json. Directory submissions to Claude (19 October 2026) and ChatGPT (20 October 2026) were awaiting review on 25 October 2026.
Try AET for your next event
Free: event page, registration, QR tickets and check-in. 0% platform fee on ticket sales.
Related answers
- Trust by design: how AnyEventTool keeps AI agents safe on your event pages
- AI agent playbooks for ten event types — and what comes next for AI agents on AnyEventTool
- How to connect Claude, ChatGPT, Claude Code, Cursor or Gemini to AnyEventTool — for people and their AI agents
- Running a live event with an AI agent: item-by-item edits, registration, materials, confirmations and undo in AnyEventTool
- Numbers without names: event statistics and dashboards an AI agent can read in AnyEventTool — without guests' personal data
- The event manager who never sleeps: what changes when your AI assistant can build, design and run an event page