Getting started
Using Ray with AI coding agents
Integrate Ray with Claude Code, Cursor or Codex using the MCP server, the TypeScript SDK, ray-skills, llms.txt, markdown docs and a copy-paste prompt.
Ray is designed to be wired in by AI coding agents such as Claude Code, Cursor and Codex. Everything an agent needs is machine-readable or callable: an MCP server that exposes the API as tools, a typed SDK, skills, these docs as markdown, and the OpenAPI contract.
Recommended setup
Use these in order. Each step is optional, but the earlier ones save the agent the most guessing.
- MCP server. Connect
https://ray-api.gege.mn/mcpto the agent. It can then look up your channels and their recipient schemas, create and test templates, send, check delivery and read the docs itself, instead of asking you for ids. In Claude Code:claude mcp add --transport http ray https://ray-api.gege.mn/mcp --header "Authorization: Bearer $RAY_API_KEY". - TypeScript SDK. Have the agent write application code against
@gege-mn/ray(npm install @gege-mn/ray) rather than hand-rolledfetchcalls. It comes with types, aRayErrorclass, anidempotencyKeyoption on sends and a webhook signature verifier, so the agent has less to get wrong. - ray-skills. Skills that teach an agent Ray's integration patterns and email template design. Install with
npx skills add gege-mn/ray-skills, or in Claude Code with/plugin marketplace add gege-mn/ray-skillsthen/plugin install ray@ray-skills. - Docs as markdown. https://ray.gege.mn/llms.txt indexes every page; append
.mdto any docs URL for that page alone. - OpenAPI 3.1 spec. The exact request and response schemas, for agents working in another language or generating their own client.
Machine-readable resources
| Resource | URL | Use it for |
|---|---|---|
| MCP server | https://ray-api.gege.mn/mcp |
Ray's API as MCP tools, authenticated with your API key. Setup for each client: MCP server. |
| Local MCP package | npx -y @gege-mn/ray-mcp |
The same tools as a stdio server, for clients that can't send a header to a remote server. Source: https://github.com/gege-mn/ray-mcp. |
| TypeScript SDK | npm install @gege-mn/ray |
Typed client for Node.js, Bun, Deno and edge runtimes. Docs: TypeScript SDK. Source: https://github.com/gege-mn/ray-node. |
| ray-skills | https://github.com/gege-mn/ray-skills | Agent skills for wiring Ray into a project and designing email templates. |
| llms.txt | https://ray.gege.mn/llms.txt | Index of all doc pages with one-line descriptions. |
| llms-full.txt | https://ray.gege.mn/llms-full.txt | All doc pages concatenated into one file. |
| Raw markdown pages | https://ray.gege.mn/docs/<page>.md |
Any single page as plain markdown, for example https://ray.gege.mn/docs/sending.md or https://ray.gege.mn/docs/channels/telegram.md. |
| OpenAPI 3.1 spec | https://ray-api.gege.mn/openapi.json | The exact request and response schemas, generated from the same schemas the API validates with. |
| API reference | https://ray-api.gege.mn/docs | Interactive reference (Scalar) for humans. |
| Channel discovery | GET https://ray-api.gege.mn/channels |
The workspace's channel configs with their channelConfigId, template kind and the JSON Schema of the recipient object. Also the list_channels MCP tool and ray.channels.list() in the SDK. |
The .md trick: every docs URL works with .md appended and returns the page without navigation or styling. When an agent needs one topic, give it that URL instead of the HTML page or the full bundle. The MCP read_docs tool returns the same markdown.
Set up access
- Create an API key in the dashboard (https://ray.gege.mn, API keys). An agent that creates templates or sends needs the
writescope. For exploring and debugging, a key with onlyreadis safer: it can't send anything. Consider dedicated keys for development so you can revoke them independently. - Put it in your environment, not in code:
# .env (keep out of version control)
RAY_API_KEY=ck_live_...
- Configure at least one channel in the dashboard under Channels. Channel credentials can't be created through the API, so the agent works with configs you have already set up and finds them with
GET /channels.
Let the agent verify access before writing code, with the whoami and list_channels MCP tools or with curl:
curl https://ray-api.gege.mn/me -H "Authorization: Bearer $RAY_API_KEY"
curl https://ray-api.gege.mn/channels -H "Authorization: Bearer $RAY_API_KEY"
Point your agent at the docs
- Claude Code: add the MCP server, install ray-skills, and add a line to your project's
CLAUDE.mdsuch asRay notification API: use the @gege-mn/ray SDK. Docs: https://ray.gege.mn/llms.txt (append .md to any docs URL for raw markdown). API spec: https://ray-api.gege.mn/openapi.json. - Cursor: add the MCP server, and add https://ray.gege.mn/llms-full.txt as a documentation source or reference the
.mdURL of the page you are working on in the chat. - Codex: add the MCP server and the same line to
AGENTS.md. - Other agents: see MCP server, or paste the prompt below.
Copy-paste prompt
Integrate the Ray notification API into this project.
Ray is a notification delivery API (not the Ray distributed computing framework).
- MCP server: https://ray-api.gege.mn/mcp (tools list_channels, send_notification, get_send_status, read_docs, ...). If it is connected, use it to inspect the workspace instead of guessing ids.
- TypeScript SDK: npm install @gege-mn/ray (docs: https://ray.gege.mn/docs/sdk.md). Use it in TypeScript/JavaScript projects instead of raw fetch.
- Docs index: https://ray.gege.mn/llms.txt
- Any docs page as markdown: append .md, e.g. https://ray.gege.mn/docs/sending.md
- OpenAPI spec (source of truth for request/response shapes): https://ray-api.gege.mn/openapi.json
- API base URL: https://ray-api.gege.mn
- Auth: header "Authorization: Bearer <key>", key read from the RAY_API_KEY environment variable. Never hardcode it or expose it to client-side code.
Requirements:
1. Find the channelConfigId, the channel's templateKind and the exact recipient JSON Schema first (list_channels tool, ray.channels.list(), or GET /channels). Do not guess recipient shapes.
2. Create a server-side helper that sends with ray.send(...) (or POST /send) and returns the sendId from the 202 response.
3. Pass an idempotency key derived from the business event (for example "order-shipped-<orderId>") on every send: the idempotencyKey option in the SDK, or the Idempotency-Key header over HTTP, so retries never double-send.
4. Without the SDK, retry only on 429 (honor Retry-After), 5xx and network errors, always with the same Idempotency-Key. Treat 400 and 404 as bugs and surface the error code and message (RayError in the SDK).
5. For reusable content, create a template (publish: true) and send with templateId; otherwise send inline content. Pass every required param. Try templates with a test send to my own address before real sends.
6. If the app shows in-app notifications, pass externalUserId and showInFeed: true (or feed) on sends, and proxy the feed (ray.notifications.list or GET /notifications?externalUserId=...) through the backend.
7. If handling delivery webhooks, verify X-Ray-Signature against the raw request body before parsing: verifyWebhookSignature from @gege-mn/ray, or HMAC-SHA256 of "<t>.<raw body>" with a 5 minute tolerance.
The notification to implement: <describe it: trigger, channel, recipient, content>.
Tips for agents
- Read the relevant page before writing code: Sending, Templates, the channel guide for the channel in use, and Errors.
recipientSchemafromGET /channelsis authoritative for therecipientobject. The template kind (templateKind) tells you which content shape to use.- Validation error messages name the field that failed (
recipient: ...,content: subject: ...,missing required params: name), so agents can fix requests from the response alone. POST /sendreturning202means accepted, not delivered. CheckGET /sends/{sendId}(theget_send_statustool) in tests or subscribe to webhooks in production.- Use
POST /templates/{id}/test-send(thetest_send_templatetool) to exercise a template against a real inbox without creating feed entries, firing webhooks or using quota.