# For developers

How to build on the Dyme MCP server when you are not one of the assistants it already knows.

## What you are connecting to

| | |
|---|---|
| Endpoint | `https://mcp.dyme.earth/api/mcp` |
| Transport | MCP Streamable HTTP: JSON-RPC over POST, answered as JSON or as an SSE stream according to the request's `Accept`. No GET stream (a GET answers 405) |
| Protocol versions | 2025-11-25 back to 2024-11-05 |
| Auth | OAuth 2.1, PKCE, dynamic client registration. Every request needs a token. See [Sign-in and authentication](https://dyme.earth/ai/docs/authentication) |
| Tools | 46, listed in [the tool reference](https://dyme.earth/ai/docs/tools) and `https://mcp.dyme.earth/docs/tools.json` |
| Cards | MCP Apps (`io.modelcontextprotocol/ui`). See [Result cards](https://dyme.earth/ai/docs/cards) |
| Health | `GET https://mcp.dyme.earth/health` returns `{ ok, version, time }`, no sign-in |
| Version | `serverInfo.version` at initialize, `version` and `fingerprint` in `tools.json`. See [Versions and changes](https://dyme.earth/ai/docs/changelog) |

## Getting your callback address approved

Dyme's sign-in returns only to callback addresses it has approved, so a new client can register but cannot finish sign-in until its address is added. This is deliberate: with open registration, the allowlist is what stops a stranger's client from collecting a signed-in traveler's code.

Email hello@dyme.earth with:

1. Your product's name and website.
2. The exact `redirect_uri` your client sends, `https://` only, no query string.
3. The user agent or `clientInfo.name` your client sends, so Dyme can recognize it.
4. Whether your client draws MCP Apps cards.

Dyme adds the address and tells you. Once added, your client goes through the same flow as Claude: register, authorize, token.

## Testing against the live server

There is no sandbox. Searches return live inventory and live prices, and the Book button opens a real checkout on travel.dyme.earth. Nothing is booked until a traveler pays there, so stopping at checkout is safe. Requests to the Dyme team (price match, call-back, change, cancellation) send a real email; tell hello@dyme.earth which account you test with so the team knows.

## Calling tools from your own code

A tool call is an ordinary MCP `tools/call`. Each result has:

- `content[0].text`: what the assistant's model reads, with a short hint on how to present it.
- `structuredContent`: the same facts as JSON, for your own code or UI. Every tool declares an `outputSchema`.
- `_meta.ui.resourceUri` on the tool (not the result) when the result has a card.

Follow-up calls (`refine_hotels`, `get_hotel_rates`, `compare_prices`, `review_booking`) take the `trip_id` a search returned. It is encrypted and tied to the signed-in traveler; a `trip_id` from another account is treated as unknown.

An argument the tool does not list is refused with `isError: true` and a sentence naming it, rather than ignored. A refine given a search-only field (dates, party, cabin) is refused with the name of the search tool to call instead.

## Conventions

- Airports and airlines are IATA codes. `resolve_airport` finds a code; an airline name in `airlines` is refused with the code it most likely means.
- Dates are `YYYY-MM-DD`. Currency is an ISO code; send the one the traveler thinks in.
- Prices are Dyme's figures. Do not add, convert or compare them in your own code and present the result as Dyme's.
- `personal: true` declares a trip personal for a company traveler. Absent means the company's rules apply. There is no `personal: false`.

## Ask

hello@dyme.earth. Include the `trip_id` or the time of the call when reporting a problem; logs hold no email addresses, so the id is how a call is found.

HTML version: https://dyme.earth/ai/docs/developers
