Coordination memory with verification: reconcile-against-GitHub, verdict freshness, provenance.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste into ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows)
A generic, project-agnostic memory / coordination / artifact server for multi-agent and multi-surface work. One FastAPI process serves a 30-tool MCP over Streamable HTTP, backed by Postgres (+ pgvector), deployed standalone on a Replit Reserved VM.
This is Tier 1 of the reusability contract: every project reuses it as-is. It carries zero domain terms β project identity lives in namespace values, never in tool names, tables, columns, or code.
verified_persisted), standardized
error payloads with remedies, write-time screening + quarantine, provenance
tiers (origin, model attribution, derived_from lineage), trust decay
(needs_reverification), PHI-safe tool_events telemetry, per-namespace
variant profiles, and an observation_log feedback channel.namespace.57P01/08xxx)
on a fresh pooled connection, and validates connections at checkout, so callers
no longer have to retry. Retries are idempotency-gated, so they never double-write.[[UNTRUSTED_DATA]], never reconstructed on read),
instruction-shaped writes are screened and quarantined (visible in the write ack;
include_quarantined: true opts reads back in), and reads come back wrapped in
<<<UNTRUSTED_DATA>>> markers; storage.sanitize.unwrap_value recovers the raw
value when a consumer needs it (e.g. to json.loads). Honest framing: these are
layers, not proofs β deterministic screens and wrappers are bypassable by an
adaptive attacker; adversarial evaluation is pending (see Phase 10 backlog in
DECISION-PROTOCOL.md)./admin dashboard.| Group | Tools |
|---|---|
| memory | memory_save memory_get memory_list memory_history memory_delete memory_search |
| handoff | handoff_save handoff_load handoff_list |
| session | session_create session_append_event session_get session_list session_events |
| artifact | artifact_put artifact_get artifact_list |
| coordination | coord_health coord_drift_scan coord_reconcile coord_curate |
| gate | intent_open skill_define gate_close_outcome gate_cache_status |
| sequence | session_bootstrap namespace_init recall |
| feedback | observation_log |
| admin | stats |
Sequences are the paths an agent should take. Correct use of this server has
always been a sequence β learn which database answered, resolve the profile,
check what is stale, then act β and that ordering used to live in tool
descriptions and skills, i.e. it was enforced by a model remembering advice
mid-task, and a skipped step produced no signal whatsoever. The three sequence
tools run their steps server-side in a fixed order and return steps_run, so
the ordering is something the server did rather than something a caller got
right. They are strictly additive: every primitive they are built from still
works exactly as before, for surgical use.
recall and memory_search now share ONE retrieval guard
(storage/retrieval.py): the same absolute floor and relative alpha the Intent
Gate applies. Previously memory_search had no floor at all while intent_open
had both, so the same store answered the same question differently depending on
which tool the caller reached for. They still differ in what they do with the
verdict β memory_search annotates every row and drops nothing, recall
filters by default and reports the rejected counts β but they can no longer
disagree about what counts as a match.
The Intent Gate (claude/intent-gate/INTENT_GATE_CHARTER.md) is a
per-namespace opt-in (variant_profiles.profile.intent_gate: "on"): mutating
tools gain a deterministic Tier-0 pre-flight (two-phase preview/confirm for
supersession + delete, dependency-freshness flags, compact gate ack block),
intent_open adds the Tier-1 memory-similarity critic, and Tier 2 (LLM
reasoning over the direct Anthropic API β never MCP sampling) ships behind
tier2: "on" (default off). Default-profile namespaces are byte-identical to
the pre-gate server.
/healthz (liveness) and the /admin token dashboard are served separately (not
MCP tools).
namespace == project == tenant. One namespace per project (e.g.
acme-billing), with conventional sub-scopes by key prefix (coord/β¦,
knowledge/β¦). Every per-project tool takes a required namespace and every
query filters on it β there are no implicit cross-project reads. The session
and session_event tables carry namespace too, so episodic memory is scoped
like everything else.
Artifacts are the deliberate exception: they are content-addressed (sha256) and dedup globally, so they are not tenant-scoped β the hash is the capability.
The per-surface tokens scope which client surface connects, not which project it may touch: any holder of any active token can pass any namespace, so namespace remains a soft boundary β real isolation for honest clients, not enforced against a misbehaving one.
v2 auth roadmap β per-project tokens/roles. A token scoped to
acme-billingmust not be able to read or writeother-project. Until then, treat the namespace boundary as a convention enforced by client configuration, not by the server. (See REUSABILITY.md β "namespace is the tenant boundary".)
MCP tokens are stored in Postgres (admin_auth_tokens) and rotatable from
/admin without a redeploy. There is one active token per surface:
| surface | label | how the client sends it |
|---|---|---|
| claude.ai web connector | web | ?token=<token> in the URL (the web connector can't send headers) |
| Claude Desktop and the Claude Code CLI | desktop-cli | Authorization: Bearer <token> |
| Cursor | cursor | Authorization: Bearer <token> (via .cursor/mcp.json) |
The gate accepts any active token, so each surface can be rotated or
revoked independently β rotating web never disturbs desktop-cli or
cursor. The
/admin page shows one card per surface with a ready-to-paste URL/command and
its own rotate button.
MCP_AUTH_TOKEN seeds the web token on initial boot (so an existing
claude.ai connector keeps working); desktop-cli and cursor are
auto-generated. After first boot the dashboard is the source of truth.
/admin is password-gated by ADMIN_PASSWORD (signed, HttpOnly session
cookie, CSRF-protected). Without it the dashboard refuses logins.GET /healthz, the streamed
GET /artifact/{sha256}, and /admin (which self-authenticates).Stateless transport. /mcp runs in stateless HTTP mode
(http_app(stateless_http=True)): every request is self-contained, with no
in-memory session affinity. Client sessions therefore survive VM
restarts/redeploys, and the three surfaces share no server-side session state.
AsyncConnectionPool created in the FastAPI lifespan (app.py), injected
via deps. Nothing else opens a connection. The pool is built with
check=AsyncConnectionPool.check_connection, so a connection terminated
server-side while idle is validated and discarded on checkout, never handed to a
caller.config.py (pydantic-settings) β the only place secrets are read.StorageBackend ABC (storage/base.py) implemented by PostgresBackend; the
18 tools map 1:1 onto it._retry_on_disconnect), so e.g. OperationalError: terminating connection due to administrator command (SQLSTATE 57P01, Neon scale-down / PgBouncer) is
retried on a fresh pooled connection instead of surfacing to the caller. Only
genuine disconnects (08xxx / 57P0x / an already-closed connection) are
retried β other operational errors (lock timeout, too-many-connections) surface
unchanged. Writes retry only when a replay is safe: artifact_put
(content-addressed) always, and memory_save/handoff_save/memory_delete
only when given an event_id (exactly-once). The session_* writes also retry,
with an explicit tradeoff: a drop in the narrow commit-ack window means
session_create may orphan an empty, unreferenced session row, and
session_append_event is at-least-once (a replay can append one duplicate
event). For an append-only session log that beats failing the call outright; a
save with no event_id still runs once and surfaces the error.sanitize strips forged delimiters/control chars; reads wrap values
in <<<UNTRUSTED_DATA>>> markers (lethal-trifecta defense). Note: the
value (and session-event payload) fields come back wrapped, so a consumer
that needs the raw value β e.g. to json.loads a value that was a JSON string β
must strip the markers first. Use storage.sanitize.unwrap_value (or
strip_untrusted for a single string); the wrapping stays applied on every read.pool.wait()), 50 MB artifact cap,
ranged blob reads, idempotent event_id writes, idempotent blob backfill.memory_search): when a VOYAGE_API_KEY is set, every
memory_save embeds the entry (Voyage, voyage-3.5-lite, 1024-dim) into a
nullable embedding vector(1024) column with an HNSW cosine index, and
memory_search ranks live entries by meaning (embedding <=> query), then
backfills keyword/substring matches up to limit. Embedding is best-effort:
it runs before a connection is taken and never blocks (or fails) a write, and
with no key the column stays NULL and search degrades to pure keyword β the
pre-Phase-3 behavior. Every leg filters on namespace first (no cross-project
recall). After enabling a key on an existing DB, embed old rows once with
python scripts/backfill_embeddings.py (idempotent, only touches NULL rows).hnsw.ef_search): the HNSW index is approximate, so a larger
store can miss relevant hits unless its query-time recall parameter is tuned.
memory_search sets hnsw.ef_search per-statement (transaction-local, semantic
leg only) from HNSW_EF_SEARCH (default 100; pgvector's own default is 40).
Higher = better recall, slightly slower search; it must be >= the search limit
to take effect. Small stores return the same rows regardless, so the default is
safe to leave alone β raise it (e.g. 200) if a large tenant reports missing
results, lower it toward 40 to shave latency. Very large tenants can also tune
the index build parameters (m, ef_construction); see
migrations/0002_embeddings.sql (changing those requires recreating the index).coord_reconcile): when GitHub access is available,
a claim (with meta.repo + meta.pr/meta.branch) is resolved against live
GitHub β is PR #N merged? what is branch X's head? β and stamped with an
append-only verdict; without access it stays unverifiable (never a wrong
current). Access is sourced in priority order: an explicit GITHUB_TOKEN
(read-only repo + PRs), else β on Replit β the connected GitHub account via
the Replit connector (token fetched fresh per cache-window so it survives OAuth
refresh), else disabled. Resolution is best-effort: a network/API failure
yields unverifiable, never a blocked write. GITHUB_WEBHOOK_SECRET enables
POST /webhook/github to reconcile affected claims on push / pull_request.coord_curate): when ANTHROPIC_API_KEY is set, a finished
session can be consolidated write-side: coord_curate(namespace, session_id) reads
the session's execution trace plus similar existing memories, asks the model what is
worth persisting, and applies the resulting ADD/UPDATE/MERGE/SUPERSEDE/NOOP
operations deterministically. Every op passes a fail-closed PHI gate first, claims
without provenance (meta.repo + meta.pr/branch) are downgraded to notes,
supersession sets a validity boundary (history is kept, never deleted), and writes are
idempotent (deterministic event_id) so re-running a session never double-writes. It
is best-effort: without the key the curator is disabled and coord_curate is a
clean no-op ({curator_enabled: false, operations: []}), and any model/parse failure
yields zero operations β never a wrong write. dry_run=True returns the proposed
operations without writing. CURATOR_MODEL and CURATOR_MAX_OUTPUT_TOKENS tune it.
Curated rows also carry a second hyde_embedding, so memory_search can match a
future question (HyDE leg) as well as the stored statement.The suite runs against a real Postgres and skips cleanly if DATABASE_URL is
unset. The neutral test project is proj-test (never a real project name).
CI (.github/workflows/test.yml) spins an ephemeral Neon branch per run,
migrates it, runs pytest, and deletes the branch. Set repo secrets
NEON_API_KEY and NEON_PROJECT_ID to enable it.
scripts/smoke_mcp.py performs the exact handshake a Claude connector does β
initialize + tools/list over /mcp with a valid token β and asserts HTTP 200
with the full 30-tool surface, plus the guard rails (no/bad token β 401,
/healthz db ok). It exists so a transport/auth/host regression (like the
fastmcp 3.4.3 421) can never ship silently again.
Blocks a bad build: the in-process half runs in CI via pytest
(tests/test_smoke_mcp.py) against the ephemeral Neon branch β a broken
handshake, gate, or tool count fails the build before it can deploy.
Flags an unhealthy live deploy: run it against the deployed URL after a deploy. It exits non-zero on any failed check:
.github/workflows/smoke.yml runs this against a live URL on manual dispatch
(or a 6-hour schedule); set repo secrets SMOKE_BASE_URL and SMOKE_TOKEN to
enable it (it no-ops cleanly when unset).
DATABASE_URL (pooled endpoint), MCP_AUTH_TOKEN, and
ADMIN_PASSWORD (plus optional Phase-3 keys).deploymentTarget = "vm") β not Autoscale;
the durability gate needs the process to persist.run step runs python scripts/migrate.py then starts uvicorn.https://<your-vm>/admin, sign in, and copy/rotate the token. Point each
client at https://<your-vm>/mcp.pyproject.toml declares loose ranges, but the deploy build, post-merge.sh, and
make install all pass -c constraints.txt, so prod installs the exact versions
verified in dev. This is the guardrail against the class of failure that caused the
prod /mcp 421 outage (an unpinned build silently resolving a newer fastmcp).
To intentionally upgrade a dependency (so pins don't rot):
Never hand-edit versions in constraints.txt; always regenerate with make lock
(scripts/lock-deps.sh). The file header documents the same procedure.
Use the pooled connection string for the running service; psycopg is
configured with prepare_threshold=None for PgBouncer transaction pooling. Tests
and scripts/migrate.py use a direct endpoint (the test pool keeps prepared
statements on).
This repo is Tier 1 only. Canvas-specific MCP tools, FHIR logic, and SDK
knowledge live in the separate canvas-sdk-tools repo β never here.
Idempotent (dedup by sha256), streams each file, skips/reports anything over the 50 MB cap, and verifies a random sample by checksum readback.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/mcp-assist-coordination-memory)<a href="https://allmcps.com/mcp/mcp-assist-coordination-memory"><img src="https://allmcps.com/api/badge/mcp-assist-coordination-memory?style=directory" alt="MCP Assist β Coordination Memory on AllMCPs" /></a>