zora-analytics
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@zora-analyticsWho are the top 10 current holders?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
On-Chain SQL + RAG Analytics Agent
Ask questions in plain English about live Zora token data or the project's analytics methodology. The agent routes quantitative questions to guarded SQL and conceptual questions to hybrid document retrieval, then returns the answer together with SQL rows and/or cited chunks.
The model provider is swappable: Anthropic and OpenAI API adapters are available, while
claude_code uses an authenticated Claude Code Pro/Max subscription without API-token billing.
Tests: 107 tests currently pass, including the SQL attack suite, circuit/cache behavior, MCP protocol handshake, retrieval provenance, and eval scorers.
What this demonstrates
Capability | Implementation |
Bounded agentic tool use |
|
Defense-in-depth SQL safety | Fail-closed SQLGlot AST policy, allowlisted tables, row/timeout caps, and a SELECT-only role |
Hybrid RAG / vector database | pgvector cosine search + PostgreSQL FTS + Reciprocal Rank Fusion in |
MCP integration | Official MCP Python SDK, FastMCP, stdio + Streamable HTTP in |
OpenTelemetry observability | OpenTelemetry-native Langfuse spans, structured stdout, and |
Resilience and cost control | Transient retries, provider circuit breaker, normalized answer cache, stale fallback |
Measured quality | A 44-case adversarial SQL harness plus a real 28-case retrieval ablation in |
Provider independence | Anthropic, OpenAI, or local Claude Code subscription through the same |
Related MCP server: ChainFETCH MCP Server
Architecture
flowchart LR
Z[Zora explorer] --> I[Scheduled indexer]
I --> P[(Postgres + pgvector)]
U[Streamlit / API client] --> A[FastAPI /ask]
A --> G[Bounded agent]
G -->|run_sql| S[AST SQL guard]
S -->|read-only transaction| P
G -->|search_docs| R[Dense + FTS + RRF]
R -->|guarded SELECTs| S
P --> G
G --> U
C[MCP client] --> M[FastMCP stdio / HTTP]
M --> S
T[OpenTelemetry spans] --> L[Langfuse optional]
G --> T
M --> TQuickstart
Prerequisites: Docker Desktop and Python 3.11+.
cp .env.example .env
python3 -m venv .venv
.venv/bin/pip install -e '.[all,dev]'
docker compose up -d postgresNew database volumes initialize the schema and roles automatically. To upgrade an existing project volume after pulling this version, apply the additive schema once:
docker compose exec -T postgres \
psql -v ON_ERROR_STOP=1 -U postgres -d zora_analytics -f /opt/zora/schema.sqlIndex the bundled Markdown corpus. The first command allows one download of the 67 MB quantized BGE ONNX model; subsequent embedding runs work from the local cache without an API key:
.venv/bin/zora-retrieval-index --allow-model-downloadRun with a Claude subscription
Confirm that the host CLI is authenticated:
claude auth statusSet these values in .env, then run the application on the host so it can invoke your logged-in
claude binary:
LLM_PROVIDER=claude_code
CLAUDE_CODE_MODEL=sonnet
CLAUDE_CODE_EFFORT_LEVEL=lowThe model alias currently selects the latest Sonnet, while the explicit low-effort setting keeps
routine Text-to-SQL requests from inheriting a higher global Claude Code effort. To favor maximum
speed over SQL reliability, set CLAUDE_CODE_MODEL=haiku; Haiku does not use an effort level.
Run the API:
.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000In a second terminal:
API_BASE_URL=http://127.0.0.1:8000 \
.venv/bin/streamlit run ui/streamlit_app.py --server.address 127.0.0.1 --server.port 8501Open the API docs or
the chat UI. Claude Code subscription usage reports
cost_usd: null; it does not pretend subscription calls have API-token cost.
Run with an API provider
Set either ANTHROPIC_API_KEY or OPENAI_API_KEY and the matching LLM_PROVIDER. The fully
containerized path is then. Compose mounts the host .cache/fastembed directory read-only, so the
model cached during the indexing step is reused without another download:
docker compose up --buildHealth and direct query examples:
curl http://127.0.0.1:8000/health
curl -X POST http://127.0.0.1:8000/ask \
-H 'Content-Type: application/json' \
-d '{"question":"Who are the top 10 current holders?"}'Guardrails
Every model- or MCP-supplied query goes through one SQLGuard and one SQLExecutor. The guard:
accepts exactly one read-only
SELECT;allowlists
tokens,holders,transfers, and retrievalembeddings;blocks writes, DDL, system schemas, cross-database references, locks, and dangerous functions;
preserves pgvector's
<=>cosine operator only onembeddings.embedding;injects or clamps
LIMIT, while the executor setsTRANSACTION READ ONLYand a statement timeout.
The database reader role is a separate safety boundary. A parser regression still cannot turn the reader credential into a writer.
Resilience and cache
Provider calls receive transient-only exponential-backoff retries. After four consecutive failures
by default, the process-wide provider/model circuit opens for 30 seconds. These values are controlled
by LLM_PROVIDER_RETRY_ATTEMPTS, LLM_CIRCUIT_FAILURE_THRESHOLD, and
LLM_CIRCUIT_RESET_SECONDS.
Successful responses are cached in answer_cache by a hash of:
case-folded, punctuation/whitespace-normalized question;
tracked token address;
current introspected schema hash.
A fresh hit skips the model. If the provider is unavailable, an expired last-good response can be served explicitly as stale. Provider and guard failures return HTTP 200 with a machine-readable contract instead of a 5xx:
{
"status": "degraded",
"answer": "I couldn't answer that confidently.",
"reason": "provider_unavailable",
"last_error": "...",
"served_from_cache": false
}GET /health includes answer-cache lookups, hits, stale hits, and process-local hit rate.
Observability
observability/tracing.py is a deliberately thin span API. One trace covers every /ask or MCP
call, with nested llm.generate, tool.run_sql, tool.search_docs, guard.validate, db.execute,
and retrieval.* spans. Provider/model, tokens, nullable cost, latency, retries, guard decision, and
row counts use the same values returned by the API and written to query_logs.
Langfuse is optional. If any of LANGFUSE_PUBLIC_KEY, LANGFUSE_SECRET_KEY, or LANGFUSE_HOST is
missing, tracing runs as a no-op and cannot break a request. For Langfuse Cloud, create a project,
put its three values in .env, and restart the API.
For local Langfuse v3:
docker compose --profile observability up -dOpen localhost:3000, create the first project/key pair, place the keys in
.env, and set LANGFUSE_HOST=http://localhost:3000 for a host-run API. For the Compose API, set
LANGFUSE_DOCKER_HOST=http://langfuse-web:3000. The optional LANGFUSE_INIT_* variables perform
the same organization/project/user setup headlessly on first startup. Change all LANGFUSE_*
local secrets before exposing that profile. OTEL_SERVICE_NAME defaults to zora-analytics-agent
so new traces have a stable service identity instead of an anonymous OpenTelemetry resource.
A local smoke run on 2026-07-18 sent a real MCP describe_schema call through this instrumentation:
Langfuse stored one mcp.call trace with a nested db.execute span. The local project uses a
non-personal demo identity; no developer account information is attached to the trace.

Langfuse's current Python SDK is OpenTelemetry-native, so the instrumentation is not coupled to a custom agent framework and can coexist with another OTel backend.
MCP server
The MCP server is a parallel interface to the same guarded executor. It exposes:
run_sql(query)— structured rows or a structured guard rejection;describe_schema()— the runtime-introspected analytics schema;data_freshness()— token watermark plus latest sync run;top_holders(limit=10)— a curated query, still passed through the guard.
Claude Desktop claude_desktop_config.json:
{
"mcpServers": {
"zora-analytics": {
"command": "/absolute/path/to/zoraHoldersAI/.venv/bin/zora-mcp",
"args": ["--transport", "stdio"],
"env": {
"DATABASE_URL": "postgresql://zora_app:zora_app@localhost:55432/zora_analytics",
"READ_ONLY_DATABASE_URL": "postgresql://zora_reader:zora_reader@localhost:55432/zora_analytics"
}
}
}
}Replace only the absolute repository path and, if necessary, the mapped PostgreSQL port. Any MCP-compatible client can use the same stdio command. Streamable HTTP is one line:
.venv/bin/zora-mcp --transport streamable-http --host 127.0.0.1 --port 8001Clients connect to http://127.0.0.1:8001/mcp.
Hybrid retrieval
The curated corpus in retrieval/corpus/ covers protocol scope, on-chain terminology, timestamp
semantics, safety, and system methodology. Heading-aware chunks keep stable provenance such as
methodology#meaning-of-first-seen.
For a query, the retriever runs two guarded SELECTs over the same PostgreSQL instance:
BGE-small dense cosine search through pgvector;
English full-text search through a generated
tsvector+ GIN index;deterministic RRF fusion; optionally, a local FastEmbed cross-encoder reranker.
No paid embedding API is required. EMBEDDINGS_PROVIDER=openai remains an explicit option, while
fastembed is the default. With FASTEMBED_LOCAL_FILES_ONLY=true, a cached BGE model is used without
network; if it is not cached, a deterministic local hash embedding keeps the feature fail-soft and is
clearly labeled as a fallback rather than BGE benchmark output.
Retrieval eval results
The table below came from a real local run on 2026-07-18 against 28 golden questions and 33 indexed
chunks. It was generated with python -m eval.run_retrieval --allow-model-download --judge; no
values were hand-waved.
Real retrieval run using BAAI/bge-small-en-v1.5; ranking cutoff k=5.
Configuration | Precision@5 | Recall@5 | MRR | Groundedness | Status |
Dense only | 22.14% | 89.29% | 0.8810 | 96.43% | completed |
Dense + FTS (RRF) | 22.86% | 92.86% | 0.9315 | 92.86% | completed |
Hybrid + reranker | 23.57% | 94.64% | 0.9643 | 96.43% | completed |
An unavailable reranker is reported explicitly and is never relabeled as a reranked run.

Run it again with:
.venv/bin/zora-retrieval-eval --allow-model-downloadAdd --judge to generate evidence-only answers and reuse the provider-neutral groundedness judge.
The default gate fails if hybrid MRR drops below dense-only MRR. Raw per-question results live in
eval/results/retrieval-*.json.
SQL eval results
Metric | Value |
Execution accuracy | 100.00% |
Valid-SQL rate | 100.00% |
Clarification accuracy | 100.00% |
Answer groundedness | 88.64% |
Mean latency | 7.01 s |
p95 latency | 10.60 s |
Mean cost / query | n/a |
Cases: 44 total / 37 executable.
ID | Difficulty | Check | Correct | Valid SQL | Latency | Cost |
q001 | easy | result_set_match | ✓ | ✓ | 6.77 s | n/a |
q002 | easy | numeric_match | ✓ | ✓ | 5.91 s | n/a |
q003 | easy | result_set_match | ✓ | ✓ | 7.32 s | n/a |
q004 | easy | numeric_match | ✓ | ✓ | 6.73 s | n/a |
q005 | easy | numeric_match | ✓ | ✓ | 5.87 s | n/a |
q006 | easy | numeric_match | ✓ | ✓ | 5.59 s | n/a |
q007 | easy | numeric_match | ✓ | ✓ | 5.01 s | n/a |
q008 | easy | numeric_match | ✓ | ✓ | 5.67 s | n/a |
q009 | easy | numeric_match | ✓ | ✓ | 5.23 s | n/a |
q010 | easy | result_set_match | ✓ | ✓ | 7.92 s | n/a |
q011 | easy | numeric_match | ✓ | ✓ | 7.90 s | n/a |
q012 | easy | result_set_match | ✓ | ✓ | 6.40 s | n/a |
q013 | easy | numeric_match | ✓ | ✓ | 5.14 s | n/a |
q014 | medium | numeric_match | ✓ | ✓ | 7.70 s | n/a |
q015 | medium | result_set_match | ✓ | ✓ | 6.29 s | n/a |
q016 | hard | numeric_match | ✓ | ✓ | 6.74 s | n/a |
q017 | medium | numeric_match | ✓ | ✓ | 7.48 s | n/a |
q018 | easy | numeric_match | ✓ | ✓ | 5.26 s | n/a |
q019 | medium | result_set_match | ✓ | ✓ | 9.64 s | n/a |
q020 | medium | numeric_match | ✓ | ✓ | 5.86 s | n/a |
q021 | medium | numeric_match | ✓ | ✓ | 7.33 s | n/a |
q022 | medium | numeric_match | ✓ | ✓ | 5.89 s | n/a |
q023 | medium | numeric_match | ✓ | ✓ | 10.15 s | n/a |
q024 | medium | numeric_match | ✓ | ✓ | 10.60 s | n/a |
q025 | medium | result_set_match | ✓ | ✓ | 6.52 s | n/a |
q026 | medium | result_set_match | ✓ | ✓ | 8.19 s | n/a |
q027 | medium | numeric_match | ✓ | ✓ | 5.85 s | n/a |
q028 | medium | numeric_match | ✓ | ✓ | 7.21 s | n/a |
q029 | hard | result_set_match | ✓ | ✓ | 8.62 s | n/a |
q030 | hard | result_set_match | ✓ | ✓ | 9.81 s | n/a |
q031 | hard | numeric_match | ✓ | ✓ | 9.40 s | n/a |
q032 | hard | numeric_match | ✓ | ✓ | 7.88 s | n/a |
q033 | medium | result_set_match | ✓ | ✓ | 5.90 s | n/a |
q034 | medium | numeric_match | ✓ | ✓ | 6.63 s | n/a |
q035 | ambiguous | clarification | ✓ | n/a | 4.12 s | n/a |
q036 | ambiguous | clarification | ✓ | n/a | 3.46 s | n/a |
q037 | ambiguous | clarification | ✓ | n/a | 3.60 s | n/a |
q038 | ambiguous | clarification | ✓ | n/a | 4.27 s | n/a |
q039 | hard | result_set_match | ✓ | ✓ | 12.75 s | n/a |
q040 | hard | numeric_match | ✓ | ✓ | 10.36 s | n/a |
q041 | hard | result_set_match | ✓ | ✓ | 15.44 s | n/a |
q042 | unsupported | clarification | ✓ | n/a | 4.46 s | n/a |
q043 | unsupported | clarification | ✓ | n/a | 6.10 s | n/a |
q044 | ambiguous | clarification | ✓ | n/a | 3.38 s | n/a |
This is a bounded regression benchmark over one indexed token snapshot, not a claim of universal
Text-to-SQL accuracy. The set deliberately includes multi-relation joins, unavailable-data
rejections, and ambiguous questions rather than only straightforward aggregate queries.
The published run used the sonnet alias at high effort; the application now defaults to low effort
for subsequent interactive queries. Changing inference settings does not alter historical results.
Evaluation-engineering highlights:
Execution accuracy is based on database result comparison, not generated-SQL string similarity.
A broken or timed-out reference query invalidates the run instead of counting as an agent error.
Result-set matching remains strict, while numeric comparisons use narrow rounding tolerance.
The groundedness judge measures faithfulness separately from SQL correctness; calibration and answer-grounding work raised the measured score from 26.32% to 88.64%.
The independent retrieval ablation improved Recall@5 from 89.29% to 94.64% (+5.35 percentage points), with sparse retrieval changing 26 of 28 rankings.
SQL execution accuracy compares each generated query's actual result set with a hand-written reference query. It is order- and alias-insensitive and tolerance-aware for numeric answers. Clarification cases accept either a direct clarification or a cited, row-free, SQL-free rejection; an unsupported metric answered with guessed SQL still fails.
Repository structure
.github/ CI for lint, formatting, tests, and both Compose configurations
agent/ bounded tool loop, cache, circuit breaker, prompts
app/ FastAPI: /ask, /admin/sync, /health
db/ Postgres/pgvector schema, roles, schema introspection
eval/ SQL golden eval + retrieval golden set/ablation
indexer/ scheduled Zora holder/transfer synchronization
llm/ Anthropic, OpenAI, and Claude Code adapters
mcp_server/ official FastMCP server and CLI
observability/ Langfuse/Otel spans + structured/query-table logging
retrieval/ corpus, chunking, embeddings, indexing, dense/FTS/RRF
sql_guard/ fail-closed AST guard + read-only executor
ui/ Streamlit chat
tests/ package-mirrored unit and protocol testsDevelopment
.venv/bin/pip install -e '.[all,dev]'
.venv/bin/pytest -q
.venv/bin/ruff check .
docker compose config --quietThe optional dependency groups are observability, mcp, and retrieval; all is used by the
runtime Docker image. No LangChain or LlamaIndex dependency is used.
License
MIT. See LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Alicense-qualityDmaintenanceAllows LLM agents and MCP clients to analyze blockchain data including wallet balances, token information, and transaction history across EVM and Solana chains through the Dune API.74MIT

ChainFETCH MCP Serverofficial
AlicenseCqualityDmaintenanceEnables AI-powered analysis of Ethereum blockchain data through semantic search, natural language queries, and structured filtering. Provides comprehensive access to addresses, transactions, blocks, tokens, and smart contracts with real-time blockchain intelligence.2671MIT- Alicense-qualityDmaintenanceEnables agents to query Base blockchain data using natural language by providing contract addresses, SQL templates, and direct access to Coinbase's SQL API for realtime on-chain analytics including DeFi, NFTs, and wallet activity.3MIT
- Flicense-qualityDmaintenanceProvides tools for querying onchain data across 12+ blockchain networks, including token balances, transaction analysis, and smart contract security auditing. It enables users to interact with multiple EVM-compatible chains and perform deep contract evaluations through natural language interfaces.1
Related MCP Connectors
32 paid x402 endpoints for crypto, Zora & on-chain analysis. 10 MCP tools. USDC on Base.
Query onchain data across EVM, Solana, Bitcoin, Substrate, and Hyperliquid via the SQD Portal API.
The grounded data layer for any LLM: governed SQL, metrics, lineage and catalog over your data.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/marblerace/zoraHoldersAI'
If you have feedback or need assistance with the MCP directory API, please join our Discord server