keyway_diff
Compare secrets between two environments to identify configuration differences for security validation.
Instructions
Compare secrets between two environments to find differences.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| env1 | Yes | First environment (e.g., "development") | |
| env2 | Yes | Second environment (e.g., "production") |
Implementation Reference
- src/tools/diff.ts:127-209 (handler)The `diff` handler function, which orchestrates fetching and comparing secrets between two environments.
export async function diff(args: { env1: string; env2: string }): Promise<CallToolResult> { // Validate inputs if (!args.env1 || !args.env2) { return { content: [{ type: 'text', text: 'Error: Both env1 and env2 are required' }], isError: true, }; } const env1 = normalizeEnvName(args.env1); const env2 = normalizeEnvName(args.env2); if (env1 === env2) { return { content: [{ type: 'text', text: 'Error: Cannot compare an environment with itself' }], isError: true, }; } const token = await getToken(); const repository = getRepository(); // Pull secrets from both environments let secrets1: Record<string, string> = {}; let secrets2: Record<string, string> = {}; let error1: Error | null = null; let error2: Error | null = null; try { const content1 = await pullSecrets(repository, env1, token); secrets1 = parseEnvContent(content1); } catch (err) { error1 = err as Error; } try { const content2 = await pullSecrets(repository, env2, token); secrets2 = parseEnvContent(content2); } catch (err) { error2 = err as Error; } // Handle errors if (error1 && error2) { return { content: [ { type: 'text', text: `Error: Failed to fetch both environments.\n${env1}: ${error1.message}\n${env2}: ${error2.message}`, }, ], isError: true, }; } // Compare secrets const result = compareSecrets(env1, env2, secrets1, secrets2); // Add warnings for empty environments const warnings: string[] = []; if (error1) { warnings.push(`Environment '${env1}' is empty or doesn't exist`); } if (error2) { warnings.push(`Environment '${env2}' is empty or doesn't exist`); } const response = { repository, ...result, ...(warnings.length > 0 && { warnings }), }; return { content: [ { type: 'text', text: JSON.stringify(response, null, 2), }, ], isError: false, }; } - src/index.ts:85-93 (registration)Registration of the `keyway_diff` tool in the MCP server.
server.tool( 'keyway_diff', 'Compare secrets between two environments to find differences.', { env1: z.string().describe('First environment (e.g., "development")'), env2: z.string().describe('Second environment (e.g., "production")'), }, async (args) => diff(args) ); - src/tools/diff.ts:73-125 (helper)Helper function that performs the actual logic of comparing secret dictionaries.
function compareSecrets( env1: string, env2: string, secrets1: Record<string, string>, secrets2: Record<string, string> ): DiffResult { const onlyInEnv1: string[] = []; const onlyInEnv2: string[] = []; const different: DiffEntry[] = []; const same: string[] = []; // Get all unique keys const allKeys = new Set([...Object.keys(secrets1), ...Object.keys(secrets2)]); const sortedKeys = Array.from(allKeys).sort(); for (const key of sortedKeys) { const inEnv1 = key in secrets1; const inEnv2 = key in secrets2; const val1 = secrets1[key] ?? ''; const val2 = secrets2[key] ?? ''; if (inEnv1 && !inEnv2) { onlyInEnv1.push(key); } else if (!inEnv1 && inEnv2) { onlyInEnv2.push(key); } else if (val1 !== val2) { different.push({ key, preview1: previewValue(val1), preview2: previewValue(val2), }); } else { same.push(key); } } return { env1, env2, onlyInEnv1, onlyInEnv2, different, same, stats: { totalEnv1: Object.keys(secrets1).length, totalEnv2: Object.keys(secrets2).length, onlyInEnv1: onlyInEnv1.length, onlyInEnv2: onlyInEnv2.length, different: different.length, same: same.length, }, }; }