Skip to main content
Glama

Claude MCP Server Integration

by mokemoke0821
file-diff.ts7.88 kB
import fs from 'fs-extra'; import { FileDiff } from '../types/index.js'; /** * 2つのファイルの内容を比較し、差分情報を返す */ export async function compareFiles( filePath1: string, filePath2: string, options: { ignoreWhitespace?: boolean; contextLines?: number; maxDiffs?: number; } = {} ): Promise<FileDiff> { const { ignoreWhitespace = false, contextLines = 3, maxDiffs = 1000, } = options; try { // ファイルの内容を読み込む const content1 = await fs.readFile(filePath1, 'utf8'); const content2 = await fs.readFile(filePath2, 'utf8'); // 行に分割 const lines1 = content1.split(/\r?\n/); const lines2 = content2.split(/\r?\n/); // 差分情報を収集 const differences: FileDiff['differences'] = []; let added = 0; let removed = 0; let changed = 0; // シンプルな差分検出アルゴリズム const lcs = longestCommonSubsequence( lines1, lines2, ignoreWhitespace ? (a, b) => a.trim() === b.trim() : undefined ); let i = 0; let j = 0; while (i < lines1.length || j < lines2.length) { if (i < lines1.length && j < lines2.length && lcs[i][j]) { // 一致している行 i++; j++; } else if (j < lines2.length && (i >= lines1.length || !lcs[i][j])) { // 追加された行 differences.push({ lineNumber: j + 1, content2: lines2[j], type: 'added', }); added++; j++; } else if (i < lines1.length && (j >= lines2.length || !lcs[i][j])) { // 削除された行 differences.push({ lineNumber: i + 1, content1: lines1[i], type: 'removed', }); removed++; i++; } else { // 変更された行 differences.push({ lineNumber: i + 1, content1: lines1[i], content2: lines2[j], type: 'changed', }); changed++; i++; j++; } // 最大差分数に達したら中断 if (differences.length >= maxDiffs) { break; } } return { path1: filePath1, path2: filePath2, differences, summary: { added, removed, changed, }, }; } catch (error) { throw new Error(`ファイル比較に失敗しました: ${(error as Error).message}`); } } /** * 2つの配列の最長共通部分列を計算 */ function longestCommonSubsequence<T>( arr1: T[], arr2: T[], equalsFn: ((a: T, b: T) => boolean) | undefined = undefined ): boolean[][] { const equals = equalsFn || ((a: T, b: T) => a === b); const m = arr1.length; const n = arr2.length; // 動的計画法で最長共通部分列を計算 const dp: number[][] = Array(m + 1) .fill(null) .map(() => Array(n + 1).fill(0)); for (let i = 1; i <= m; i++) { for (let j = 1; j <= n; j++) { if (equals(arr1[i - 1], arr2[j - 1])) { dp[i][j] = dp[i - 1][j - 1] + 1; } else { dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); } } } // バックトラックして共通部分を特定 const lcs: boolean[][] = Array(m) .fill(null) .map(() => Array(n).fill(false)); let i = m; let j = n; while (i > 0 && j > 0) { if (equals(arr1[i - 1], arr2[j - 1])) { lcs[i - 1][j - 1] = true; i--; j--; } else if (dp[i - 1][j] > dp[i][j - 1]) { i--; } else { j--; } } return lcs; } /** * ファイル差分をわかりやすく整形したテキストで出力 */ export function formatFileDiff(diff: FileDiff): string { let output = `--- ${diff.path1}\n+++ ${diff.path2}\n\n`; output += `概要: ${diff.summary.added} 行追加, ${diff.summary.removed} 行削除, ${diff.summary.changed} 行変更\n\n`; for (const d of diff.differences) { switch (d.type) { case 'added': output += `@@ ${d.lineNumber}: 追加 @@\n+ ${d.content2}\n\n`; break; case 'removed': output += `@@ ${d.lineNumber}: 削除 @@\n- ${d.content1}\n\n`; break; case 'changed': output += `@@ ${d.lineNumber}: 変更 @@\n- ${d.content1}\n+ ${d.content2}\n\n`; break; } } return output; } /** * ファイル差分をHTMLで整形して出力 */ export function formatFileDiffHtml(diff: FileDiff): string { const html = ` <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>ファイル比較: ${diff.path1} - ${diff.path2}</title> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; padding: 20px; background-color: #f8f9fa; color: #333; } .diff-header { margin-bottom: 20px; } .diff-summary { background-color: #e9ecef; padding: 10px; border-radius: 4px; margin-bottom: 20px; } .diff-block { margin-bottom: 20px; border: 1px solid #dee2e6; border-radius: 4px; overflow: hidden; } .diff-line-number { background-color: #f1f3f5; padding: 8px 12px; font-family: monospace; font-weight: bold; } .diff-content { font-family: monospace; white-space: pre-wrap; padding: 10px; background-color: #fff; } .diff-added { background-color: #e6ffed; border-left: 4px solid #28a745; } .diff-removed { background-color: #ffeef0; border-left: 4px solid #dc3545; } .diff-changed .old { background-color: #ffeef0; border-left: 4px solid #dc3545; margin-bottom: 4px; } .diff-changed .new { background-color: #e6ffed; border-left: 4px solid #28a745; } </style> </head> <body> <div class="diff-header"> <h1>ファイル比較</h1> <div> <p><strong>ファイル1:</strong> ${diff.path1}</p> <p><strong>ファイル2:</strong> ${diff.path2}</p> </div> </div> <div class="diff-summary"> <h2>概要</h2> <p> <span>${diff.summary.added} 行追加</span>, <span>${diff.summary.removed} 行削除</span>, <span>${diff.summary.changed} 行変更</span> </p> </div> <div class="diff-content"> ${diff.differences.map(d => { switch (d.type) { case 'added': return ` <div class="diff-block diff-added"> <div class="diff-line-number">行 ${d.lineNumber}: 追加</div> <div class="diff-content">+ ${d.content2}</div> </div> `; case 'removed': return ` <div class="diff-block diff-removed"> <div class="diff-line-number">行 ${d.lineNumber}: 削除</div> <div class="diff-content">- ${d.content1}</div> </div> `; case 'changed': return ` <div class="diff-block diff-changed"> <div class="diff-line-number">行 ${d.lineNumber}: 変更</div> <div class="diff-content old">- ${d.content1}</div> <div class="diff-content new">+ ${d.content2}</div> </div> `; default: return ''; } }).join('')} </div> </body> </html> `; return html; }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mokemoke0821/claude-mcp-integration'

If you have feedback or need assistance with the MCP directory API, please join our Discord server