Agent guide
For coding agents (Claude Code or any other) and the people directing them. It says what the system is, where to change what, how to verify, and what goes wrong. It is agent-agnostic; the worked example uses Claude Code.
Where to run this. Docker Compose commands run inside a clone of this repository (
git clone https://github.com/grand-stack/grandstack, thencd grandstack), with the repositories it builds cloned beside it. The quickstart has the exact steps.
Mental model in six lines
- Neo4j holds a graph. The GraphQL API is generated from
grandstack-server/schema/schema.graphql; there are no resolvers to edit. - The web app (
grandstack-web) asks that API for data using operations written withgraphql()and typed bynpm run codegenagainst the live schema. - State a user may want to share (filters) lives in the URL.
- Sample data comes from a versioned Cypher seed; titles and names are unique keys.
- Writes go through generated mutations; a server plugin keeps stored film counts correct.
- The default stack has no authentication, and the app can edit and delete everything. An opt-in setup (
compose.auth.yaml, thegrandstack-authrepo) adds login, a gateway and row-level rules;grandstack-webis intentionally not part of it.
Notes on setup
npm ci prints npm warn install-scripts ... approve warnings; they are harmless. The package versions in package.json match the git tags.
Where to change what
| You want to… | Change | Then run |
|---|---|---|
| Add or change a field, type or relationship | grandstack-server/schema/schema.graphql (+ seed if it needs data) | server checks, then npm run codegen in the web repo |
| Change the sample data | grandstack-server/seed/movies.cypher, bump // seed-version | restart the server |
| Add a computed field | @cypher in the schema | server checks |
| Change server behaviour or add a setting | grandstack-server/src/ | server checks |
| Show a field, add a filter, page or form | grandstack-web/src/ | web checks |
| Change look and feel | grandstack-web/src/theme/tokens.css, src/styles.css | web checks |
| Change ports, services or settings of the stack | grandstack/compose.yaml | docker compose up --build |
| Change who can do what (roles, permissions, rules) | grandstack-auth (gateway/rules.ts, schema/auth.graphql, keycloak/grandstack-realm.json) | its smoke script and tests |
| Add login to a front end | follow grandstack-auth/docs/exercise-add-auth-to-grandstack-web.md in your own copy | its acceptance check |
| Use your own dataset | see build your own app |
Each repository has recipes for the common changes: docs/recipes.md in grandstack-server and in grandstack-web. Start there.
Start the stack
cd grandstack
cp .env.example .env # once
docker compose up --build # web :3000, GraphQL :4000/graphql, Neo4j browser :7474
# with login (development only): gateway :4000, Keycloak :8081, auth demo client :3001
docker compose -f compose.yaml -f compose.auth.yaml up --build
# with federation (Wikipedia summaries on films and people; needs internet): gateway :4000, subgraphs :4001 and :4010
docker compose -f compose.yaml -f compose.federation.yaml up --build
# with an MCP server for agents: http://localhost:4100/mcp (read-only; AGENTS_ALLOW_MUTATIONS=true to write)
docker compose -f compose.yaml -f compose.agents.yaml up --build
Wait for the server log line GraphQL ready at http://localhost:4000/graphql. Use docker compose down -v to reset the data to the sample.
Verification checklist
Run what matches the repo you changed. All of it must pass before you call a change done.
# grandstack-server
npm run lint && npm test && npm run schema:check && npm run docs:check
# grandstack-web (codegen needs a server running with the schema you want; it reads
# GRAPHQL_URL, default http://localhost:4000/graphql)
npm run codegen && npm run typecheck && npm test && npm run docs:check
# grandstack-web browser checks (freshly seeded stack; creates and deletes ZZ-prefixed test data)
npm run e2e
# grandstack-auth: unit tests, then (with the auth stack running) the token-level and browser checks
(cd gateway && npm ci && npm test && npm run typecheck)
(cd client && npm ci && npm test && npm run typecheck)
npm ci && npm run docs:check && npm run smoke && npm run e2e
# grandstack-agents: tests and types need no stack; live needs the open stack, auth:check the auth stack
npm ci && npm test && npm run typecheck && npm run docs:check && npm run live
# grandstack-federation: tests and types need no stack; smoke needs the federation stack and internet
npm ci && npm test && npm run typecheck && npm run docs:check && npm run smoke
schema:check needs a git checkout with the schema committed at HEAD (commit the baseline before you edit). docs:check fails until you have run npm run docs:schema after a schema change; commit the regenerated reference.
For a server schema change also check the live API, for example:
curl -s localhost:4000/graphql -H 'content-type: application/json' \
-d '{"query":"{ movies(limit:1) { title rating } }"}'
Do not claim a UI change works from a build alone: open the page (or use npm run e2e screenshots with OUT=dir) and look.
Using the API without writing code
The endpoint accepts introspection, so an agent can discover the schema itself:
curl -s localhost:4000/graphql -H 'content-type: application/json' \
-d '{"query":"{ __schema { queryType { fields { name description } } mutationType { fields { name } } } }"}'
Every type and field defined in the schema has a description (lint enforces it), and grandstack-server/docs/schema-reference.md lists them. The generated queries and mutations (movies, createMovies, …) have no descriptions but follow a fixed naming pattern per type (see that repo’s docs/schema-guide.md). Filters look like movies(where: { rating: { gte: 8 }, genres: { some: { name: { in: ["Drama"] } } } }); writes are covered in grandstack-server/docs/mutations.md. An MCP server that runs GraphQL statements is available with compose.agents.yaml (port 4100, read-only by default); see grandstack-agents (README.md, docs/direct-graphql.md). How well an agent does depends on how the type definitions are written; see grandstack-agents docs/schema-design-for-agents.md.
Gotchas that cost time
- Every type, field and argument needs a description in the schema, or lint fails.
- Types need
@node(v7). New Neo4j directives need a stub ingrandstack-server/tooling/neo4j-directives.graphql. - Scalar updates are
{ set: value }. A bare value fails validation. - Relationship updates must be diffs. “Disconnect all, then connect” in one mutation removes the new links, because the disconnect runs last.
buildUpdateInputin the web repo shows the right shape. containsis case-sensitive. The web app matches several casings.- Seed changes need a bumped
// seed-version. Cypher map keys are bare words. - Operation names must be unique across the web app, and variables used in non-null positions must be declared non-null.
- Keep
graphqlon 16.x in both repos. Pin exact dependency versions. - Do not add
@neo4j-ndl/*to the web app: it is GPL-3.0 and the repo is Apache-2.0. VITE_GRAPHQL_URLis fixed at build time and must be reachable from the browser; the server’sCORS_ORIGINmust allow the web origin.- Text inputs that write to the URL need local state and a debounce, or fast typing drops characters.
- Never expose the stack publicly. The default stack has no auth, and the auth setup is development-only (demo realm, known passwords, plain HTTP).
- Auth: a new mutation must be added to
MUTATION_PERMISSIONSingrandstack-auth/gateway/rules.tsor the gateway refuses it; server rules must befilter(notvalidate) and avoidDateTime(both need APOC);AUTH_ISSUERis the browser-facing URL whileAUTH_JWKS_URLis the container-reachable one; realm edits need a recreated Keycloak. More ingrandstack-auth/AGENTS.md. - Commit generated files when they change:
grandstack-web/src/graphql/generated/andgrandstack-server/docs/schema-reference.md.
Keep the docs true
Docs live next to the code they describe. When you change behaviour, update the doc in the same change: environment variables in docs/configuration.md (server), routes and folder map in docs/architecture.md (web), versions in versioning and releases. npm run docs:check fails on a stale schema reference (server) or a broken relative link (all repos).
Example: asking Claude Code to make a change
Open Claude Code in the folder that holds the repositories, then give it a task and point it at the docs:
Add a
countryfield to Movie and show it on the movie page. Followgrandstack/docs/agents.mdand the recipes in each repo’sdocs/recipes.md. Run the checks before you say it is done.
A good run follows the recipes: add the described field to the schema, lint and check for breaking changes, regenerate the schema reference, restart the server, add country to the movie query in the web app, run npm run codegen, render it in MovieDetail.tsx, then run the checks above and look at the page. Any other agent can follow the same documents.
Source: grandstack/docs/agents.md (this page is a copy; edit it in the repository)