Skip to content
Conduit

mcp server

graphcode

Governed graph substrate for coding agents (Claude Code, OpenCode, any MCP-stdio host) — Apply-Gate + on-disk Kuzu store + MCP tools

andreassigloch/graphcodecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

Before you start

  • Node.js 20 or newer
~/Library/Application Support/Claude/claude_desktop_config.json
{
  "mcpServers": {
    "andreassigloch-graphcode": {
      "command": "npx",
      "args": [
        "-y",
        "@sigloch/graphcode"
      ]
    }
  }
}

same format Cursor, GitHub Copilot CLI, Junie, JetBrains AI Assistant, Augment (Auggie CLI), Factory Droid, Kiro, Devin CLI +12

Values in angle brackets are placeholders. Restart Claude Desktop after editing its config.Claude Desktop docs

README

synced Oct 17, 2026

GraphCode — governed graph substrate for coding agents

GraphCode gives a coding agent (Claude Code, OpenCode, …) a governed graph of a project's model — requirements, tests, modules, traces — behind an MCP-stdio surface. The agent KNOWS the elements to touch from a precise graph query instead of guessing them with grep. Code stays as text in the repo; the model lives in the graph.

Commands

All commands run inside the target repository:

npx @sigloch/graphcode init          # THE install. One command, one download: scaffolds .mcp.json,
                                     # GRAPHCODE.md and the store dir; the GVE viewer comes along as a
                                     # dependency, so the first start needs no network.
                                     # ↓ everything below is run FOR you or only when you need it ↓
npx @sigloch/graphcode mcp           # THE server — your agent host starts this from .mcp.json; you
                                     # normally never type it. Brings up all three: MCP-stdio, the
                                     # read-only HTTP/SSE bridge, and the GVE dashboard.
npx @sigloch/graphcode status        # is my host up, and where is MY dashboard? Read-only: asks the
                                     # viewer via api/config WHICH REPO it serves instead of
                                     # guessing a port. Exit 1 if either is missing.
npx @sigloch/graphcode host          # FALLBACK only — the bridge alone, for a repo with no agent
                                     # session running. Alongside a live `mcp` it hits the store lock.
npx @sigloch/graphcode rewind <ref>  # recall the graph state committed at <ref> — reads the snapshot
                                     # from git object storage, so the working tree is NOT touched.
                                     # Aborts while un-exported model edits are pending (--force drops them)
npx @sigloch/graphcode upgrade       # install the latest version, refresh artifacts, stop the old host — PRESERVES the store
npx @sigloch/graphcode skills sync   # re-copy the shipped se-* skills (overwrites on version mismatch)
npx @sigloch/graphcode remove        # remove all scaffolded artifacts (restlos)

Get started

Prerequisite: Node ≥ 22. Nothing is installed globally — graphcode lives per repo, as a dependency in its package.json.

Fresh clone of a repo that already uses graphcode? Skip init — .mcp.json and GRAPHCODE.md are committed. Run npm install and open the agent host: the store .graphcode/ is gitignored, and the host builds it on first start from the committed model (docs/graph/*.graph.json, seed-on-empty). Then continue at step 3.

1. Scaffold (idempotent, self-contained):

npx @sigloch/graphcode init
ArtifactPurposeCommit?
.mcp.jsonClaude-schema host config — launches npx @sigloch/graphcode mcp✅ commit
opencode.jsonthe same server in OpenCode's schema (mcp.graphcode)✅ commit
.graphcode/the per-repo Kuzu store (.graphcode/kuzu), created lazily on first run❌ gitignore
GRAPHCODE.mdguardrails for agents working in the repo✅ commit
GRAPHCODE-STEERING.mdthe human's companion — the four decisions only you can make, and what docs/views/ is✅ commit
package.jsongains the @sigloch/graphcode dependency✅ commit

Both host configs are written every time and merged, never overwritten: a foreign MCP server in .mcp.json, or your provider / model block in opencode.json, survives init, update and remove — only the graphcode entry is ours.

Both clients get the full tool list and write through the gate themselves. That holds for a local model too — a local model with a thinking level follows the vorschlag each write returns.

The executor in the host (graphcode run, graph_delegate, the switches GRAPHCODE_CLIENT_LLM / GRAPHCODE_WRITE_PATH, the executor section in graphcode.config.jsonc) was moved out of the core on 2026-10-09; its last state is the tag executor-geparkt-2026-10-09. update drops the two switches from the host configs.

2. Add .graphcode/ to .gitignore and reload your agent host. Claude Code picks up .mcp.json, OpenCode picks up opencode.json. The agent then sees a graphcode MCP server exposing the tools below.

The MCP surface is the agent-agnostic contract — any MCP-stdio host works. The se-* skills are Claude Code surface (.claude/skills/); an OpenCode user drives the same tools directly.

3. Say this to your agent. That is the whole first step — the server prints the same line on startup:

Lies GRAPHCODE.md, dann: graph_readiness — wo steht das Projekt und was ist der nächste Schritt?

On an empty repo (no model yet) start from the intent instead:

Lies GRAPHCODE.md, dann leg mit se:generate los: "<was das System tun soll, in einem Satz>"

4. Dashboard — already running. graphcode mcp starts the GVE viewer itself, along with the read-only /health + /events (SSE) bridge. Do not start a second server — ask where yours is:

npx @sigloch/graphcode status
graphcode status — auth-service  (/Users/you/dev/auth-service)
  MCP-Host    OK             pid 40001, seit 2026-08-19T09:12:03.000Z
  Dashboard   OK             http://localhost:4318/

status reads docs/views/dashboard.url (GVE writes its actual bound address there on startup, removes it on shutdown) and then asks that instance api/config which repo it serves. The address itself is stable per repo — GVE derives it from the repo path (43000–43999) — so your bookmark keeps working across restarts; the identity check stays as the safety net for the rare collision, where Vite bumps and a neighbour's viewer could end up on the address yours left behind. Only an instance serving THIS repo is reported as yours. Opt out with GRAPHCODE_NO_GVE=1; the bridge port comes from GRAPHCODE_HOST_PORT in .mcp.json env, which init/update scaffolds per repo.

Lifecycle — the host lives exactly as long as your session (CR-GC-370..372). Closing the editor (or anything that ends the stdio client) shuts the host down in order: viewer → HTTP bridge → host.sock → store lock. Reopening therefore wins a REAL election instead of proxying a host whose editor is gone, and brings its dashboard back at the same address. Three guards behind that: a viewer that dies mid-session is restarted (1 s / 3 s / 10 s, then it says so and stops trying); the store lock carries a heartbeat, so a lock without a pulse for 90 s is free even if its PID is alive (PID reuse after a reboot, a hung host); and a host whose lock was taken over notices at its next beat and ends its session rather than becoming a second writer.

5. To upgrade: run graphcode upgrade — one command: installs the latest version, lets THAT build refresh .mcp.json, GRAPHCODE.md and skills, never touches the store.

Using it — the agent loop (over MCP)

The agent drives the whole loop through MCP tools — every write goes through the one Apply-Gate (mutate()): rule-checked, author-logged, blocked on new violations.

  1. Spec — graph_mutate adds requirements/tests/modules + traces through the gate. A REQ with no verifying TEST (or unresolved by a MOD) is rejected — drift can't land.
  2. KNOW, not grep — graph_impact(id) returns the exact blast-radius (the incoming dependents: the tests and modules that touch the changed node) as a bounded Format-E slice, never a full dump. graph_expand deepens one branch on demand.
  3. Implement — graph_mutate updates node status as code lands; the change persists to disk Kuzu.
  4. Re-export — graph_export serializes the live graph to commit-able docs: canonical docs/graph/<member>.graph.json (the SSOT) + deterministic docs/views/*.md (GENERATED headers). This is the single sync path — never hand-edit the graph JSON.

The member name (<member>.graph.json) is derived from the repo's package.json name (unscoped), falling back to the repo directory name.

MCP tools

All 23 MCP tools, grouped by role — this table is complete, and a test asserts the count against the live registry so it cannot silently fall behind the code.

RoleToolWhat it does
readgraph_elementsfiltered slice of the graph, never a full dump
graph_get_nodeone node with its attributes
graph_get_edgesthe traces of one node
graph_impactthe exact blast-radius: who breaks if I change this (KNOW, not grep)
graph_expanddeepen one branch on demand
graph_contextthe definition-of-done pack for ONE node — spec closure in one call
writegraph_mutatethe write path — through the Apply-Gate (human or AI, same gate); also binds model to code: ~ FUNC-x + @realRef {…}, @testRefs [...] on a TEST
graph_mergereplay a branch worktree's command log onto this store (semantic rebase); the node merge is Format-E M source + target via graph_mutate
graph_reseedin-process reseed from the committed SSOT, with an automatic backup
measurerules_evaluate, rules_get_violationsrun the SE rules read-only
graph_readinessthe readiness report: dimensions, the marks SRR/PDR/CDR/TRR/Bau, blocking errors
graph_metricsper-module architecture metrics plus the thresholds they were judged against
generategraph_generatethe cold-start driver: seed → expand → handoff, as a state machine
graph_suggestrank candidate fixes by how far they move the graph toward your target
graph_authoring_guidethe legal edges for a given element type, before you write
test evidencegraph_test_ingestfeed a test run's results back into the graph
graph_test_reportthe report over those results
graph_testswhich tests exist, and what they are bound to
exportgraph_exportre-export the live graph to docs/graph + docs/views
auditaudit_trail, audit_statsmutation history — every gate write logged
helpgraph_helpexplain any rule ID, gate, or dashboard token (read-only)

The realRef/testRefs binding and graph_test_ingest/graph_test_report/graph_tests are the answer to the obvious objection that a model can drift from its code: the binding is an attribute the rules check (R-19, R-20), and "done" means a test result landed in the graph, not that someone ticked a box.

Bringing an existing project in

You do not have to start from an empty graph. Two import paths ship, with deliberately different semantics — the difference is safety-relevant, not cosmetic:

SkillSourceSemantics
se:import-codean existing TypeScript codebasedeterministic extraction via graphify — no LLM. Produces FUNC/MOD/FLOW/SCHEMA, never use cases or requirements (nobody can read intent out of code). RESEED, not merge: the graph is replaced, and a backup is written automatically first.
se:import-doca document (PDF / Markdown / text)two-stage: the skeleton is shown to you, you decide the element types in chat, then an LLM extract goes through the Apply-Gate like any other write. MERGE (adds only) — nothing existing is removed.

Use import-code to bootstrap a brownfield repo's architecture layer, then import-doc to bring the specification in on top of it.

Generated documents

The SE document family is rendered from the graph, deterministically — same graph, same bytes. Each has a skill that renders it into docs/views/:

SkillDocument
se-view:rtmRequirements Traceability Matrix
se-view:archArchitecture allocation (SDD)
se-view:icdInterface Control Document
se-view:nfrNFR register
se-view:testconcept · se-view:testmatrixtest concept (pyramid + computed E2E gap) · VCRM
se-view:intplan · se-view:implplanintegration & test plan · implementation plan (MS/CR)
se-view:conopsConcept of Operations (ISO 29148 §5.2.4)
se-view:fmeaFMEA — failure modes, S/O/D, action priority
se-view:changelogchange log from the CR history

Because they are generated, "the document is incomplete" and "the gate is still open" are the same fact with the same cause — there is no separate documentation debt to track.

se-retro computes the six KPIs over a finished stretch of work (graph-vs-grep, tool usage, token/LOC, plan conformance, gate health, binding coverage).

Help — explain any item, for both audiences

Every on-screen token is explained in three layers (plain · in SE terms · the exact fix), for a systems engineer who doesn't know this encoding and a user with no SE background:

  • se:help <token> — explain a rule (R-04), mark (CDR), panel, or artifact (fmea).
  • se:help (no argument) — ranked, explained next steps from the live readiness + violations.
  • graph_help — the read-only MCP tool the skill is a thin surface over.

The plain/SE wording is authored once (src/surface/help-content.ts); titles, severity, and the owning gate are derived from V3_RULES + readiness, so help never drifts from the live model.

What it is / is not

  • IS: Bridge + Store + MCP tools + Apply-Gate. Agent-agnostic, headless, one Kuzu store per repo.
  • IS NOT: a generator (→ aimprove), a learning engine (→ learning-core), a viewer/dashboard, or an extractor (→ graphify).

Viewer integration

GraphCode is headless and is not itself a viewer — it ships the read-only data layer, and graph-view-edit (GVE) is the renderer that consumes it. The elected host spawns GVE for you (see Get started step 4); these are the pieces it wires up:

  • the read-only HTTP/SSE bridge — owns the single Kuzu store and serves /health + /events. No mutating HTTP verb is reachable; the write path stays MCP-stdio.
  • panel shapers (readinessPanel, impactPanel, artifactsPanel, …) — pure read-only view-models over the MCP tools; the renderer consumes these and fills the render mount-slot.
  • live-update events — every gate mutation emits one update event the host broadcasts over SSE.

The bound address is announced in docs/views/dashboard.url, never a fixed port. The shared view catalog (which documents exist, under which filename) lives in @sigloch/graphcode-client so the viewer can list them without depending on the exporter.

Local development (this repo)

npm install        # everything from the registry — no private repo needed
npm run build      # tsc → dist/
npm test           # vitest — real disk Kuzu, no mocks

The five @sigloch/* dependencies (contracts, graph-api-core, graph-cypher-wasm, learning-core, se-steering) are published packages, so a clone builds and tests without any sibling checkout. To work against a local checkout of those packages instead, run npm run link:siblings (npm link, reversible with npm install).

Migrating a graph across the REQ-kinds major (contracts 10.x)

contracts 10.x narrows REQ.kinds to exactly one of functional | non-functional, moves risk/mitigation to attributes.role, drops precondition/postcondition, and lets a FCHAIN satisfy only non-functional. An old graph still boots; R-18 flags the affected satisfy edges. scripts/migrate-req-kinds.mjs (CR-GC-669) moves it in two steps:

node scripts/migrate-req-kinds.mjs propose --repo <repo> [--graph <name>] --out decisions.json
# review: every entry with decidedBy "heuristic" or "open" is a per-REQ decision — edit kinds/
# role/dropSatisfy/addSatisfy, then set decidedBy to your name
node scripts/migrate-req-kinds.mjs apply --repo <repo> [--graph <name>] --decisions decisions.json

apply writes one batch through graph_mutate and exports via graph_export — no hand edit. It refuses unconfirmed heuristic entries (--accept-heuristic only for trial runs on copies) and a decision file that no longer matches the graph. It opens the repo's store itself, so stop the repo's MCP host first.

Constraints (locked)

  • One store = Kuzu, embedded, single-writer, on disk (.graphcode/kuzu) — never :memory:.
  • One transport = MCP-stdio — no Express/REST in the core.
  • One Apply-Gate = mutate() — every edit (human or AI) goes through it; the author is only logged.
  • SE ontology + V3_RULES from @sigloch/contracts/se — imported, never forked. A new ElementType/TraceType/rule requires a family review + version bump.

Model & docs

GraphCode owns the implementation plus its own graph model. The graph SSOT for graphcode's own model is docs/graph/graphcode.graph.json; the claims, definition of done and test status are in docs/graphcode_leitlinie.md (German). docs/articles/ is the plain-language introduction; the numbers it cites come from docs/archive/spikes/ (raw benchmark runs; the rigs and evaluators that produce current measurements live in a separate private repository). GraphCode is part of a larger internal toolchain; some design-history documents reference private governance docs that are not part of this repository.

License

MIT — see LICENSE.

↑↓ navigate openesc close