A service you point an agent at should tell you who operates it, what it records, how long it keeps it, and what happens when you exceed its limits. All of it, on one page.
Farseen Shaikh, trading as ScarAI. An individual, not a company — which is stated here because it changes what you can expect: there is no support rota and no legal entity behind the endpoint.
Cloudflare Workers, D1, R2, KV and Durable Objects — one processor, no third-party analytics, no CDN scripts, no trackers. The site itself loads no external resource.
farseen@scarai.xyz, or an issue on the public repository. Maintainers asking for a repository to be de-indexed: same address, and it is removed.
There is no status dashboard, and inventing one would be a page that says "all systems operational" whether or not they are. The server's own health endpoint is the status: it reports live counts read from the database it is actually serving.
curl https://mcp.scarai.xyz/health
https://mcp.scarai.xyz/mcp
Free, authless, no signup. Higher limits at /mcp/oauth with GitHub.
You get an HTTP error, not a wrong answer. The server has no fallback path that answers from anything other than the pinned corpus.
| tier | cost units / minute | cost units / day | how you get it |
|---|---|---|---|
authless | 120 | 2,000 | no account — keyed by IP |
free | 300 | 20,000 | sign in with GitHub — keyed by user id |
pro | 3,000 | 100,000 | not offered yet |
| tool | cost units per call |
|---|---|
web3_lookup | 1 |
web3_grep | 1 |
web3_fetch | 1 |
web3_search | 2 |
web3_deps | 3 |
The field names below are parsed from the source of the log line itself at build time. If the log line changes and this page is not updated, the build fails.
Per tool call: at · tool · cost · tier · inline_bodies_under · elapsed_ms · rows_read · rows_read_by_tool · d1_duration_ms · d1_calls · retries · l0 · bodies · ua · outcome · replica
Per initialize handshake: at · tier · client · client_version · protocol · ua
Visible only through Cloudflare's live tail. Not persisted to any store we operate, and there is no query text and no caller identity in it — ua and client are the connecting software's own labels, capped, never a person.
Per tool call, indexed by tool: tier · l0Hit · replicaPrimary · ua · outcome · rowsRead · cost · d1Ms · d1Calls · elapsedMs.
Per handshake, indexed by 'initialize': tier · client · clientVersion · protocol · ua.
No identifier of a person is attached — no IP, no user id, no query. The client name and user-agent identify software (a monitor, an editor, an agent), and are self-declared, so they count rather than identify.
Rate-limit counters — four integers per caller, reset on a rolling 24 hours.
A response cache — keyed by a SHA-256 of your arguments, expiring in 300 seconds.
The demand ledger — date · project id · decline class · count, 180-day retention, 7 declared classes, and nothing in it can be joined back to a request.
Written by enumerating what the deployed Worker actually writes — its log calls, its KV writes, its Durable Object storage and its OAuth grants. Where the code and the document disagree, the code is right and the document is a defect.
Controller: web3ctx, a ScarAI product, operated by Farseen Shaikh · Contact:
Last reviewed against the code: 2026-09-15.
⚠ **Every statement below was written by enumerating what the deployed Worker actually
writes** — its console.log calls, its KV writes, its Durable Object storage and its OAuth
grants — not from what the design intends. Where the code and this document ever disagree,
the code is right and this document is a defect. The sources are apps/mcp/src/index.ts
(emit), cache.ts, ratelimit.ts, telemetry.ts and oauth.ts.
A read-only retrieval endpoint for public, open-source Web3 documentation and code. It
serves excerpts of public repositories together with citations to the exact commit they came
from. It stores no user content, accepts no uploads, and writes nothing to any blockchain.
…/mcp: no account, no credential, no sign-up.…/mcp/oauth: optional, via GitHub, and it buys **only a higherrate limit**. It grants no additional data and unlocks no additional content.
| recorded | example |
|---|---|
| which tool was called | web3_search |
| the tier | authless / free |
| cost units charged, elapsed ms | 2, 375 |
| database rows scanned, query count, retries | 660, 3, 0 |
| cache hit or miss; whether a body was found | {hits:0,misses:1} |
| which region served the database read | APAC, primary: false |
| the HTTP user-agent string, cut at 80 characters (added 2026-09-15) | claude-code/2.1.0, node, SentinelOracle/1.0 |
| what the call came to — one of a closed set of labels (added 2026-09-22) | ok, empty, abstained, refused:INVALID_INPUT |
And per initialize handshake (added 2026-09-15 — the activation instrument): the tier, the
client software's self-declared name and version from the MCP handshake (clientInfo.name /
clientInfo.version, each cut at 64 characters), the protocol version it asked for, and the same
user-agent string. These identify software — an editor, an agent, a monitor — not a person, and
they are self-declared, so they are counted rather than trusted. They exist because on 2026-09-14
about 800 requests a day completed a handshake and never called a tool, and nothing could say whether
those were monitors or people whose agent never picked a tool.
Not recorded: your query text, the arguments you send, or the payload you receive.
⚠ One exception, stated because it is easy to miss: when a tool call throws, the error
message is logged. Error messages can quote an identifier or a selector taken from your
request — for example an unparseable selector. They are not intended to carry query text and
generally do not, but this is a best-effort statement about message contents, not a guarantee.
Logs are visible only through Cloudflare's live tail and are **not persisted to any store we
operate**.
One datapoint per tool call in Cloudflare Analytics Engine, indexed by tool name and
carrying the numbers in the table above plus the capped user-agent string and the outcome label
(a closed set read out of our own payload — the label for a refusal is the handler's constant, never
its message, so it cannot quote an argument); and one datapoint per
initialize handshake, indexed by the literal initialize and carrying the five handshake labels
above. No identifier of a person is attached — no IP, no user id, no query. The client name and
user-agent are software labels, capped, and enforced as such by the code's own tests
(apps/mcp/test/client-info.spec.ts pushes a sentence and an extra field through every path).
The free tier is anonymous, so the only thing available to meter is the network address.
Your IP is used as the name of a counter holding two numbers: units spent this minute and
units spent today.
counter does.** We do not consider this anonymous, and it is why it is described here
rather than omitted as "just rate limiting".
Authenticating removes this: an authenticated caller's counter is keyed by their GitHub
numeric user id instead, and the IP is not used.
⚠ **This is the one place your query text is stored, and the 2026-08-13 text named the wrong
mechanism for it. The cache KEY is a SHA-256 hash** of the tool name and your arguments, so
nothing readable is stored there and no key can be turned back into a question. What can contain
your words is the cached response: when the server declines to scope a question it quotes the
question back — "No scoping evidence in \"…\"" — and that sentence is part of the payload that
gets cached. Same conclusion, accurate mechanism; the previous wording is in git history.
is not associated with your IP or identity, and is shared by anyone who sends the same
query.
claiming one would be a control we do not have.
Added 2026-08-28. When the server declines — it did not recognise a project, it knows a
project and has not indexed its source, it withheld a human-signed recipe from a family question,
or it found nothing — it increments one counter in its own database.
| the row is | example |
|---|---|
| a UTC date | 2026-08-28 |
| a project id from our own published corpus, or empty | ethena |
| a decline class from a fixed, published list of seven | known-not-ingested |
| a count | 3 |
That is the whole row. No query text, no fragment of one, no IP, no user id, no time finer
than a day — and the guard is structural rather than a promise: every value is checked against the
shape of a corpus project id (^[a-z0-9][a-z0-9._-]{0,63}$) before it is written, so nothing
containing a space can reach the table. The test suite tries to push a sentence in through each
field in turn.
what the declines added up to. It is how a project that people keep asking for gets indexed.
eighty times and eighty callers asking once are the same row.
We request no OAuth scopes at all. From GitHub's public profile we read and store:
| stored | why |
|---|---|
| your GitHub numeric id | the rate-limit key |
| your GitHub login | so a grant can be recognised in support |
| your display name, if public | shown on the consent record |
⚠ The GitHub access token is used once — to read that profile — and then discarded. It
is never stored. This service cannot act on your GitHub account, read your repositories, or
see your email.
Access tokens we issue expire in 1 hour; refresh tokens are stored until revoked. You can
revoke access at any time in your GitHub settings under **Applications → Authorized OAuth
Apps**, which invalidates future logins immediately.
accepts one**, and none of the five tools takes a credential of any kind.
Nothing is sold, rented, or shared with third parties for any purpose.
Data is processed on Cloudflare infrastructure (Workers, D1, R2, KV, Durable Objects,
Analytics Engine) as our sole processor. GitHub sees an authentication request when — and
only when — you choose to sign in.
Excerpts of public, open-source repositories, each carrying its licence-bearing source URL
and a 40-character commit hash so you can verify it. Excerpts are served under the terms of
their original licences. If you are a maintainer and want a repository removed, open an issue
on this project's repository and it will be removed from the index.
Email farseen@scarai.xyz, or open an issue on
the project repository.
To ask for your data: the only records tied to an identity are the OAuth grant fields in §6.
Revoking the app in your GitHub settings ends future logins immediately; email the address
above to have the stored grant deleted.
Material changes are dated at the top of this document, and **the previous text stays in the
repository's git history** rather than being overwritten — the same rule this project follows
for everything it publishes.
⚠ This is a factual description of what the software does, not legal advice, and it has
not been reviewed by a lawyer.
⚠ No GDPR or CCPA rights section appears here, deliberately. Those sections name a
controller's legal basis, jurisdiction and statutory procedures, and this service is operated
by an individual with no company entity behind it. **Boilerplate asserting compliance
machinery that does not exist would be a worse document than one that omits it** — and this
is a service whose entire pitch is not asserting things it cannot support. If an entity is
formed, this section is where that changes.
Take it, change it, ship it.
Republish, quote, re-score and disagree with any measurement — attribution keeps the number attached to the run that produced it.
Every unit is an excerpt of a third-party open-source repository, served under its own licence, with a 40-character commit pin so you can read that licence at the exact revision quoted.