easyread-mcp 0.1.0 → 0.1.2

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 CHANGED
@@ -1,31 +1,74 @@
1
1
  # EASYREAD — 쉬운 정보(Easy-Read) MCP 서버
2
2
 
3
- 발달장애인·저문해력 사용자를 위해 복잡한 한국어 텍스트를 **쉬운 정보(Easy-Read)** 로 바꾸는 일을 돕는 MCP(Model Context Protocol) 서버 프로젝트입니다.
3
+ [![npm](https://img.shields.io/npm/v/easyread-mcp)](https://www.npmjs.com/package/easyread-mcp)
4
+ [![CI](https://github.com/SWJoong/EASYREAD/actions/workflows/ci.yml/badge.svg)](https://github.com/SWJoong/EASYREAD/actions/workflows/ci.yml)
5
+ [![license: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+ [![Node](https://img.shields.io/badge/node-%3E%3D22-brightgreen.svg)](#설치하기)
7
+
8
+ 발달장애인·저문해력 사용자를 위해 복잡한 한국어 텍스트를 **쉬운 정보(Easy-Read)** 로 바꾸는 일을 돕는 MCP(Model Context Protocol) 서버입니다.
4
9
 
5
10
  - 서버는 **지침·사전·검증 도구**를 제공하고, 실제 문장 변환은 클라이언트 LLM(Claude 등)이 수행합니다.
6
- - 기술 스택: TypeScript + `@modelcontextprotocol/sdk` (Node.js 22 LTS+)
11
+ - 모든 검증은 **오프라인·결정적**으로 동작하며, 입력을 저장하거나 기록하지 않습니다.
12
+ - 기술 스택: TypeScript + `@modelcontextprotocol/sdk` (Node.js 22 LTS+), 런타임 의존성 2개.
13
+
14
+ > ⚠️ **이 도구의 결과는 감수를 거치지 않은 초안입니다.** 도구는 감수를 대신하지 않습니다.
15
+ > 최종본은 반드시 **발달장애인 당사자의 확인·감수**를 거쳐 정합니다.
7
16
 
8
17
  ## 설치하기
9
18
 
10
- Node.js 22 이상이 필요합니다. 인터넷은 설치할 때만 필요하고, 검사할 때는 필요 없습니다.
19
+ Node.js 22 이상이 필요합니다. 인터넷은 **설치할 때만** 필요하고, 검사할 때는 필요 없습니다.
20
+
21
+ **Claude Code** — 터미널에 한 줄:
22
+
23
+ ```bash
24
+ claude mcp add easyread -- npx -y easyread-mcp
25
+ ```
11
26
 
12
- - **Claude Desktop**: [docs/install/claude-desktop.md](docs/install/claude-desktop.md) — 설정 파일에 아래 블록을 넣고 앱을 다시 시작합니다.
13
- - **Claude Code**: [docs/install/claude-code.md](docs/install/claude-code.md) — `claude mcp add easyread -- npx -y easyread-mcp` 한 줄.
27
+ **Claude Desktop** — 설정 파일에 아래 블록을 넣고 앱을 다시 시작합니다:
14
28
 
15
29
  ```json
16
30
  { "mcpServers": { "easyread": { "command": "npx", "args": ["-y", "easyread-mcp"] } } }
17
31
  ```
18
32
 
19
- ## 이 서버가 제공하는 것
33
+ 자세한 순서: [Claude Desktop 설치](docs/install/claude-desktop.md) · [Claude Code 설치](docs/install/claude-code.md)
34
+
35
+ **처음이라면 → [처음 사용해보기(따라하기)](docs/install/first-use.md)** — 예시 문장으로 한 번 연습해 봅니다.
36
+
37
+ ## 써보기
38
+
39
+ 1. 어려운 글을 한 토막 준비합니다.
40
+ 2. `simplify-text` 프롬프트로 **쉬운 정보 초안**을 만듭니다.
41
+ 3. `validate_easy_read`로 초안을 **원문과 함께** 검사합니다 — 규칙 위반과 함께, 날짜·금액·기관명이 바뀌지 않았는지(정확성 ACC)를 확인합니다.
42
+ 4. `error`가 없어질 때까지 고치고, 마지막에 **당사자 감수**를 받습니다.
43
+
44
+ ## 빠르고 효율적으로 쓰기 (팁)
45
+
46
+ 토큰을 아끼고 변환을 빠르게 하는 실전 팁입니다.
20
47
 
21
- - **도구 4종**: `validate_easy_read`(규칙 검증), `analyze_readability`(가독성 지표), `lookup_easy_word`(쉬운 낱말 찾기), `get_guidelines`(작성 지침).
48
+ - **필요한 부분만 넣기** — 긴 원문·웹페이지 전체 대신 제목·자격·근무조건·기한·연락처만 골라 토막으로 바꿉니다(검사 한도 5만 자).
49
+ - **먼저 `analyze_readability`** — 가장 긴 문장·어려운 낱말을 찾아 그 부분부터 고칩니다.
50
+ - **어려운 낱말만 골라 찾기** — 위에서 찾은 낱말만 `lookup_easy_word`로 확인합니다. 아무 낱말이나 반복 조회하지 않습니다.
51
+ - **검사는 초안 단위로** — 문장마다 말고, 초안 한 편이 되면 `validate_easy_read`를 한 번 부릅니다. `original`(원문)을 함께 주면 사실 보존까지 한 번에 봅니다.
52
+ - **오탐은 끄기** — 보조 검사가 잘못 잡으면 `config.excludeRules`(예: `["SEN-02"]`)로 규칙을 끕니다. 단, 정확성(ACC)은 사람이 꼭 확인합니다.
53
+ - **프롬프트 재사용** — `simplify-text`를 그대로 쓰면 지시를 매번 새로 쓰지 않아도 됩니다. 결과는 바뀐 글만 내보내면 토큰이 줍니다.
54
+
55
+ 위 팁은 Claude 안에서 `easyread://guidelines` 리소스로도 볼 수 있습니다 — `easyread://`는 웹 주소가 아니라 클라이언트(Claude Desktop·Code)에서 여는 MCP 리소스입니다. 본문을 바로 보려면 소스 [`src/data/guidelines.ts`](src/data/guidelines.ts)의 `GUIDELINES_MARKDOWN`을 참고하세요.
56
+
57
+ ## 제공 기능
58
+
59
+ - **도구 4종**: `validate_easy_read`(규칙 검증·원문 대조), `analyze_readability`(가독성 지표), `lookup_easy_word`(쉬운 낱말 찾기), `get_guidelines`(작성 지침).
22
60
  - **프롬프트 2종**: `simplify-text`(쉬운 정보로 바꾸기), `easy-read-review`(초안 검토).
23
- - **리소스**: `easyread://guidelines`·`.../checklist`·`easyread://dictionary`·`easyread://resources`.
24
- - **검증 규칙 25종**(SEN·VOC·NUM·STR·TYP·ACC). 모두 오프라인·결정적으로 동작합니다.
61
+ - **리소스 4종**: `easyread://guidelines` · `.../checklist` · `easyread://dictionary` · `easyread://resources`.
62
+ - **검증 규칙 25종**: 문장(SEN)·어휘(VOC)·숫자(NUM)·구성(STR)·표기(TYP)·정확성(ACC).
63
+ - **번들 데이터**: 쉬운 낱말 사전과 Easy-Read 근거·표준·사례 카탈로그 66건(각 항목에 출처·라이선스 분류 보존).
64
+
65
+ ## 보안·프라이버시
66
+
67
+ 오프라인 동작(네트워크 미호출), 입력 무저장·무로깅, 입력 크기 상한. 자세한 내용은 [SECURITY.md](SECURITY.md)를 참고하세요. 버전별 변경 내역은 [CHANGELOG.md](CHANGELOG.md)에 있습니다.
25
68
 
26
- ## 문서
69
+ ## 개발·기여
27
70
 
28
- 구현 계획은 [docs/plan/](docs/plan/) 아래에 파트별로 정리되어 있습니다.
71
+ 구현 계획은 [docs/plan/](docs/plan/) 아래 파트별로 정리되어 있습니다.
29
72
 
30
73
  | 문서 | 담당 역할 |
31
74
  |---|---|
@@ -36,6 +79,16 @@ Node.js 22 이상이 필요합니다. 인터넷은 설치할 때만 필요하고
36
79
  | [04-qa-plan.md](docs/plan/04-qa-plan.md) | QA — 테스트 전략 |
37
80
  | [05-release-plan.md](docs/plan/05-release-plan.md) | DevOps — 배포/릴리스 |
38
81
 
39
- ## 역할별 Skill
82
+ `.claude/skills/` 아래 역할별 스킬(`/pm`·`/pl`·`/backend`·`/qa`·`/devops`)로 각 파트 계획을 작성·갱신할 수 있고, `easyread-domain` 스킬이 쉬운 정보 작성 도메인 지식을 제공합니다.
83
+
84
+ 빌드·테스트:
85
+
86
+ ```bash
87
+ npm ci && npm run build && npm test
88
+ ```
89
+
90
+ 버그·오탐/미탐 신고는 [이슈 템플릿](.github/ISSUE_TEMPLATE)으로 받습니다.
91
+
92
+ ## 라이선스
40
93
 
41
- `.claude/skills/` 아래에 역할별 스킬이 있습니다. Claude Code에서 `/pm`, `/pl`, `/backend`, `/qa`, `/devops` 로 호출해 각 파트의 계획을 작성·갱신할 수 있으며, `easyread-domain` 스킬이 쉬운 정보 작성 지침 도메인 지식을 제공합니다.
94
+ [MIT](LICENSE). 번들 데이터의 출처·이용조건 고지는 LICENSE의 데이터 고지(NFR-04)와 `assets/resources.json`의 `license` 필드를 참고하세요.
@@ -33,6 +33,16 @@ export const GUIDELINES_MARKDOWN = `# 쉬운 정보(Easy-Read) 작성 지침
33
33
  ## 절차·감수
34
34
  - 변환 절차를 따르고, 결과물은 당사자 감수 전 초안임을 밝힙니다.
35
35
  - 발달장애인 당사자의 확인·감수를 거쳐 최종본을 확정합니다.
36
+
37
+ ## 효율적으로 바꾸기 (도구 활용·속도·토큰 절약)
38
+ - **필요한 부분만 넣기**: 긴 원문·웹페이지는 전체를 넣지 말고 꼭 필요한 부분(제목·자격·근무조건·기한·연락처)만 골라 넣습니다. 검사 한도는 5만 자이며, 길면 토막으로 나눠 바꿉니다.
39
+ - **먼저 분석, 나중에 검증**: analyze_readability로 가장 긴 문장·어려운 낱말을 먼저 찾아 그 부분부터 고칩니다. 다 고친 뒤 validate_easy_read로 한 번에 검사하면 왕복이 줍니다.
40
+ - **검사는 초안 단위로**: 문장마다 검사하지 말고, 초안 한 편이 되면 validate_easy_read를 한 번 부릅니다.
41
+ - **사실은 원문과 함께**: validate_easy_read에 original(원문)을 함께 주면 날짜·금액·기관명이 바뀌지 않았는지 한 번에 대조합니다. 따로 확인하지 않아도 됩니다.
42
+ - **어려운 낱말만 골라 찾기**: analyze_readability가 알려 준 어려운 낱말만 lookup_easy_word로 찾습니다. 아무 낱말이나 반복 조회하지 않습니다.
43
+ - **오탐은 규칙 끄기**: 보조 검사가 잘못 잡으면(예: '주 5일'을 날짜로 오인) config.excludeRules에 규칙 ID(예: ["SEN-02"])를 넣어 잡음을 줄입니다. 다만 정확성(ACC)은 사람이 꼭 확인합니다.
44
+ - **지시는 프롬프트 재사용**: simplify-text 프롬프트를 그대로 쓰면 변환 지시를 매번 새로 쓰지 않아도 됩니다.
45
+ - **결과는 바뀐 글만**: 변환 결과는 설명·군말 없이 바뀐 글만 내보내면 토큰이 줍니다.
36
46
  `;
37
47
  const GROUP_TITLE = {
38
48
  SEN: "문장",
@@ -1 +1 @@
1
- {"version":3,"file":"guidelines.js","sourceRoot":"","sources":["../../src/data/guidelines.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+BlC,CAAC;AAEF,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,KAAK;CACX,CAAC;AACF,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAE/D;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAsB;IAC3D,MAAM,KAAK,GAAG,CAAC,eAAe,EAAE,EAAE,EAAE,4BAA4B,EAAE,EAAE,CAAC,CAAC;IACtE,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QACvD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,KAAK,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,KAAK,GAAG,CAAC,CAAC;QAC3D,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
1
+ {"version":3,"file":"guidelines.js","sourceRoot":"","sources":["../../src/data/guidelines.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyClC,CAAC;AAEF,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,KAAK;CACX,CAAC;AACF,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC;AAE/D;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAsB;IAC3D,MAAM,KAAK,GAAG,CAAC,eAAe,EAAE,EAAE,EAAE,4BAA4B,EAAE,EAAE,CAAC,CAAC;IACtE,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;QACvD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACnC,KAAK,CAAC,IAAI,CAAC,MAAM,WAAW,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,KAAK,GAAG,CAAC,CAAC;QAC3D,KAAK,MAAM,CAAC,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QAChF,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -2,6 +2,18 @@
2
2
  // 근거: Inclusion Europe #8(일상어) · 국내 지침 어휘(한국어 대체어 우선).
3
3
  // 어절당 최대 1건, (a) 우선. 단일 로마자('A형')는 제외.
4
4
  const LATIN_2PLUS = /[A-Za-z]{2,}/;
5
+ // 이메일·URL·도메인·측정단위는 '바꿀 외국어'가 아니다 → 외국어 판정에서 제외(오탐 방지).
6
+ function isTechnicalToken(t) {
7
+ if (t.includes("@"))
8
+ return true; // 이메일(job@jobcenter.or.kr)
9
+ if (/https?:\/\//i.test(t) || /^www\./i.test(t))
10
+ return true; // URL
11
+ if (/[A-Za-z]\.[A-Za-z]{2,}/.test(t))
12
+ return true; // 도메인(jobcenter.or.kr)
13
+ if (/^\d[\d.,]*[A-Za-z]+$/.test(t))
14
+ return true; // 측정단위(20kg, 3.5m)
15
+ return false;
16
+ }
5
17
  export const voc02 = {
6
18
  id: "VOC-02",
7
19
  group: "VOC",
@@ -10,6 +22,8 @@ export const voc02 = {
10
22
  const findings = [];
11
23
  for (const s of ctx.sentences) {
12
24
  for (const w of s.words) {
25
+ if (isTechnicalToken(w.text))
26
+ continue; // 이메일·URL·도메인·단위는 제외
13
27
  // (a) 사전 loanword 정확 일치 — 우선
14
28
  const entry = ctx.dictionary.lookup(w.text);
15
29
  if (entry?.category === "loanword") {
@@ -1 +1 @@
1
- {"version":3,"file":"voc-02.js","sourceRoot":"","sources":["../../../src/rules/voc/voc-02.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,uDAAuD;AACvD,uCAAuC;AACvC,MAAM,WAAW,GAAG,cAAc,CAAC;AAEnC,MAAM,CAAC,MAAM,KAAK,GAAS;IACzB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,KAAK;IACZ,eAAe,EAAE,SAAS;IAC1B,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAkB,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAC9B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACxB,6BAA6B;gBAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC5C,IAAI,KAAK,EAAE,QAAQ,KAAK,UAAU,EAAE,CAAC;oBACnC,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC3C,QAAQ,CAAC,IAAI,CAAC;wBACZ,MAAM,EAAE,QAAQ;wBAChB,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,kBAAkB,IAAI,mBAAmB;wBAC5D,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,UAAU,EAAE,IAAI;qBACjB,CAAC,CAAC;oBACH,SAAS,CAAC,UAAU;gBACtB,CAAC;gBACD,gCAAgC;gBAChC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7B,QAAQ,CAAC,IAAI,CAAC;wBACZ,MAAM,EAAE,QAAQ;wBAChB,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,qCAAqC;wBACxD,IAAI,EAAE,CAAC,CAAC,IAAI;qBACb,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"voc-02.js","sourceRoot":"","sources":["../../../src/rules/voc/voc-02.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,uDAAuD;AACvD,uCAAuC;AACvC,MAAM,WAAW,GAAG,cAAc,CAAC;AAEnC,wDAAwD;AACxD,SAAS,gBAAgB,CAAC,CAAS;IACjC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,2BAA2B;IAC7D,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,MAAM;IACpE,IAAI,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,uBAAuB;IAC1E,IAAI,sBAAsB,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,mBAAmB;IACpE,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAS;IACzB,EAAE,EAAE,QAAQ;IACZ,KAAK,EAAE,KAAK;IACZ,eAAe,EAAE,SAAS;IAC1B,KAAK,CAAC,GAAG;QACP,MAAM,QAAQ,GAAkB,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;YAC9B,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAC;gBACxB,IAAI,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;oBAAE,SAAS,CAAC,qBAAqB;gBAC7D,6BAA6B;gBAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;gBAC5C,IAAI,KAAK,EAAE,QAAQ,KAAK,UAAU,EAAE,CAAC;oBACnC,MAAM,IAAI,GAAG,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC3C,QAAQ,CAAC,IAAI,CAAC;wBACZ,MAAM,EAAE,QAAQ;wBAChB,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,kBAAkB,IAAI,mBAAmB;wBAC5D,IAAI,EAAE,CAAC,CAAC,IAAI;wBACZ,UAAU,EAAE,IAAI;qBACjB,CAAC,CAAC;oBACH,SAAS,CAAC,UAAU;gBACtB,CAAC;gBACD,gCAAgC;gBAChC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC7B,QAAQ,CAAC,IAAI,CAAC;wBACZ,MAAM,EAAE,QAAQ;wBAChB,OAAO,EAAE,IAAI,CAAC,CAAC,IAAI,qCAAqC;wBACxD,IAAI,EAAE,CAAC,CAAC,IAAI;qBACb,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 도구 스키마의 JSON Schema 방언을 최신 MCP 클라이언트와 호환되게 맞춘다.
3
+ *
4
+ * 배경: `@modelcontextprotocol/sdk` 1.30.0의 `McpServer`는 zod 스키마를 JSON Schema로
5
+ * 변환할 때 방언 target을 지정하지 않아 `zod-to-json-schema` 기본값인 **draft-07**로
6
+ * `$schema`를 방출한다(server/mcp.js의 tools/list 핸들러). 최신 MCP 클라이언트는 JSON
7
+ * Schema **2020-12**만 검증하므로, draft-07을 선언한 도구 outputSchema를 거부한다
8
+ * ("unsupported dialect ($schema: http://json-schema.org/draft-07/schema#)").
9
+ *
10
+ * 도구 스키마 본문은 두 방언에서 동일한 키워드(type·properties·required·enum·min/max
11
+ * 등)만 쓰므로, tools/list 응답에서 최상위 `$schema` 선언만 제거하면 클라이언트가 기본
12
+ * 방언(2020-12)으로 해석해 통과한다. SDK가 변환 target을 지정하는 공개 API를 노출하지
13
+ * 않으므로, 응답을 후처리하는 이 방식이 가장 작은 수정이다.
14
+ *
15
+ * 반드시 도구 등록 **전**에 호출한다. SDK가 tools/list 핸들러를 설치하는 순간을 가로채
16
+ * 응답을 감싸야 하기 때문이다.
17
+ */
18
+ export function normalizeToolSchemaDialect(server) {
19
+ const low = server.server;
20
+ const originalSetRequestHandler = low.setRequestHandler.bind(low);
21
+ low.setRequestHandler = (schema, handler) => {
22
+ originalSetRequestHandler(schema, async (...args) => {
23
+ const result = await handler(...args);
24
+ const tools = result?.tools;
25
+ if (Array.isArray(tools)) {
26
+ for (const tool of tools) {
27
+ stripDollarSchema(tool.inputSchema);
28
+ stripDollarSchema(tool.outputSchema);
29
+ }
30
+ }
31
+ return result;
32
+ });
33
+ };
34
+ }
35
+ /** JSON Schema 객체의 최상위 `$schema` 방언 선언을 제거한다(있을 때만). */
36
+ function stripDollarSchema(schema) {
37
+ if (typeof schema === "object" && schema !== null && "$schema" in schema) {
38
+ delete schema.$schema;
39
+ }
40
+ }
41
+ //# sourceMappingURL=schema-dialect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-dialect.js","sourceRoot":"","sources":["../src/schema-dialect.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAK1D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAmC,CAAC;IACvD,MAAM,yBAAyB,GAAG,GAAG,CAAC,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAClE,GAAG,CAAC,iBAAiB,GAAG,CAAC,MAAe,EAAE,OAAuB,EAAQ,EAAE;QACzE,yBAAyB,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,IAAe,EAAoB,EAAE;YAC/E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;YACtC,MAAM,KAAK,GAAI,MAAmD,EAAE,KAAK,CAAC;YAC1E,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;oBACzB,iBAAiB,CAAE,IAAkC,CAAC,WAAW,CAAC,CAAC;oBACnE,iBAAiB,CAAE,IAAmC,CAAC,YAAY,CAAC,CAAC;gBACvE,CAAC;YACH,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC;AACJ,CAAC;AAED,wDAAwD;AACxD,SAAS,iBAAiB,CAAC,MAAe;IACxC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,SAAS,IAAI,MAAM,EAAE,CAAC;QACzE,OAAQ,MAAkC,CAAC,OAAO,CAAC;IACrD,CAAC;AACH,CAAC"}
package/dist/server.js CHANGED
@@ -7,11 +7,15 @@ import { registerAnalyzeTool } from "./tools/analyze.js";
7
7
  import { registerGuidelinesTool } from "./tools/guidelines.js";
8
8
  import { registerLookupTool } from "./tools/lookup.js";
9
9
  import { registerValidateTool } from "./tools/validate.js";
10
+ import { normalizeToolSchemaDialect } from "./schema-dialect.js";
10
11
  /**
11
12
  * EASYREAD MCP 서버를 조립한다. 도구·프롬프트·리소스 등록만 담당하고 로직은 갖지 않는다(03 §1).
12
13
  */
13
14
  export function createServer(dictionary, catalog) {
14
- const server = new McpServer({ name: "easyread", version: "0.1.0" });
15
+ const server = new McpServer({ name: "easyread", version: "0.1.2" });
16
+ // SDK가 도구 스키마를 draft-07로 방출 → 최신 클라이언트(2020-12) 호환되게 정규화.
17
+ // 반드시 도구 등록 전에 호출한다(tools/list 핸들러 설치 시점을 가로채기 위함).
18
+ normalizeToolSchemaDialect(server);
15
19
  registerValidateTool(server, dictionary);
16
20
  registerAnalyzeTool(server, dictionary);
17
21
  registerLookupTool(server, dictionary);
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
@@ -10,16 +10,30 @@ const AMOUNT_RE = /\d[\d,]*\s*(?:억|만|천|백)\s*(?:원|명|개|건|가지)?|
10
10
  // 연락처: 전화(\d{2,4}-\d{3,4}-\d{4}) · URL(http(s):// … 또는 www. …).
11
11
  const CONTACT_RE = new RegExp("\\d{2,4}-\\d{3,4}-\\d{4}|https?://\\S+|www\\.\\S+", "g");
12
12
  // 기관명 후보 접미(휴리스틱). 조사를 떼고 이 접미로 끝나는 어절을 기관명 후보로 본다.
13
+ // 단자 접미('청'·'부'·'원')는 흔한 명사(신청·일부·인원…)를 과매칭해 제외하고,
14
+ // 자주 쓰는 구체 기관명을 명시 접미로 둔다(정밀도 우선 — ACC-03은 보조 warning).
13
15
  const INSTITUTION_SUFFIXES = [
14
16
  "공단", "공사", "위원회", "구청", "시청", "도청", "교육청",
15
- "연구원", "진흥원", "재단", "협회", "조합", "센터", "청", "부", "원",
17
+ "경찰청", "국세청", "소방청", "연구원", "진흥원", "병원", "법원",
18
+ "재단", "협회", "조합", "센터",
16
19
  ];
17
20
  const TRAILING_JOSA = /(?:은|는|이|가|을|를|에서|에게|에|의|과|와|도|으로|로|까지|부터)$/;
18
21
  function stripSpaces(s) {
19
22
  return s.replace(/\s+/g, "");
20
23
  }
21
24
  export function extractDates(text) {
22
- return [...text.matchAll(DATE_RE)].map((m) => stripSpaces(m[0]));
25
+ const out = [];
26
+ for (const m of text.matchAll(DATE_RE)) {
27
+ const token = m[0];
28
+ // '주 5일'·'주5일'(근무 빈도)의 'N일'은 날짜가 아니다 → 바로 앞이 '주'면 제외.
29
+ if (/^\s*\d+\s*일$/.test(token)) {
30
+ const before = text.slice(0, m.index).replace(/\s+$/, "");
31
+ if (before.endsWith("주"))
32
+ continue;
33
+ }
34
+ out.push(stripSpaces(token));
35
+ }
36
+ return out;
23
37
  }
24
38
  export function extractAmounts(text) {
25
39
  return [...text.matchAll(AMOUNT_RE)].map((m) => stripSpaces(m[0]));
@@ -30,6 +44,9 @@ export function extractContacts(text) {
30
44
  export function extractProperNouns(text) {
31
45
  const names = [];
32
46
  for (const token of text.split(/\s+/)) {
47
+ // 가운뎃점 압축표기(시·군·구청)는 풀어쓴 형태(시청·군청·구청)와 문자열 매칭이 안 돼 오탐 → 제외.
48
+ if (token.includes("·"))
49
+ continue;
33
50
  const word = token.replace(TRAILING_JOSA, "");
34
51
  if (word.length >= 2 && INSTITUTION_SUFFIXES.some((suffix) => word.endsWith(suffix))) {
35
52
  names.push(word);
@@ -1 +1 @@
1
- {"version":3,"file":"extractors.js","sourceRoot":"","sources":["../../src/text/extractors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wEAAwE;AACxE,MAAM,OAAO,GACX,iGAAiG,CAAC;AACpG,mEAAmE;AACnE,MAAM,SAAS,GACb,iFAAiF,CAAC;AACpF,gEAAgE;AAChE,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAC;AAExF,oDAAoD;AACpD,MAAM,oBAAoB,GAAG;IAC3B,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK;IAC1C,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG;CACpD,CAAC;AACF,MAAM,aAAa,GAAG,6CAA6C,CAAC;AAEpE,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACrF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"extractors.js","sourceRoot":"","sources":["../../src/text/extractors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wEAAwE;AACxE,MAAM,OAAO,GACX,iGAAiG,CAAC;AACpG,mEAAmE;AACnE,MAAM,SAAS,GACb,iFAAiF,CAAC;AACpF,gEAAgE;AAChE,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,mDAAmD,EAAE,GAAG,CAAC,CAAC;AAExF,oDAAoD;AACpD,mDAAmD;AACnD,wDAAwD;AACxD,MAAM,oBAAoB,GAAG;IAC3B,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK;IAC1C,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI;IAC7C,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;CACvB,CAAC;AACF,MAAM,aAAa,GAAG,6CAA6C,CAAC;AAEpE,SAAS,WAAW,CAAC,CAAS;IAC5B,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACnB,sDAAsD;QACtD,IAAI,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAC1D,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,SAAS;QACrC,CAAC;QACD,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,OAAO,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzD,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QACtC,4DAA4D;QAC5D,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;YAAE,SAAS;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,CAAC,CAAC;QAC9C,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,oBAAoB,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACrF,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -106,7 +106,11 @@ export function splitSentenceSpans(text, offset = 0) {
106
106
  !(text[j + 1] === "." && isDigit(text[j]))) {
107
107
  j++;
108
108
  }
109
- push(j + 1);
109
+ // 종결부호는 뒤가 공백/끝일 때만 경계로 삼는다 — 도메인·이메일 내부 '.'
110
+ // (예: jobcenter.or.kr)은 어절을 쪼개지 않는다. 닫는 기호 뒤 경계는 maybeCloseBoundary가 처리.
111
+ const after = text[j + 1];
112
+ if (after === undefined || isSpace(after))
113
+ push(j + 1);
110
114
  i = j;
111
115
  continue;
112
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sentences.js","sourceRoot":"","sources":["../../src/text/sentences.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sentences.js","sourceRoot":"","sources":["../../src/text/sentences.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easyread-mcp",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "복잡한 한국어 텍스트를 쉬운 정보(Easy-Read)로 바꾸는 작업을 돕는 MCP 서버. 검증·분석·사전·지침을 오프라인 결정적 도구로 제공한다.",
5
5
  "keywords": [
6
6
  "mcp",