Skip to content

Commit ede29e8

Browse files
committed
feat(mcp, scripts), plan(SurrealDB-MCP): wire the surrealdb MCP server via Docker, first verification pass on FreedomFest 2026 finds the affiliation gap
Every check on the canonical layer so far meant writing a disposable Node script per check. SurrealDB Cloud is now directly queryable from Claude Code: .mcp.json runs the official surrealmcp server via Docker, with a wrapper script that sources .env from its own location rather than depending on the launching shell already having SURREAL_* exported. Docker over a submodule + build step: surrealmcp is a Rust binary with only two distribution paths (cargo install --path ., or the official Docker image) — no npm/PyPI package, so no uvx/npx-style one-liner. A submodule-plus-symlink shape (mirroring how skills are discovered) was considered and rejected — MCP servers are found exclusively through .mcp.json, never by scanning a folder. First real verification pass, on tonight's FreedomFest 2026 batch: client tagging is clean across all 65 persons and 40 organizations created tonight. Person-to-org affiliation is mostly missing — only 1 of 65 people actually has an affiliations edge, even though 39 more organizations were independently created/matched in the same run. Also surfaced two duplicate person rows (Ethan Akimoto, Rudolfo Beltran) and a likely title-stripped duplicate (Lt Gov Stavros Anthony / Stavros Anthony). Findings and full detail recorded in the plan doc and changelog entry below. Files changed: - .mcp.json (new) - scripts/mcp-surrealdb.sh (new) - context-v/plans/SurrealDB-MCP-Plus-Skill-for-Canonical-Layer-Verification.md - changelog/2026-07-07_05_SurrealDB-MCP-Wired-Plus-First-Verification-Pass-On-FreedomFest.md
1 parent 05de631 commit ede29e8

4 files changed

Lines changed: 376 additions & 0 deletions

File tree

.mcp.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"mcpServers": {
3+
"surrealdb": {
4+
"command": "${CLAUDE_PROJECT_DIR:-.}/scripts/mcp-surrealdb.sh"
5+
}
6+
}
7+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
---
2+
date_created: 2026-07-07
3+
date_modified: 2026-07-07
4+
title: "SurrealDB now queryable directly from Claude Code — and the first real verification pass finds the affiliation gap"
5+
lede: "Every check on the canonical layer so far meant writing a disposable Node script, running it once, deleting it. Now there's an MCP server wired straight into SurrealDB Cloud, and a skill that carries the schema knowledge and the verification discipline. First real run: tonight's FreedomFest 2026 batch is tagged correctly across all 65 people and 40 orgs — but only 1 of those 65 people actually got RELATEd to an organization."
6+
publish: true
7+
authors:
8+
- Michael Staton
9+
augmented_with:
10+
- Claude Code on Claude Sonnet 5
11+
files_changed:
12+
- .mcp.json
13+
- scripts/mcp-surrealdb.sh
14+
- context-v/plans/SurrealDB-MCP-Plus-Skill-for-Canonical-Layer-Verification.md
15+
tags:
16+
- Progress-Update
17+
- SurrealDB
18+
- MCP
19+
- Canonical-Layer
20+
- Person-DB-Resolver
21+
- Reach-Edu
22+
- FreedomFest
23+
- Verification
24+
---
25+
26+
## Why Care?
27+
28+
The person-db-resolver work from earlier tonight
29+
([[2026-07-07_03_Person-DB-Resolver-A-Sibling-Remote-For-People-Not-Orgs]],
30+
[[2026-07-07_04_People-CSV-Flow-Goes-From-Wired-Up-To-Actually-Usable]])
31+
wrote real data into the canonical layer, and the natural next question —
32+
"did it actually land right?" — used to mean writing a fresh throwaway
33+
script every time. That's now a solved problem: SurrealDB Cloud is directly
34+
queryable from Claude Code, and the first thing that query access surfaced
35+
is a real gap worth knowing about before it compounds across more events.
36+
37+
## What's New?
38+
39+
- **The `surrealdb` MCP server is live**, project-scoped in `.mcp.json`,
40+
running the official `surrealmcp` via Docker
41+
(`surrealdb/surrealmcp:latest`) — no submodule, no build step. A wrapper
42+
script (`scripts/mcp-surrealdb.sh`) sources `.env` from its own location
43+
rather than depending on the launching shell already having `SURREAL_*`
44+
exported, since that's not this repo's habit.
45+
- **A companion skill** (`surrealdb-canonical-layer`, in the lossless-skills
46+
tree) carries the schema knowledge and the verification discipline going
47+
forward — most importantly, that client tagging has to be checked
48+
explicitly per row, never inferred from a query that already filters by
49+
client.
50+
- **First real verification pass, on tonight's FreedomFest 2026 batch:**
51+
- **Client tagging: clean.** All 65 `persons` rows and 40 `organizations`
52+
rows created tonight carry `client_access: ["reach-edu"]` correctly —
53+
including on the re-check that deliberately didn't filter by client
54+
first, which is the check that would catch a row created without the
55+
tag.
56+
- **Person↔org affiliation: mostly missing.** Only Ethan Akimoto (the
57+
very first hand-test) has an actual `affiliations` edge. The other 64
58+
people resolved through the batch run have `has_name` + `speaker_at`
59+
observations but no edge to an org — even though 39 more organizations
60+
got independently created/matched in the same run. Org and person got
61+
resolved as two disconnected actions per row.
62+
- **A duplicate-row finding, new tonight:** "Ethan Akimoto" and "Rudolfo
63+
Beltran" each have two separate `persons` rows, and "Lt Gov Stavros
64+
Anthony" / "Stavros Anthony" look like the same person split by
65+
title-stripping. Candidate matching should have caught these.
66+
67+
## The Story
68+
69+
The affiliation gap isn't a surprise, exactly — the prior changelog entry
70+
already named the open question ("an org resolved before its person is
71+
resolved doesn't retroactively RELATE"). What direct query access adds is
72+
the actual *scale* of it: not a theoretical edge case, but 64 out of 65
73+
people in a single batch. Timestamps make the mechanism visible — "Lt Gov
74+
Stavros Anthony" (person, 09:01:27) and "State of Nevada" (org, 09:01:22)
75+
were created five seconds apart, clearly the same operator pass through the
76+
same CSV row, and still no edge connects them.
77+
78+
```mermaid
79+
flowchart LR
80+
A[CSV row: name + org] --> B[person.apply\ncreates/matches person]
81+
A --> C[resolver.apply\ncreates/matches org]
82+
B -.->|no automatic link| C
83+
D[person.affiliate\nthe ONLY path that RELATEs] -.->|not called\n64 of 65 times| B
84+
```
85+
86+
Without a query surface, this would have stayed invisible until someone
87+
went looking for a specific person's org in the UI and found nothing. With
88+
one, it's a known, scoped, three-sentence finding instead of a surprise
89+
discovered piecemeal later.
90+
91+
## Under the Hood
92+
93+
`.mcp.json`:
94+
95+
```json
96+
{
97+
"mcpServers": {
98+
"surrealdb": {
99+
"command": "${CLAUDE_PROJECT_DIR:-.}/scripts/mcp-surrealdb.sh"
100+
}
101+
}
102+
}
103+
```
104+
105+
`scripts/mcp-surrealdb.sh` sources `.env` relative to its own path
106+
(`$(dirname "${BASH_SOURCE[0]}")`), not `$PWD`, so it works regardless of
107+
where a Claude Code session was started — and independent of whether the
108+
launching shell happens to have `SURREAL_*` exported, since Claude Code's
109+
`${VAR}` expansion in `.mcp.json` reads only the launching shell's
110+
environment, never `.env` files directly.
111+
112+
**Still full read-write, not scoped down.** The Docker container runs with
113+
the same credentials the app services already use, and `surrealmcp`
114+
additionally exposes SurrealDB Cloud instance management (create/pause/
115+
resume) — more capability than a verification connector needs. Flagged in
116+
the skill as a standing caution, not yet resolved with an actual scoped
117+
role.
118+
119+
## What's Next
120+
121+
- Not fixing the People-CSV UI right now — this pass was diagnostics first,
122+
iteration later, per the user's explicit call.
123+
- When UI iteration resumes: the affiliation-gap question (does org-first
124+
resolution need a retroactive RELATE step, or is "resolve person before
125+
org" just the documented right order?) and the duplicate-person question
126+
(why did candidate matching miss Ethan Akimoto and Rudolfo Beltran the
127+
second time through) are both live threads to pick up.
128+
- A scoped read-only SurrealDB Cloud role, before this MCP connection
129+
becomes routine infrastructure rather than an occasional tool.
130+
131+
## Related
132+
133+
- [[2026-07-07_04_People-CSV-Flow-Goes-From-Wired-Up-To-Actually-Usable]] — the flow this batch ran through
134+
- `context-v/plans/SurrealDB-MCP-Plus-Skill-for-Canonical-Layer-Verification.md` — the plan, updated with these findings
135+
- `context-v/skills/surrealdb-canonical-layer/SKILL.md` (lossless-skills repo) — the shipped skill
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
---
2+
title: "SurrealDB MCP + a verification skill — querying augment-it's canonical layer directly, starting with FreedomFest 2026"
3+
date_created: 2026-07-07
4+
date_modified: 2026-07-07
5+
status: Shipped
6+
tags:
7+
- Plan
8+
- MCP
9+
- Agent-Skills
10+
- SurrealDB
11+
- Augment-It
12+
- Canonical-Layer
13+
- FreedomFest
14+
---
15+
16+
## Why this exists
17+
18+
Tonight's work on `apps/person-db-resolver` created real `persons` +
19+
`organizations` + `affiliations` + `observations` rows in reach-edu's
20+
SurrealDB Cloud instance — a handful of FreedomFest 2026 speakers and
21+
orgs (Ethan Akimoto / Carl Menger Institute, Lyn Ulbricht, Rudolfo
22+
Beltran, Kevin Brady, Lt Gov Stavros Anthony / State of Nevada, a
23+
President-of-Basin-Ventures record, and others), created by hand while
24+
testing the new UI. Every check on this data so far has been an ad-hoc
25+
Node script (`connect → signin → use → query`, written fresh each time,
26+
deleted after) — the same pattern used earlier tonight to clean up a
27+
mis-created `ethan-akimoto` organization row. That's fine for a one-off
28+
cleanup; it's the wrong tool for "verify tonight's batch is linked up
29+
correctly," which is a recurring need every time a new event or client
30+
gets processed through the resolver apps.
31+
32+
The fix is the same shape already proven for the Chroma corpus in this
33+
tree ([[../skills/search-lossless-corpus/SKILL.md]]): an **MCP server**
34+
for the raw query access, plus a **Skill** that carries the schema
35+
knowledge and the verification discipline, so a future session doesn't
36+
have to re-derive "what does an affiliation edge look like" from reading
37+
`resolver.ts` again.
38+
39+
## Component 1 — SurrealMCP, project-scoped — SHIPPED
40+
41+
[SurrealDB ships an official MCP server](https://github.com/surrealdb/surrealmcp)
42+
(`surrealmcp`) that talks to both self-hosted SurrealDB and SurrealDB
43+
Cloud — exactly augment-it's setup — over stdio/HTTP with bearer-token
44+
auth. This is the right default over a community alternative
45+
(`lfnovo/surreal-mcp`, `nsxdavid/surrealdb-mcp-server` also exist) since
46+
it's maintained by SurrealDB itself and Cloud auth is a first-class case,
47+
not a workaround.
48+
49+
**Resolved 2026-07-07:** `surrealmcp` is a Rust binary with exactly two
50+
distribution paths — build from source (`cargo install --path .`) or the
51+
`surrealdb/surrealmcp:latest` Docker image; no npm/PyPI package, so no
52+
`uvx`/`npx`-style one-liner like the `chroma` MCP server gets. Went with
53+
**Docker** — zero submodule, zero build step, and Docker's already part of
54+
this stack (`Dockerfile`, `docker-compose.yml`). A "submodule in an
55+
`mcps/` folder, symlinked" structure (mirroring the skills-symlink
56+
convention) was considered and rejected: MCP servers are discovered
57+
exclusively through `.mcp.json`'s `command`/`args`, never by scanning a
58+
folder, so a symlink step there would be pure overhead with no functional
59+
effect.
60+
61+
Shipped:
62+
63+
- `scripts/mcp-surrealdb.sh` — sources `.env` relative to its own
64+
location (not `$PWD`, not the launching shell's environment) and execs
65+
the Docker container. This matters because Claude Code's `${VAR}`
66+
expansion in `.mcp.json` only reads variables already exported in the
67+
shell that launched `claude` — it does not read `.env` files — and this
68+
repo's habit is sourcing `.env` per-command, not exporting at shell
69+
startup.
70+
- `.mcp.json` (project scope, per [[feedback_mcp_project_scope]]):
71+
```json
72+
{
73+
"mcpServers": {
74+
"surrealdb": {
75+
"command": "${CLAUDE_PROJECT_DIR:-.}/scripts/mcp-surrealdb.sh"
76+
}
77+
}
78+
}
79+
```
80+
- Verified via `claude mcp list``surrealdb: ... ✔ Connected`.
81+
82+
**Open question — read-only vs. read-write. Still not resolved.** The
83+
Docker container runs with the same full read-write credentials the app's
84+
services already use, and surrealmcp additionally exposes **Cloud
85+
instance management** (create/pause/resume a SurrealDB Cloud instance) —
86+
capability none of augment-it's own services need. This is more blast
87+
radius than a verification connector strictly requires. Flagged in the
88+
`surrealdb-canonical-layer` skill as a standing caution (never issue a
89+
write/Cloud-management tool call from a verification pass without an
90+
explicit ask) rather than solved — provisioning a scoped read-only
91+
SurrealDB Cloud role is worth doing before this becomes routine
92+
infrastructure rather than an occasional tool.
93+
94+
## Component 2 — a verification skill — SHIPPED
95+
96+
Authored at `context-v/skills/surrealdb-canonical-layer/SKILL.md` in the
97+
lossless-skills repo (per
98+
[[feedback_skill_authoring_in_lossless_skills]] — never inside augment-it
99+
directly), then symlinked via `sync-skills-symlinks.sh`.
100+
101+
**Scope decision:** generalized past augment-it, not augment-it-only.
102+
Written with augment-it's live schema as the worked example (source of
103+
truth: the code, cited explicitly as a snapshot that can drift) plus an
104+
explicit "adapting this skill to a new project" section, since
105+
`dididecks-ai` and `memopop-ai` are named as likely future SurrealDB
106+
adopters of the same schemaless-canonical-layer + observations-as-log
107+
pattern.
108+
109+
**What it carries** (matches what was scoped here originally):
110+
111+
- The schema shape: `persons`, `organizations`, `affiliations` (a real
112+
`RELATE` edge, `in`/`out`/`kind`/`client_access`/`added_at`),
113+
`observations` (`subject`/`predicate`/`object`/`source`/`observed_at`/
114+
`client` — schemaless, predicates grow freely: `has_name`,
115+
`has_email`, `has_linkedin_url`, `affiliated_with`, `located_in`, and
116+
the event-tie family `speaker_at`/`sponsor_of`/`exhibitor_at`/
117+
`attended`), `events` (`slug`/`name`/`client`/`client_access`/`source`).
118+
- **The verification pattern**, generalized past tonight's specific
119+
case: given a batch, confirm (a) rows exist with the right fields, (b)
120+
client tagging is correct **checked explicitly, not inferred from a
121+
filtered query**, (c) relationships exist where the write path is
122+
supposed to create them. Flag, don't silently fix — a gap can be a
123+
correct outcome (skip is first-class; an org resolved independently of
124+
any person is first-class).
125+
- Query recipes for the common shapes, including the
126+
`observed_at`-must-be-in-projection SurrealDB 2.x gotcha hit while
127+
building tonight's diagnostic.
128+
- **Client tagging as its own explicit check** — the per-table shape
129+
documented precisely: `persons`/`organizations`/`events`/`affiliations`
130+
use `client_access: string[]`; `observations` uses `client: string`
131+
(singular — a real inconsistency, not a typo); `events` carries both.
132+
133+
## Tonight's actual verification task — RUN, findings below
134+
135+
Ran directly against SurrealDB Cloud (a disposable diagnostic script,
136+
before the MCP+skill were wired — the MCP server came online mid-session
137+
after this ran). Scope: everything touched by the People-CSV flow in the
138+
last 3 days, cross-checked against
139+
`clients/reach-edu/inputs/events/freedomfest/2026-07-08_freedomfest-2026-speakers.csv`.
140+
141+
**1. Client tagging — clean.** All 65 `persons` rows and all 40
142+
`organizations` rows created in the batch carry `client_access:
143+
["reach-edu"]` correctly. Zero tagging gaps found, including on the
144+
re-check that didn't filter by client first (the check the plan called
145+
out as the one a naive filtered query would miss).
146+
147+
**2. Person↔org affiliation — mostly missing.** Only **1 of 65 persons**
148+
(Ethan Akimoto, the original hand-test) has an `affiliations` edge. The
149+
other 64 — all created via the batch run — have `has_name` +
150+
`speaker_at` observations but no affiliation edge, even though 39
151+
additional organizations were independently created/matched during the
152+
same run, correctly tagged, just never `RELATE`d to their person. This is
153+
exactly the gap the prior changelog entry (person-db-resolver's ship
154+
entry) flagged as an open question: nothing retroactively relates a
155+
person to an org resolved on the same row unless both steps happen in one
156+
pass with the person resolved first.
157+
158+
**3. Duplicate person rows — a new finding, not previously flagged.**
159+
"Ethan Akimoto" and "Rudolfo Beltran" each have two separate `persons`
160+
rows (one from early hand-testing, one from the batch run); "Lt Gov
161+
Stavros Anthony" and "Stavros Anthony" look like the same person split
162+
across two rows by title-stripping. Candidate matching should have caught
163+
these on the second pass — worth investigating whether the fuzzy-match
164+
threshold or the create-without-reviewing-candidates path is the cause.
165+
166+
**Coverage:** 65 of 200 CSV speakers resolved so far (the batch run
167+
stopped partway through, around "Jonathan Riches").
168+
169+
## Open questions
170+
171+
- ~~Exact skill name~~ — resolved: `surrealdb-canonical-layer`, written
172+
generally per the scope decision above.
173+
- Read-only MCP role (see Component 1) — still open, resolve before this
174+
becomes a routine tool, not just an occasional one.
175+
- Whether the skill should also carry write patterns — resolved: no,
176+
stays read/verify only, so the UI's match/create/skip discipline (and
177+
its idempotency guarantees) isn't bypassed by a raw `CREATE`/`RELATE`
178+
run through MCP.
179+
- New from tonight's findings: does the People-CSV flow's next iteration
180+
need a "resolve org, then retroactively RELATE any already-resolved
181+
person on the same row" step, or should the operator workflow just be
182+
documented as "resolve person before org, always"? Deferred — the user
183+
is not fixing the UI right now, may iterate after more diagnostics.
184+
- New from tonight's findings: the duplicate-person gap (Ethan Akimoto,
185+
Rudolfo Beltran, Stavros Anthony) — worth a look whenever UI iteration
186+
resumes.
187+
188+
## See also
189+
190+
- `augment-it/context-v/plans/Person-Aware-Canonical-Resolver-Extension.md`
191+
— the schema and the person/org/affiliation/observation write path this
192+
skill reads, doesn't reinvent.
193+
- `context-v/skills/search-lossless-corpus/SKILL.md` — the Chroma
194+
precedent this plan's shape (MCP for access, Skill for discipline) is
195+
copied from.
196+
- `context-v/skills/surrealdb-canonical-layer/SKILL.md` — the shipped skill.
197+
- [SurrealMCP GitHub](https://github.com/surrealdb/surrealmcp),
198+
[SurrealMCP announcement](https://surrealdb.com/blog/introducing-surrealmcp)

scripts/mcp-surrealdb.sh

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#!/usr/bin/env bash
2+
# Launches the surrealmcp Docker image against this repo's SurrealDB Cloud
3+
# instance. Invoked by Claude Code as the `surrealdb` MCP server's command
4+
# (see ../.mcp.json) — never run by hand.
5+
#
6+
# Sources .env relative to this script's own location (not $PWD) so it works
7+
# regardless of which directory `claude` was started from, and independent
8+
# of whether the launching shell happens to have SURREAL_* already exported.
9+
# .env is gitignored; nothing here reads or writes secrets to a committed file.
10+
set -euo pipefail
11+
12+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
13+
ENV_FILE="$SCRIPT_DIR/../.env"
14+
15+
if [ -f "$ENV_FILE" ]; then
16+
set -a
17+
# shellcheck disable=SC1090
18+
source "$ENV_FILE"
19+
set +a
20+
fi
21+
22+
required=(SURREAL_URL SURREAL_NS SURREAL_DB SURREAL_USER SURREAL_PASS)
23+
for var in "${required[@]}"; do
24+
if [ -z "${!var:-}" ]; then
25+
echo "mcp-surrealdb.sh: missing $var (checked $ENV_FILE)" >&2
26+
exit 1
27+
fi
28+
done
29+
30+
exec docker run --rm -i --pull always \
31+
-e SURREALDB_URL="$SURREAL_URL" \
32+
-e SURREALDB_NS="$SURREAL_NS" \
33+
-e SURREALDB_DB="$SURREAL_DB" \
34+
-e SURREALDB_USER="$SURREAL_USER" \
35+
-e SURREALDB_PASS="$SURREAL_PASS" \
36+
surrealdb/surrealmcp:latest start

0 commit comments

Comments
 (0)