Node.js + TypeScript MCP 서버 구현: - 5개 도구: inspect_page, inspect_site, get_inspection, get_issues, get_history - 듀얼 트랜스포트: stdio (Claude Desktop) + Streamable HTTP (Docker/원격) - i18n 지원 (영어/한국어) - Docker 통합 (port 3100) + Nginx /mcp 프록시 - Smithery 레지스트리 배포 설정 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
123 lines
3.6 KiB
TypeScript
123 lines
3.6 KiB
TypeScript
import { ApiClient, type InspectionResult, type SiteInspectionResult } from "../api-client.js";
|
|
import { t } from "../i18n/index.js";
|
|
|
|
export async function getInspection(
|
|
client: ApiClient,
|
|
id: string,
|
|
lang: "en" | "ko",
|
|
): Promise<string> {
|
|
// Try single-page first, then site inspection
|
|
try {
|
|
const r = await client.getInspection(id);
|
|
return formatSingleResult(r, lang);
|
|
} catch {
|
|
// Might be a site inspection ID
|
|
}
|
|
|
|
try {
|
|
const r = await client.getSiteInspection(id);
|
|
return formatSiteResult(r, lang);
|
|
} catch {
|
|
return t("get_inspection.not_found", lang, { id });
|
|
}
|
|
}
|
|
|
|
function formatSingleResult(r: InspectionResult, lang: "en" | "ko"): string {
|
|
const lines: string[] = [];
|
|
const duration = r.duration_seconds
|
|
? `${r.duration_seconds.toFixed(1)}s`
|
|
: "—";
|
|
|
|
lines.push(`# ${t("result.title", lang)}`);
|
|
lines.push(`**URL**: ${r.url}`);
|
|
lines.push(`**Status**: ${r.status}`);
|
|
lines.push(`**Inspection ID**: ${r.inspection_id}`);
|
|
|
|
if (r.status !== "completed") {
|
|
return lines.join("\n");
|
|
}
|
|
|
|
lines.push(
|
|
`**${t("result.overall_score", lang)}**: ${r.overall_score}/100 (${r.grade})`,
|
|
);
|
|
lines.push(`**${t("result.duration", lang)}**: ${duration}`);
|
|
if (r.accessibility_standard) {
|
|
lines.push(`**${t("result.standard", lang)}**: ${r.accessibility_standard}`);
|
|
}
|
|
lines.push("");
|
|
|
|
// Category scores
|
|
lines.push(`## ${t("result.category_scores", lang)}`);
|
|
lines.push(
|
|
`| ${t("result.category", lang)} | ${t("result.score", lang)} | ${t("result.grade", lang)} | ${t("result.issues", lang)} |`,
|
|
);
|
|
lines.push("|---|---|---|---|");
|
|
|
|
for (const [key, label] of [
|
|
["html_css", "HTML/CSS"],
|
|
["accessibility", "Accessibility"],
|
|
["seo", "SEO"],
|
|
["performance_security", "Performance/Security"],
|
|
] as const) {
|
|
const cat = r.categories[key];
|
|
if (!cat) continue;
|
|
lines.push(
|
|
`| ${label} | ${cat.score} | ${cat.grade} | ${cat.total_issues} |`,
|
|
);
|
|
}
|
|
lines.push("");
|
|
|
|
lines.push(
|
|
`> ${t("result.more_issues_hint", lang, { inspectionId: r.inspection_id })}`,
|
|
);
|
|
|
|
return lines.join("\n");
|
|
}
|
|
|
|
function formatSiteResult(r: SiteInspectionResult, lang: "en" | "ko"): string {
|
|
const lines: string[] = [];
|
|
|
|
lines.push(`# Site ${t("result.title", lang)}`);
|
|
lines.push(`**Root URL**: ${r.root_url}`);
|
|
lines.push(`**Domain**: ${r.domain}`);
|
|
lines.push(`**Status**: ${r.status}`);
|
|
lines.push(`**Site Inspection ID**: ${r.site_inspection_id}`);
|
|
|
|
if (r.aggregate_scores) {
|
|
const a = r.aggregate_scores;
|
|
lines.push("");
|
|
lines.push(
|
|
`**${t("result.overall_score", lang)}**: ${a.overall_score}/100 (${a.grade})`,
|
|
);
|
|
lines.push(`**Pages**: ${a.pages_inspected}/${a.pages_total}`);
|
|
lines.push("");
|
|
|
|
lines.push(`## ${t("result.category_scores", lang)}`);
|
|
lines.push(
|
|
`| ${t("result.category", lang)} | ${t("result.score", lang)} |`,
|
|
);
|
|
lines.push("|---|---|");
|
|
lines.push(`| HTML/CSS | ${a.html_css} |`);
|
|
lines.push(`| Accessibility | ${a.accessibility} |`);
|
|
lines.push(`| SEO | ${a.seo} |`);
|
|
lines.push(`| Performance/Security | ${a.performance_security} |`);
|
|
lines.push("");
|
|
lines.push(`**Total Issues**: ${a.total_issues}`);
|
|
}
|
|
|
|
// Page list
|
|
if (r.discovered_pages.length > 0) {
|
|
lines.push("");
|
|
lines.push("## Pages");
|
|
lines.push("| URL | Score | Grade | Status |");
|
|
lines.push("|---|---|---|---|");
|
|
for (const p of r.discovered_pages) {
|
|
const score = p.overall_score !== undefined ? String(p.overall_score) : "—";
|
|
const grade = p.grade || "—";
|
|
lines.push(`| ${p.url} | ${score} | ${grade} | ${p.status} |`);
|
|
}
|
|
}
|
|
|
|
return lines.join("\n");
|
|
}
|