Quickstart

From zero to your AI agent using exray. About 5 minutes.

What exray is: an agent-native MCP web-scraping runtime. It isn't a tool you click — it's the tool backend your AI agent (Claude Code / Cursor / Windsurf) uses. Your agent scrapes pages through it, writes its own scraping logic, registers that logic as reusable tools, and can render results as a shareable web page. Zero ops on your side — it all runs on Cloudflare.

Mental model (get this first, the rest follows)

exray has three faces:

FaceWho uses itWhat it does
MCP server (mcp.exray.dev/mcp)your agentThe real work: scraping, defining/publishing assets, running tools. You speak plainly; the agent translates into tool calls.
CLI (exray)you (human)What agents can't or shouldn't do: issue tokens, enable result pages, toggle bindings, script things in CI
Dashboard (app.exray.dev)you (human)Read-only: usage, job records, source of registered assets, audit trail

The key idea: you barely "operate" exray. You talk to Claude Code / Cursor, and it calls exray for you. Configuration and execution live on the agent side or in the CLI — the dashboard does not configure anything.


Step 1: Sign up, get an onboarding token

  1. Open the dashboard and sign in:
    • Production: https://app.exray.dev/sign-in
    • Staging (for trying things out): https://app-staging.exray.dev/sign-in
  2. You land on /welcome, which issues you a one-time onboarding token (shaped like exr_<id>_<secret>).
    • ⚠️ The plaintext is shown exactly once. Copy it now. If you lose it, issue a new one at /console/tokens.
    • The welcome page already renders client config snippets with your token filled in.

That token carries four scopes — tools.read, tools.execute, tools.define, tools.publish — enough for an agent to scrape, define and publish its own tools end to end. It deliberately does not include admin: creating/revoking tokens, enabling result pages and toggling bindings all require admin, which you issue separately when you need it.


Step 2: Install an MCP client

Config snippets live in Connect an MCP client — that page is the single source for configuration, with complete setups for Claude Code, Cursor and Windsurf plus protocol details and troubleshooting.

Three things to know before you start:

  • The endpoint is <mcp-url>/mcp (production: https://mcp.exray.dev/mcp)
  • The token goes in the Authorization: Bearer header, never in the URL
  • Restart your client after installing — MCP config is read at startup

Step 3: Verify the connection

Say this to your agent:

Use exray's scrape tool on https://example.com and give me markdown

You should see it call scrape and return:

# Example Domain

This domain is for use in documentation examples...

That's a working connection. (Under the hood: initializetools/listtools/call scrape, all handled by the agent.)

If it doesn't work, see troubleshooting — a 401 response carries a reason field that tells you whether the token was mistyped, expired, or never sent by the client at all.


Step 4: Everyday use

Once installed, you just talk to your agent. exray exposes 15 tools and the agent picks:

What you wantWhat you sayTool it calls
Scrape a page"grab the content of example.com"scrape
Pull fields out of a page"extract the title and price"extract
Write a dedicated scraper"define a fetcher for this shop's product pages"define_fetcher
Write an extractor"write an extractor that pulls product info from pages like this"define_extractor
Build a result page"make a web page listing what you scraped"define_handler
Publish it for reuse"publish that fetcher"publish_definition
Use your own scraper"use the fetcher I just made on these URLs"fetcher_<your-name>
See what's registered"list all my fetchers"list_definitions
Crawl a site"crawl example.com, 2 levels deep, 50 pages max"crawl / define_crawl
Fetch crawl results"give me the results of that crawl"get_crawl_results

The rest are asset lifecycle (deprecate_definition / disable_definition / delete_definition) and evaluation (define_eval / run_eval).

What makes exray different: other scraping APIs hand you rough markdown and leave complex extraction to another LLM call — slow and expensive. exray lets your agent write TypeScript scraping and extraction code and register it as a tool. Write it once, then call it by name forever. Your extraction logic becomes an asset.

What actually happens inside

You say: "define a fetcher called shop-probe that opens the given URL and returns the title." The agent calls define_fetcher with source like:

import type { FetcherModule } from "@exray/exray-api";

export default {
  async fetch(ctx) {
    await ctx.page.goto(String(ctx.params.url));
    return await ctx.page.snapshot();
  },
} satisfies FetcherModule;

exray statically validates and compiles it. You say "publish", the agent calls publish_definition, and from then on fetcher_shop-probe is a named tool you can call repeatedly. Every call is metered, audited, and inspectable in the dashboard.


Step 5 (optional): Turn results into a web page

Once you've scraped something, the next question is usually "how do I show this to someone?"

Have your agent write a handler (entry point handle(request, ctx)). Publish it, enable a subdomain, and you get a public address at <username>-<project>.exray.app:

exray site enable          # requires an admin token
exray site status

A handler gets read-only access to your job data (list jobs, fetch a result), so it can be a real dashboard rather than JSON pasted into a chat window.

Full walkthrough: Result pages.


Step 6 (optional): Give your agent storage

By default agent code has no persistence at all — it forgets everything when it finishes. When you need it to remember things across calls, enable bindings per project:

BindingWhat the agent gets
kvKey-value store (ctx.kv) — good for caching and dedupe markers
dbStructured document store (ctx.db) — queryable
storageObject storage (ctx.storage) — for larger artifacts
exray bindings get              # all three are off by default
exray bindings set --kv on      # requires an admin token

Off-by-default is deliberate. Before you enable one, agent code calling it gets an explicit binding_not_enabled error rather than silently landing a writable store. Result pages (handlers) get read-only access to all three — a public request should never be able to write your data.


Doing it without an agent

Everything above works from the command line. See CLI reference. Typical uses: registering and publishing assets from CI, scripted token issuance, bulk-enabling result pages.

exray define handler mysite --file ./site.ts
exray publish handler mysite
exray site enable

What to look at in the dashboard

After signing in to /console (read-only):

  • Overview (/console) — Browser-seconds, job count, error rate, per-project usage this month
  • Projects (/console/projects) — your namespaces (isolating assets and tokens per agent or workload)
  • Tokens (/console/tokens) — issue and revoke tokens, inspect scopes
  • Code (/console/projects/<slug>/code) — source and version history of registered assets
  • Jobs (/console/jobs) — every tool call, with duration, metering and errors
  • Audit (/console/audit) — a full operation trail

Common problems

  • 401 on tool calls — check the reason field in the response body; see the 401 table.
  • 403 forbidden — the token lacks a scope (execution needs tools.execute, publishing needs tools.publish, enabling result pages needs admin). exray token list shows current scopes.
  • 402 — you've used up this month's quota. exray budget shows usage.
  • Empty tool list — nine times out of ten the URL ends in /mcp/mcp, or the client wasn't restarted.
  • Full checklist: troubleshooting.