korean-law-mcp 2.3.0 → 2.3.1

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
@@ -14,7 +14,15 @@
14
14
 
15
15
  ---
16
16
 
17
- ## v2.3.0 변경사항
17
+ ## v2.3.1 변경사항
18
+
19
+ - **URL 쿼리 API 키 지원** — 원격 MCP 접속 시 `?oc=your-key`로 API 키를 URL에 포함 가능. 매 요청마다 키를 전달할 필요 없이 세션 전체에 자동 적용. Claude.ai 등 커스텀 헤더 설정이 어려운 웹 클라이언트에서 유용.
20
+ - **체인 도구 자동 전문 조회** — `chain_ordinance_compare`가 자치법규 검색 후 상위 1건 전문을 자동 조회하여 반환. 별도로 `get_ordinance`를 호출할 필요 없음.
21
+ - **lite 프로필 도구 라우팅 개선** — 체인/메타 도구 description을 INPUT 의도 기반으로 재작성. 예시 포함으로 Claude 웹이 자치법규 검색·조회 등 올바른 도구를 선택하도록 개선.
22
+ - **도구 힌트 execute_tool 경로 통일** — 비lite 도구 안내를 `execute_tool()` 호출 예시로 변경. Claude 웹이 존재하지 않는 도구를 직접 호출하는 문제 방지.
23
+
24
+ <details>
25
+ <summary>v2.3.0 변경사항</summary>
18
26
 
19
27
  - **도구 프로필 (lite/full)** — 웹 클라이언트(Claude.ai 등)용 lite 프로필 도입. 89개 → 14개로 자동 축소하여 컨텍스트 소비 87% 절감. `/mcp?profile=lite`로 사용.
20
28
  - **체인 8개** + 핵심 직접 도구 4개 + 메타 도구 2개 = 14개로 동일 기능 커버
@@ -22,6 +30,8 @@
22
30
  - `execute_tool`: discover로 찾은 도구를 프록시 실행
23
31
  - **kordoc 통합 파서** — 자체 HWP5/HWPX/PDF 파서 5개를 [kordoc](https://github.com/chrisryugj/kordoc) 통합 파서로 교체. 의존성 경량화.
24
32
 
33
+ </details>
34
+
25
35
  <details>
26
36
  <summary>v2.2.0 변경사항</summary>
27
37
 
@@ -89,11 +99,13 @@ API 키는 [법제처 Open API](https://open.law.go.kr/LSO/openApi/guideResult.d
89
99
 
90
100
  ### 원격 MCP (설치 없이 바로)
91
101
 
102
+ API 키를 URL에 포함하여 바로 사용:
103
+
92
104
  ```json
93
105
  {
94
106
  "mcpServers": {
95
107
  "korean-law": {
96
- "url": "https://korean-law-mcp.fly.dev/mcp"
108
+ "url": "https://korean-law-mcp.fly.dev/mcp?oc=your-api-key"
97
109
  }
98
110
  }
99
111
  }
@@ -101,18 +113,22 @@ API 키는 [법제처 Open API](https://open.law.go.kr/LSO/openApi/guideResult.d
101
113
 
102
114
  **Claude.ai 등 웹 클라이언트** — 컨텍스트 절약을 위해 lite 프로필 권장:
103
115
 
104
- ```json
105
- {
106
- "mcpServers": {
107
- "korean-law": {
108
- "url": "https://korean-law-mcp.fly.dev/mcp?profile=lite"
109
- }
110
- }
111
- }
116
+ ```
117
+ https://korean-law-mcp.fly.dev/mcp?profile=lite&oc=your-api-key
112
118
  ```
113
119
 
114
120
  > lite 프로필은 체인 8개 + 핵심 4개 + 메타 2개 = **14개 도구**로 동일 기능 커버. 특수 도구가 필요하면 `discover_tools` → `execute_tool`로 접근.
115
121
 
122
+ **API 키 전달 방법** (우선순위순):
123
+
124
+ | 방법 | 예시 | 설명 |
125
+ |------|------|------|
126
+ | URL 쿼리 | `?oc=your-key` | 웹 클라이언트에서 가장 간편. 세션 전체에 자동 적용 |
127
+ | HTTP 헤더 | `apikey: your-key` | `law-oc`, `x-api-key`, `Authorization: Bearer` 등도 지원 |
128
+ | 도구 파라미터 | `apiKey: "your-key"` | 개별 도구 호출 시 직접 전달 |
129
+
130
+ > API 키는 [법제처 Open API](https://open.law.go.kr/LSO/openApi/guideResult.do)에서 무료 발급.
131
+
116
132
  ### CLI
117
133
 
118
134
  ```bash
@@ -101,8 +101,10 @@ export async function startHTTPServer(createServer, port) {
101
101
  // POST /mcp - 클라이언트 요청 처리
102
102
  app.post("/mcp", async (req, res) => {
103
103
  console.error(`[POST /mcp] Received request`);
104
- // Extract API key from various possible header locations
105
- const apiKeyFromHeader = req.headers["apikey"] ||
104
+ // Extract API key: URL query > header > 기존 세션
105
+ const apiKeyFromQuery = req.query.oc;
106
+ const apiKeyFromHeader = apiKeyFromQuery ||
107
+ req.headers["apikey"] ||
106
108
  req.headers["law_oc"] ||
107
109
  req.headers["law-oc"] ||
108
110
  req.headers["LAW_OC"] ||
@@ -670,6 +670,9 @@ export function registerTools(server, apiClient, profile = "full") {
670
670
  };
671
671
  }
672
672
  catch (error) {
673
+ console.error(`[CallTool] Error in ${name}:`, error instanceof Error ? error.message : error);
674
+ if (error instanceof Error && error.stack)
675
+ console.error(error.stack);
673
676
  const errResult = formatToolError(error, name);
674
677
  return {
675
678
  content: errResult.content.map(c => ({ type: "text", text: c.text })),
@@ -59,7 +59,7 @@ export async function searchAdminRule(apiClient, input) {
59
59
  resultText += ` - 구분: ${ruleType}\n`;
60
60
  resultText += ` - 소관부처: ${orgName}\n\n`;
61
61
  }
62
- resultText += `\n💡 상세 내용을 조회하려면 get_admin_rule Tool을 사용하세요.`;
62
+ resultText += `\n💡 상세 조회: execute_tool(tool_name="get_admin_rule", params={adminRuleSeq:"일련번호"})`;
63
63
  return {
64
64
  content: [{
65
65
  type: "text",
@@ -16,6 +16,7 @@ import { searchAdminAppeals } from "./admin-appeals.js";
16
16
  import { compareOldNew } from "./comparison.js";
17
17
  import { getArticleHistory } from "./article-history.js";
18
18
  import { searchOrdinance } from "./ordinance-search.js";
19
+ import { getOrdinance } from "./ordinance.js";
19
20
  import { getAnnexes } from "./annex.js";
20
21
  import { searchAiLaw } from "./life-law.js";
21
22
  import { getLawText } from "./law-text.js";
@@ -382,6 +383,15 @@ export async function chainOrdinanceCompare(apiClient, input) {
382
383
  const ordinances = await callTool(searchOrdinance, apiClient, { query: ordinanceQuery, display: 20, apiKey: input.apiKey });
383
384
  if (!ordinances.isError)
384
385
  parts.push(sec("전국 자치법규 검색 결과", ordinances.text));
386
+ // Step 3: 상위 1건 전문 자동 조회
387
+ if (!ordinances.isError) {
388
+ const seqMatch = ordinances.text.match(/\[(\d+)\]/);
389
+ if (seqMatch) {
390
+ const fullText = await callTool(getOrdinance, apiClient, { ordinSeq: seqMatch[1], apiKey: input.apiKey });
391
+ if (!fullText.isError)
392
+ parts.push(sec("조례 전문 (상위 1건)", fullText.text));
393
+ }
394
+ }
385
395
  // 키워드 확장
386
396
  const exp = detectExpansions(input.query);
387
397
  if (exp.includes("interpretation")) {
@@ -70,7 +70,7 @@ export async function searchCustomsInterpretations(apiClient, args) {
70
70
  }
71
71
  output += `\n`;
72
72
  }
73
- output += `\n💡 전문을 조회하려면 get_customs_interpretation_text Tool을 사용하세요.\n`;
73
+ output += `\n💡 전문 조회: execute_tool(tool_name="get_customs_interpretation_text", params={id:"해석례ID"})\n`;
74
74
  return {
75
75
  content: [{
76
76
  type: "text",
@@ -80,7 +80,7 @@ export async function searchInterpretations(apiClient, args) {
80
80
  }
81
81
  output += `\n`;
82
82
  }
83
- output += `\n💡 전문을 조회하려면 get_interpretation_text Tool을 사용하세요.\n`;
83
+ output += `\n💡 전문 조회: execute_tool(tool_name="get_interpretation_text", params={id:"해석례ID"})\n`;
84
84
  return {
85
85
  content: [{
86
86
  type: "text",
@@ -89,7 +89,7 @@ export async function getLawTree(apiClient, input) {
89
89
  output += ` └─ ... 외 ${structure.rule.length - 5}개 조항\n`;
90
90
  }
91
91
  }
92
- output += `\n\n💡 상세한 위임 관계는 get_three_tier Tool을 사용하세요.`;
92
+ output += `\n\n💡 위임관계: chain_law_system(query="법령명") 또는 execute_tool(tool_name="get_three_tier", params={mst:"법령MST"})`;
93
93
  return {
94
94
  content: [{
95
95
  type: "text",
@@ -79,7 +79,7 @@ export async function searchOrdinance(apiClient, input) {
79
79
  }
80
80
  output += `\n`;
81
81
  }
82
- output += `\n💡 전문을 조회하려면 get_ordinance Tool을 사용하세요.\n`;
82
+ output += `\n💡 전문 조회: execute_tool(tool_name="get_ordinance", params={ordinSeq:"일련번호"})\n`;
83
83
  return {
84
84
  content: [{
85
85
  type: "text",
@@ -77,7 +77,7 @@ export async function getOrdinance(apiClient, input) {
77
77
  parentLawHints.forEach(h => { resultText += ` - ${h}\n`; });
78
78
  }
79
79
  else {
80
- resultText += `\n💡 상위법령 확인: search_law 또는 get_related_laws 도구를 사용하세요.`;
80
+ resultText += `\n💡 상위법령 확인: search_law(query="법령명") 또는 execute_tool(tool_name="get_related_laws", params={query:"키워드"})`;
81
81
  }
82
82
  return {
83
83
  content: [{
@@ -65,7 +65,7 @@ export async function searchTaxTribunalDecisions(apiClient, args) {
65
65
  }
66
66
  output += `\n`;
67
67
  }
68
- output += `\n💡 전문을 조회하려면 get_tax_tribunal_decision_text Tool을 사용하세요.\n`;
68
+ output += `\n💡 전문 조회: execute_tool(tool_name="get_tax_tribunal_decision_text", params={id:"사건ID"})\n`;
69
69
  return {
70
70
  content: [{
71
71
  type: "text",
@@ -60,7 +60,7 @@ export async function searchTreaties(apiClient, args) {
60
60
  }
61
61
  output += `\n`;
62
62
  }
63
- output += `\n전문을 조회하려면 get_treaty_text Tool을 사용하세요.\n`;
63
+ output += `\n전문 조회: execute_tool(tool_name="get_treaty_text", params={treatySeq:"조약번호"})\n`;
64
64
  return { content: [{ type: "text", text: truncateResponse(output) }] };
65
65
  }
66
66
  catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "korean-law-mcp",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "description": "국가법령정보센터 API 기반 MCP 서버 - 한국 법령 조회·비교 도구",
5
5
  "type": "module",
6
6
  "main": "build/index.js",