Graph Cypher
graph_cypherExecute read-only Cypher queries on the memory graph to retrieve custom data not available through standard tools. Restricted to bootstrap tenant for security.
Instructions
Execute a read-only Cypher query against the memory graph. You generate the Cypher — this tool just runs it. Enforced read-only via Neo4j executeRead(). Use for custom queries not covered by other tools. Admin-only (must be the bootstrap tenant) — non-admin tenants would otherwise be able to bypass tenant filtering by writing raw Cypher.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| cypher | Yes | Cypher query to execute (read-only) | |
| params | No | Query parameters |
Implementation Reference
- src/mcp-server/index.ts:531-556 (handler)The MCP tool handler for graph_cypher. Registers the tool, validates admin-only access via isAdminTenant(), then delegates to client.executeCypher() for read-only Cypher execution.
server.registerTool("graph_cypher", { title: "Graph Cypher", description: "Execute a read-only Cypher query against the memory graph. You generate the Cypher — this tool just runs it. Enforced read-only via Neo4j executeRead(). Use for custom queries not covered by other tools. Admin-only (must be the bootstrap tenant) — non-admin tenants would otherwise be able to bypass tenant filtering by writing raw Cypher.", inputSchema: { cypher: z.string().describe("Cypher query to execute (read-only)"), params: z.record(z.string(), z.unknown()).optional().describe("Query parameters"), }, annotations: { readOnlyHint: true }, }, async (args) => { const tenantId = currentTenant(); if (!isAdminTenant(tenantId)) { return toolError( `graph_cypher is admin-only (your tenant: ${tenantId}). Use graph_query, graph_entities, or graph_search instead — those are tenant-scoped.`, ); } try { const result = await client.executeCypher(args.cypher, args.params ?? {}); return toolResult({ cypher: args.cypher, ...result, }); } catch (err) { return toolError(`graph_cypher failed: ${err instanceof Error ? err.message : String(err)}`); } }); - src/mcp-server/index.ts:531-556 (registration)Tool registration via server.registerTool with name 'graph_cypher', title 'Graph Cypher', read-only annotation, and Zod input schema (cypher string + optional params record).
server.registerTool("graph_cypher", { title: "Graph Cypher", description: "Execute a read-only Cypher query against the memory graph. You generate the Cypher — this tool just runs it. Enforced read-only via Neo4j executeRead(). Use for custom queries not covered by other tools. Admin-only (must be the bootstrap tenant) — non-admin tenants would otherwise be able to bypass tenant filtering by writing raw Cypher.", inputSchema: { cypher: z.string().describe("Cypher query to execute (read-only)"), params: z.record(z.string(), z.unknown()).optional().describe("Query parameters"), }, annotations: { readOnlyHint: true }, }, async (args) => { const tenantId = currentTenant(); if (!isAdminTenant(tenantId)) { return toolError( `graph_cypher is admin-only (your tenant: ${tenantId}). Use graph_query, graph_entities, or graph_search instead — those are tenant-scoped.`, ); } try { const result = await client.executeCypher(args.cypher, args.params ?? {}); return toolResult({ cypher: args.cypher, ...result, }); } catch (err) { return toolError(`graph_cypher failed: ${err instanceof Error ? err.message : String(err)}`); } }); - src/mcp-server/index.ts:535-537 (schema)Input schema for graph_cypher: requires a Cypher query string, optionally accepts a params record of key-value pairs for parameterized queries.
inputSchema: { cypher: z.string().describe("Cypher query to execute (read-only)"), params: z.record(z.string(), z.unknown()).optional().describe("Query parameters"), - src/shared/neo4j-client.ts:1127-1135 (helper)Neo4jClient.executeCypher() method that runs the Cypher query via runReadOnly (READ mode, enforced read-only), returns results with count and execution time.
async executeCypher( cypher: string, params: Record<string, unknown> = {}, ): Promise<{ results: Record<string, unknown>[]; result_count: number; execution_time_ms: number }> { const start = Date.now(); const rows = await this.runReadOnly(cypher, params); const elapsed = Date.now() - start; return { results: rows, result_count: rows.length, execution_time_ms: elapsed }; } - src/shared/auth.ts:219-223 (helper)Admin tenant check used as a guard by graph_cypher. Only the BOOTSTRAP_TENANT_ID tenant may execute raw Cypher queries.
export function isAdminTenant(tenantId: string): boolean { const bootstrap = process.env.BOOTSTRAP_TENANT_ID; if (!bootstrap) return false; return tenantId === bootstrap; }