Graph Contradictions
graph_contradictionsIdentify conflicting facts in your memory graph by detecting pairs connected by a CONTRADICTS edge. Use during reviews or before graph decay to surface unresolved contradictions.
Instructions
Find facts that contradict each other in the memory graph — pairs connected by a CONTRADICTS edge. Use during reviews, before a graph_decay run, or when the user asks about conflicting information. Returns {contradictions: [{node_a, node_b, description, detected_date, resolved}], count} ordered by most-recently detected. By default only unresolved pairs are surfaced; set include_resolved=true to audit historical resolutions. Resolve a contradiction by graph_weaken on the wrong edge or by graph_relate with relation=SUPERSEDES on the new fact.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| include_resolved | No | Include resolved contradictions (default: false) |
Implementation Reference
- src/mcp-server/index.ts:429-446 (registration)Registration of the graph_contradictions tool on the MCP server with input schema (include_resolved), description, and read-only annotation.
// ─── Tool: graph_contradictions ─── server.registerTool("graph_contradictions", { title: "Graph Contradictions", description: "Find facts that contradict each other in the memory graph — pairs connected by a CONTRADICTS edge. Use during reviews, before a graph_decay run, or when the user asks about conflicting information. Returns `{contradictions: [{node_a, node_b, description, detected_date, resolved}], count}` ordered by most-recently detected. By default only unresolved pairs are surfaced; set include_resolved=true to audit historical resolutions. Resolve a contradiction by graph_weaken on the wrong edge or by graph_relate with relation=SUPERSEDES on the new fact.", inputSchema: { include_resolved: z.boolean().optional().default(false).describe("Include resolved contradictions (default: false)"), }, annotations: { readOnlyHint: true }, }, async (args) => { try { const result = await client.findContradictions(currentTenant(), args.include_resolved ?? false); return toolResult(result); } catch (err) { return toolError(`graph_contradictions failed: ${err instanceof Error ? err.message : String(err)}`); } }); - src/shared/neo4j-client.ts:824-865 (handler)The findContradictions handler method that queries the Neo4j database for CONTRADICTS edges between entities, optionally filtering for unresolved contradictions, ordered by most-recently detected.
async findContradictions(tenantId: string, includeResolved = false): Promise<{ contradictions: Array<{ node_a: { id: string; type: string; name: string }; node_b: { id: string; type: string; name: string }; description: string; detected_date: string; resolved: boolean; }>; count: number; }> { const resolvedFilter = includeResolved ? "" : "AND r.resolved = false"; const rows = await this.run( ` MATCH (a:Entity {tenant_id: $tenantId})-[r:CONTRADICTS]->(b:Entity {tenant_id: $tenantId}) WHERE 1=1 ${resolvedFilter} RETURN a.id AS aId, labels(a) AS aLabels, a.name AS aName, b.id AS bId, labels(b) AS bLabels, b.name AS bName, r.description AS description, r.detected_date AS detected_date, r.resolved AS resolved ORDER BY r.detected_date DESC `, { tenantId }, ); const contradictions = rows.map((row) => ({ node_a: { id: String(row["aId"]), type: (row["aLabels"] as string[]).find((l) => l !== "Entity") ?? "Entity", name: String(row["aName"]), }, node_b: { id: String(row["bId"]), type: (row["bLabels"] as string[]).find((l) => l !== "Entity") ?? "Entity", name: String(row["bName"]), }, description: String(row["description"] ?? ""), detected_date: toISOString(row["detected_date"]), resolved: Boolean(row["resolved"]), })); return { contradictions, count: contradictions.length }; } - src/shared/types.ts:15-40 (schema)Definition of RELATIONSHIP_TYPES including 'CONTRADICTS' (line 23) as a valid relationship type used by the contradictions tool.
export const RELATIONSHIP_TYPES = [ "WORKS_ON", "PREFERS", "KNOWS_ABOUT", "DEPENDS_ON", "USES_TECH", "DECIDED_FOR", "SUPERSEDES", "CONTRADICTS", "RELATED_TO", "ALIAS_OF", "PARTICIPATED_IN", "OCCURRED_DURING", "PRODUCED", "TRIGGERED_BY", "USES", "HOSTED_ON", "PRODUCED_BY", // Reasoning trace edges (Reasoning -> any) "LED_TO", // Reasoning -> Decision/Event/Fact (this thinking led to that outcome) "INVOLVED_IN", // Reasoning -> Person/Project/Concept/Object (these entities took part in the reasoning) // Organizational relationships (added 2026-05-06 after claude.ai introduced them) "WORKS_AT", // Person -> Organization (employment / membership) "REPORTS_TO", // Person -> Person (org hierarchy) "STAKEHOLDER_IN", // Person -> Decision/Project/Event (interested party, not necessarily decider) ] as const; - src/mcp-server/index.ts:439-443 (helper)The async handler function that calls client.findContradictions and returns the result via toolResult, with error handling.
}, async (args) => { try { const result = await client.findContradictions(currentTenant(), args.include_resolved ?? false); return toolResult(result); } catch (err) {