You are working on a Graphitron subgraph with an MCP-aware coding agent (Claude Code, Cursor), and you want the agent to understand your project: which tables the catalog holds, which @service classes exist, what the directive vocabulary means, and where to find the right pattern in the manual. mvn graphitron:dev already runs an MCP server for exactly this. Point your agent at it once and the agent stops guessing.
|
This is the companion to the dev loop. The same |
TLDR
| Step | What to run |
|---|---|
1. Start the dev server |
|
2. Register the server |
Run the copy-pasteable line the dev server prints on startup: |
3. Ask the agent |
Run the |
The MCP server is loopback-only and read-only: it exposes context about the project you are running graphitron:dev in, nothing else.
What the MCP server gives an agent
An MCP-aware agent connected to the server gets three kinds of context, all served off the same warm store, which every dev-loop pass captures into. status tells you how current that capture is rather than leaving you to assume.
An about prompt (surfaced as the /mcp__graphitron__about slash command) that orients the agent on the dev loop and how to use the other tools. Run it first in a fresh session.
A directives resource, your schema’s directive vocabulary: per directive its arguments (with their types and defaults), the locations it may be applied at, whether it is repeatable, and its description. Every directive the schema defines, graphitron’s own and any you declare yourself, so the agent knows what @table, @field, @reference, @service and your own @guard mean without you pasting a reference in. Read off the live build, so before the first successful build it says so rather than answering with a partial vocabulary.
A set of tools the agent calls to answer questions:
| Tool | What it answers |
|---|---|
|
Natural-language retrieval over the bundled manual ("which directive joins two tables?"). Returns the matching passages with a link into this site, so the agent finds the right pattern without already knowing the vocabulary to grep for. |
|
Semantic, fuzzy discovery over your jOOQ catalog ("where are customer addresses stored?"). Returns the most relevant tables ranked by similarity, by schema-qualified SQL name. Feed a hit straight into |
|
Lists the tables in the catalog. |
|
Full column / key / foreign-key detail for one table. Columns arrive in the table definition’s order, and the keys are what the database declares: a unique constraint over the primary key’s own columns is reported rather than filtered out, so the answer is the catalog rather than a row-identity view of it. |
|
Describes what graphitron made of your schema, one entry per type and per |
|
Lists the consumer Java classes on the compile classpath, selected by |
|
The current schema validation diagnostics, the same |
|
Counts those same diagnostics grouped by dimensions you choose (what kind of error, which attempted name, which file or directory) instead of listing them. On a schema mid-migration this answers "what is broken, in what proportion" in one small result, and each group’s key feeds back into |
|
Whether the dev-loop tools are answering, plus how the facts behind them stand: whether anything has captured this module yet, and whether the newest read of your schema refused a file. When it did, the other tools go on answering from the last clean read rather than falling silent, and this is where you see that they are. |
|
The connection coordinates of this session’s read-only SQL console onto its own fact store, for a question none of the tools above asks. Fields, not prose, plus a ready-to-run command. Always present: without a console it answers |
|
Runs a GraphQL query or mutation against your generated resolvers in-process, on the dev database you configure, and returns exactly what the query returns. Mutations always roll back. Appears only when a dev database is configured; see Run a query without an app server below. |
|
|
Run a query without an app server
Point graphitron at your database once, in the plugin configuration or via environment variables, and the MCP execute tool runs a GraphQL query or mutation against your generated resolvers in-process: no Quarkus, no deploy, and the agent (or you) gets back exactly what the query returns. Mutations run in a transaction that is always rolled back, so you can probe writes freely without changing data.
The configuration is plain connection coordinates. In the plugin:
<devDatabase>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<user>dev</user>
<password>dev</password>
<dialect>POSTGRES</dialect> <!-- or ORACLE; always explicit -->
</devDatabase>
Or via environment variables, which override the POM so credentials stay out of the checked-in file: GRAPHITRON_DEV_DB_URL, GRAPHITRON_DEV_DB_USER, GRAPHITRON_DEV_DB_PASSWORD, GRAPHITRON_DEV_DB_DIALECT. The JDBC driver is one your project already has: any scope works (your application’s runtime dependency, or the test-scope driver your database tests use; Quarkus apps typically only have the latter in their Maven graph).
Row-level security still applies. If your schema mounts identity (graphitron’s <sessionState> mount method), give the dev tool a claims payload via GRAPHITRON_DEV_CLAIMS: the same string your mount expects in production (claims JSON, or a genuinely issued dev token if your routine verifies signatures). Use @/path/to/file to keep tokens out of environment listings; the file is re-read on every call, so edits apply immediately. The tool runs your real mount method and sees only what the mounted identity is permitted, just like your running service. If claims are required and missing, the tool says so loudly instead of running unsecured; if your mount rejects the payload, you get its error message verbatim, so you can fix the payload and retry.
No database configured? The execute tool simply does not appear; every other dev tool (schema, catalog, diagnostics, docs) works with no database at all.
Three current limits: federation subgraphs are not yet supported by execute (every other tool works on them); writes cannot be persisted (rollback is unconditional by design); and the rollback guarantee covers mutation-field transactions, so a query-path @service or @routine that writes runs outside it, exactly as it runs outside graphitron’s transaction demarcation in production.
Query the fact store from an agent
The tools above are the questions somebody wrote in advance. When an agent has one they do not cover, the fact store behind them holds a couple of hundred relations that do, and store.console is the door: with the console enabled it returns the connection as fields (host, port, user, password, database) plus a ready connectCommand. Read the fields; there is nothing to parse.
The tool is always present. Without a console it answers {"status": "disabled"} and names the command that starts one, so an agent can tell you what to restart with rather than guessing why psql refuses. The port is fresh per session, so re-read the coordinates after a restart instead of caching them.
The client is psql: the server speaks the PostgreSQL wire protocol, but a JDBC driver cannot connect to it, and the backslash commands are unimplemented, so introspect through information_schema (its identifiers are upper-case). Writes to the store’s relations are refused. Turning the console on, and what it does and does not guarantee, is in Query the fact store while the session runs.
Connect your agent
The server speaks the Model Context Protocol over Streamable HTTP at http://127.0.0.1:8488/mcp. Any MCP-aware agent that supports HTTP transport can connect.
Claude Code
The dev server prints a copy-pasteable line on startup:
claude mcp add --transport http graphitron http://127.0.0.1:8488/mcp
Run it once and Claude Code registers the graphitron server. Alternatively, commit a .mcp.json at the project root so anyone who clones the repo gets the server with zero configuration:
{
"mcpServers": {
"graphitron": {
"type": "http",
"url": "http://127.0.0.1:8488/mcp"
}
}
}
The graphitron-sakila-example project ships exactly this file, so cloning it needs no MCP setup at all.
Cursor and other agents
Any MCP client that accepts an HTTP endpoint works: register a server named graphitron pointing at http://127.0.0.1:8488/mcp with transport type HTTP (sometimes labelled "Streamable HTTP" or "http"). The shape is the same as the .mcp.json above; consult your agent’s MCP configuration docs for where that config lives.
Port and lifecycle
The MCP port is fixed at 8488 (the LSP’s is 8487) and, unlike the LSP port, is not overridable. It stays a well-known value so a committed .mcp.json never drifts. If 8488 is already taken, typically a second graphitron:dev for another project, the dev server fails fast on startup with a message naming the conflict rather than silently rebinding, so a stale .mcp.json never points an agent at the wrong project’s server. Stop the other session occupying the port, then retry.
The server is part of the one graphitron:dev JVM: no daemon, no separate process to manage. Ctrl+C on the dev goal closes the MCP server along with the LSP socket and the watchers.
See also
-
The dev loop (Quarkus + LSP): the editor-facing half of the same
graphitron:devprocess. -
Dev loop reference (architecture docs): how the
devgoal wires the LSP, MCP server, watchers, and generator dispatch into one JVM.