npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hwpx-mcp

v0.8.3

Published

한글 문서(HWPX)를 읽고 쓰는 MCP 서버

Readme

HWPX MCP

한글 문서(.hwpx)를 한글 프로그램 없이 읽고 쓰는 MCP 서버.

AI 에게 「이 가정통신문 양식을 열어서 날짜랑 장소를 채워 줘」 라고 시키면, AI 가 이 서버를 통해 파일을 열고 고치고 저장한다. 한글이 안 깔린 리눅스 서버에서도 돈다.

npx hwpx-mcp
  • 라이선스 MIT
  • 필요한 것 Node 20 이상. 그 밖의 의존은 MCP SDK 하나뿐이다
  • 붙는 곳 Claude Code · Claude Desktop · Codex CLI · Gemini CLI · 안티그래비티, 그리고 MCP 를 말하는 아무 클라이언트

1. 무엇을 해 주나

HWPX 는 겉보기엔 한 덩어리 파일이지만 속은 zip 으로 묶인 XML 꾸러미다. 그 안을 안전하게 다루는 일을 대신한다.

| 이런 걸 시키면 | 이렇게 된다 | |---|---| | 「양식 열어서 빈칸 채워 줘」 | 표·서식·머리말을 그대로 두고 글자만 바뀐다 | | 「표에 줄 두 개 더 넣어 줘」 | 테두리·열 폭을 본떠서 늘린다 | | 「계획서 하나 새로 써 줘」 | 제목·개조식·표·쪽번호까지 갖춘 문서가 나온다 | | 「이 문서에 뭐가 들었어?」 | 문단·표·칸을 훑어 ID 와 함께 알려 준다 | | 「이거 좀 보여 줘」 | HTML 한 장으로 엮는다 — 브라우저가 열고, 인쇄하면 PDF 다 |

만들어진 파일은 한글이 그냥 연다. 그게 이 프로젝트가 계속 재고 있는 것이다 (뒤의 「6. 어떻게 믿나」).


2. 설치와 연결

Claude Code / Claude Desktop

~/.claude.json (Desktop 은 claude_desktop_config.json):

{
  "mcpServers": {
    "hwpx": {
      "command": "npx",
      "args": ["-y", "hwpx-mcp"]
    }
  }
}

Codex CLI

~/.codex/config.toml:

[mcp_servers.hwpx]
command = "npx"
args = ["-y", "hwpx-mcp"]

Gemini CLI

~/.gemini/settings.json — Claude 와 같은 mcpServers 꼴이다.

안티그래비티

~/.gemini/config/mcp_config.json — settings.json 이 아니다. 같은 .gemini 아래에 있어도 Gemini CLI 와 다른 파일을 본다.

붙으면 stderr 에 hwpx-mcp: stdio 로 붙었다 가 뜬다. 자세한 것은 기획/07-설치와-쓰기.md.


3. 도구 열두 개

적다는 것이 이 도구의 성질이다. tools/list 는 매 요청마다 모델의 컨텍스트를 먹으므로, 도구를 잘게 쪼갤수록 정작 일할 자리가 줄어든다.

문서 다루기

| 도구 | 언제 | |---|---| | open_document | 있는 파일을 연다. doc_id 를 받아 끝까지 쓴다 | | create_document | 맨바닥에서 새로 쓸 때만 | | save_document | 저장한다 | | close_document | 닫는다 | | list_documents | 지금 열어 둔 것들 |

들여다보기

| 도구 | 언제 | |---|---| | get_outline | 문서 뼈대. in_tables: true 면 표 안까지 | | get_content | 요소 하나를 자세히. id 를 안 주면 문서 전체 글 | | find | 글이나 종류로 찾아 ID 목록을 받는다 | | get_styles | 이 문서가 쓰는 글꼴·글자 크기 |

내보내기

| 도구 | 언제 | |---|---| | render_html | 문서를 HTML 한 장으로. 미리보기·PDF·워드로 가는 길 |

.hwpx 는 브라우저가 못 연다. 그래서 문서를 만들어 주는 앱은 미리보기도 PDF 도 못 준다 — 한글이 깔려 있어야만 눈으로 볼 수 있었다.

.hwpx ──render_html──▶ HTML ─┬─▶ 화면에 그대로 미리보기
                             ├─▶ 브라우저 인쇄 → PDF (한글 없이)
                             └─▶ 워드에 붙여넣기

그림은 파일 안에 박는다(data:). 딸린 폴더가 없어서 HTML 하나만 옮기면 된다. 큰 문서는 path 를 줘라 — 안 주면 답에 그대로 실어 몇 MB 가 된다.

고치기

| 도구 | 언제 | |---|---| | compose | 블록 목록으로 한 번에 짠다 (맨바닥에서 쓸 때). 표는 cells 로 칸마다 배경·선·정렬을 준다 | | edit | 있는 것을 고친다. 여러 개를 묶어서 준다 |

edit 은 op 하나로 갈린다:

글

  set_text          그 자리 글을 통째로 다시 쓴다
  replace           어구를 찾아 바꾼다
  set_style         굵게·기울임·밑줄·취소선·첨자·강조점·크기·색·정렬·테두리

표

  insert_row        표에 줄을 넣는다
  delete_row        표에서 줄을 뺀다 — **빈 줄만** (양식에 남는 줄을 걷어낸다)
  insert_col        표에 칸(열)을 넣는다
  delete_col        표에서 칸을 뺀다 — **빈 칸만**
  merge_cells       칸을 합친다
  split_cell        합침을 푼다. rows·cols 를 주면 성한 칸에 금을 긋는다
  split_table       표를 두 개로 가른다
  join_tables       표 둘을 이어 붙인다
  set_table         쪽 경계에서 나누는 방식 · 머리 줄 되풀이 · 글자처럼 취급 (끄면 표가 쪽을 넘긴다)

넣고 빼기

  insert_image      그림을 넣는다
  insert_equation   수식을 넣는다 (한글 수식 스크립트)
  insert_note       각주·미주를 단다 (note: footnote | endnote)
  insert_memo       메모를 단다 — 본문에 안 찍히고 「메모 보기」에만 보인다
  delete_paragraph  문단을 지운다 — **빈 것만** (force 로 넘긴다)
  delete_table      표를 지운다 — **빈 것만**

가리키기

  set_link          어구에 하이퍼링크를 건다
  set_bookmark      책갈피를 단다 (링크가 갈 곳)

쪽·구역

  set_page          쪽 테두리·배경
  set_columns       단을 나눈다 (count: 몇 단, gap: 사이 간격)
  set_outline       문단에 개요 수준을 준다 (level 1~10, 0 이면 끈다)
  set_master_page   바탕쪽에 글을 놓는다 — 모든 쪽 뒤에 깔린다

delete_row · delete_col 은 지우는 것이라 막는 것이 셋 있다 — 마지막 줄·칸, 합친 칸을 반만 지우는 것, 그리고 글이 든 것. 셋째는 force: true 로 넘길 수 있지만 지운 글은 못 되돌린다.

칸을 넣고 빼도 표 전체 폭은 그대로다. 새 칸에 이웃 폭을 주면 표가 넓어져 쪽 밖으로 나간다 — 양식은 폭이 정해진 것이라 있던 폭을 줄여 자리를 낸다.

merge_cells · split_cell 은 칸 ID 로 가리킨다. 합친 칸은 늘 왼쪽 위가 대표다 — 덮인 자리를 가리키면 어느 칸을 쓰라고 짚어 준다.

{ "op": "merge_cells", "id": "cell_9c1_0_0", "rowspan": 2, "colspan": 3 }
{ "op": "split_cell",  "id": "cell_9c1_0_0" }

요소를 가리키는 ID

p_3f2a           문단
tbl_9c1          표
cell_9c1_0_2     표의 0째 줄 2째 칸

get_outline 이나 find 가 준 값을 그대로 쓴다. 한 번 매기면 고쳐도 안 밀린다 — 표 맨 뒤에 줄을 넣어도 앞의 ID 는 그대로다.


4. 써 보기

양식을 채우는 흐름이 가장 흔하다.

1. open_document           양식 사본을 연다
2. find(kind:"paragraph")  문단을 문서 차례대로 훑는다
3.                         preview 가 빈 것이 채울 자리다
4. edit                    여러 곳을 한 번에 채운다
5. save_document           같은 파일에 저장한다

빈 자리는 글이 없어서 text 로 못 찾는다. 인사말 자리, 예산 표의 빈 칸이 그런 꼴이다. 그래서 kind 로 훑어 preview 가 빈 것을 고른다.

칸 안의 문단에는 in_cell 이 붙어 어느 칸인지 알려 준다.

{ "id": "p_8b7r", "kind": "paragraph", "preview": "", "in_cell": "cell_glzp_0_0" }

한 칸에 두 줄을 넣어야 하면 \n 을 쓴다:

{ "op": "set_text", "id": "cell_glzp_0_0", "text": "윗줄\n아랫줄" }

합친 칸

표에 합친 칸이 있으면 get_content(id: 표ID) 가 그렇다고 말해 준다.

{ "id": "cell_x_0_0", "text": "가", "row": 0, "col": 0, "rowspan": 2 }
{ "id": "cell_x_1_0", "text": "",   "row": 1, "col": 0, "covered_by": "cell_x_0_0" }

덮인 자리는 칸이 아니다. 거기 글을 쓰려 하면 어느 칸을 쓰라고 짚어 준다. find(kind:"cell") 도 덮인 자리는 안 낸다 — 같은 칸이 두 번 나오면 안 되니까.

답을 읽는 법

도구는 답을 세 겹으로 준다.

content[0]          "49개 찾았다"                     사람이 읽는 글
content[1]          {"ok":true,"count":49,...}        JSON 글자
structuredContent   { ok: true, count: 49, ... }      객체

프로그램으로 부를 때는 structuredContent 를 쓴다. 옛 규격을 쓰는 클라이언트는 앞의 두 겹으로 읽는다.

잘렸으면 잘렸다고 말한다. find · get_outline 은 truncated 를 함께 낸다. true 면 limit 을 키워 다시 부른다 — 잘린 채로 채우면 뒷절을 통째로 빼먹는다.


5. 어떻게 만들어져 있나

계층

owpml     XML 파서와 OWPML(KS X 6101) 규격
  ↓
hwpx      zip 컨테이너 — mimetype·부품·매니페스트
  ↓
doc       문서 모델 — 구역·문단·표·머리글
  ↓
compose   조판 — 블록 목록을 문서로
render    엮기 — 문서를 HTML 한 장으로
  ↓
server    MCP 도구 표면

아래가 위를 모른다. owpml 은 MCP 가 있는 줄도 모르고, doc 은 zip 을 모른다.

핵심 — 안 고친 것은 원본 바이트 그대로

이 프로젝트의 설계는 한 문장으로 줄어든다.

XML 노드가 원본에서의 자리(start~end) 를 들고 있고, 안 고친 노드는 원본 조각을 그대로 뱉는다.

zip 도 같은 수를 쓴다 — 안 고친 부품은 원본 압축 바이트를 그대로 옮긴다. 그래서 아무것도 안 고치고 저장하면 바이트가 같다. 왕복 무손실이 시험이 아니라 성질로 보장된다.

이 설계를 고른 까닭이 있다. HWPX 를 문자열로 보고 정규식으로 고치면 <hp:p> 를 찾는 식이 <hp:pic> 을 물고, 문단의 끝을 첫 </hp:p> 로 잡아 각주 안에 미주가 들어간다. 그런 것들은 XML 검사를 통과한다. PDF 로 뽑아 눈으로 보기 전에는 아무도 모른다.

이 저장소에는 정규식으로 XML 을 만지는 자리가 0곳이다.

저장 길목에서 규격을 본다

저장할 때 규격 검사를 돌린다. 다만 새로 낸 흠만 막는다 — 남의 문서에는 원래 흠이 있고(표 기하만 161편 중 64건), 그것까지 막으면 남의 문서를 열지도 못한다.


6. 어떻게 믿나

「됐다」와 「쓸 만하다」는 다르다. 그래서 세 겹으로 잰다.

규격    XML·zip 이 규격에 맞나
왕복    한글이 실제로 여나 — COM 으로 열어 본다
시각    PDF 로 구워 원본과 픽셀을 댄다

npm test 하나로 다 돈다.

npm test           # 한글 없이 (15갈래)
npm run test:한글   # 한글 COM 까지 (25갈래)

| | | |---|---| | 낱개 시험 | 663개 | | 고장 내보기 | 143건 | | 검증 갈래 | 25 |

고장 내보기가 이 프로젝트의 특징이다. 소스 한 줄을 일부러 고장 내고 시험이 정말 깨지는지 본다. 안 깨지면 그 시험은 아무것도 안 보고 있던 것이다. 실제로 그런 시험을 여럿 잡았다.

같은 뜻에서 헛도는 시험을 찾는 갈래도 있다 — 앞에 expect 가 없는 건너뛰기, 이 기계에만 있는 절대 경로, expect 가 하나도 없는 it.


7. 아직 못 하는 것

정직하게 적는다.

  • 양식을 채우는 쪽은 무탈하다. 학교 문서 양식 16개 × 손 5가지 = 57판, 달라진 픽셀 0.04~0.07%.

  • 맨바닥에서 통째로 짜는 쪽은 덜 됐다. 정부 문서를 재현하면 잰 35쪽 가운데 6쪽이 세로로 넘치고, 디자인 닮음이 75% 다(글자 크기 57%, 선 42%).

  • HTML 로 엮는 쪽은 글은 살리지만 쪽은 못 맞춘다. 표본 9편을 한글 PDF 와 대니 글이 99.5% 남고, 쪽수까지 같은 것은 9편 중 5편, 디자인 닮음 69% 다. 한글과 브라우저는 줄 끊는 규칙이 달라 몇 쪽짜리가 되느냐가 달라진다 — 미리보기·검토용이지 「인쇄본과 한 픽셀도 안 틀리게」는 못 한다. 쪽번호(hp:pageNum)와 목차 점선은 HTML 에서 못 만든다.

  • 각주·미주·메모·수식은 읽기만 된다. 다단·개요 번호·바탕쪽·타원/다각형도 아직이다. 실측으로 보면 이것들은 다 한두 편에 한둘씩이고, 바탕쪽과 개요 번호는 표본 33편에 하나도 없다 — 없는 것은 만들어도 맞는지 잴 길이 없다.

  • 하이퍼링크와 책갈피는 된다. 링크는 fieldBegin~fieldEnd 쌍이라 짝이 어긋나면 한글이 문서 끝까지 링크로 만든다 — id 를 쓰인 것 다음 수로 정한다.

  • 글자 꾸밈은 다 된다 — 굵게·기울임·밑줄·취소선·첨자·강조점·음영·자간·장평. 문단 테두리·배경과 쪽 테두리·배경도 준다.

  • 표는 줄·칸을 넣고 빼고, 합치고 풀고, 나누고, 가르고 붙일 수 있다. 계산식과 표 뒤집기는 아직이다. 셀 배경의 무늬(hatchStyle)와 그림 채우기도 못 준다 — 읽을 때도 그림 채우기는 색으로 흉내 내지 않고 비워 둔다.

  • ID 는 연 문서 안에서만 산다. 줄·칸을 넣거나 표를 가르는 것처럼 구조를 고치고 저장하면, 그 파일을 다시 열었을 때 표 ID 가 전부 달라진다. 고친 표만이 아니다 — 표 65개짜리 문서에서 65개가 다 달라졌다. 구역이 여럿이면 뒤 구역의 문단 ID 까지 밀린다.

    글만 고치는 것(set_text·replace·set_style)은 저장을 넘겨도 안 밀린다. 구조를 고쳤으면 save_document 가 ids_stale: true 로 알린다 — 그때는 들고 있던 ID 를 버리고 get_outline·find 로 다시 받아야 한다. 까닭과 잰 것은 자료/실측.md 27항에 있다.

숫자는 다 잰 것이다. 어림해 적었다가 틀린 적이 있어 이제는 재서 적는다.


8. 더 읽을 것

| 문서 | 무엇이 적혀 있나 | |---|---| | 기획/07-설치와-쓰기.md | 클라이언트별 설정과 첫 문서 만들기 | | 기획/04-도구-설계.md | 도구를 왜 이만큼으로 정했나 | | 기획/02-설계.md | 계층 구조와 설계 원칙 | | 기획/05-검증.md | 삼중 검증 — 이 프로젝트의 심장 | | 자료/실측.md | 재서 알아낸 것. 짐작하면 틀리는 것들 | | 기획/06-계획.md | 단계별 기록과 기능표 | | 기획/01-왜-다시-만드나.md | 이 설계를 고른 까닭 | | 기획/03-MCP-규격과-범용성.md | MCP 규격 반영과 클라이언트 호환 | | 작성지침.md | 코드와 문서를 어떻게 쓰나 | | CHANGELOG.md | 판올림 기록 |

기준 파일

자료/기준파일/ref-*.hwpx    한글 2024 로 직접 만든 것
자료/규격/                  OWPML(KS X 6101) 형식 문서

「한글이 이 기능을 어떤 XML 로 저장하는가」를 추측하지 않고 만들어서 확인한 것이다. 새 기능을 만들 때는 기준 파일부터 만든다.


소스에서 돌리기

npm install
npm run 배포굽기        # dist 를 굽는다 — 서버가 이걸 읽는다
node packages/server/bin/hwpx-mcp.mjs

소스를 고쳤으면 다시 구워야 한다. npm test 가 이것도 같이 한다.


만든 이

owm21 · MIT · github.com/wonmukoh/hwpx-mcp-server

구조를 참고한 곳: Dayoooun/hwpx-mcp (MIT)