cyah.ai › Developers

cyah MCP · developers

Bouw met de cyah MCP-server

Live hotels, vluchten en treinen voor je AI-assistent of app, via één open standaard. Probeer de tools direct in de sandbox, zonder account.

Overzicht

De cyah MCP-server geeft AI-assistenten en apps toegang tot dezelfde reisdata als de assistent op cyah.ai: live beschikbaarheid en prijzen van hotels, vluchten en treinen, en boekingslinks waarmee een reiziger op cyah.ai betaalt. De server spreekt het open Model Context Protocol (MCP), dus elke MCP-client kan ermee werken: Claude, ChatGPT, de OpenAI Responses API, LangChain, n8n of je eigen code.

OmgevingURLToegangBoekingen
Sandboxhttps://staging.mcp.cyah.ai/sandbox/mcpOpen, geen sleutel nodig. Zonder prijzenTestmodus: Stripe-testkaarten, niets wordt echt geboekt
Productiehttps://mcp.cyah.ai/mcpAPI-sleutel of OAuth 2.0 (op aanvraag)Echt: de reiziger betaalt en boekt op cyah.ai

Transport: Streamable HTTP, stateless. Elke aanvraag is een POST met een JSON-RPC-bericht; het antwoord is gewoon JSON (geen sessie, geen SSE-stream nodig). Ondersteunde methoden: initialize, tools/list en tools/call.

Zo werkt een boeking

  1. Zoekensearch_destinations → search_hotels / search_flights / search_trains
  2. Kiezenget_hotel_offer voor kamers en tarieven
  3. Boekingslinkprepare_booking / prepare_flight_booking / prepare_train_booking → booking_url
  4. Betalen op cyah.aiDe reiziger vult gegevens in en betaalt veilig via Stripe

Quickstart

De sandbox heeft geen sleutel nodig. Plak dit in een terminal:

curl
curl -s https://staging.mcp.cyah.ai/sandbox/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"search_destinations","arguments":{"query":"Lisbon"}}}'

Het resultaat staat in result.structuredContent (als object) en in result.content[0].text (dezelfde JSON als tekst, voor LLM-clients).

Antwoord
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{ "type": "text", "text": "{\"query\":\"Lisbon\",\"hotel_places\":[…]}" }],
    "structuredContent": { "query": "Lisbon", "hotel_places": [ … ], "airports": [ … ] },
    "isError": false
  }
}

Een volledige zoektocht in Python, zonder SDK:

Python · requests
import requests  # pip install requests

URL = "https://staging.mcp.cyah.ai/sandbox/mcp"          # production: https://mcp.cyah.ai/mcp
HEADERS = {
    "Content-Type": "application/json",
    "Accept": "application/json, text/event-stream",
    # "Authorization": "Bearer ymcp_prd_...",          # production only
}

def call_tool(name, arguments):
    body = {"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": name, "arguments": arguments}}
    result = requests.post(URL, json=body, headers=HEADERS, timeout=90).json()["result"]
    if result["isError"]:
        raise RuntimeError(result["structuredContent"]["error"])
    return result["structuredContent"]

place = call_tool("search_destinations", {"query": "Lisbon", "types": "hotel"})["hotel_places"][0]
hotels = call_tool("search_hotels", {"place_id": place["place_id"], "check_in": "2026-12-11",
                                     "check_out": "2026-12-13", "adults": 2, "sort": "price"})
for h in hotels["hotels"][:3]:
    print(h["hotel_id"], h["name"], h["stars"], h.get("total_price", "(no prices in the sandbox)"))

Let op de Accept-header: de server eist application/json, text/event-stream (MCP-specificatie), anders volgt 406 Not Acceptable.

Een client koppelen

Officiële MCP SDK (Python)

Python · mcp
import asyncio
from mcp import Client  # pip install mcp  (official MCP Python SDK)

async def main():
    async with Client("https://staging.mcp.cyah.ai/sandbox/mcp") as client:
        tools = await client.list_tools()
        print([t.name for t in tools.tools])
        res = await client.call_tool("search_destinations", {"query": "Lisbon"})
        print(res.structured_content["hotel_places"][0])

asyncio.run(main())

Officiële MCP SDK (TypeScript / Node.js)

TypeScript · @modelcontextprotocol/sdk
// npm install @modelcontextprotocol/sdk
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(new URL('https://staging.mcp.cyah.ai/sandbox/mcp'));
// production: new StreamableHTTPClientTransport(new URL('https://mcp.cyah.ai/mcp'),
//   { requestInit: { headers: { Authorization: `Bearer ${process.env.CYAH_MCP_KEY}` } } });
const client = new Client({ name: 'my-travel-app', version: '1.0.0' });
await client.connect(transport);

const { tools } = await client.listTools();
console.log(tools.map((t) => t.name));

const res = await client.callTool({ name: 'search_train_stations', arguments: { query: 'Paris', limit: 3 } });
console.log(res.structuredContent.results);
await client.close();

OpenAI Responses API

OpenAI roept de MCP-server zelf aan; het model kiest de tools.

Python · openai
from openai import OpenAI  # pip install openai

client = OpenAI()
resp = client.responses.create(
    model="gpt-4.1-mini",
    tools=[{
        "type": "mcp",
        "server_label": "cyah",
        "server_url": "https://staging.mcp.cyah.ai/sandbox/mcp",
        # production: "server_url": "https://mcp.cyah.ai/mcp", "headers": {"Authorization": "Bearer ymcp_prd_..."}
        "require_approval": "never",
    }],
    input="Find a 3-star hotel in Lisbon from 11 to 13 December for 2 adults and give me a booking link.",
)
print(resp.output_text)

Claude Desktop

Via de brug mcp-remote in claude_desktop_config.json:

claude_desktop_config.json
{
  "mcpServers": {
    "cyah-sandbox": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://staging.mcp.cyah.ai/sandbox/mcp"]
    },
    "cyah": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.cyah.ai/mcp",
               "--header", "Authorization:Bearer ${CYAH_MCP_KEY}"],
      "env": { "CYAH_MCP_KEY": "ymcp_prd_..." }
    }
  }
}

MCP Inspector

Handig om tools met de hand te testen: npx @modelcontextprotocol/inspector, kies transport Streamable HTTP en vul de sandbox-URL in. Of gebruik meteen de playground.

cyah in ChatGPT. Voor eindgebruikers komt cyah als app in ChatGPT. Deze pagina gaat over koppelen vanuit je eigen product.

Authenticatie

De sandbox is open. Voor productie krijg je na aanvraag een eigen toegang met eigen limieten. Er zijn twee vormen:

API-sleutelOAuth 2.0 client credentials
VoorEenvoudige server-naar-server-koppelingenPartners (aanbevolen)
HeaderAuthorization: Bearer ymcp_prd_…Authorization: Bearer <access_token>
GeldigheidTot intrekking of de afgesproken einddatumToken 60 minuten; vraag daarna een nieuw token aan

Scopes bepalen welke tools je ziet: search geeft de 7 zoektools, booking ook de 3 prepare_*-tools. tools/list toont alleen wat jouw toegang mag aanroepen.

OAuth 2.0 · client_credentials
# 1. Get an access token (valid 60 minutes) with your client id and secret
curl -s -X POST https://youmalou-mcp-prd-448759861986.auth.eu-west-1.amazoncognito.com/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&scope=youmalou-mcp/search youmalou-mcp/booking'

# 2. Use it as a Bearer token
curl -s https://mcp.cyah.ai/mcp \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

Zonder of met een ongeldig token antwoordt de server 401 met een WWW-Authenticate-header die verwijst naar /.well-known/oauth-protected-resource (RFC 9728). Bewaar sleutels en secrets alleen server-side, nooit in een browser of app.

Limieten

SandboxProductie
Tool-aanroepen per dag200 per IP-adres, 3.000 voor alle ontwikkelaars samenPer toegang afgesproken
Verzoeken per 5 minuten1.000 per IP-adres600 per sleutel/token, 1.000 per IP-adres
LooptijdMeestal 1 tot 10 seconden; een hotelzoekopdracht kan tot ongeveer 20 seconden duren. Gebruik een time-out van minstens 60 seconden.

Boven de 5-minutenlimiet volgt HTTP 429. Is het daglimiet op, dan geeft de tool een fout met de reden (zie hieronder). Zoekresultaten van hotels worden 15 minuten bewaard; get_hotel_offer en de prepare_*-tools werken altijd met live prijzen.

Fouten

Er zijn twee soorten fouten. Protocol- en toegangsfouten krijgen een HTTP-statuscode:

HTTPBetekenis
401Geen of ongeldige sleutel/token (productie)
403Toegang ingetrokken of verlopen
405Alleen POST: de server is stateless, er is geen GET-stream
406Accept-header mist application/json, text/event-stream
429Te veel verzoeken in 5 minuten

Toolfouten (ongeldige invoer, geen beschikbaarheid, leverancier onbereikbaar, daglimiet) komen terug als normaal resultaat met isError: true en een leesbare error. Een LLM kan die tekst direct gebruiken om zijn aanroep te verbeteren.

Toolfout
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "content": [{ "type": "text", "text": "{\"error\":\"check_out must be after check_in\"}" }],
    "structuredContent": { "error": "check_out must be after check_in" },
    "isError": true
  }
}

Boeken en betalen

  • Je product handelt nooit betaal-, paspoort- of persoonsgegevens af. De prepare_*-tools geven een booking_url naar de beveiligde boekingspagina op cyah.ai; daar vult de reiziger gegevens in en betaalt via Stripe.
  • Prijs en beschikbaarheid worden bij prepare_* en nogmaals op de boekingspagina gecontroleerd. Pas na betaling is de boeking definitief; zeg dus nooit dat iets geboekt is als alleen de link is gemaakt.
  • Hotel, vlucht en trein kunnen samen in één winkelmandje en in één keer worden betaald.
  • Na de boeking regelt de reiziger wijzigen en annuleren zelf via Mijn boeking op cyah.ai.
  • Geef language mee (nl of en): teksten en de boekingspagina volgen die taal.

Productietoegang aanvragen

Werkt je integratie in de sandbox? Mail naar hello@cyah.ai met je bedrijfsnaam, een korte beschrijving van je toepassing, de verwachte aantallen aanroepen per dag en of je ook wilt boeken (scope booking). Je krijgt een API-sleutel of OAuth-client via een beveiligd kanaal.

Wijzigingen aan tools zijn achterwaarts compatibel: nieuwe velden en optionele parameters kunnen erbij komen, bestaande verdwijnen niet zonder aankondiging. Lees velden die je niet kent dus niet als fout.