ScarAI · web3ctx

Who runs this, and what it keeps.

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.

The operator

One person, named.

Who

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.

Where it runs

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.

Contact

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.

Status

The endpoint answers for itself

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.

Health

curl https://mcp.scarai.xyz/health

The MCP endpoint

https://mcp.scarai.xyz/mcp

Free, authless, no signup. Higher limits at /mcp/oauth with GitHub.

If it is down

You get an HTTP error, not a wrong answer. The server has no fallback path that answers from anything other than the pinned corpus.

Limits

Published, not discovered by hitting them

rate limits · read from the deployed code at build time
tiercost units / minutecost units / dayhow you get it
authless1202,000no account — keyed by IP
free30020,000sign in with GitHub — keyed by user id
pro3,000100,000not offered yet
toolcost units per call
web3_lookup1
web3_grep1
web3_fetch1
web3_search2
web3_deps3
A refusal arrives as a readable tool result naming the window that blocked — never an HTTP 429. A transport-level 429 would kill the whole JSON-RPC exchange including the calls that were within budget.
What is recorded

Read out of the logging code, not written from memory.

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 request — logged, not stored

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.

Aggregate metrics

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.

What is durable

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.

The full policy

Privacy, in detail

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.

Privacy Policy — Web3 Context MCP

Controller: web3ctx, a ScarAI product, operated by Farseen Shaikh · Contact:

farseen@scarai.xyz

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.


What this service is

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.

  • Free tier — …/mcp: no account, no credential, no sign-up.
  • Authenticated tier — …/mcp/oauth: optional, via GitHub, and it buys **only a higher

rate limit**. It grants no additional data and unlocks no additional content.


What is recorded

1. Operational logs — per request

recordedexample
which tool was calledweb3_search
the tierauthless / free
cost units charged, elapsed ms2, 375
database rows scanned, query count, retries660, 3, 0
cache hit or miss; whether a body was found{hits:0,misses:1}
which region served the database readAPAC, 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**.

2. Aggregate metrics

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).

3. Your IP address — as a rate-limit key, on the free tier

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.

  • It is not written to any log, not sent to Analytics Engine, and not shared.
  • The counters reset on a rolling basis and are not retained beyond 24 hours of activity.
  • ⚠ **The address is the counter's name, so it exists in that storage for as long as the

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.

4. Cached responses — and your query text, for five minutes

⚠ **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.

  • Retention: 5 minutes, by an expiry set at write time.
  • It is a cache key, not a log: it is never read back as a record of what anyone asked,

is not associated with your IP or identity, and is shared by anyone who sends the same

query.

  • If this matters for a particular query, do not send it — there is no opt-out flag, and

claiming one would be a control we do not have.

5. The demand ledger — what was asked for and could not be served

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 isexample
a UTC date2026-08-28
a project id from our own published corpus, or emptyethena
a decline class from a fixed, published list of sevenknown-not-ingested
a count3

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.

  • Why it exists: every decline this service makes is deliberate, and until now nothing recorded

what the declines added up to. It is how a project that people keep asking for gets indexed.

  • Retention: 180 days, after which rows are deleted.
  • It cannot be joined back to a request, by us or by anyone with the database: one caller asking

eighty times and eighty callers asking once are the same row.

6. If you authenticate with GitHub

We request no OAuth scopes at all. From GitHub's public profile we read and store:

storedwhy
your GitHub numeric idthe rate-limit key
your GitHub loginso a grant can be recognised in support
your display name, if publicshown 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.


What is never recorded

  • Payload contents returned to you.
  • Any wallet address, private key, seed phrase or signature. **The service has no field that

accepts one**, and none of the five tools takes a credential of any kind.

  • Cookies, analytics pixels, device fingerprints, cross-site trackers.
  • Your email address.

Sharing

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.


Content we serve

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.


Contact and changes

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.


Scope of this document

⚠ 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.

Licence

Two licences, because this is two kinds of thing

Code and site — MIT

Take it, change it, ship it.

Evidence — CC BY 4.0

Republish, quote, re-score and disagree with any measurement — attribution keeps the number attached to the run that produced it.

🔴 Served content — not ours

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.