@clazic/kordoc 2.7.4 → 2.7.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -29
- package/dist/{chunk-EJZO6DUI.js → chunk-URSQEMVJ.js} +345 -521
- package/dist/chunk-URSQEMVJ.js.map +1 -0
- package/dist/{chunk-CIR4TB4K.js → chunk-X7UUXEMM.js} +2 -2
- package/dist/cli.js +5 -87
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +447 -631
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -135
- package/dist/index.d.ts +4 -135
- package/dist/index.js +440 -621
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +2 -43
- package/dist/mcp.js.map +1 -1
- package/dist/{utils-LYW4Z2Z6.js → utils-QQVZGOGU.js} +2 -2
- package/dist/{watch-CVSZKJE3.js → watch-RQYUNSSH.js} +3 -3
- package/package.json +1 -2
- package/dist/chunk-EJZO6DUI.js.map +0 -1
- /package/dist/{chunk-CIR4TB4K.js.map → chunk-X7UUXEMM.js.map} +0 -0
- /package/dist/{utils-LYW4Z2Z6.js.map → utils-QQVZGOGU.js.map} +0 -0
- /package/dist/{watch-CVSZKJE3.js.map → watch-RQYUNSSH.js.map} +0 -0
package/dist/mcp.js
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import {
|
|
3
3
|
blocksToMarkdown,
|
|
4
4
|
compare,
|
|
5
|
-
convertToPdf,
|
|
6
5
|
detectFormat,
|
|
7
6
|
extractFormFields,
|
|
8
7
|
extractHwp5MetadataOnly,
|
|
@@ -11,13 +10,13 @@ import {
|
|
|
11
10
|
markdownToHwpx,
|
|
12
11
|
markdownToXlsx,
|
|
13
12
|
parse
|
|
14
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-URSQEMVJ.js";
|
|
15
14
|
import {
|
|
16
15
|
KordocError,
|
|
17
16
|
VERSION,
|
|
18
17
|
sanitizeError,
|
|
19
18
|
toArrayBuffer
|
|
20
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-X7UUXEMM.js";
|
|
21
20
|
import "./chunk-MOL7MDBG.js";
|
|
22
21
|
import "./chunk-S7BHLD2V.js";
|
|
23
22
|
import "./chunk-YW5G6BCJ.js";
|
|
@@ -221,46 +220,6 @@ ${warnings.map((w) => ` ${w}`).join("\n")}`);
|
|
|
221
220
|
}
|
|
222
221
|
}
|
|
223
222
|
);
|
|
224
|
-
server.tool(
|
|
225
|
-
"convert_to_pdf",
|
|
226
|
-
"HWP \uB610\uB294 HWPX \uD30C\uC77C\uC744 PDF\uB85C \uBCC0\uD658\uD558\uC5EC \uC800\uC7A5\uD569\uB2C8\uB2E4.",
|
|
227
|
-
{
|
|
228
|
-
input_path: z.string().min(1).describe("\uBCC0\uD658\uD560 HWP/HWPX \uD30C\uC77C\uC758 \uC808\uB300 \uACBD\uB85C"),
|
|
229
|
-
output_path: z.string().min(1).describe("\uC800\uC7A5\uD560 PDF \uD30C\uC77C\uC758 \uC808\uB300 \uACBD\uB85C"),
|
|
230
|
-
pages: z.string().optional().describe("\uD398\uC774\uC9C0 \uBC94\uC704 (\uC608: '1-3,5')"),
|
|
231
|
-
timeout: z.number().optional().describe("\uBCC0\uD658 \uD0C0\uC784\uC544\uC6C3 (ms, \uAE30\uBCF8 60000)")
|
|
232
|
-
},
|
|
233
|
-
async ({ input_path, output_path, pages, timeout }) => {
|
|
234
|
-
try {
|
|
235
|
-
const resolvedInput = resolve(input_path);
|
|
236
|
-
const resolvedOutput = resolve(output_path);
|
|
237
|
-
const result = await convertToPdf(resolvedInput, {
|
|
238
|
-
pages,
|
|
239
|
-
timeoutMs: timeout
|
|
240
|
-
});
|
|
241
|
-
if (!result.success) {
|
|
242
|
-
return {
|
|
243
|
-
content: [{ type: "text", text: `\uBCC0\uD658 \uC2E4\uD328: ${result.error}` }],
|
|
244
|
-
isError: true
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
writeFileSync(resolvedOutput, result.pdf);
|
|
248
|
-
return {
|
|
249
|
-
content: [{
|
|
250
|
-
type: "text",
|
|
251
|
-
text: `\u2705 \uBCC0\uD658 \uC644\uB8CC: ${resolvedOutput}
|
|
252
|
-
\uC6D0\uBCF8: ${result.sourceFormat}
|
|
253
|
-
\uD06C\uAE30: ${(result.pdf.byteLength / 1024).toFixed(1)}KB`
|
|
254
|
-
}]
|
|
255
|
-
};
|
|
256
|
-
} catch (err) {
|
|
257
|
-
return {
|
|
258
|
-
content: [{ type: "text", text: `\uC624\uB958: ${sanitizeError(err)}` }],
|
|
259
|
-
isError: true
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
);
|
|
264
223
|
server.tool(
|
|
265
224
|
"detect_format",
|
|
266
225
|
"\uD30C\uC77C\uC758 \uD3EC\uB9F7\uC744 \uB9E4\uC9C1 \uBC14\uC774\uD2B8\uB85C \uAC10\uC9C0\uD569\uB2C8\uB2E4 (hwpx, hwp, pdf, unknown).",
|
package/dist/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/** kordoc MCP 서버 — Claude/Cursor에서 문서 파싱 도구로 사용 */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { readFileSync, realpathSync, openSync, readSync, closeSync, statSync, mkdirSync, writeFileSync, readdirSync, existsSync } from \"fs\"\nimport { resolve, isAbsolute, extname, basename } from \"path\"\nimport { parse, detectFormat, blocksToMarkdown, compare, extractFormFields, markdownToHwpx, markdownToXlsx, convertToPdf } from \"./index.js\"\nimport type { ExtractedImage } from \"./types.js\"\nimport { VERSION, toArrayBuffer, sanitizeError, KordocError } from \"./utils.js\"\nimport { createLoggerFromEnv, generateRunId } from \"./logging/logger.js\"\nimport { extractHwp5MetadataOnly } from \"./hwp5/parser.js\"\nimport { extractHwpxMetadataOnly } from \"./hwpx/parser.js\"\nimport { extractPdfMetadataOnly } from \"./pdf/parser.js\"\n\n/** 허용 파일 확장자 */\nconst ALLOWED_EXTENSIONS = new Set([\".hwp\", \".hwpx\", \".pdf\", \".xlsx\", \".docx\"])\n/** 최대 파일 크기 (500MB) */\nconst MAX_FILE_SIZE = 500 * 1024 * 1024\n\n/** 경로 정규화 및 보안 검증 */\nfunction safePath(filePath: string): string {\n if (!filePath) throw new KordocError(\"파일 경로가 비어있습니다\")\n const resolved = resolve(filePath)\n const real = realpathSync(resolved)\n if (!isAbsolute(real)) throw new KordocError(\"절대 경로만 허용됩니다\")\n const ext = extname(real).toLowerCase()\n if (!ALLOWED_EXTENSIONS.has(ext)) throw new KordocError(`지원하지 않는 확장자입니다: ${ext} (허용: ${[...ALLOWED_EXTENSIONS].join(\", \")})`)\n return real\n}\n\n/** 최대 파일 크기 — metadata 전용 (50MB, 전체 파싱보다 보수적) */\nconst MAX_METADATA_FILE_SIZE = 50 * 1024 * 1024\n\n/** 파일 읽기 + 크기 검증 공통 로직 */\nfunction readValidatedFile(filePath: string, maxSize = MAX_FILE_SIZE): { buffer: ArrayBuffer; resolved: string } {\n const resolved = safePath(filePath)\n const fileSize = statSync(resolved).size\n if (fileSize > maxSize) {\n throw new KordocError(`파일이 너무 큽니다: ${(fileSize / 1024 / 1024).toFixed(1)}MB (최대 ${maxSize / 1024 / 1024}MB)`)\n }\n const raw = readFileSync(resolved)\n return { buffer: toArrayBuffer(raw), resolved }\n}\n\n/** 파일 헤더(16바이트)만 읽어 포맷 감지 — 전체 파일 로드 불필요 */\nfunction detectFormatFromHeader(resolved: string): ReturnType<typeof detectFormat> {\n const fd = openSync(resolved, \"r\")\n try {\n const headerBuf = Buffer.alloc(16)\n readSync(fd, headerBuf, 0, 16, 0)\n return detectFormat(toArrayBuffer(headerBuf))\n } finally {\n closeSync(fd)\n }\n}\n\nconst server = new McpServer({\n name: \"kordoc\",\n version: VERSION,\n})\n\nconst mcpLogger = createLoggerFromEnv().withRun(generateRunId(\"mcp\")).child({ component: \"mcp.ts\" })\n\n// ─── 도구: parse_document ────────────────────────────\n\nserver.tool(\n \"parse_document\",\n \"한국 문서 파일(HWP, HWPX, PDF, XLSX, DOCX)을 마크다운으로 변환합니다. 파일 경로를 입력하면 포맷을 자동 감지하여 텍스트를 추출합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\n image_dir: z.string().optional().describe(\"이미지 저장 폴더 경로 (기본: 파일명과 같은 이름의 폴더)\"),\n ocr: z.enum([\"auto\", \"gemini\", \"claude\", \"codex\", \"ollama\", \"off\"]).optional().describe(\"OCR 모드 (이미지 기반 PDF용): auto, gemini, claude, codex, ollama, off\"),\n },\n async ({ file_path, image_dir, ocr }) => {\n mcpLogger.log({ level: \"info\", stage: \"detect\", event: \"start\", message: \"MCP parse_document 시작\", meta: { file_path } })\n try {\n const { buffer, resolved: resolvedFilePath } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer, { ocrMode: ocr as import(\"./types.js\").OcrMode | undefined })\n\n if (!result.success) {\n mcpLogger.log({ level: \"error\", stage: \"finalize\", event: \"error\", message: \"MCP parse_document 실패\", meta: { file_path, error: result.error, code: result.code } })\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n result.metadata?.title ? `제목: ${result.metadata.title}` : null,\n result.metadata?.author ? `작성자: ${result.metadata.author}` : null,\n result.isImageBased ? \"이미지 기반 PDF (텍스트 추출 불가)\" : null,\n ].filter(Boolean).join(\" | \")\n\n // outline/warnings 부가 정보 추가\n const parts: string[] = [`[${meta}]`]\n\n if (result.outline && result.outline.length > 0) {\n const outlineText = result.outline.map(o => `${\" \".repeat(o.level - 1)}- ${o.text}`).join(\"\\n\")\n parts.push(`\\n📑 문서 구조:\\n${outlineText}`)\n }\n\n if (result.warnings && result.warnings.length > 0) {\n const warnText = result.warnings.map(w => `- [p${w.page || \"?\"}] ${w.message}`).join(\"\\n\")\n parts.push(`\\n⚠️ 경고:\\n${warnText}`)\n }\n\n // 이미지 저장\n const savedImages: string[] = []\n if (result.images?.length) {\n const defaultDir = resolve(resolvedFilePath, \"..\", basename(resolvedFilePath).replace(/\\.[^.]+$/, \"\"))\n const imgDir = image_dir ? resolve(image_dir) : defaultDir\n mkdirSync(imgDir, { recursive: true })\n for (const img of result.images) {\n const imgPath = resolve(imgDir, img.filename)\n writeFileSync(imgPath, img.data)\n savedImages.push(imgPath)\n }\n parts.push(`\\n💾 저장된 이미지 (${savedImages.length}개):\\n${savedImages.map(p => ` ${p}`).join(\"\\n\")}`)\n }\n\n parts.push(`\\n\\n${result.markdown}`)\n mcpLogger.log({ level: \"info\", stage: \"finalize\", event: \"done\", message: \"MCP parse_document 완료\", meta: { file_path, fileType: result.fileType } })\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\") }],\n }\n } catch (err) {\n mcpLogger.log({ level: \"error\", stage: \"finalize\", event: \"error\", message: \"MCP parse_document 예외\", error: { message: sanitizeError(err), name: err instanceof Error ? err.name : \"Error\", stack: err instanceof Error ? err.stack : undefined } })\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: convert_document ──────────────────────────\n\nserver.tool(\n \"convert_document\",\n \"마크다운 텍스트를 HWPX 또는 XLSX 파일로 변환하여 저장합니다.\",\n {\n markdown: z.string().min(1).describe(\"변환할 마크다운 텍스트\"),\n output_path: z.string().min(1).describe(\"저장할 파일의 절대 경로 (.hwpx 또는 .xlsx)\"),\n format: z.enum([\"hwpx\", \"xlsx\"]).optional().default(\"hwpx\").describe(\"출력 포맷 (기본: hwpx)\"),\n image_dir: z.string().optional().describe(\"이미지 폴더 경로 (기본: output_path 파일명 폴더)\"),\n template_path: z.string().optional().describe(\"HWPX 템플릿 파일 경로 (hwpx 전용)\"),\n },\n async ({ markdown, output_path, format, image_dir, template_path }) => {\n try {\n const resolvedOutput = resolve(output_path)\n const outputExt = extname(resolvedOutput).toLowerCase()\n if (outputExt !== \".hwpx\" && outputExt !== \".xlsx\") {\n return {\n content: [{ type: \"text\", text: `오류: 출력 파일 확장자는 .hwpx 또는 .xlsx만 허용됩니다: ${outputExt || \"(없음)\"}` }],\n isError: true,\n }\n }\n const warnings: string[] = []\n\n // 이미지 폴더에서 이미지 로드\n const stem = basename(resolvedOutput).replace(/\\.[^.]+$/, \"\")\n const defaultImgDir = resolve(resolvedOutput, \"..\", stem)\n const imgDir = image_dir ? resolve(image_dir) : defaultImgDir\n const images: ExtractedImage[] = []\n\n if (existsSync(imgDir)) {\n const mimeMap: Record<string, string> = {\n png: \"image/png\", jpg: \"image/jpeg\", jpeg: \"image/jpeg\",\n gif: \"image/gif\", bmp: \"image/bmp\",\n }\n for (const entry of readdirSync(imgDir, { withFileTypes: true })) {\n if (!entry.isFile()) continue\n const fname = entry.name\n const ext = extname(fname).slice(1).toLowerCase()\n if (!mimeMap[ext]) continue\n const data = readFileSync(resolve(imgDir, fname))\n images.push({ filename: fname, data: new Uint8Array(data), mimeType: mimeMap[ext] })\n }\n }\n\n let buf: ArrayBuffer\n if (format === \"xlsx\") {\n if (template_path) warnings.push(\"[warn] --template은 hwpx 전용입니다. 무시됩니다.\")\n buf = await markdownToXlsx(markdown, { warnings, images: images.length ? images : undefined })\n } else {\n let templateArrayBuffer: ArrayBuffer | undefined\n if (template_path) {\n const tmpl = readFileSync(safePath(template_path))\n templateArrayBuffer = tmpl.buffer.slice(tmpl.byteOffset, tmpl.byteOffset + tmpl.byteLength)\n }\n buf = await markdownToHwpx(markdown, {\n warnings,\n images: images.length ? images : undefined,\n templateArrayBuffer,\n })\n }\n\n writeFileSync(resolvedOutput, Buffer.from(buf))\n\n const parts = [\n `✅ 변환 완료: ${resolvedOutput}`,\n `포맷: ${format.toUpperCase()}, 크기: ${(buf.byteLength / 1024).toFixed(1)}KB`,\n ]\n if (images.length) parts.push(`포함된 이미지: ${images.length}개 (${imgDir})`)\n if (warnings.length) parts.push(`경고:\\n${warnings.map(w => ` ${w}`).join(\"\\n\")}`)\n\n return { content: [{ type: \"text\", text: parts.join(\"\\n\") }] }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: convert_to_pdf ────────────────────────────\n\nserver.tool(\n \"convert_to_pdf\",\n \"HWP 또는 HWPX 파일을 PDF로 변환하여 저장합니다.\",\n {\n input_path: z.string().min(1).describe(\"변환할 HWP/HWPX 파일의 절대 경로\"),\n output_path: z.string().min(1).describe(\"저장할 PDF 파일의 절대 경로\"),\n pages: z.string().optional().describe(\"페이지 범위 (예: '1-3,5')\"),\n timeout: z.number().optional().describe(\"변환 타임아웃 (ms, 기본 60000)\"),\n },\n async ({ input_path, output_path, pages, timeout }) => {\n try {\n const resolvedInput = resolve(input_path)\n const resolvedOutput = resolve(output_path)\n\n const result = await convertToPdf(resolvedInput, {\n pages,\n timeoutMs: timeout,\n })\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `변환 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n writeFileSync(resolvedOutput, result.pdf)\n\n return {\n content: [{\n type: \"text\",\n text: `✅ 변환 완료: ${resolvedOutput}\\n원본: ${result.sourceFormat}\\n크기: ${(result.pdf.byteLength / 1024).toFixed(1)}KB`,\n }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: detect_format ─────────────────────────────\n\nserver.tool(\n \"detect_format\",\n \"파일의 포맷을 매직 바이트로 감지합니다 (hwpx, hwp, pdf, unknown).\",\n {\n file_path: z.string().min(1).describe(\"감지할 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n return {\n content: [{ type: \"text\", text: `${file_path}: ${format}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_metadata ────────────────────────────\n\nserver.tool(\n \"parse_metadata\",\n \"문서의 메타데이터(제목, 작성자, 날짜 등)만 빠르게 추출합니다. 전체 파싱 없이 헤더/매니페스트만 읽습니다.\",\n {\n file_path: z.string().min(1).describe(\"메타데이터를 추출할 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n // metadata 전용 크기 제한 (50MB)\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\n\n let metadata\n switch (format) {\n case \"hwp\":\n metadata = extractHwp5MetadataOnly(Buffer.from(buffer))\n break\n case \"hwpx\":\n metadata = await extractHwpxMetadataOnly(buffer)\n break\n case \"pdf\":\n metadata = await extractPdfMetadataOnly(buffer)\n break\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ format, ...metadata }, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_pages ──────────────────────────────\n\nserver.tool(\n \"parse_pages\",\n \"문서의 특정 페이지/섹션 범위만 파싱합니다. PDF는 정확한 페이지, HWP/HWPX는 섹션 단위 근사치입니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n pages: z.string().min(1).describe(\"페이지 범위 (예: '1-3', '1,3,5-7')\"),\n },\n async ({ file_path, pages }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer, { pages })\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n `범위: ${pages}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n ].filter(Boolean).join(\" | \")\n\n return {\n content: [{ type: \"text\", text: `[${meta}]\\n\\n${result.markdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_table ──────────────────────────────\n\nserver.tool(\n \"parse_table\",\n \"문서에서 N번째 테이블만 추출합니다 (0-based index). 테이블이 없거나 인덱스 범위를 초과하면 오류를 반환합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n table_index: z.number().int().min(0).describe(\"추출할 테이블 인덱스 (0부터 시작)\"),\n },\n async ({ file_path, table_index }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const tableBlocks = result.blocks.filter(b => b.type === \"table\" && b.table)\n if (tableBlocks.length === 0) {\n return {\n content: [{ type: \"text\", text: `문서에 테이블이 없습니다.` }],\n isError: true,\n }\n }\n\n if (table_index >= tableBlocks.length) {\n return {\n content: [{ type: \"text\", text: `테이블 인덱스 초과: ${table_index} (총 ${tableBlocks.length}개 테이블)` }],\n isError: true,\n }\n }\n\n const tableBlock = tableBlocks[table_index]\n const tableMarkdown = blocksToMarkdown([tableBlock])\n\n return {\n content: [{ type: \"text\", text: `[테이블 #${table_index} / 총 ${tableBlocks.length}개]\\n\\n${tableMarkdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: compare_documents ─────────────────────────\n\nserver.tool(\n \"compare_documents\",\n \"두 한국 문서 파일을 비교하여 추가/삭제/변경된 블록을 표시합니다. 신구대조표 생성에 활용됩니다. 크로스 포맷(HWP↔HWPX) 비교 가능.\",\n {\n file_path_a: z.string().min(1).describe(\"비교 원본 문서의 절대 경로\"),\n file_path_b: z.string().min(1).describe(\"비교 대상 문서의 절대 경로\"),\n },\n async ({ file_path_a, file_path_b }) => {\n try {\n const { buffer: bufA } = readValidatedFile(file_path_a)\n const { buffer: bufB } = readValidatedFile(file_path_b)\n\n const result = await compare(bufA, bufB)\n const { stats, diffs } = result\n\n const lines: string[] = [\n `## 문서 비교 결과`,\n `추가: ${stats.added} | 삭제: ${stats.removed} | 변경: ${stats.modified} | 동일: ${stats.unchanged}`,\n \"\",\n ]\n\n for (const d of diffs) {\n const prefix = d.type === \"added\" ? \"+\" : d.type === \"removed\" ? \"-\" : d.type === \"modified\" ? \"~\" : \" \"\n const text = d.after?.text || d.before?.text || (d.after?.table ? \"[테이블]\" : d.before?.table ? \"[테이블]\" : \"\")\n const sim = d.similarity !== undefined ? ` (${(d.similarity * 100).toFixed(0)}%)` : \"\"\n lines.push(`${prefix} ${text.substring(0, 200)}${sim}`)\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_form ───────────────────────────────\n\nserver.tool(\n \"parse_form\",\n \"한국 서식 문서에서 레이블-값 쌍을 구조화된 JSON으로 추출합니다. 양식/서식 문서에 최적화.\",\n {\n file_path: z.string().min(1).describe(\"서식 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n const form = extractFormFields(result.blocks)\n return {\n content: [{ type: \"text\", text: JSON.stringify(form, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 서버 시작 ───────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err) => { console.error(err); process.exit(1) })\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;AACrC,SAAS,SAAS;AAClB,SAAS,cAAc,cAAc,UAAU,UAAU,WAAW,UAAU,WAAW,eAAe,aAAa,kBAAkB;AACvI,SAAS,SAAS,YAAY,SAAS,gBAAgB;AAUvD,IAAM,qBAAqB,oBAAI,IAAI,CAAC,QAAQ,SAAS,QAAQ,SAAS,OAAO,CAAC;AAE9E,IAAM,gBAAgB,MAAM,OAAO;AAGnC,SAAS,SAAS,UAA0B;AAC1C,MAAI,CAAC,SAAU,OAAM,IAAI,YAAY,sEAAe;AACpD,QAAM,WAAW,QAAQ,QAAQ;AACjC,QAAM,OAAO,aAAa,QAAQ;AAClC,MAAI,CAAC,WAAW,IAAI,EAAG,OAAM,IAAI,YAAY,gEAAc;AAC3D,QAAM,MAAM,QAAQ,IAAI,EAAE,YAAY;AACtC,MAAI,CAAC,mBAAmB,IAAI,GAAG,EAAG,OAAM,IAAI,YAAY,+EAAmB,GAAG,mBAAS,CAAC,GAAG,kBAAkB,EAAE,KAAK,IAAI,CAAC,GAAG;AAC5H,SAAO;AACT;AAGA,IAAM,yBAAyB,KAAK,OAAO;AAG3C,SAAS,kBAAkB,UAAkB,UAAU,eAA0D;AAC/G,QAAM,WAAW,SAAS,QAAQ;AAClC,QAAM,WAAW,SAAS,QAAQ,EAAE;AACpC,MAAI,WAAW,SAAS;AACtB,UAAM,IAAI,YAAY,wDAAgB,WAAW,OAAO,MAAM,QAAQ,CAAC,CAAC,oBAAU,UAAU,OAAO,IAAI,KAAK;AAAA,EAC9G;AACA,QAAM,MAAM,aAAa,QAAQ;AACjC,SAAO,EAAE,QAAQ,cAAc,GAAG,GAAG,SAAS;AAChD;AAGA,SAAS,uBAAuB,UAAmD;AACjF,QAAM,KAAK,SAAS,UAAU,GAAG;AACjC,MAAI;AACF,UAAM,YAAY,OAAO,MAAM,EAAE;AACjC,aAAS,IAAI,WAAW,GAAG,IAAI,CAAC;AAChC,WAAO,aAAa,cAAc,SAAS,CAAC;AAAA,EAC9C,UAAE;AACA,cAAU,EAAE;AAAA,EACd;AACF;AAEA,IAAM,SAAS,IAAI,UAAU;AAAA,EAC3B,MAAM;AAAA,EACN,SAAS;AACX,CAAC;AAED,IAAM,YAAY,oBAAoB,EAAE,QAAQ,cAAc,KAAK,CAAC,EAAE,MAAM,EAAE,WAAW,SAAS,CAAC;AAInG,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,2GAA+C;AAAA,IACrF,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iJAAmC;AAAA,IAC7E,KAAK,EAAE,KAAK,CAAC,QAAQ,UAAU,UAAU,SAAS,UAAU,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,wGAAgE;AAAA,EAC1J;AAAA,EACA,OAAO,EAAE,WAAW,WAAW,IAAI,MAAM;AACvC,cAAU,IAAI,EAAE,OAAO,QAAQ,OAAO,UAAU,OAAO,SAAS,SAAS,mCAAyB,MAAM,EAAE,UAAU,EAAE,CAAC;AACvH,QAAI;AACF,YAAM,EAAE,QAAQ,UAAU,iBAAiB,IAAI,kBAAkB,SAAS;AAC1E,YAAM,SAAS,aAAa,MAAM;AAElC,UAAI,WAAW,WAAW;AACxB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,sFAAqB,SAAS,GAAG,CAAC;AAAA,UAClE,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,MAAM,QAAQ,EAAE,SAAS,IAAgD,CAAC;AAE/F,UAAI,CAAC,OAAO,SAAS;AACnB,kBAAU,IAAI,EAAE,OAAO,SAAS,OAAO,YAAY,OAAO,SAAS,SAAS,mCAAyB,MAAM,EAAE,WAAW,OAAO,OAAO,OAAO,MAAM,OAAO,KAAK,EAAE,CAAC;AAClK,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,8BAAU,OAAO,QAAQ,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,UAC/E,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,OAAO;AAAA,QACX,iBAAO,OAAO,SAAS,YAAY,CAAC;AAAA,QACpC,OAAO,YAAY,uBAAQ,OAAO,SAAS,KAAK;AAAA,QAChD,OAAO,UAAU,QAAQ,iBAAO,OAAO,SAAS,KAAK,KAAK;AAAA,QAC1D,OAAO,UAAU,SAAS,uBAAQ,OAAO,SAAS,MAAM,KAAK;AAAA,QAC7D,OAAO,eAAe,uFAA2B;AAAA,MACnD,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK;AAG5B,YAAM,QAAkB,CAAC,IAAI,IAAI,GAAG;AAEpC,UAAI,OAAO,WAAW,OAAO,QAAQ,SAAS,GAAG;AAC/C,cAAM,cAAc,OAAO,QAAQ,IAAI,OAAK,GAAG,KAAK,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,IAAI;AAC/F,cAAM,KAAK;AAAA;AAAA,EAAgB,WAAW,EAAE;AAAA,MAC1C;AAEA,UAAI,OAAO,YAAY,OAAO,SAAS,SAAS,GAAG;AACjD,cAAM,WAAW,OAAO,SAAS,IAAI,OAAK,OAAO,EAAE,QAAQ,GAAG,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,IAAI;AACzF,cAAM,KAAK;AAAA;AAAA,EAAa,QAAQ,EAAE;AAAA,MACpC;AAGA,YAAM,cAAwB,CAAC;AAC/B,UAAI,OAAO,QAAQ,QAAQ;AACzB,cAAM,aAAa,QAAQ,kBAAkB,MAAM,SAAS,gBAAgB,EAAE,QAAQ,YAAY,EAAE,CAAC;AACrG,cAAM,SAAS,YAAY,QAAQ,SAAS,IAAI;AAChD,kBAAU,QAAQ,EAAE,WAAW,KAAK,CAAC;AACrC,mBAAW,OAAO,OAAO,QAAQ;AAC/B,gBAAM,UAAU,QAAQ,QAAQ,IAAI,QAAQ;AAC5C,wBAAc,SAAS,IAAI,IAAI;AAC/B,sBAAY,KAAK,OAAO;AAAA,QAC1B;AACA,cAAM,KAAK;AAAA,mDAAiB,YAAY,MAAM;AAAA,EAAQ,YAAY,IAAI,OAAK,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,MACnG;AAEA,YAAM,KAAK;AAAA;AAAA,EAAO,OAAO,QAAQ,EAAE;AACnC,gBAAU,IAAI,EAAE,OAAO,QAAQ,OAAO,YAAY,OAAO,QAAQ,SAAS,mCAAyB,MAAM,EAAE,WAAW,UAAU,OAAO,SAAS,EAAE,CAAC;AAEnJ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,KAAK,EAAE,EAAE,CAAC;AAAA,MAClD;AAAA,IACF,SAAS,KAAK;AACZ,gBAAU,IAAI,EAAE,OAAO,SAAS,OAAO,YAAY,OAAO,SAAS,SAAS,mCAAyB,OAAO,EAAE,SAAS,cAAc,GAAG,GAAG,MAAM,eAAe,QAAQ,IAAI,OAAO,SAAS,OAAO,eAAe,QAAQ,IAAI,QAAQ,OAAU,EAAE,CAAC;AACnP,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,UAAe,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,gEAAc;AAAA,IACxD,aAAe,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,4FAAgC;AAAA,IAC1E,QAAe,EAAE,KAAK,CAAC,QAAQ,MAAM,CAAC,EAAE,SAAS,EAAE,QAAQ,MAAM,EAAE,SAAS,gDAAkB;AAAA,IAC9F,WAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0GAAoC;AAAA,IAClF,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uEAA0B;AAAA,EAC1E;AAAA,EACA,OAAO,EAAE,UAAU,aAAa,QAAQ,WAAW,cAAc,MAAM;AACrE,QAAI;AACF,YAAM,iBAAiB,QAAQ,WAAW;AAC1C,YAAM,YAAY,QAAQ,cAAc,EAAE,YAAY;AACtD,UAAI,cAAc,WAAW,cAAc,SAAS;AAClD,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,mIAAyC,aAAa,gBAAM,GAAG,CAAC;AAAA,UAChG,SAAS;AAAA,QACX;AAAA,MACF;AACA,YAAM,WAAqB,CAAC;AAG5B,YAAM,OAAO,SAAS,cAAc,EAAE,QAAQ,YAAY,EAAE;AAC5D,YAAM,gBAAgB,QAAQ,gBAAgB,MAAM,IAAI;AACxD,YAAM,SAAS,YAAY,QAAQ,SAAS,IAAI;AAChD,YAAM,SAA2B,CAAC;AAElC,UAAI,WAAW,MAAM,GAAG;AACtB,cAAM,UAAkC;AAAA,UACtC,KAAK;AAAA,UAAa,KAAK;AAAA,UAAc,MAAM;AAAA,UAC3C,KAAK;AAAA,UAAa,KAAK;AAAA,QACzB;AACA,mBAAW,SAAS,YAAY,QAAQ,EAAE,eAAe,KAAK,CAAC,GAAG;AAChE,cAAI,CAAC,MAAM,OAAO,EAAG;AACrB,gBAAM,QAAQ,MAAM;AACpB,gBAAM,MAAM,QAAQ,KAAK,EAAE,MAAM,CAAC,EAAE,YAAY;AAChD,cAAI,CAAC,QAAQ,GAAG,EAAG;AACnB,gBAAM,OAAO,aAAa,QAAQ,QAAQ,KAAK,CAAC;AAChD,iBAAO,KAAK,EAAE,UAAU,OAAO,MAAM,IAAI,WAAW,IAAI,GAAG,UAAU,QAAQ,GAAG,EAAE,CAAC;AAAA,QACrF;AAAA,MACF;AAEA,UAAI;AACJ,UAAI,WAAW,QAAQ;AACrB,YAAI,cAAe,UAAS,KAAK,8FAAuC;AACxE,cAAM,MAAM,eAAe,UAAU,EAAE,UAAU,QAAQ,OAAO,SAAS,SAAS,OAAU,CAAC;AAAA,MAC/F,OAAO;AACL,YAAI;AACJ,YAAI,eAAe;AACjB,gBAAM,OAAO,aAAa,SAAS,aAAa,CAAC;AACjD,gCAAsB,KAAK,OAAO,MAAM,KAAK,YAAY,KAAK,aAAa,KAAK,UAAU;AAAA,QAC5F;AACA,cAAM,MAAM,eAAe,UAAU;AAAA,UACnC;AAAA,UACA,QAAQ,OAAO,SAAS,SAAS;AAAA,UACjC;AAAA,QACF,CAAC;AAAA,MACH;AAEA,oBAAc,gBAAgB,OAAO,KAAK,GAAG,CAAC;AAE9C,YAAM,QAAQ;AAAA,QACZ,qCAAY,cAAc;AAAA,QAC1B,iBAAO,OAAO,YAAY,CAAC,oBAAU,IAAI,aAAa,MAAM,QAAQ,CAAC,CAAC;AAAA,MACxE;AACA,UAAI,OAAO,OAAQ,OAAM,KAAK,0CAAY,OAAO,MAAM,WAAM,MAAM,GAAG;AACtE,UAAI,SAAS,OAAQ,OAAM,KAAK;AAAA,EAAQ,SAAS,IAAI,OAAK,KAAK,CAAC,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE;AAEhF,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,KAAK,IAAI,EAAE,CAAC,EAAE;AAAA,IAC/D,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,0EAAwB;AAAA,IAC/D,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,qEAAmB;AAAA,IAC3D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAqB;AAAA,IAC3D,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gEAAwB;AAAA,EAClE;AAAA,EACA,OAAO,EAAE,YAAY,aAAa,OAAO,QAAQ,MAAM;AACrD,QAAI;AACF,YAAM,gBAAgB,QAAQ,UAAU;AACxC,YAAM,iBAAiB,QAAQ,WAAW;AAE1C,YAAM,SAAS,MAAM,aAAa,eAAe;AAAA,QAC/C;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAED,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,8BAAU,OAAO,KAAK,GAAG,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAEA,oBAAc,gBAAgB,OAAO,GAAG;AAExC,aAAO;AAAA,QACL,SAAS,CAAC;AAAA,UACR,MAAM;AAAA,UACN,MAAM,qCAAY,cAAc;AAAA,gBAAS,OAAO,YAAY;AAAA,iBAAU,OAAO,IAAI,aAAa,MAAM,QAAQ,CAAC,CAAC;AAAA,QAChH,CAAC;AAAA,MACH;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,iEAAe;AAAA,EACvD;AAAA,EACA,OAAO,EAAE,UAAU,MAAM;AACvB,QAAI;AACF,YAAM,WAAW,SAAS,SAAS;AACnC,YAAM,SAAS,uBAAuB,QAAQ;AAC9C,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,GAAG,SAAS,KAAK,MAAM,GAAG,CAAC;AAAA,MAC7D;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,mHAAyB;AAAA,EACjE;AAAA,EACA,OAAO,EAAE,UAAU,MAAM;AACvB,QAAI;AACF,YAAM,WAAW,SAAS,SAAS;AACnC,YAAM,SAAS,uBAAuB,QAAQ;AAE9C,UAAI,WAAW,WAAW;AACxB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,sFAAqB,SAAS,GAAG,CAAC;AAAA,UAClE,SAAS;AAAA,QACX;AAAA,MACF;AAGA,YAAM,EAAE,OAAO,IAAI,kBAAkB,WAAW,sBAAsB;AAEtE,UAAI;AACJ,cAAQ,QAAQ;AAAA,QACd,KAAK;AACH,qBAAW,wBAAwB,OAAO,KAAK,MAAM,CAAC;AACtD;AAAA,QACF,KAAK;AACH,qBAAW,MAAM,wBAAwB,MAAM;AAC/C;AAAA,QACF,KAAK;AACH,qBAAW,MAAM,uBAAuB,MAAM;AAC9C;AAAA,MACJ;AAEA,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,EAAE,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAC,EAAE,CAAC;AAAA,MACpF;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,8EAAkB;AAAA,IACxD,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,4DAA8B;AAAA,EAClE;AAAA,EACA,OAAO,EAAE,WAAW,MAAM,MAAM;AAC9B,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,kBAAkB,SAAS;AAC9C,YAAM,SAAS,aAAa,MAAM;AAElC,UAAI,WAAW,WAAW;AACxB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,sFAAqB,SAAS,GAAG,CAAC;AAAA,UAClE,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,MAAM,QAAQ,EAAE,MAAM,CAAC;AAE5C,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,8BAAU,OAAO,QAAQ,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,UAC/E,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,OAAO;AAAA,QACX,iBAAO,OAAO,SAAS,YAAY,CAAC;AAAA,QACpC,iBAAO,KAAK;AAAA,QACZ,OAAO,YAAY,uBAAQ,OAAO,SAAS,KAAK;AAAA,MAClD,EAAE,OAAO,OAAO,EAAE,KAAK,KAAK;AAE5B,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,IAAI,IAAI;AAAA;AAAA,EAAQ,OAAO,QAAQ,GAAG,CAAC;AAAA,MACrE;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,8EAAkB;AAAA,IACxD,aAAa,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,EAAE,SAAS,uFAAsB;AAAA,EACtE;AAAA,EACA,OAAO,EAAE,WAAW,YAAY,MAAM;AACpC,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,kBAAkB,SAAS;AAC9C,YAAM,SAAS,aAAa,MAAM;AAElC,UAAI,WAAW,WAAW;AACxB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,sFAAqB,SAAS,GAAG,CAAC;AAAA,UAClE,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,SAAS,MAAM,MAAM,MAAM;AAEjC,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,8BAAU,OAAO,QAAQ,MAAM,OAAO,KAAK,GAAG,CAAC;AAAA,UAC/E,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,cAAc,OAAO,OAAO,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,KAAK;AAC3E,UAAI,YAAY,WAAW,GAAG;AAC5B,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,wEAAiB,CAAC;AAAA,UAClD,SAAS;AAAA,QACX;AAAA,MACF;AAEA,UAAI,eAAe,YAAY,QAAQ;AACrC,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,uDAAe,WAAW,YAAO,YAAY,MAAM,6BAAS,CAAC;AAAA,UAC7F,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,aAAa,YAAY,WAAW;AAC1C,YAAM,gBAAgB,iBAAiB,CAAC,UAAU,CAAC;AAEnD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,wBAAS,WAAW,aAAQ,YAAY,MAAM;AAAA;AAAA,EAAS,aAAa,GAAG,CAAC;AAAA,MAC1G;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,wEAAiB;AAAA,IACzD,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,wEAAiB;AAAA,EAC3D;AAAA,EACA,OAAO,EAAE,aAAa,YAAY,MAAM;AACtC,QAAI;AACF,YAAM,EAAE,QAAQ,KAAK,IAAI,kBAAkB,WAAW;AACtD,YAAM,EAAE,QAAQ,KAAK,IAAI,kBAAkB,WAAW;AAEtD,YAAM,SAAS,MAAM,QAAQ,MAAM,IAAI;AACvC,YAAM,EAAE,OAAO,MAAM,IAAI;AAEzB,YAAM,QAAkB;AAAA,QACtB;AAAA,QACA,iBAAO,MAAM,KAAK,oBAAU,MAAM,OAAO,oBAAU,MAAM,QAAQ,oBAAU,MAAM,SAAS;AAAA,QAC1F;AAAA,MACF;AAEA,iBAAW,KAAK,OAAO;AACrB,cAAM,SAAS,EAAE,SAAS,UAAU,MAAM,EAAE,SAAS,YAAY,MAAM,EAAE,SAAS,aAAa,MAAM;AACrG,cAAM,OAAO,EAAE,OAAO,QAAQ,EAAE,QAAQ,SAAS,EAAE,OAAO,QAAQ,yBAAU,EAAE,QAAQ,QAAQ,yBAAU;AACxG,cAAM,MAAM,EAAE,eAAe,SAAY,MAAM,EAAE,aAAa,KAAK,QAAQ,CAAC,CAAC,OAAO;AACpF,cAAM,KAAK,GAAG,MAAM,IAAI,KAAK,UAAU,GAAG,GAAG,CAAC,GAAG,GAAG,EAAE;AAAA,MACxD;AAEA,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,MAAM,KAAK,IAAI,EAAE,CAAC;AAAA,MACpD;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA;AAAA,IACE,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,SAAS,wEAAiB;AAAA,EACzD;AAAA,EACA,OAAO,EAAE,UAAU,MAAM;AACvB,QAAI;AACF,YAAM,EAAE,OAAO,IAAI,kBAAkB,SAAS;AAC9C,YAAM,SAAS,MAAM,MAAM,MAAM;AAEjC,UAAI,CAAC,OAAO,SAAS;AACnB,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,8BAAU,OAAO,KAAK,GAAG,CAAC;AAAA,UAC1D,SAAS;AAAA,QACX;AAAA,MACF;AAEA,YAAM,OAAO,kBAAkB,OAAO,MAAM;AAC5C,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,CAAC;AAAA,MACjE;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,iBAAO,cAAc,GAAG,CAAC,GAAG,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACF;AAIA,eAAe,OAAO;AACpB,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAChC;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AAAE,UAAQ,MAAM,GAAG;AAAG,UAAQ,KAAK,CAAC;AAAE,CAAC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/mcp.ts"],"sourcesContent":["/** kordoc MCP 서버 — Claude/Cursor에서 문서 파싱 도구로 사용 */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\"\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\"\nimport { z } from \"zod\"\nimport { readFileSync, realpathSync, openSync, readSync, closeSync, statSync, mkdirSync, writeFileSync, readdirSync, existsSync } from \"fs\"\nimport { resolve, isAbsolute, extname, basename } from \"path\"\nimport { parse, detectFormat, blocksToMarkdown, compare, extractFormFields, markdownToHwpx, markdownToXlsx } from \"./index.js\"\nimport type { ExtractedImage } from \"./types.js\"\nimport { VERSION, toArrayBuffer, sanitizeError, KordocError } from \"./utils.js\"\nimport { createLoggerFromEnv, generateRunId } from \"./logging/logger.js\"\nimport { extractHwp5MetadataOnly } from \"./hwp5/parser.js\"\nimport { extractHwpxMetadataOnly } from \"./hwpx/parser.js\"\nimport { extractPdfMetadataOnly } from \"./pdf/parser.js\"\n\n/** 허용 파일 확장자 */\nconst ALLOWED_EXTENSIONS = new Set([\".hwp\", \".hwpx\", \".pdf\", \".xlsx\", \".docx\"])\n/** 최대 파일 크기 (500MB) */\nconst MAX_FILE_SIZE = 500 * 1024 * 1024\n\n/** 경로 정규화 및 보안 검증 */\nfunction safePath(filePath: string): string {\n if (!filePath) throw new KordocError(\"파일 경로가 비어있습니다\")\n const resolved = resolve(filePath)\n const real = realpathSync(resolved)\n if (!isAbsolute(real)) throw new KordocError(\"절대 경로만 허용됩니다\")\n const ext = extname(real).toLowerCase()\n if (!ALLOWED_EXTENSIONS.has(ext)) throw new KordocError(`지원하지 않는 확장자입니다: ${ext} (허용: ${[...ALLOWED_EXTENSIONS].join(\", \")})`)\n return real\n}\n\n/** 최대 파일 크기 — metadata 전용 (50MB, 전체 파싱보다 보수적) */\nconst MAX_METADATA_FILE_SIZE = 50 * 1024 * 1024\n\n/** 파일 읽기 + 크기 검증 공통 로직 */\nfunction readValidatedFile(filePath: string, maxSize = MAX_FILE_SIZE): { buffer: ArrayBuffer; resolved: string } {\n const resolved = safePath(filePath)\n const fileSize = statSync(resolved).size\n if (fileSize > maxSize) {\n throw new KordocError(`파일이 너무 큽니다: ${(fileSize / 1024 / 1024).toFixed(1)}MB (최대 ${maxSize / 1024 / 1024}MB)`)\n }\n const raw = readFileSync(resolved)\n return { buffer: toArrayBuffer(raw), resolved }\n}\n\n/** 파일 헤더(16바이트)만 읽어 포맷 감지 — 전체 파일 로드 불필요 */\nfunction detectFormatFromHeader(resolved: string): ReturnType<typeof detectFormat> {\n const fd = openSync(resolved, \"r\")\n try {\n const headerBuf = Buffer.alloc(16)\n readSync(fd, headerBuf, 0, 16, 0)\n return detectFormat(toArrayBuffer(headerBuf))\n } finally {\n closeSync(fd)\n }\n}\n\nconst server = new McpServer({\n name: \"kordoc\",\n version: VERSION,\n})\n\nconst mcpLogger = createLoggerFromEnv().withRun(generateRunId(\"mcp\")).child({ component: \"mcp.ts\" })\n\n// ─── 도구: parse_document ────────────────────────────\n\nserver.tool(\n \"parse_document\",\n \"한국 문서 파일(HWP, HWPX, PDF, XLSX, DOCX)을 마크다운으로 변환합니다. 파일 경로를 입력하면 포맷을 자동 감지하여 텍스트를 추출합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로 (HWP, HWPX, PDF, XLSX, DOCX)\"),\n image_dir: z.string().optional().describe(\"이미지 저장 폴더 경로 (기본: 파일명과 같은 이름의 폴더)\"),\n ocr: z.enum([\"auto\", \"gemini\", \"claude\", \"codex\", \"ollama\", \"off\"]).optional().describe(\"OCR 모드 (이미지 기반 PDF용): auto, gemini, claude, codex, ollama, off\"),\n },\n async ({ file_path, image_dir, ocr }) => {\n mcpLogger.log({ level: \"info\", stage: \"detect\", event: \"start\", message: \"MCP parse_document 시작\", meta: { file_path } })\n try {\n const { buffer, resolved: resolvedFilePath } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer, { ocrMode: ocr as import(\"./types.js\").OcrMode | undefined })\n\n if (!result.success) {\n mcpLogger.log({ level: \"error\", stage: \"finalize\", event: \"error\", message: \"MCP parse_document 실패\", meta: { file_path, error: result.error, code: result.code } })\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n result.metadata?.title ? `제목: ${result.metadata.title}` : null,\n result.metadata?.author ? `작성자: ${result.metadata.author}` : null,\n result.isImageBased ? \"이미지 기반 PDF (텍스트 추출 불가)\" : null,\n ].filter(Boolean).join(\" | \")\n\n // outline/warnings 부가 정보 추가\n const parts: string[] = [`[${meta}]`]\n\n if (result.outline && result.outline.length > 0) {\n const outlineText = result.outline.map(o => `${\" \".repeat(o.level - 1)}- ${o.text}`).join(\"\\n\")\n parts.push(`\\n📑 문서 구조:\\n${outlineText}`)\n }\n\n if (result.warnings && result.warnings.length > 0) {\n const warnText = result.warnings.map(w => `- [p${w.page || \"?\"}] ${w.message}`).join(\"\\n\")\n parts.push(`\\n⚠️ 경고:\\n${warnText}`)\n }\n\n // 이미지 저장\n const savedImages: string[] = []\n if (result.images?.length) {\n const defaultDir = resolve(resolvedFilePath, \"..\", basename(resolvedFilePath).replace(/\\.[^.]+$/, \"\"))\n const imgDir = image_dir ? resolve(image_dir) : defaultDir\n mkdirSync(imgDir, { recursive: true })\n for (const img of result.images) {\n const imgPath = resolve(imgDir, img.filename)\n writeFileSync(imgPath, img.data)\n savedImages.push(imgPath)\n }\n parts.push(`\\n💾 저장된 이미지 (${savedImages.length}개):\\n${savedImages.map(p => ` ${p}`).join(\"\\n\")}`)\n }\n\n parts.push(`\\n\\n${result.markdown}`)\n mcpLogger.log({ level: \"info\", stage: \"finalize\", event: \"done\", message: \"MCP parse_document 완료\", meta: { file_path, fileType: result.fileType } })\n\n return {\n content: [{ type: \"text\", text: parts.join(\"\") }],\n }\n } catch (err) {\n mcpLogger.log({ level: \"error\", stage: \"finalize\", event: \"error\", message: \"MCP parse_document 예외\", error: { message: sanitizeError(err), name: err instanceof Error ? err.name : \"Error\", stack: err instanceof Error ? err.stack : undefined } })\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: convert_document ──────────────────────────\n\nserver.tool(\n \"convert_document\",\n \"마크다운 텍스트를 HWPX 또는 XLSX 파일로 변환하여 저장합니다.\",\n {\n markdown: z.string().min(1).describe(\"변환할 마크다운 텍스트\"),\n output_path: z.string().min(1).describe(\"저장할 파일의 절대 경로 (.hwpx 또는 .xlsx)\"),\n format: z.enum([\"hwpx\", \"xlsx\"]).optional().default(\"hwpx\").describe(\"출력 포맷 (기본: hwpx)\"),\n image_dir: z.string().optional().describe(\"이미지 폴더 경로 (기본: output_path 파일명 폴더)\"),\n template_path: z.string().optional().describe(\"HWPX 템플릿 파일 경로 (hwpx 전용)\"),\n },\n async ({ markdown, output_path, format, image_dir, template_path }) => {\n try {\n const resolvedOutput = resolve(output_path)\n const outputExt = extname(resolvedOutput).toLowerCase()\n if (outputExt !== \".hwpx\" && outputExt !== \".xlsx\") {\n return {\n content: [{ type: \"text\", text: `오류: 출력 파일 확장자는 .hwpx 또는 .xlsx만 허용됩니다: ${outputExt || \"(없음)\"}` }],\n isError: true,\n }\n }\n const warnings: string[] = []\n\n // 이미지 폴더에서 이미지 로드\n const stem = basename(resolvedOutput).replace(/\\.[^.]+$/, \"\")\n const defaultImgDir = resolve(resolvedOutput, \"..\", stem)\n const imgDir = image_dir ? resolve(image_dir) : defaultImgDir\n const images: ExtractedImage[] = []\n\n if (existsSync(imgDir)) {\n const mimeMap: Record<string, string> = {\n png: \"image/png\", jpg: \"image/jpeg\", jpeg: \"image/jpeg\",\n gif: \"image/gif\", bmp: \"image/bmp\",\n }\n for (const entry of readdirSync(imgDir, { withFileTypes: true })) {\n if (!entry.isFile()) continue\n const fname = entry.name\n const ext = extname(fname).slice(1).toLowerCase()\n if (!mimeMap[ext]) continue\n const data = readFileSync(resolve(imgDir, fname))\n images.push({ filename: fname, data: new Uint8Array(data), mimeType: mimeMap[ext] })\n }\n }\n\n let buf: ArrayBuffer\n if (format === \"xlsx\") {\n if (template_path) warnings.push(\"[warn] --template은 hwpx 전용입니다. 무시됩니다.\")\n buf = await markdownToXlsx(markdown, { warnings, images: images.length ? images : undefined })\n } else {\n let templateArrayBuffer: ArrayBuffer | undefined\n if (template_path) {\n const tmpl = readFileSync(safePath(template_path))\n templateArrayBuffer = tmpl.buffer.slice(tmpl.byteOffset, tmpl.byteOffset + tmpl.byteLength)\n }\n buf = await markdownToHwpx(markdown, {\n warnings,\n images: images.length ? images : undefined,\n templateArrayBuffer,\n })\n }\n\n writeFileSync(resolvedOutput, Buffer.from(buf))\n\n const parts = [\n `✅ 변환 완료: ${resolvedOutput}`,\n `포맷: ${format.toUpperCase()}, 크기: ${(buf.byteLength / 1024).toFixed(1)}KB`,\n ]\n if (images.length) parts.push(`포함된 이미지: ${images.length}개 (${imgDir})`)\n if (warnings.length) parts.push(`경고:\\n${warnings.map(w => ` ${w}`).join(\"\\n\")}`)\n\n return { content: [{ type: \"text\", text: parts.join(\"\\n\") }] }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: detect_format ─────────────────────────────\n\nserver.tool(\n \"detect_format\",\n \"파일의 포맷을 매직 바이트로 감지합니다 (hwpx, hwp, pdf, unknown).\",\n {\n file_path: z.string().min(1).describe(\"감지할 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n return {\n content: [{ type: \"text\", text: `${file_path}: ${format}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_metadata ────────────────────────────\n\nserver.tool(\n \"parse_metadata\",\n \"문서의 메타데이터(제목, 작성자, 날짜 등)만 빠르게 추출합니다. 전체 파싱 없이 헤더/매니페스트만 읽습니다.\",\n {\n file_path: z.string().min(1).describe(\"메타데이터를 추출할 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const resolved = safePath(file_path)\n const format = detectFormatFromHeader(resolved)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n // metadata 전용 크기 제한 (50MB)\n const { buffer } = readValidatedFile(file_path, MAX_METADATA_FILE_SIZE)\n\n let metadata\n switch (format) {\n case \"hwp\":\n metadata = extractHwp5MetadataOnly(Buffer.from(buffer))\n break\n case \"hwpx\":\n metadata = await extractHwpxMetadataOnly(buffer)\n break\n case \"pdf\":\n metadata = await extractPdfMetadataOnly(buffer)\n break\n }\n\n return {\n content: [{ type: \"text\", text: JSON.stringify({ format, ...metadata }, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_pages ──────────────────────────────\n\nserver.tool(\n \"parse_pages\",\n \"문서의 특정 페이지/섹션 범위만 파싱합니다. PDF는 정확한 페이지, HWP/HWPX는 섹션 단위 근사치입니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n pages: z.string().min(1).describe(\"페이지 범위 (예: '1-3', '1,3,5-7')\"),\n },\n async ({ file_path, pages }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer, { pages })\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const meta = [\n `포맷: ${result.fileType.toUpperCase()}`,\n `범위: ${pages}`,\n result.pageCount ? `페이지: ${result.pageCount}` : null,\n ].filter(Boolean).join(\" | \")\n\n return {\n content: [{ type: \"text\", text: `[${meta}]\\n\\n${result.markdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_table ──────────────────────────────\n\nserver.tool(\n \"parse_table\",\n \"문서에서 N번째 테이블만 추출합니다 (0-based index). 테이블이 없거나 인덱스 범위를 초과하면 오류를 반환합니다.\",\n {\n file_path: z.string().min(1).describe(\"파싱할 문서 파일의 절대 경로\"),\n table_index: z.number().int().min(0).describe(\"추출할 테이블 인덱스 (0부터 시작)\"),\n },\n async ({ file_path, table_index }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const format = detectFormat(buffer)\n\n if (format === \"unknown\") {\n return {\n content: [{ type: \"text\", text: `지원하지 않는 파일 형식입니다: ${file_path}` }],\n isError: true,\n }\n }\n\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패 (${result.fileType}): ${result.error}` }],\n isError: true,\n }\n }\n\n const tableBlocks = result.blocks.filter(b => b.type === \"table\" && b.table)\n if (tableBlocks.length === 0) {\n return {\n content: [{ type: \"text\", text: `문서에 테이블이 없습니다.` }],\n isError: true,\n }\n }\n\n if (table_index >= tableBlocks.length) {\n return {\n content: [{ type: \"text\", text: `테이블 인덱스 초과: ${table_index} (총 ${tableBlocks.length}개 테이블)` }],\n isError: true,\n }\n }\n\n const tableBlock = tableBlocks[table_index]\n const tableMarkdown = blocksToMarkdown([tableBlock])\n\n return {\n content: [{ type: \"text\", text: `[테이블 #${table_index} / 총 ${tableBlocks.length}개]\\n\\n${tableMarkdown}` }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: compare_documents ─────────────────────────\n\nserver.tool(\n \"compare_documents\",\n \"두 한국 문서 파일을 비교하여 추가/삭제/변경된 블록을 표시합니다. 신구대조표 생성에 활용됩니다. 크로스 포맷(HWP↔HWPX) 비교 가능.\",\n {\n file_path_a: z.string().min(1).describe(\"비교 원본 문서의 절대 경로\"),\n file_path_b: z.string().min(1).describe(\"비교 대상 문서의 절대 경로\"),\n },\n async ({ file_path_a, file_path_b }) => {\n try {\n const { buffer: bufA } = readValidatedFile(file_path_a)\n const { buffer: bufB } = readValidatedFile(file_path_b)\n\n const result = await compare(bufA, bufB)\n const { stats, diffs } = result\n\n const lines: string[] = [\n `## 문서 비교 결과`,\n `추가: ${stats.added} | 삭제: ${stats.removed} | 변경: ${stats.modified} | 동일: ${stats.unchanged}`,\n \"\",\n ]\n\n for (const d of diffs) {\n const prefix = d.type === \"added\" ? \"+\" : d.type === \"removed\" ? \"-\" : d.type === \"modified\" ? \"~\" : \" \"\n const text = d.after?.text || d.before?.text || (d.after?.table ? \"[테이블]\" : d.before?.table ? \"[테이블]\" : \"\")\n const sim = d.similarity !== undefined ? ` (${(d.similarity * 100).toFixed(0)}%)` : \"\"\n lines.push(`${prefix} ${text.substring(0, 200)}${sim}`)\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 도구: parse_form ───────────────────────────────\n\nserver.tool(\n \"parse_form\",\n \"한국 서식 문서에서 레이블-값 쌍을 구조화된 JSON으로 추출합니다. 양식/서식 문서에 최적화.\",\n {\n file_path: z.string().min(1).describe(\"서식 문서 파일의 절대 경로\"),\n },\n async ({ file_path }) => {\n try {\n const { buffer } = readValidatedFile(file_path)\n const result = await parse(buffer)\n\n if (!result.success) {\n return {\n content: [{ type: \"text\", text: `파싱 실패: ${result.error}` }],\n isError: true,\n }\n }\n\n const form = extractFormFields(result.blocks)\n return {\n content: [{ type: \"text\", text: JSON.stringify(form, null, 2) }],\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `오류: ${sanitizeError(err)}` }],\n isError: true,\n }\n }\n }\n)\n\n// ─── 서버 시작 ───────────────────────────────────────\n\nasync function main() {\n const transport = new StdioServerTransport()\n await server.connect(transport)\n}\n\nmain().catch((err) => { console.error(err); process.exit(1) })\n"],"mappings":"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","names":[]}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
sanitizeError,
|
|
10
10
|
sanitizeHref,
|
|
11
11
|
toArrayBuffer
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-X7UUXEMM.js";
|
|
13
13
|
import "./chunk-ZWE3DS7E.js";
|
|
14
14
|
export {
|
|
15
15
|
KordocError,
|
|
@@ -22,4 +22,4 @@ export {
|
|
|
22
22
|
sanitizeHref,
|
|
23
23
|
toArrayBuffer
|
|
24
24
|
};
|
|
25
|
-
//# sourceMappingURL=utils-
|
|
25
|
+
//# sourceMappingURL=utils-QQVZGOGU.js.map
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
detectFormat,
|
|
4
4
|
parse
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-URSQEMVJ.js";
|
|
6
6
|
import {
|
|
7
7
|
toArrayBuffer
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-X7UUXEMM.js";
|
|
9
9
|
import "./chunk-MOL7MDBG.js";
|
|
10
10
|
import "./chunk-S7BHLD2V.js";
|
|
11
11
|
import "./chunk-YW5G6BCJ.js";
|
|
@@ -136,4 +136,4 @@ async function sendWebhook(url, payload) {
|
|
|
136
136
|
export {
|
|
137
137
|
watchDirectory
|
|
138
138
|
};
|
|
139
|
-
//# sourceMappingURL=watch-
|
|
139
|
+
//# sourceMappingURL=watch-RQYUNSSH.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clazic/kordoc",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.6",
|
|
4
4
|
"description": "Parse Korean documents (HWP, HWPX, PDF, XLSX, DOCX) to Markdown",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -62,7 +62,6 @@
|
|
|
62
62
|
"commander": "^14.0.3",
|
|
63
63
|
"exceljs": "^4.4.0",
|
|
64
64
|
"jszip": "^3.10.1",
|
|
65
|
-
"libreoffice-convert": "^1.8.1",
|
|
66
65
|
"pdfjs-dist": "^5.6.205",
|
|
67
66
|
"zod": "^4.3.6"
|
|
68
67
|
},
|