Agent memory is usually a vendor problem. You pay per token to store context, or you hand your data to a managed service that charges for embeddings and retrieval. Setoku takes a different approach: a self-hosted MCP server that combines a ClickHouse data lake with a knowledge layer that records tribal business logic. The result is an agent that remembers how your company counts active users or calculates LTV, so every session doesn’t rediscover the same definitions.
The project ships as a Docker image with Claude Code skills for setup and connector addition. Production instances run on cheap VPS boxes (OVHCloud VPS-3 for company use, $5/month VPS-1 for personal finance). No inference happens on the server itself. All thinking runs in the AI you already pay for, so the total bill is hosting plus your existing Claude or GPT subscription.
Architecture: Data Lake Plus Knowledge Layer
Setoku splits into two pieces:
-
Data ingestion and storage: ClickHouse holds structured data from Mercury (spending and account data), Vercel logs, Render logs, Slack channels, GitHub activity, Gmail, and Monarch Money finances. Connectors are added via Claude Code skills that generate the ingestion pipeline.
-
Knowledge layer: A separate schema that stores metric definitions, gotchas, and business rules. When an agent asks “how many hires have we made,” the knowledge layer surfaces the recorded definition (for example, “exclude placements that fell through and include the two real hires the old dashboard dropped”). The agent doesn’t re-derive the answer from raw data every time.
The MCP protocol nudges the AI to ask about and record tribal knowledge as it finds it. When an agent encounters ambiguity (three different answers for “active users” depending on which column you check), it works through the logic once with a human, records the canonical definition, and every subsequent session gets the same number.
State Management
State lives in three places:
- ClickHouse tables: Raw data, immutable after ingestion.
- Knowledge schema: Definitions, rules, and gotchas. Append-only with timestamps, so you can audit what was learned when.
- Published apps: Dashboards and charts that hook up to live data. Each app gets a URL and a query definition stored in the knowledge layer. The app re-runs the query on page load.
The admin UI lets you audit and prune learned knowledge. If two agents record conflicting definitions, the UI flags the conflict and you pick the canonical version. The knowledge layer doesn’t auto-merge or vote; it surfaces the collision and waits for a human decision.
MCP Protocol for Publishing Dashboards
Setoku extends the MCP protocol with a publish_app tool. An LLM can:
- Generate a SQL query or aggregation logic.
- Define a chart type (bar, line, table).
- Call
publish_appwith the query and chart spec. - Receive a public URL that renders the chart on live data.
Example flow:
User: "Chart revenue by line."
Agent: [queries knowledge layer for revenue definition]
Agent: [generates SQL: SELECT line_item, SUM(amount) FROM revenue GROUP BY line_item]
Agent: [calls publish_app with query and bar chart spec]
Agent: "Here's your dashboard: https://setoku.example.com/app/abc123"
The app URL is a thin frontend that runs the query on page load and renders the chart. No caching, no stale data. The query definition lives in the knowledge layer, so the app stays in sync with any updated business logic.
Security Boundaries
- MCP tokens: Each user gets a unique, revocable token that rides in the MCP connection URL. The token is the key, like a database connection string. Revoke it from the admin UI and the agent loses access.
- Read-only by default: The MCP server exposes read-only queries. Write access (for recording knowledge or publishing apps) requires an elevated token.
- No model on the server: Setoku doesn’t run inference, so it can’t leak data through prompt injection or model extraction. The attack surface is SQL injection (mitigated by parameterized queries) and token theft (mitigated by short-lived tokens and IP allowlists).
Deployment Shape
| Component | Technology | Cost | Failure Mode |
|---|---|---|---|
| Data lake | ClickHouse | Included in VPS | Disk full, query timeout |
| Knowledge layer | SQLite or Postgres | Included in VPS | Lock contention on write-heavy workloads |
| MCP server | Docker container | $5-$20/month VPS | OOM if too many concurrent queries |
| Connectors | Python scripts | Included | API rate limits, auth token expiry |
| Admin UI | Web frontend | Included | No auth = public knowledge layer |
A fresh setup takes about 20 minutes. The Docker image includes the MCP server, ClickHouse, and the admin UI. Claude Code skills handle connector addition (Mercury, Slack, GitHub, etc.). The skills generate Python scripts that poll APIs and insert into ClickHouse.
Observability
Setoku logs:
- Query execution: SQL text, duration, result row count.
- Knowledge writes: What was learned, when, and by which agent session.
- App publishes: Query definition, chart type, URL.
Logs go to stdout (captured by Docker) and to a system_log table in ClickHouse. The admin UI surfaces slow queries and knowledge conflicts.
Likely Failure Modes
-
Knowledge drift: Two agents record conflicting definitions. The knowledge layer flags the conflict but doesn’t auto-resolve. If you ignore the flag, agents will get inconsistent answers depending on which definition they hit first.
-
Connector auth expiry: Mercury, Slack, and GitHub tokens expire. The connector script fails silently until you notice missing data. Mitigation: log auth failures to a dedicated table and surface them in the admin UI.
-
Query timeout: ClickHouse is fast, but a poorly written aggregation over millions of rows can hang. The MCP server kills queries after 30 seconds by default. The agent sees a timeout error and can retry with a narrower query.
-
Disk full: ClickHouse compresses well, but log ingestion (Vercel, Render) can fill a small VPS. Mitigation: set retention policies in ClickHouse (drop logs older than 90 days) and monitor disk usage.
-
No inference = no smart retry: If the agent writes a bad query, Setoku returns an error. The agent has to parse the error and retry. Unlike a managed service with a model in the loop, Setoku won’t auto-fix the query.
Production Use at Hedgy
The team at Hedgy runs Setoku on an OVHCloud VPS-3. Ingested data includes:
- Mercury spending and account balances
- Vercel deployment logs
- Render service logs
- Slack channels (filtered to avoid PII)
- GitHub activity (PRs, commits, issues)
The knowledge layer has recorded:
- How to count active users (check
last_seen_atwithin 30 days, exclude test accounts) - How to calculate LTV (sum of all transactions, exclude refunds and chargebacks)
- How many hires were made (exclude placements that fell through, include the two the old dashboard missed)
Non-technical team members use claude.ai + Setoku as their only coding environment. They’ve shipped internal tools (a hiring dashboard, a spending report, a Slack bot that answers “how much did we spend on X this month”) without touching code directly. The agent writes the query, publishes the app, and the user gets a link.
The log ingestion was added as a test, but it made agentic debugging faster. When a deployment fails, the agent can query Vercel logs, correlate with Render logs, and surface the root cause without switching tools.
Code Snippet: Recording Tribal Knowledge
When an agent encounters ambiguity, the MCP server nudges it to record the resolution. Here’s the flow in pseudocode:
# Agent asks: "How many active users?"
# MCP server checks knowledge layer for a definition.
definition = knowledge_layer.get("active_users")
if definition is None:
# No definition recorded. Prompt the agent to ask the user.
response = agent.ask_user("How do you define an active user?")
# User: "last_seen_at within 30 days, exclude test accounts"
knowledge_layer.record(
key="active_users",
definition="last_seen_at within 30 days, exclude test accounts",
recorded_by=session_id,
timestamp=now()
)
definition = response
# Now run the query using the definition.
query = f"SELECT COUNT(*) FROM users WHERE last_seen_at > NOW() - INTERVAL 30 DAY AND is_test = FALSE"
result = clickhouse.execute(query)
return result
The knowledge layer is append-only. If a second agent records a conflicting definition, both entries remain. The admin UI flags the conflict and you pick the canonical version.
Technical Verdict
Use Setoku when:
- You already pay for Claude or GPT and don’t want to add a managed memory service.
- Your team has tribal knowledge (metric definitions, business rules) that agents rediscover every session.
- You need financial data ingestion (Mercury, Monarch Money) or log aggregation (Vercel, Render) in the same box.
- You want agents to publish live dashboards without building a separate BI tool.
- You can run a VPS and manage Docker containers.
Avoid Setoku when:
- You need multi-tenant isolation (one instance per user is expensive at scale).
- You want a managed service with SLAs and support.
- Your data is too large for a single ClickHouse instance (petabyte scale).
- You need real-time streaming ingestion (Setoku polls APIs on a schedule).
- You want the agent to auto-fix bad queries (no inference on the server means no smart retry).
The project is open source (Apache 2.0) and ships with a live demo wired to a fictional sports club. The demo shows how the knowledge layer prevents rediscovery (for example, “how many unique fans” returns 71,204 deduplicated by email, not the raw 92,118). The MCP protocol is standard, so you can swap Claude for any other agent that speaks MCP.