MCP Connect AI clients to GraphJin through catalog-first Model Context Protocol tools. agentic guide agentic/mcp agentic/mcp.md

MCP

Connect AI clients to GraphJin through catalog-first Model Context Protocol tools.

Try it with no setup at all

Nothing to start, no Docker, no config file, no model key:

Shell
claude mcp add graphjin -- graphjin mcp --demo
codex mcp add graphjin -- graphjin mcp --demo

graphjin mcp --demo extracts the built-in SaaS ops demo to ./graphjin-demo — SQLite, in-process, no containers — and serves MCP over stdio. Your IDE’s own model does the reasoning, so no provider key is needed. Delete ./graphjin-demo to reset. Point at another demo vertical from a repo clone with --path examples/<name>.

The catalog-first, governed path is measured across models in DeepORG .

The demo deliberately keeps its own configuration out of reach. It sets mcp.allow_config_updates: false and puts the gj_config root behind admin, so a stdio caller gets validate_config — enough to dry-run a change — but neither get_current_config nor update_current_config. To configure GraphJin from your IDE, scaffold your own project with graphjin serve new my-api; the generated dev.yml enables all three.

Connect to a running server

Shell
graphjin mcp add codex
graphjin mcp add claude

Defaults are client codex, server http://localhost:8080, and project scope; the URL is normalized to /api/v1/mcp. Use --global to make the connection available outside the current project.

This path is HTTP-only and requires GraphJin to already be running — the command sends a real MCP initialize probe and stops if nothing answers. Start the server first:

Shell
graphjin serve --demo     # terminal 1
graphjin mcp add codex    # terminal 2

For hosted GraphJin:

Shell
codex mcp add graphjin --url https://graphjin.example.com/api/v1/mcp
claude mcp add --transport http graphjin https://graphjin.example.com/api/v1/mcp
GraphJin MCPdiscovery to governed actionDiscovergj_catalogValidatewhere-clauseCheckgj_securityActgoverned graphobserve & refresh

Tool philosophy

GraphJin’s MCP surface starts with discovery:

  • Search catalog rows before writing a query.
  • Ask for query syntax and examples before choosing operators.
  • Inspect gj_security before writes, workflows, config changes, file access, or code changes.
  • Execute through saved queries or validated GraphQL.

query_catalog and execute_saved_query use the same artifact overlay as HTTP GraphQL. With a configured artifact store and user_id, the caller sees global config files plus their own gj_artifacts rows.

Exact query_catalog(id: ...) and query_catalog(ids: ...) lookups also merge visible approved catalog annotations as explicitly untrusted organizational context. Raw gj_catalog and broad catalog searches do not contain annotation text.

Declared-task lifecycle and journal writes stay on GraphQL roots gj_task and gj_task_entry; GraphJin adds no task-specific MCP tools or resources. The one MCP addition is optional task_id on ask_graphjin_agent, which loads the same-owner open or verifying task as untrusted warm-start context and journals the run. Task closes can declare a saved-query check in verify_json; GraphJin runs it as the stored owner and returns verified, failed, or pending state through the same GraphQL row. Agent responses also surface active and failed task notices. See Declared Tasks .

Verified by TestMCPCLIParity cmd/mcp_parity_test.go:18
Verified by TestProcessCursorsForMCP serv/mcp_cursor_test.go:20
Verified by TestUserArtifactSavedQueryOverridesGlobalOnlyForOwner serv/artifact_overlay_test.go:94

Cursor IDs

MCP responses replace opaque GraphJin cursor strings with short cursor IDs when cursor caching is available. Clients pass those IDs back to execute_graphql or execute_saved_query; GraphJin expands them to the original encrypted cursor before execution.

Do not hardcode gj-, __gj-enc:, or any cursor prefix in an MCP client. GraphJin uses a dynamic security prefix, and prefix guessing can make encrypted cursor recognition fail.

JSON
{
  "name": "ProductsPage",
  "variables": {
    "cursor": "cursor_01H..."
  }
}

Verified by TestMCP_CursorRoundtripIntegration serv/mcp_test.go:282
Verified by TestMCP_AlreadyEncryptedCursorUnchanged serv/mcp_test.go:669

Production identity

HTTP MCP endpoints can be protected by OAuth or the same JWT/OIDC context as the main API. Stdio mode is useful for local development.

Capability-aware tools

The MCP tool list is catalog-first in both sources and non-sources configs. In dev and agentic, graphql_help, query_catalog, execute_saved_query, validate_where_clause, and the server-side agent are available without feature toggles, with the remaining policy-allowed primitive tools alongside them. Set mcp.include_tools_with_agent: false only when the agent should be the single front door. Raw GraphQL remains separately gated by mcp.allow_raw_queries.

The same Streamable HTTP endpoint serves modern stateless MCP 2026-07-28 requests and legacy stateful clients automatically. The built-in agent always uses GraphJin-owned provider credentials and fails closed when they are absent.

For local development, named query auto-save and workflow saves fall back to config files only when there is no user_id or no artifact store.

Verified by TestRegisterTools_SourcesUsedRawGraphQLCapabilityControlsTool serv/mcp_registration_test.go:316
Verified by TestMCPCallerCapabilityProfileReflectsSourceRootAccess serv/mcp_registration_test.go:551

Configure GraphJin from your AI IDE

While you are building, you can stop editing config files by hand. Ask your IDE for a database connection or a role that only sees its own rows; GraphJin checks the change against your real databases, then applies it and writes it back to dev.yml.

Three dev-mode tools do this. They are registered even when the built-in agent is the MCP front door, so an AI IDE keeps first-class config access:

ToolWhat it does
get_current_configReads the running config with secrets redacted. Optional section: sources, system, workflows, databases, relationships, tables, roles, blocklist, functions, resolvers, mcp, or all.
validate_configA real dry run, not a lint. Runs the entire update pipeline — databases actually connected, schema actually discovered, reload impact classified — then discards the staged runtime. Returns valid, errors, a change summary, scope, reload_mode, and reload_strategy. Nothing is written, not even a preview.
update_current_configApplies the change and reloads. Additionally requires mcp.allow_config_updates.

What an agent can change

Databases, roles and RBAC in full (per-role, per-table query/insert/update/upsert/delete with limit, filters, columns, presets, and block), source access policy, tables, blocklist, functions, resolvers, relationships, workflows, and an allowlisted slice of serv — the agent’s model, max_steps, timeout_seconds, read_only, return_trace, plus log_level, log_format, web_ui, http_compress, server_timing, and rate_limiter.

Database changes are all-or-nothing: every new or changed connection is tested live, and if any one fails, no database change is applied.

What it can never change

Off-limitsWhy
auth, redis, uploadsSecret-bearing server settings. Read-only on gj_config by design — edit the file and restart.
agent.enabled, provider, api_key_env, base_urlGate startup wiring or name secrets. Only agent tuning fields are writable.
read_only: true databasesSnapshotted at startup. A runtime patch flipping one to false is forced back to true and logged.
System database namespostgres, mysql, information_schema, master and friends are rejected unless explicitly allowed.
Plaintext secretsRejected unless a local keystore key is configured.

Beyond dev, the doors close. In agentic mode these tools are not registered at all; config writes move to the gj_config GraphQL root, which is admin-only and still needs mcp.allow_config_updates — the shipped agentic.yml sets it to false. In production the surface is off and fails closed.

Note that the source-mode previewapply handshake is a consistency guard, not a human approval step: apply must carry the preview_id and the exact same payload, matched by catalog revision and payload hash.

Recipes an agent can follow

The catalog ships 15 config_recipe rows, each with preflight checks, the apply mutation, verification, and stop conditions. An agent finds them through query_catalog — adding a role, setting source access defaults, classifying tables, enabling artifacts, tasks or watches, rate limiting, agent tuning, JWT auth, Redis caching, uploads, and production hardening.

See How Configuration Works for every config interface, including the graphjin config CLI and GJ_* environment variables.

Tool inventory

GraphJin deliberately exposes a small MCP surface. A scaffolded dev project has nine tools:

graphql_help, query_catalog, validate_where_clause, execute_saved_query, execute_graphql, get_current_config, validate_config, update_current_config, and ask_graphjin_agent.

The shipped demos have seven: they set mcp.allow_config_updates: false, which drops update_current_config, and they put the gj_config root behind admin, which hides get_current_config from a non-admin caller. Agentic and production deployments drop the config tools entirely, and execute_graphql stays gated behind mcp.allow_raw_queries.

The tool list is filtered per caller, so what you see depends on your role as well as the mode. Tools that need a GraphJin system root — query_catalog and ask_graphjin_agent need gj_catalog, the config tools need gj_config — disappear for callers who cannot reach that root.

Watch event resource

When watches are enabled, MCP exposes an aggregate caller-scoped resource and a per-watch template:

text
graphjin://watch-events/unseen
graphjin://watch-events/unseen/{watch_id}

Clients should retain the ID returned by gj_watch, RFC 6570-expand the template so reserved characters in the ID are percent-encoded, and subscribe to that concrete per-watch URI. Exact subscriptions receive notifications/resources/updated only for that watch; the URI identifies which watch changed, and a read returns compact metadata only. The aggregate resource retains owner/account-wide compatibility for hosts without per-URI subscription support; those hosts must filter to the conversation’s watch IDs before reading full events or marking them seen. Full event payloads remain behind gj_watch_event or the REST/GraphQL watch APIs.

Unsubscribing from this MCP resource only removes the in-memory resource subscription. It never pauses, expires, deletes, or cleans up watch definitions.

Creation, flow preview/approval, autonomous-action approval, pause/resume, and updates all use gj_watch. See Choosing Watches, Flows, and Workflows for the decision matrix and review examples.

Verified by TestWatchMCPPerWatchRoutingSameOwnerSessions serv/watches_test.go:1571
Docs