curl (streamable-http transport)
Step 1: initialize and capture session id from the mcp-session-id response header
bash
curl -i -X POST https://api.twinkleai.tw/mcp/ \ -H "Authorization: Bearer sk-..." \ -H "Accept: application/json, text/event-stream" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'
Step 2: tools/call
bash
curl -X POST https://api.twinkleai.tw/mcp/ \ -H "Authorization: Bearer sk-..." \ -H "Accept: application/json, text/event-stream" \ -H "Content-Type: application/json" \ -H "mcp-session-id: <from-step-1>" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"tw_list_domains","arguments":{}}}'
Response uses SSE-style framing — find the last line starting with data: and parse the JSON.
Python (fastmcp)
python
import asyncio from fastmcp.client import Client from fastmcp.client.auth import BearerAuth async def main(): async with Client( "https://api.twinkleai.tw/mcp/", auth=BearerAuth("sk-..."), ) as client: tools = await client.list_tools() print([t.name for t in tools]) r = await client.call_tool( "tw_search_datasets", {"query": "AQI", "domain": "environment", "limit": 5}, ) # response 在 r.content[0].text — JSON string import json print(json.loads(r.content[0].text)) asyncio.run(main())
Tools return CallToolResult.content[0].text as a JSON string. License, citation and other metadata pass through in the _meta field.
Tools reference
| Tool | Arguments | Returns (brief) |
|---|---|---|
| tw_list_domains | — | [{key, name_zh, scope, typical_questions}] |
| tw_search_datasets | {query, domain?, limit?} | [{dataset_id, title, score, license}] |
| tw_get_dataset | {dataset_id} | {schema, columns, license, source_url} |
| tw_query_rows | {dataset_id, where?, limit?} | [row, ...] |
| tw_materialize_dataset | {dataset_id, format?} | 全表 CSV / JSON |
Full schemas via tools/ — auto-generated as JSON Schema.
Authentication
- Bearer token — header
Authorization: Bearer sk-.... A Twinkle-Hub-issued virtual key, one per user. Get yours from /dashboard. - No OAuth flow — no PKCE / refresh tokens needed; just a long-lived bearer.
- Rate limit — gated by a rolling credit allowance per plan, not a USD budget or per-key RPM/TPM. The numbers for each plan are on the pricing page.
Pricing
Free allowance, rate limits and how the plans differ are all on the pricing page. When the allowance runs out, the gateway rejects calls until it refills, then resumes on its own — nothing to reset.