get_conflicts
Audit disagreements between design sources like Figma and codebase. Returns conflict count, pending/resolved status, and suggested fixes.
Instructions
Get conflicts between design sources. Read-only, no side effects. Returns JSON with conflict count, actionable count, and a list of conflicts with type, name, resolution status, and suggested fixes. Pass type: 'all' | 'token' | 'component'. Pass status: 'all' | 'pending' | 'resolved'. Use this to audit disagreements between sources (e.g. Figma vs codebase). For resolved design values, use get_token or get_component instead.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| type | Yes | ||
| status | Yes |
Implementation Reference
- src/mcp/server.ts:262-287 (handler)The actual handler function for the 'get_conflicts' MCP tool. Filters contract.conflicts by type ('all' | 'token' | 'component') and status ('all' | 'pending' | 'resolved'), then returns JSON with count, actionableCount, pendingDecisionCount, and the list of conflicts with their suggestedFix and sources.
async (args) => { if (!this.contract) return this.noContract() const type = args.type || "all" const status = args.status || "pending" let conflicts = this.contract.conflicts if (type !== "all") conflicts = conflicts.filter((c) => c.type === type) if (status !== "all") conflicts = conflicts.filter((c) => status === "pending" ? c.resolution === "pending" : c.resolution !== "pending" ) const actionableCount = conflicts.filter((c) => c.actionable === true).length const pendingDecisionCount = conflicts.filter((c) => c.actionable === false).length return this.json({ count: conflicts.length, actionableCount, pendingDecisionCount, conflicts: conflicts.map((c) => ({ type: c.type, name: c.name, resolution: c.resolution, actionable: c.actionable ?? false, suggestedFix: c.suggestedFix, sources: c.sources })) }) } - src/mcp/server.ts:252-288 (registration)Registration of the 'get_conflicts' tool on the MCP server via this.server.registerTool(), including the inputSchema (type and status as z.string()) and the description.
this.server.registerTool( "get_conflicts", { description: "Get conflicts between design sources. Read-only, no side effects. Returns JSON with conflict count, actionable count, and a list of conflicts with type, name, resolution status, and suggested fixes. Pass type: 'all' | 'token' | 'component'. Pass status: 'all' | 'pending' | 'resolved'. Use this to audit disagreements between sources (e.g. Figma vs codebase). For resolved design values, use get_token or get_component instead.", inputSchema: { type: z.string(), status: z.string() } }, async (args) => { if (!this.contract) return this.noContract() const type = args.type || "all" const status = args.status || "pending" let conflicts = this.contract.conflicts if (type !== "all") conflicts = conflicts.filter((c) => c.type === type) if (status !== "all") conflicts = conflicts.filter((c) => status === "pending" ? c.resolution === "pending" : c.resolution !== "pending" ) const actionableCount = conflicts.filter((c) => c.actionable === true).length const pendingDecisionCount = conflicts.filter((c) => c.actionable === false).length return this.json({ count: conflicts.length, actionableCount, pendingDecisionCount, conflicts: conflicts.map((c) => ({ type: c.type, name: c.name, resolution: c.resolution, actionable: c.actionable ?? false, suggestedFix: c.suggestedFix, sources: c.sources })) }) } ) - src/types.ts:120-131 (helper)The Conflict TypeScript interface that defines the shape of conflict objects used by get_conflicts and created during contract building.
export interface Conflict { type: "token" | "component" name: string sources: Array<{ source: SourceProvenance value: string }> resolved?: string resolution?: "auto" | "manual" | "pending" suggestedFix?: string actionable?: boolean } - src/contract/contract.ts:135-181 (helper)The buildFixMessage helper method in ContractBuilder that generates suggestedFix strings and determines if a conflict is actionable — these messages are stored in the conflict objects returned by get_conflicts.
private buildFixMessage( conflictType: "token" | "component", name: string, sources: Array<{ source: { adapter: string }; value: string }> ): { suggestedFix: string; actionable: boolean } { const sot = this.config.governance.sourceOfTruth const winner = sources.find((s) => s.source.adapter === sot) const losers = sources.filter((s) => s.source.adapter !== sot) if (conflictType === "token") { if (winner) { return { suggestedFix: `Token '${name}' conflicts across sources. ` + `'${sot}' is the source of truth (value: '${winner.value}'). ` + `Update ${losers.map((s) => `'${s.source.adapter}'`).join(", ")} to match, ` + `or change \`governance.sourceOfTruth\` in primitiv.config.js.`, actionable: true } } return { suggestedFix: `Token '${name}' conflicts across sources (${sources.map((s) => `${s.source.adapter}: '${s.value}'`).join(", ")}). ` + `No source of truth is configured for these sources. ` + `Set \`governance.sourceOfTruth\` in primitiv.config.js to resolve.`, actionable: false } } if (winner) { return { suggestedFix: `Component '${name}' is defined in multiple sources. ` + `'${sot}' is the source of truth (path: '${winner.value}'). ` + `Remove the duplicate from ${losers.map((s) => `'${s.source.adapter}'`).join(", ")}, ` + `or change \`governance.sourceOfTruth\` in primitiv.config.js.`, actionable: true } } return { suggestedFix: `Component '${name}' is defined in multiple sources (${sources.map((s) => `${s.source.adapter}: '${s.value}'`).join(", ")}). ` + `Set \`governance.sourceOfTruth\` in primitiv.config.js to resolve.`, actionable: false } } }