Demo
Five questions an agent would ask about a codebase, asked over MCP, answered from the graph.
This page is static, so nothing on it executes. Every response below is averbatim transcript captured from a realgopgql-mcp process — the stack inrun it locally, brought up againstdeploy/seed/seed.sql — not a hand-written illustration and not a browser re-implementation. Bring the same stack up yourself and these queries return these responses. Unlikegopgql's playground, which compiles its own Go to WebAssembly and really does run in the page, CodiQ's read path is a PostgreSQL 19 GRAPH_TABLE query — there is no honest way to run it without the database, so the demo records the exchange instead of faking it.The corpus
Three files of one Go package, hand-extracted the way the M2 extractor will produce them, chosen so that every one of the five derived cross-file edge kinds is actually present. A one-file corpus could not exercise any of them.
cmd/codiq/main.go ──imports──▶ internal/graph/store.go ──implements──▶ internal/graph/iface.go
main() ──calls──▶ Store.Put() (cross-file, derived)
Store ──implements──▶ Storer (cross-file, derived)
store ──type_defines──▶ Store (cross-file, derived)
graph.Store (ref) ──resolves_to──▶ Store (def) (cross-file, derived)
s.db (ref) ──references_local──▶ db (def) (same file, extracted)The exchanges
Two definitions are named Put — the interface method and the concrete one. Only the concrete one is called, and the caller is in a third file. Nothing in store.go recorded that main.go exists; the calls edge was derived by matching descriptors after both files were loaded.
Request — sent as the query tool's query argument{
occurrence(name: "Put", role: "definition") {
name
descriptor
definedIn { path }
calledBy { name definedIn { path } }
}
}
Response — captured verbatim{
"occurrence": [
{
"calledBy": [],
"definedIn": [
{ "path": "internal/graph/iface.go" }
],
"descriptor": "scip-go gomod github.com/gaarutyunov/codiq v0.1.0 internal/graph/Storer#Put().",
"name": "Put"
},
{
"calledBy": [
{
"definedIn": [ { "path": "cmd/codiq/main.go" } ],
"name": "main"
}
],
"definedIn": [
{ "path": "internal/graph/store.go" }
],
"descriptor": "scip-go gomod github.com/gaarutyunov/codiq v0.1.0 internal/graph/Store#Put().",
"name": "Put"
}
]
}
This is the whole architecture in one response. Both rows are a reference to the identifier Store, and they carry the same descriptor. The one at byte 222 is the receiver in store.go, whose definition is in the same CST — extraction resolved it, so it has a references_local edge. The one at byte 155 is graph.Store{…} in main.go; a file-local extractor could not resolve it, so it was written with the target descriptor unresolved and the link pass matched it afterwards into resolves_to. Same question, two edge kinds, decided by which file the definition happened to be in.
Request — sent as the query tool's query argument{
occurrence(name: "Store", role: "reference") {
name
rangeStart
resolvesTo { descriptor definedIn { path } }
referencesLocal { descriptor definedIn { path } }
}
}
Response — captured verbatim{
"occurrence": [
{
"name": "Store",
"rangeStart": 222,
"referencesLocal": [
{
"definedIn": [ { "path": "internal/graph/store.go" } ],
"descriptor": "scip-go gomod github.com/gaarutyunov/codiq v0.1.0 internal/graph/Store#"
}
],
"resolvesTo": []
},
{
"name": "Store",
"rangeStart": 155,
"referencesLocal": [],
"resolvesTo": [
{
"definedIn": [ { "path": "internal/graph/store.go" } ],
"descriptor": "scip-go gomod github.com/gaarutyunov/codiq v0.1.0 internal/graph/Store#"
}
]
}
]
}
The SCIP implementation relationship, across files. Storer is declared in iface.go and Store in store.go; neither file mentions the other by identity, and the edge is derived. Note symbolKind is neutral-core vocabulary — interface and type here, not Go‑specific node names.
Request — sent as the query tool's query argument{
occurrence(name: "Storer") {
name
symbolKind
implementedBy { name symbolKind definedIn { path } }
}
}
Response — captured verbatim{
"occurrence": [
{
"implementedBy": [
{
"definedIn": [ { "path": "internal/graph/store.go" } ],
"name": "Store",
"symbolKind": "type"
}
],
"name": "Storer",
"symbolKind": "interface"
}
]
}
One import "internal/graph" in the source becomes one edge per file of that package, because the model’s unit is the file, not the package. pkgName is the descriptor prefix the coordinate resolver supplied from go.mod — it is a property of the project, and no file could have produced it on its own.
Request — sent as the query tool's query argument{
file(path: "cmd/codiq/main.go") {
path
lang
pkgName
imports { path lang }
}
}
Response — captured verbatim{
"file": [
{
"imports": [
{ "lang": "go", "path": "internal/graph/iface.go" },
{ "lang": "go", "path": "internal/graph/store.go" }
],
"lang": "go",
"path": "cmd/codiq/main.go",
"pkgName": "github.com/gaarutyunov/codiq"
}
]
}
The containment skeleton, asked from the scope side. A scope says where a name could resolve and never what it resolves to — so what comes back is byte ranges and the occurrences inside them, with no resolution implied. The second scope is main(): it defines store and then references Store and Put, both of which live in another file.
Request — sent as the query tool's query argument{
scope(kind: "function") {
kind
rangeStart
rangeEnd
occurrences { name role symbolKind }
}
}
Response — captured verbatim{
"scope": [
{
"kind": "function",
"occurrences": [
{ "name": "db", "role": "reference", "symbolKind": "field" }
],
"rangeEnd": 415,
"rangeStart": 210
},
{
"kind": "function",
"occurrences": [
{ "name": "store", "role": "definition", "symbolKind": "variable" },
{ "name": "Store", "role": "reference", "symbolKind": "type" },
{ "name": "Put", "role": "reference", "symbolKind": "method" }
],
"rangeEnd": 305,
"rangeStart": 120
}
]
}
Run them yourself
Bring the stack up, point an MCP client athttp://127.0.0.1:8080/mcp, and ask. The server exposes two tools:introspect, which answers what is queryable without touching the database, and query, which runs a GraphQL operation against it.
docker compose -f deploy/docker-compose.yml up -d --buildRun it locallyThe MCP surface
Provenance
Servergopgql-mcp, serverInfo{ "name": "gopgql", "version": "dev" }, built from source at the commit pinned in deploy/gopgql.Dockerfile.
TransportMCP streamable HTTP, protocol version 2025-06-18.
Databasepostgres:19beta2, migrated with the committedschema/migrations/.
Datadeploy/seed/seed.sql, unmodified.
Captured2026-07-30, while authoring this page.
Responses are reproduced with their key ordering and values unchanged; whitespace is reflowed so the JSON fits the column.