Resources
The two event resources an assistant can attach as context without spending a tool call, and why they do not appear in resources/list
Resources
Two resources, both about one event: the event itself, and its sales figures. They let an assistant attach an event to a conversation as context rather than calling a tool for it.
swarmtix://events/{idOrSlug}
swarmtix://events/{idOrSlug}/analyticsThey are templated, so they are listed by resources/templates/list — not resources/list. A client that only calls resources/list gets an empty array back and concludes this server has no resources. It is not broken and you are not missing a scope: there are no parameterless resources here, so the parameterless list is genuinely empty. Everything is under the template list.
This is the single most common confusion about this surface. If you are hand-rolling a client, call resources/templates/list.
Why a resource rather than a tool
A tool call is a turn. A resource is context you hand the model up front — "here is the event we are talking about" — so a conversation about one event does not spend its first call fetching it.
That is the only difference. A resource reads the same rows the equivalent tool reads, costs the same scope, and goes through the same ownership check. It is not a cheaper way in, and it was deliberately not built as one: a resource that cost less than its tool would be a way around the consent screen.
Each resource returns exactly the JSON its equivalent tool returns, because both are built by the same code. There is one answer on this server to "what is an event".
The two resources
| URI template | What it carries | Scope | Same as |
|---|---|---|---|
swarmtix://events/{idOrSlug} | One event: schedule, location, host details, ticket types, currency and publication status | events:read | swarmtix_events_get |
swarmtix://events/{idOrSlug}/analytics | One event's sales and traffic figures: revenue, tickets sold against capacity, complimentary and door sales, refunds, unique visits and the daily series behind them | analytics:read | swarmtix_analytics_event |
{idOrSlug} is the event's id (a GUID) or its URL slug, the same pair every tool accepts. swarmtix://events/spring-summit-2026 works as well as a GUID.
Both return application/json, and the URI in the response is echoed back exactly as you asked for it — attach swarmtix://events/spring-summit-2026 and the contents come back labelled with the slug, not rewritten to the id.
The analytics resource covers every date of the event. There is nowhere in the URI to put one scheduled date without inventing a second template. If you want one date, use swarmtix_analytics_event with its eventDate argument.
Mcp-Name is mandatory on a read
Under the 2026-07-28 revision, any request addressing a primitive has to name that primitive in the Mcp-Name header. For a resource the primitive is named by its URI, so the URI goes in the header as well as in the body.
Leave it out and the request fails before the URI is looked at:
Missing required Mcp-Name header.That is not a scope problem, an ownership problem or a typo in the URI — none of those have been reached yet. It catches anyone hand-rolling a client, because it is the one header a tools-only client never had to send.
curl -X POST https://swarmtix.com/mcp/v1 \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: resources/read" \
-H "Mcp-Name: swarmtix://events/spring-summit-2026" \
-d '{
"jsonrpc":"2.0",
"id":3,
"method":"resources/read",
"params":{"uri":"swarmtix://events/spring-summit-2026"}
}'Listing the templates addresses no primitive, so it needs no Mcp-Name:
curl -X POST https://swarmtix.com/mcp/v1 \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "MCP-Protocol-Version: 2026-07-28" \
-H "Mcp-Method: resources/templates/list" \
-d '{"jsonrpc":"2.0","id":1,"method":"resources/templates/list"}'A client speaking an older revision is not required to send the header. Most managed clients — Claude, ChatGPT, Claude Code, VS Code, Cursor — handle all of this for you; this section is for a raw HTTP client.
Scopes hide a resource, the same way they hide a tool
A resource whose scope you did not approve is absent from resources/templates/list, not present and failing when read. Approve analytics:read and not events:read and the template list carries the analytics resource alone.
Asking for it anyway is refused:
This connection is not authorized for events:read. Reconnect and grant it to use this resource.The two templates are told apart by their shape rather than by which was registered first, so the analytics URI is always read — and always authorized — as the analytics resource. analytics:read never quietly buys you events:read.
Another organisation's event is refused identically
Every read runs the same ownership gate the tools run: the event must belong to the organisation this connection was granted, checked before anything is read.
An event in another organisation and an event that does not exist return the same sentence, and it names nothing about either:
Event not found, or this connection does not have access to it.That is deliberate, for the reason set out on the tool reference. A distinguishable "you do not have access" would confirm the record exists, which is itself a fact about somebody else's data.
A refused read is a JSON-RPC error, not a result. Tool results carry an isError flag, so a failed tool call is still a result. A resource read has no such flag — it is contents or nothing — so every refusal above arrives as a JSON-RPC error with the sentence as its message. A client that only inspects results will see the read fail rather than see the explanation.
What the analytics resource does not carry
Totals only. No attendee names, no buyer names, no order previews — the same guarantee analytics:read makes everywhere else, and for the same reason: that scope's consent sentence promises figures and says nothing about reading anybody's name.
The record behind it does hold short previews of real attendees and orders. They are not in the projection, here or in the tool.
Names live behind orders:read and attendees:read, whose consent text says so. And no resource carries a ticket barcode or security code, under any scope — see Barcodes are never returned.
What is not here
There is no swarmtix://docs/{page} resource, and there is not going to be one.
It was in the original plan and was dropped on the way to shipping. The Help Center it would have served is already public and unauthenticated at /docs/mcp/*, so the resource would have added no reach — only a second copy of the same pages, listed on every connection. It would also have been the only resource on this server whose contents are not the organiser's own data, which leaves no honest answer to the question of which scope reading a marketing page should cost. And a {page} parameter resolving to a file on disk is a path-traversal surface bought for no benefit.
A shorter resource list that means one thing is worth more than a longer one.
Troubleshooting
resources/list comes back empty
Expected. Both resources are templated, and resources/list covers parameterless URIs only. Call resources/templates/list.
"Missing required Mcp-Name header."
Your client sent resources/read without naming the primitive. Add Mcp-Name carrying the same URI as the body, or speak an older protocol revision where the header is not required. See above.
"This connection cannot read swarmtix://invoices/42. List the available resources to see what it is authorized for."
No resource on this server answers to that URI. The two that exist are at the top of this page. The message reads the same way for a URI that could never exist and for one whose scope you were not granted — telling you a URI exists but is closed to you would be the same membership leak as a distinguishable not-found.
"This connection is not authorized for analytics:read. Reconnect and grant it to use this resource."
The URI resolved, but the connection does not hold the scope it costs. Reconnect and approve it. See Authentication and Scopes.
The sentence ends in resource rather than tool. Tools, resources and prompts each get their own ending, so a refusal always names the primitive you actually asked for — the leading phrase is the same across all three, which is what the troubleshooting sections key on.
"Event not found, or this connection does not have access to it."
The event does not exist, or it belongs to a different organisation than this connection covers. Both cases return this sentence on purpose — see above.
The template list is missing one of the two
You were not granted its scope. events:read for the event resource, analytics:read for the analytics one. A resource you cannot read is hidden rather than offered.
Your client shows no resources at all
Either it only calls resources/list, or neither scope was approved. Check resources/templates/list first, then the connection's scopes in Settings → Developers → Connections.