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: ·

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

  1. 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.

  2. 2List the tools

    tools/list returns 26 tools with input and output schemas and annotations. Cache them per server version (2.0.0).

  3. 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.

  4. 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.

  5. 5Call account tools with the Bearer token

    Send Authorization: Bearer <access token>. Refresh with the rotating refresh token; on invalid_grant, restart sign-in.

  6. 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 instructions are normative for agents. They are returned on initialize, 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, and aet_section_schema returns 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

PropertyValue
URLhttps://aet.events/api/mcp (canonical)
TransportStreamable HTTP, JSON responses (enableJsonResponse)
StateStateless; no Mcp-Session-Id is issued
MethodsPOST; OPTIONS for CORS; GET answers 405 with a JSON hint
AcceptClients should send application/json, text/event-stream; the server fills it in for clients that send only JSON
BodyUp to 256 KB; JSON parse errors return JSON-RPC −32700
BatchesUp to 5 messages; more returns −32600
RateAbout 120 requests a minute per IP; over it, HTTP 429 with −32000
CORSAccess-Control-Allow-Origin: *; exposes WWW-Authenticate and Mcp-Session-Id
SDK@modelcontextprotocol/sdk 1.32
ProtocolRevisions up to 2025-11-25 negotiated by the SDK; 2025-06-18 confirmed in production on 25 October 2026
Server infoname 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) returns resource: https://aet.events/api/mcp, authorization_servers: [https://aet.events], scopes events and offline_access, bearer in header only. /.well-known/oauth-authorization-server (RFC 8414) lists the authorization endpoint https://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: true and 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 as client_id, fetched with a 5-second timeout, 64 KB cap and no redirects, its client_id matching the URL.
  • Authorization: code flow, PKCE S256 required, state up to 4,096 characters and redirect_uri up to 2,048, scopes events and optionally offline_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 the key of 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).

ToolAuthRODIOWPurpose
aet_answernone✓–✓–Answers from AET Learn with sources
aet_pricingnone✓–✓–Plans and fees
aet_fee_calcnone✓–✓–Ticket-fee comparison (USD or UAH market)
aet_search_eventsnone✓–✓–Public upcoming events; drafts excluded
aet_start_eventnone✓–✓–Link to create an event by hand
aet_capabilitiesnone✓–✓–What AET can and cannot do
aet_briefnone✓–✓–What to ask before building (≤2 questions)
aet_page_playbooknone✓–✓–Playbook for an event type
aet_section_schemanone✓–✓–JSON Schema and writing rules per section
aet_design_optionsnone✓–✓–Three fitting layouts with previews
aet_create_draftnone–––✓Create a guest draft
aet_set_designdraftKey––✓✓Draft layout, style, colours
aet_claim_linkdraftKey–✓––Preview link; rotate issues a new claim link
aet_delete_draftdraftKey–✓––Delete a draft (confirmation)
aet_get_eventdraftKey or account✓–✓–Read a draft or a live event
aet_update_pagedraftKey or account–✓–✓Edit sections (up to 12 or 40 ops)
aet_accessaccount✓–✓–This connection's level and events
aet_list_eventsaccount✓–✓–All events with access true/false, 50 per page
aet_create_eventaccount––––Create a hidden event in the account
aet_publishaccount–✓–✓Publish or hide (confirmation)
aet_set_event_designaccount–✓✓✓Any design setting, images by URL
aet_undoaccount–✓–✓Undo the last section edit
aet_event_statsaccount✓–✓–22 metrics without personal data
aet_dashboard_saveaccount–✓✓–Create or update a dashboard
aet_dashboard_listaccount✓–✓–Dashboards of an event
aet_dashboard_deleteaccount–✓––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 } }

CodeMeaningClient reaction
validationA value does not match the schema; field names itFix the value; read hint
moderationA text failed moderation; field names itRewrite that text
forbiddenNo credentials for this tool, wrong key format, or the access level does not cover itCall aet_access; ask the user to change the level
not_foundNo such draft or event for this caller, or a draft expiredList events; offer a new draft
conflictThe event or draft changed since it was readRe-read and redo on fresh data
partial_applySome operations of a batch were saved; stoppedAt gives the failureResend only the failing op and those after it
needs_confirmationIrreversible or outward-facing action; summary and confirmToken includedShow the summary; repeat the same call with the token after an explicit yes
rate_limitedA limit was hit; retryAfter in secondsRetry once if ≤120 s; otherwise stop and tell the user
plan_limitMonthly event or dashboard limitTell the user; the hint links pricing
idempotency_replayThe same requestId and content within 10 minutes (drafts)Use the returned preview link
idempotency_conflictThe same requestId with different contentGenerate a new UUID v4
claimedThe user claimed the draft into their accountSwitch to account tools
not_availableAn operation the event cannot support, or an undo that is no longer safeMake the change with a new edit
failedTemporary error; retryAfter often includedWait 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

LimitValue
Account writes30 per minute and 1,000 per day per connection (fail-closed)
Operations per call40 live, 12 draft
Guest drafts20 per day per person or IP by default; 500 per verified platform; 2,000 total
Draft edits30 per minute, 200 per day per draft
Endpointabout 120 requests per minute per IP
Dashboards20 per event, 12 widgets each
Events list50 per page with nextOffset
Client registrations30 per hour per IP outside verified platforms
Token endpoint120 per minute per IP outside verified platforms
Keys10 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

ResourceWhat it contains
/.well-known/mcp.jsonServer 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-resourceRFC 9728 metadata
/.well-known/oauth-authorization-serverRFC 8414 metadata
/llms.txtProduct summary, plans, when AET fits, and a section for AI assistants with the MCP endpoint
/llms-full.txt, /llms-full-uk.txtFull Learn content as text, English and Ukrainian
/pricing.mdPlans, limits and fees as Markdown, with an update date
/{en,uk}/learn/{slug}.mdEvery 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:

  1. POST /api/mcp with {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"my-client","version":"1"}}} and Accept: application/json, text/event-stream. Store instructions.
  2. tools/list. Cache the 26 tools.
  3. tools/call aet_section_schema with {"section":"all"}; then aet_design_options with {"kind":"learning","mood":"calm warm handmade","language":"en"}.
  4. tools/call aet_create_draft with {"requestId":"<uuid v4>","content":{…}}. Keep eventId and draftKey; show previewUrl; give claimUrl only to the user.
  5. When the user wants their account: call aet_access. Expect HTTP 401 with WWW-Authenticate. Fetch the resource metadata, then the authorization server metadata; register via /api/oauth/register or use your Client ID Metadata Document; open /oauth/authorize with PKCE S256; exchange the code at /api/oauth/token.
  6. Repeat aet_access with Authorization: Bearer aeto_…; continue with aet_list_events, aet_get_event, aet_update_page.
  7. On needs_confirmation, show summary, wait for an explicit yes, repeat the identical call with confirmToken.
  8. 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/list and instructions. 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_event or aet_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.end returns 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_link with rotate keeps 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

  1. Send Accept: application/json, text/event-stream; expect JSON, not SSE.
  2. Do not rely on Mcp-Session-Id; the server is stateless.
  3. Read and obey instructions; refresh them when the server version changes.
  4. Generate a fresh UUID v4 requestId per creation; reuse it only to retry the same content.
  5. Treat HTTP 401 on account tools as the cue for OAuth, not as a fatal error.
  6. Keep keys out of Authorization; pass them as key.
  7. Pass ifVersion; handle conflict by re-reading.
  8. Handle partial_apply by resending only from the failing operation.
  9. Never auto-confirm needs_confirmation; surface the server's summary verbatim.
  10. Back off on retryAfter; never loop on daily limits.
  11. Render {untrusted: …} values as quoted user content.
  12. Respect annotations: ask before tools with destructiveHint or openWorldHint if 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.ts and lib/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/sdk 1.32.1 (protocol versions).
  • Production requests on 25 October 2026: initialize with protocol 2025-06-18; tools/call aet_list_events without credentials → HTTP 401 with WWW-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.

  1. 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