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 mvn graphitron:dev process serves both the LSP (for your editor) and the MCP server (for your agent) off one warm workspace. If you have the dev loop running, the MCP server is already up.

TLDR

Step What to run

1. Start the dev server

mvn graphitron:dev in a side terminal. Alongside the LSP on localhost:8487, it binds an MCP server on http://127.0.0.1:8488/mcp (Streamable HTTP, loopback only).

2. Register the server

Run the copy-pasteable line the dev server prints on startup: claude mcp add --transport http graphitron http://127.0.0.1:8488/mcp. Or commit a .mcp.json (see Connect your agent).

3. Ask the agent

Run the /mcp__graphitron__about slash command for orientation, then ask questions in natural language ("which directive joins two tables?", "where are customer addresses stored?"). The agent calls the MCP tools to answer.

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 workspace the dev loop keeps current.

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, the Graphitron directive vocabulary, so the agent knows what @table, @field, @reference, @service, and the rest mean without you pasting the reference in.

A set of tools the agent calls to answer questions:

Tool What it answers

docs.search

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.

catalog.search

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 catalog.describe.

catalog.tables

Lists the tables in the catalog.

catalog.describe

Full column / key / foreign-key detail for one table.

schema

Describes the classified GraphQL schema.

services, conditions, records

List the @service classes, @condition methods, and @record classes the generator resolved on the classpath.

edges

Traverses the cross-reference edges between schema elements and their backing Java / jOOQ.

diagnostics

The current schema validation diagnostics, the same (file, location, message) triples the LSP delivers to your editor.

status

Dev-loop status (ports, warm state).

execute

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.

catalog.search and docs.search are backed by a semantic index that loads asynchronously when graphitron:dev starts and refreshes after a schema change. While the index warms, those two tools return a short notice and point the agent at the structured tools (catalog.tables, catalog.describe, schema, and the rest), which answer immediately in the meantime.

catalog.search is much stronger when your jOOQ-generated catalog carries table and column comments. Enable comment generation in your jOOQ codegen configuration so the database’s own documentation feeds the search; without comments, search still works on table and column names alone.

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> connect hook), give the dev tool a claims payload via GRAPHITRON_DEV_CLAIMS: the same string your connect function expects in production (claims JSON, or a genuinely issued dev token if your function 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 connect function 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 connect function 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.

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