building-standards-act-mcp 0.2.0 → 0.3.0
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 +50 -20
- package/dist/lib/article-analyzer.d.ts +5 -0
- package/dist/lib/article-analyzer.js +106 -0
- package/dist/lib/article-analyzer.js.map +1 -0
- package/dist/lib/citation-verifier.d.ts +14 -0
- package/dist/lib/citation-verifier.js +139 -0
- package/dist/lib/citation-verifier.js.map +1 -0
- package/dist/lib/related-law-finder.d.ts +5 -0
- package/dist/lib/related-law-finder.js +103 -0
- package/dist/lib/related-law-finder.js.map +1 -0
- package/dist/lib/types.d.ts +72 -0
- package/dist/server.js +13 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/analyze-article.d.ts +2 -0
- package/dist/tools/analyze-article.js +31 -0
- package/dist/tools/analyze-article.js.map +1 -0
- package/dist/tools/get-laws-batch.d.ts +2 -0
- package/dist/tools/get-laws-batch.js +165 -0
- package/dist/tools/get-laws-batch.js.map +1 -0
- package/dist/tools/suggest-related.d.ts +2 -0
- package/dist/tools/suggest-related.js +62 -0
- package/dist/tools/suggest-related.js.map +1 -0
- package/dist/tools/verify-citation.d.ts +2 -0
- package/dist/tools/verify-citation.js +98 -0
- package/dist/tools/verify-citation.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ AI の建築法規回答に含まれるハルシネーションを、条文原
|
|
|
15
15
|
- **法令検索** — キーワードで法令を横断検索
|
|
16
16
|
- **法令全文取得** — 法令全体のテキストを取得
|
|
17
17
|
- **告示取得** — 建築基準法が技術基準を委任している告示を国土交通省の告示データベース(PDF)から取得
|
|
18
|
-
- **主要法令プリセット** — 建築基準法・消防法・都市計画法ほか 115 法令の law_id
|
|
18
|
+
- **主要法令プリセット** — 建築基準法・消防法・都市計画法ほか 115 法令の law_id をプリセット登録済み
|
|
19
19
|
- **プリセット検証** — `validate_presets` ツールでプリセットの有効性をランタイム検証
|
|
20
20
|
- **略称対応** — 「建基法」「都計法」「消防法」等の略称で指定可能
|
|
21
21
|
- **キャッシュ** — 検索 30 分・法令データ 24 時間の TTL キャッシュ
|
|
@@ -73,14 +73,18 @@ npm run build
|
|
|
73
73
|
|
|
74
74
|
## MCP ツール
|
|
75
75
|
|
|
76
|
-
| ツール | 説明
|
|
77
|
-
| ------------------- |
|
|
78
|
-
| `get_law` | 条番号を指定して法令の条文を取得する。略称・正式名称のいずれでも指定可能
|
|
79
|
-
| `get_full_law` | 法令の全文を取得する。条番号を指定せず法令全体のテキストを返す
|
|
80
|
-
| `search_law` | キーワードで建築関連法令を横断検索する。プリセット法令と e-Gov API の両方を検索
|
|
81
|
-
| `get_kokuji` | 建築基準法が技術基準を委任している告示の全文を取得する
|
|
82
|
-
| `validate_presets` | プリセット法令の law_id が e-Gov API で有効かを一括検証する
|
|
83
|
-
| `check_law_updates` | 法令の改正状況を e-Gov API で確認する(単体/バッチ/改正履歴表示に対応)
|
|
76
|
+
| ツール | 説明 |
|
|
77
|
+
| ------------------- | -------------------------------------------------------------------------------- |
|
|
78
|
+
| `get_law` | 条番号を指定して法令の条文を取得する。略称・正式名称のいずれでも指定可能 |
|
|
79
|
+
| `get_full_law` | 法令の全文を取得する。条番号を指定せず法令全体のテキストを返す |
|
|
80
|
+
| `search_law` | キーワードで建築関連法令を横断検索する。プリセット法令と e-Gov API の両方を検索 |
|
|
81
|
+
| `get_kokuji` | 建築基準法が技術基準を委任している告示の全文を取得する |
|
|
82
|
+
| `validate_presets` | プリセット法令の law_id が e-Gov API で有効かを一括検証する |
|
|
83
|
+
| `check_law_updates` | 法令の改正状況を e-Gov API で確認する(単体/バッチ/改正履歴表示に対応) |
|
|
84
|
+
| `get_laws_batch` | 複数の法令・条文を一括取得する(最大20件、同一法令は1回のAPI呼び出しで効率処理) |
|
|
85
|
+
| `verify_citation` | AIの回答に含まれる法令引用を検証する(条文存在確認・テキスト照合、最大10件) |
|
|
86
|
+
| `suggest_related` | 指定した条文の関連法令・委任先・同法令内参照を自動抽出して提案する |
|
|
87
|
+
| `analyze_article` | 条文の構造解析メタデータ(項数・号数・参照統計・プレビュー)をJSON形式で返す |
|
|
84
88
|
|
|
85
89
|
### パラメータ
|
|
86
90
|
|
|
@@ -124,6 +128,32 @@ npm run build
|
|
|
124
128
|
| `group` | 章グループ(バッチチェック、省略時は全章) | `1章` |
|
|
125
129
|
| `show_history` | 改正履歴の詳細を表示(`law_name` 指定時) | `true` |
|
|
126
130
|
|
|
131
|
+
#### `get_laws_batch`
|
|
132
|
+
|
|
133
|
+
| パラメータ | 説明 | 例 |
|
|
134
|
+
| ---------- | ---------------------------------------------------------------------- | --- |
|
|
135
|
+
| `requests` | 取得対象の配列。各要素に `law_name`, `article_number`, `format` を指定 | — |
|
|
136
|
+
|
|
137
|
+
#### `verify_citation`
|
|
138
|
+
|
|
139
|
+
| パラメータ | 説明 | 例 |
|
|
140
|
+
| ----------- | ------------------------------------------------------------------------------- | --- |
|
|
141
|
+
| `citations` | 検証対象の配列。各要素に `law_name`, `article_number`, `claimed_text`(省略可) | — |
|
|
142
|
+
|
|
143
|
+
#### `suggest_related`
|
|
144
|
+
|
|
145
|
+
| パラメータ | 説明 | 例 |
|
|
146
|
+
| ---------------- | ---------------------------- | ------------ |
|
|
147
|
+
| `law_name` | 法令名(正式名称または略称) | `建築基準法` |
|
|
148
|
+
| `article_number` | 条文番号 | `第20条` |
|
|
149
|
+
|
|
150
|
+
#### `analyze_article`
|
|
151
|
+
|
|
152
|
+
| パラメータ | 説明 | 例 |
|
|
153
|
+
| ---------------- | ---------------------------- | ------------ |
|
|
154
|
+
| `law_name` | 法令名(正式名称または略称) | `建築基準法` |
|
|
155
|
+
| `article_number` | 条文番号 | `第20条` |
|
|
156
|
+
|
|
127
157
|
## 使用例
|
|
128
158
|
|
|
129
159
|
### 条文取得
|
|
@@ -172,7 +202,7 @@ npm run build
|
|
|
172
202
|
|
|
173
203
|
## 対応法令一覧(115 法令)
|
|
174
204
|
|
|
175
|
-
###
|
|
205
|
+
### 総則(4 法令)
|
|
176
206
|
|
|
177
207
|
| 法令名 | 略称 |
|
|
178
208
|
| ------------------ | -------------------- |
|
|
@@ -181,7 +211,7 @@ npm run build
|
|
|
181
211
|
| 建築基準法施行規則 | 建基規則 |
|
|
182
212
|
| 民法 | — |
|
|
183
213
|
|
|
184
|
-
###
|
|
214
|
+
### 手続関連規定(6 法令)
|
|
185
215
|
|
|
186
216
|
| 法令名 | 略称 |
|
|
187
217
|
| ---------------- | ------------ |
|
|
@@ -192,7 +222,7 @@ npm run build
|
|
|
192
222
|
| 建設業法施行令 | — |
|
|
193
223
|
| 建設業法施行規則 | — |
|
|
194
224
|
|
|
195
|
-
###
|
|
225
|
+
### 集団規定・街づくり(13 法令)
|
|
196
226
|
|
|
197
227
|
| 法令名 | 略称 |
|
|
198
228
|
| -------------------------------------------------- | ---------------------- |
|
|
@@ -210,7 +240,7 @@ npm run build
|
|
|
210
240
|
| 国土利用計画法 | 国土利用計画法, 国土法 |
|
|
211
241
|
| 都市計画法施行規則 | 都計規則 |
|
|
212
242
|
|
|
213
|
-
###
|
|
243
|
+
### 防火・耐火規定(4 法令)
|
|
214
244
|
|
|
215
245
|
| 法令名 | 略称 |
|
|
216
246
|
| ------------------------ | ---------- |
|
|
@@ -219,7 +249,7 @@ npm run build
|
|
|
219
249
|
| 消防法施行規則 | 消防規則 |
|
|
220
250
|
| 危険物の規制に関する規則 | 危険物規則 |
|
|
221
251
|
|
|
222
|
-
###
|
|
252
|
+
### 構造規定(7 法令)
|
|
223
253
|
|
|
224
254
|
| 法令名 | 略称 |
|
|
225
255
|
| ------------------------------------------ | ---------------------- |
|
|
@@ -231,7 +261,7 @@ npm run build
|
|
|
231
261
|
| 建築物の耐震改修の促進に関する法律施行令 | 耐震改修促進法施行令 |
|
|
232
262
|
| 建築物の耐震改修の促進に関する法律施行規則 | 耐震改修促進法施行規則 |
|
|
233
263
|
|
|
234
|
-
###
|
|
264
|
+
### 建築材料(3 法令)
|
|
235
265
|
|
|
236
266
|
| 法令名 | 略称 |
|
|
237
267
|
| ---------------- | ---------------------- |
|
|
@@ -239,7 +269,7 @@ npm run build
|
|
|
239
269
|
| 製造物責任法 | PL法, 製造物責任法 |
|
|
240
270
|
| 石綿障害予防規則 | 石綿則, アスベスト規則 |
|
|
241
271
|
|
|
242
|
-
###
|
|
272
|
+
### 設備関連規定(5 法令)
|
|
243
273
|
|
|
244
274
|
| 法令名 | 略称 |
|
|
245
275
|
| -------------------------- | ---------------- |
|
|
@@ -249,7 +279,7 @@ npm run build
|
|
|
249
279
|
| 浄化槽法施行令 | — |
|
|
250
280
|
| 環境省関係浄化槽法施行規則 | 浄化槽法施行規則 |
|
|
251
281
|
|
|
252
|
-
###
|
|
282
|
+
### その他(5 法令)
|
|
253
283
|
|
|
254
284
|
| 法令名 | 略称 |
|
|
255
285
|
| ---------------------------------------------- | ------------------ |
|
|
@@ -259,7 +289,7 @@ npm run build
|
|
|
259
289
|
| 不動産の鑑定評価に関する法律 | 不動産鑑定法 |
|
|
260
290
|
| 住宅宿泊事業法 | 民泊新法 |
|
|
261
291
|
|
|
262
|
-
###
|
|
292
|
+
### 建築基準関係規定・建築関連法令(52 法令)
|
|
263
293
|
|
|
264
294
|
<details>
|
|
265
295
|
<summary>法令一覧を表示</summary>
|
|
@@ -321,7 +351,7 @@ npm run build
|
|
|
321
351
|
|
|
322
352
|
</details>
|
|
323
353
|
|
|
324
|
-
###
|
|
354
|
+
### 省エネ・住宅関連法(13 法令)
|
|
325
355
|
|
|
326
356
|
| 法令名 | 略称 |
|
|
327
357
|
| ------------------------------------------------------------------ | ---------------------- |
|
|
@@ -339,7 +369,7 @@ npm run build
|
|
|
339
369
|
| 長期優良住宅の普及の促進に関する法律施行規則 | 長期優良住宅法施行規則 |
|
|
340
370
|
| 建築物のエネルギー消費性能の向上等に関する法律施行規則 | 建築物省エネ法施行規則 |
|
|
341
371
|
|
|
342
|
-
###
|
|
372
|
+
### 既存建築物関連(3 法令)
|
|
343
373
|
|
|
344
374
|
| 法令名 | 略称 |
|
|
345
375
|
| ------------------------------------------ | ---------------- |
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { getLawData } from "./egov-client.js";
|
|
2
|
+
import { parseArticleStructured } from "./egov-parser.js";
|
|
3
|
+
import { LawRegistry } from "./law-registry.js";
|
|
4
|
+
const registry = new LawRegistry();
|
|
5
|
+
const PREVIEW_LENGTH = 100;
|
|
6
|
+
/**
|
|
7
|
+
* Remove parentheses from caption text.
|
|
8
|
+
* "(構造耐力)" → "構造耐力"
|
|
9
|
+
*/
|
|
10
|
+
function stripParentheses(caption) {
|
|
11
|
+
return caption.replace(/^[((]/, "").replace(/[))]$/, "");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Count total characters in a structured article.
|
|
15
|
+
*/
|
|
16
|
+
function countCharacters(article) {
|
|
17
|
+
let count = 0;
|
|
18
|
+
for (const para of article.paragraphs) {
|
|
19
|
+
count += para.paragraph_sentence.length;
|
|
20
|
+
for (const item of para.items) {
|
|
21
|
+
count += item.item_sentence.length;
|
|
22
|
+
for (const sub of item.subitems) {
|
|
23
|
+
count += countSubitemChars(sub);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return count;
|
|
28
|
+
}
|
|
29
|
+
function countSubitemChars(sub) {
|
|
30
|
+
let count = sub.subitem_sentence.length;
|
|
31
|
+
for (const child of sub.subitems) {
|
|
32
|
+
count += countSubitemChars(child);
|
|
33
|
+
}
|
|
34
|
+
return count;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Count total subitems recursively.
|
|
38
|
+
*/
|
|
39
|
+
function countSubitems(article) {
|
|
40
|
+
let count = 0;
|
|
41
|
+
for (const para of article.paragraphs) {
|
|
42
|
+
for (const item of para.items) {
|
|
43
|
+
count += countSubitemsRecursive(item.subitems);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return count;
|
|
47
|
+
}
|
|
48
|
+
function countSubitemsRecursive(subs) {
|
|
49
|
+
let count = subs.length;
|
|
50
|
+
for (const sub of subs) {
|
|
51
|
+
count += countSubitemsRecursive(sub.subitems);
|
|
52
|
+
}
|
|
53
|
+
return count;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Analyze a single article and return structured metadata.
|
|
57
|
+
*/
|
|
58
|
+
export async function analyzeArticle(lawName, articleNumber) {
|
|
59
|
+
const preset = registry.findByName(lawName);
|
|
60
|
+
if (!preset) {
|
|
61
|
+
throw new Error(`法令「${lawName}」がプリセットに見つかりませんでした。`);
|
|
62
|
+
}
|
|
63
|
+
const lawData = await getLawData(preset.law_id);
|
|
64
|
+
const article = parseArticleStructured(lawData.law_full_text, articleNumber);
|
|
65
|
+
if (!article) {
|
|
66
|
+
throw new Error(`${preset.title}に第${articleNumber}条が見つかりませんでした。`);
|
|
67
|
+
}
|
|
68
|
+
const itemCount = article.paragraphs.reduce((sum, p) => sum + p.items.length, 0);
|
|
69
|
+
const refs = article.references ?? [];
|
|
70
|
+
const crossLawRefs = refs.filter((r) => r.ref_type === "cross_law");
|
|
71
|
+
const referencedLaws = [
|
|
72
|
+
...new Set(crossLawRefs
|
|
73
|
+
.map((r) => r.target_law)
|
|
74
|
+
.filter((l) => l !== undefined)),
|
|
75
|
+
];
|
|
76
|
+
return {
|
|
77
|
+
law_name: preset.title,
|
|
78
|
+
law_num: preset.law_num,
|
|
79
|
+
article_num: article.article_num,
|
|
80
|
+
article_title: article.article_title,
|
|
81
|
+
caption: stripParentheses(article.article_caption),
|
|
82
|
+
structure: {
|
|
83
|
+
paragraph_count: article.paragraphs.length,
|
|
84
|
+
item_count: itemCount,
|
|
85
|
+
subitem_count: countSubitems(article),
|
|
86
|
+
total_characters: countCharacters(article),
|
|
87
|
+
},
|
|
88
|
+
paragraph_summaries: article.paragraphs.map((p) => ({
|
|
89
|
+
paragraph_num: p.paragraph_num,
|
|
90
|
+
preview: p.paragraph_sentence.length > PREVIEW_LENGTH
|
|
91
|
+
? p.paragraph_sentence.slice(0, PREVIEW_LENGTH) + "…"
|
|
92
|
+
: p.paragraph_sentence,
|
|
93
|
+
item_count: p.items.length,
|
|
94
|
+
})),
|
|
95
|
+
reference_summary: {
|
|
96
|
+
total: refs.length,
|
|
97
|
+
cross_law: crossLawRefs.length,
|
|
98
|
+
same_law: refs.filter((r) => r.ref_type === "same_law").length,
|
|
99
|
+
relative: refs.filter((r) => r.ref_type === "relative").length,
|
|
100
|
+
delegation: refs.filter((r) => r.ref_type === "delegation").length,
|
|
101
|
+
referenced_laws: referencedLaws,
|
|
102
|
+
},
|
|
103
|
+
structured_data: article,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=article-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article-analyzer.js","sourceRoot":"","sources":["../../src/lib/article-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CitationVerification } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Normalize text for comparison: collapse whitespace, remove formatting artifacts.
|
|
4
|
+
*/
|
|
5
|
+
export declare function normalizeText(text: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Calculate match score between claimed text and actual text.
|
|
8
|
+
* Returns a value between 0.0 and 1.0.
|
|
9
|
+
*/
|
|
10
|
+
export declare function calculateMatchScore(claimed: string, actual: string): number;
|
|
11
|
+
/**
|
|
12
|
+
* Verify a single citation against the actual law text.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifyCitation(lawName: string, articleNumber: string, claimedText?: string): Promise<CitationVerification>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { getLawData } from "./egov-client.js";
|
|
2
|
+
import { parseArticle } from "./egov-parser.js";
|
|
3
|
+
import { LawRegistry } from "./law-registry.js";
|
|
4
|
+
const registry = new LawRegistry();
|
|
5
|
+
const MAX_PREVIEW_LENGTH = 200;
|
|
6
|
+
/**
|
|
7
|
+
* Normalize text for comparison: collapse whitespace, remove formatting artifacts.
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeText(text) {
|
|
10
|
+
return text
|
|
11
|
+
.replace(/\s+/g, "")
|
|
12
|
+
.replace(/[、。,.・]/g, "")
|
|
13
|
+
.replace(/[()()「」『』【】]/g, "");
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Calculate match score between claimed text and actual text.
|
|
17
|
+
* Returns a value between 0.0 and 1.0.
|
|
18
|
+
*/
|
|
19
|
+
export function calculateMatchScore(claimed, actual) {
|
|
20
|
+
const normClaimed = normalizeText(claimed);
|
|
21
|
+
const normActual = normalizeText(actual);
|
|
22
|
+
if (normClaimed.length === 0 || normActual.length === 0) {
|
|
23
|
+
return 0;
|
|
24
|
+
}
|
|
25
|
+
// Check if claimed text is contained in actual text
|
|
26
|
+
if (normActual.includes(normClaimed)) {
|
|
27
|
+
return 1.0;
|
|
28
|
+
}
|
|
29
|
+
// Check if actual text is contained in claimed text
|
|
30
|
+
if (normClaimed.includes(normActual)) {
|
|
31
|
+
return 1.0;
|
|
32
|
+
}
|
|
33
|
+
// Calculate longest common substring ratio
|
|
34
|
+
const lcsLength = longestCommonSubstringLength(normClaimed, normActual);
|
|
35
|
+
const maxLen = Math.max(normClaimed.length, normActual.length);
|
|
36
|
+
return lcsLength / maxLen;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Find the length of the longest common substring.
|
|
40
|
+
* Uses a space-optimized DP approach.
|
|
41
|
+
*/
|
|
42
|
+
function longestCommonSubstringLength(a, b) {
|
|
43
|
+
// Limit input size to avoid excessive memory usage
|
|
44
|
+
const maxLen = 500;
|
|
45
|
+
const sa = a.length > maxLen ? a.slice(0, maxLen) : a;
|
|
46
|
+
const sb = b.length > maxLen ? b.slice(0, maxLen) : b;
|
|
47
|
+
let maxFound = 0;
|
|
48
|
+
const prev = new Uint16Array(sb.length + 1);
|
|
49
|
+
const curr = new Uint16Array(sb.length + 1);
|
|
50
|
+
for (let i = 1; i <= sa.length; i++) {
|
|
51
|
+
for (let j = 1; j <= sb.length; j++) {
|
|
52
|
+
if (sa[i - 1] === sb[j - 1]) {
|
|
53
|
+
curr[j] = prev[j - 1] + 1;
|
|
54
|
+
if (curr[j] > maxFound) {
|
|
55
|
+
maxFound = curr[j];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
curr[j] = 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
prev.set(curr);
|
|
63
|
+
curr.fill(0);
|
|
64
|
+
}
|
|
65
|
+
return maxFound;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Truncate text for preview, adding ellipsis if needed.
|
|
69
|
+
*/
|
|
70
|
+
function truncate(text, maxLen) {
|
|
71
|
+
if (text.length <= maxLen)
|
|
72
|
+
return text;
|
|
73
|
+
return text.slice(0, maxLen) + "…";
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Verify a single citation against the actual law text.
|
|
77
|
+
*/
|
|
78
|
+
export async function verifyCitation(lawName, articleNumber, claimedText) {
|
|
79
|
+
try {
|
|
80
|
+
const preset = registry.findByName(lawName);
|
|
81
|
+
if (!preset) {
|
|
82
|
+
return {
|
|
83
|
+
law_name: lawName,
|
|
84
|
+
article_number: articleNumber,
|
|
85
|
+
status: "law_not_found",
|
|
86
|
+
error_message: `法令「${lawName}」がプリセットに見つかりませんでした。`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
const lawData = await getLawData(preset.law_id);
|
|
90
|
+
const article = parseArticle(lawData.law_full_text, articleNumber);
|
|
91
|
+
if (!article) {
|
|
92
|
+
return {
|
|
93
|
+
law_name: preset.title,
|
|
94
|
+
article_number: articleNumber,
|
|
95
|
+
status: "article_not_found",
|
|
96
|
+
error_message: `${preset.title}に第${articleNumber}条が見つかりませんでした。`,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
// If no claimed text, just verify existence
|
|
100
|
+
if (!claimedText || claimedText.trim() === "") {
|
|
101
|
+
return {
|
|
102
|
+
law_name: preset.title,
|
|
103
|
+
article_number: articleNumber,
|
|
104
|
+
status: "verified",
|
|
105
|
+
actual_text: truncate(article.text, MAX_PREVIEW_LENGTH),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// Compare claimed text against actual text
|
|
109
|
+
const score = calculateMatchScore(claimedText, article.text);
|
|
110
|
+
if (score >= 0.8) {
|
|
111
|
+
return {
|
|
112
|
+
law_name: preset.title,
|
|
113
|
+
article_number: articleNumber,
|
|
114
|
+
status: "verified",
|
|
115
|
+
match_score: Math.round(score * 100) / 100,
|
|
116
|
+
actual_text: truncate(article.text, MAX_PREVIEW_LENGTH),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
law_name: preset.title,
|
|
121
|
+
article_number: articleNumber,
|
|
122
|
+
status: "mismatch",
|
|
123
|
+
match_score: Math.round(score * 100) / 100,
|
|
124
|
+
actual_text: truncate(article.text, MAX_PREVIEW_LENGTH),
|
|
125
|
+
mismatch_detail: score < 0.3
|
|
126
|
+
? "主張されたテキストと実際の条文に大きな相違があります。"
|
|
127
|
+
: "主張されたテキストと実際の条文に部分的な相違があります。",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
return {
|
|
132
|
+
law_name: lawName,
|
|
133
|
+
article_number: articleNumber,
|
|
134
|
+
status: "error",
|
|
135
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=citation-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"citation-verifier.js","sourceRoot":"","sources":["../../src/lib/citation-verifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC;AAEnC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAE/B;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI;SACR,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;SACnB,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC;SACvB,OAAO,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC;AAClC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,MAAc;IACjE,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,oDAAoD;IACpD,IAAI,UAAU,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACrC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,oDAAoD;IACpD,IAAI,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO,GAAG,CAAC;IACb,CAAC;IAED,2CAA2C;IAC3C,MAAM,SAAS,GAAG,4BAA4B,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACxE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;IAE/D,OAAO,SAAS,GAAG,MAAM,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,SAAS,4BAA4B,CAAC,CAAS,EAAE,CAAS;IACxD,mDAAmD;IACnD,MAAM,MAAM,GAAG,GAAG,CAAC;IACnB,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAEtD,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC5C,MAAM,IAAI,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAE5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;gBAC1B,IAAI,IAAI,CAAC,CAAC,CAAC,GAAG,QAAQ,EAAE,CAAC;oBACvB,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;gBACrB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;YACd,CAAC;QACH,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACf,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,QAAQ,CAAC,IAAY,EAAE,MAAc;IAC5C,IAAI,IAAI,CAAC,MAAM,IAAI,MAAM;QAAE,OAAO,IAAI,CAAC;IACvC,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,EACf,aAAqB,EACrB,WAAoB;IAEpB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO;gBACL,QAAQ,EAAE,OAAO;gBACjB,cAAc,EAAE,aAAa;gBAC7B,MAAM,EAAE,eAAe;gBACvB,aAAa,EAAE,MAAM,OAAO,qBAAqB;aAClD,CAAC;QACJ,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC;QAEnE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,KAAK;gBACtB,cAAc,EAAE,aAAa;gBAC7B,MAAM,EAAE,mBAAmB;gBAC3B,aAAa,EAAE,GAAG,MAAM,CAAC,KAAK,KAAK,aAAa,eAAe;aAChE,CAAC;QACJ,CAAC;QAED,4CAA4C;QAC5C,IAAI,CAAC,WAAW,IAAI,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;YAC9C,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,KAAK;gBACtB,cAAc,EAAE,aAAa;gBAC7B,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,kBAAkB,CAAC;aACxD,CAAC;QACJ,CAAC;QAED,2CAA2C;QAC3C,MAAM,KAAK,GAAG,mBAAmB,CAAC,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAE7D,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;YACjB,OAAO;gBACL,QAAQ,EAAE,MAAM,CAAC,KAAK;gBACtB,cAAc,EAAE,aAAa;gBAC7B,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;gBAC1C,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,kBAAkB,CAAC;aACxD,CAAC;QACJ,CAAC;QAED,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,KAAK;YACtB,cAAc,EAAE,aAAa;YAC7B,MAAM,EAAE,UAAU;YAClB,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG;YAC1C,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,kBAAkB,CAAC;YACvD,eAAe,EACb,KAAK,GAAG,GAAG;gBACT,CAAC,CAAC,6BAA6B;gBAC/B,CAAC,CAAC,8BAA8B;SACrC,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO;YACL,QAAQ,EAAE,OAAO;YACjB,cAAc,EAAE,aAAa;YAC7B,MAAM,EAAE,OAAO;YACf,aAAa,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SACtE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { getLawData } from "./egov-client.js";
|
|
2
|
+
import { parseArticle } from "./egov-parser.js";
|
|
3
|
+
import { LawRegistry } from "./law-registry.js";
|
|
4
|
+
const registry = new LawRegistry();
|
|
5
|
+
/**
|
|
6
|
+
* Infer delegation target type from raw reference text.
|
|
7
|
+
*/
|
|
8
|
+
function inferDelegationType(rawText) {
|
|
9
|
+
if (rawText.includes("政令"))
|
|
10
|
+
return "政令";
|
|
11
|
+
if (rawText.includes("省令"))
|
|
12
|
+
return "省令";
|
|
13
|
+
if (rawText.includes("国土交通大臣"))
|
|
14
|
+
return "告示";
|
|
15
|
+
if (rawText.includes("条例"))
|
|
16
|
+
return "条例";
|
|
17
|
+
return "その他";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Find related laws for a given article by analyzing its cross-references.
|
|
21
|
+
*/
|
|
22
|
+
export async function findRelatedLaws(lawName, articleNumber) {
|
|
23
|
+
const preset = registry.findByName(lawName);
|
|
24
|
+
if (!preset) {
|
|
25
|
+
throw new Error(`法令「${lawName}」がプリセットに見つかりませんでした。`);
|
|
26
|
+
}
|
|
27
|
+
const lawData = await getLawData(preset.law_id);
|
|
28
|
+
const article = parseArticle(lawData.law_full_text, articleNumber);
|
|
29
|
+
if (!article) {
|
|
30
|
+
throw new Error(`${preset.title}に第${articleNumber}条が見つかりませんでした。`);
|
|
31
|
+
}
|
|
32
|
+
const refs = article.references ?? [];
|
|
33
|
+
// Categorize references
|
|
34
|
+
const directlyReferenced = extractCrossLawRefs(refs);
|
|
35
|
+
const delegatedTo = extractDelegationRefs(refs);
|
|
36
|
+
const sameLawRefs = extractSameLawRefs(refs);
|
|
37
|
+
// Get same-group laws (excluding the source law itself)
|
|
38
|
+
const sameGroupLaws = registry
|
|
39
|
+
.getByGroup(preset.group)
|
|
40
|
+
.filter((p) => p.law_id !== preset.law_id)
|
|
41
|
+
.map((p) => ({ law_name: p.title, law_id: p.law_id }));
|
|
42
|
+
return {
|
|
43
|
+
source_law: preset.title,
|
|
44
|
+
source_article: articleNumber,
|
|
45
|
+
directly_referenced: directlyReferenced,
|
|
46
|
+
delegated_to: delegatedTo,
|
|
47
|
+
same_law_references: sameLawRefs,
|
|
48
|
+
same_group_laws: sameGroupLaws,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function extractCrossLawRefs(refs) {
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
const results = [];
|
|
54
|
+
for (const ref of refs) {
|
|
55
|
+
if (ref.ref_type !== "cross_law" || !ref.target_law)
|
|
56
|
+
continue;
|
|
57
|
+
const key = `${ref.target_law}:${ref.target_article ?? ""}`;
|
|
58
|
+
if (seen.has(key))
|
|
59
|
+
continue;
|
|
60
|
+
seen.add(key);
|
|
61
|
+
const preset = registry.findByName(ref.target_law);
|
|
62
|
+
results.push({
|
|
63
|
+
law_name: ref.target_law,
|
|
64
|
+
article: ref.target_article,
|
|
65
|
+
raw_text: ref.raw_text,
|
|
66
|
+
preset_available: preset !== null,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return results;
|
|
70
|
+
}
|
|
71
|
+
function extractDelegationRefs(refs) {
|
|
72
|
+
const seen = new Set();
|
|
73
|
+
const results = [];
|
|
74
|
+
for (const ref of refs) {
|
|
75
|
+
if (ref.ref_type !== "delegation")
|
|
76
|
+
continue;
|
|
77
|
+
if (seen.has(ref.raw_text))
|
|
78
|
+
continue;
|
|
79
|
+
seen.add(ref.raw_text);
|
|
80
|
+
results.push({
|
|
81
|
+
raw_text: ref.raw_text,
|
|
82
|
+
target_type: inferDelegationType(ref.raw_text),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return results;
|
|
86
|
+
}
|
|
87
|
+
function extractSameLawRefs(refs) {
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
const results = [];
|
|
90
|
+
for (const ref of refs) {
|
|
91
|
+
if (ref.ref_type !== "same_law" || !ref.target_article)
|
|
92
|
+
continue;
|
|
93
|
+
if (seen.has(ref.target_article))
|
|
94
|
+
continue;
|
|
95
|
+
seen.add(ref.target_article);
|
|
96
|
+
results.push({
|
|
97
|
+
article: ref.target_article,
|
|
98
|
+
raw_text: ref.raw_text,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return results;
|
|
102
|
+
}
|
|
103
|
+
//# sourceMappingURL=related-law-finder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"related-law-finder.js","sourceRoot":"","sources":["../../src/lib/related-law-finder.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -110,6 +110,78 @@ export interface LawPreset {
|
|
|
110
110
|
tier: "Act" | "CabinetOrder" | "MinisterialOrdinance" | "Other";
|
|
111
111
|
verified_at: string;
|
|
112
112
|
}
|
|
113
|
+
export interface BatchFetchItem {
|
|
114
|
+
law_name: string;
|
|
115
|
+
article_number: string;
|
|
116
|
+
status: "success" | "law_not_found" | "article_not_found" | "error";
|
|
117
|
+
text?: string;
|
|
118
|
+
structured?: StructuredArticle;
|
|
119
|
+
error_message?: string;
|
|
120
|
+
}
|
|
121
|
+
export interface BatchFetchResult {
|
|
122
|
+
total: number;
|
|
123
|
+
success: number;
|
|
124
|
+
failed: number;
|
|
125
|
+
results: BatchFetchItem[];
|
|
126
|
+
}
|
|
127
|
+
export interface CitationVerification {
|
|
128
|
+
law_name: string;
|
|
129
|
+
article_number: string;
|
|
130
|
+
status: "verified" | "mismatch" | "article_not_found" | "law_not_found" | "error";
|
|
131
|
+
actual_text?: string;
|
|
132
|
+
match_score?: number;
|
|
133
|
+
mismatch_detail?: string;
|
|
134
|
+
error_message?: string;
|
|
135
|
+
}
|
|
136
|
+
export interface RelatedLawSuggestion {
|
|
137
|
+
source_law: string;
|
|
138
|
+
source_article: string;
|
|
139
|
+
directly_referenced: Array<{
|
|
140
|
+
law_name: string;
|
|
141
|
+
article?: string;
|
|
142
|
+
raw_text: string;
|
|
143
|
+
preset_available: boolean;
|
|
144
|
+
}>;
|
|
145
|
+
delegated_to: Array<{
|
|
146
|
+
raw_text: string;
|
|
147
|
+
target_type: string;
|
|
148
|
+
}>;
|
|
149
|
+
same_law_references: Array<{
|
|
150
|
+
article: string;
|
|
151
|
+
raw_text: string;
|
|
152
|
+
}>;
|
|
153
|
+
same_group_laws: Array<{
|
|
154
|
+
law_name: string;
|
|
155
|
+
law_id: string;
|
|
156
|
+
}>;
|
|
157
|
+
}
|
|
158
|
+
export interface ArticleAnalysis {
|
|
159
|
+
law_name: string;
|
|
160
|
+
law_num: string;
|
|
161
|
+
article_num: string;
|
|
162
|
+
article_title: string;
|
|
163
|
+
caption: string;
|
|
164
|
+
structure: {
|
|
165
|
+
paragraph_count: number;
|
|
166
|
+
item_count: number;
|
|
167
|
+
subitem_count: number;
|
|
168
|
+
total_characters: number;
|
|
169
|
+
};
|
|
170
|
+
paragraph_summaries: Array<{
|
|
171
|
+
paragraph_num: string;
|
|
172
|
+
preview: string;
|
|
173
|
+
item_count: number;
|
|
174
|
+
}>;
|
|
175
|
+
reference_summary: {
|
|
176
|
+
total: number;
|
|
177
|
+
cross_law: number;
|
|
178
|
+
same_law: number;
|
|
179
|
+
relative: number;
|
|
180
|
+
delegation: number;
|
|
181
|
+
referenced_laws: string[];
|
|
182
|
+
};
|
|
183
|
+
structured_data: StructuredArticle;
|
|
184
|
+
}
|
|
113
185
|
export interface KokujiPreset {
|
|
114
186
|
law_id: string;
|
|
115
187
|
law_num: string;
|
package/dist/server.js
CHANGED
|
@@ -5,8 +5,12 @@ import { registerSearchLawTool } from "./tools/search-law.js";
|
|
|
5
5
|
import { registerGetKokujiTool } from "./tools/get-kokuji.js";
|
|
6
6
|
import { registerValidatePresetsTool } from "./tools/validate-presets.js";
|
|
7
7
|
import { registerCheckLawUpdatesTool } from "./tools/check-law-updates.js";
|
|
8
|
+
import { registerGetLawsBatchTool } from "./tools/get-laws-batch.js";
|
|
9
|
+
import { registerVerifyCitationTool } from "./tools/verify-citation.js";
|
|
10
|
+
import { registerSuggestRelatedTool } from "./tools/suggest-related.js";
|
|
11
|
+
import { registerAnalyzeArticleTool } from "./tools/analyze-article.js";
|
|
8
12
|
const SERVER_NAME = "building-law-mcp";
|
|
9
|
-
const SERVER_VERSION = "0.
|
|
13
|
+
const SERVER_VERSION = "0.3.0";
|
|
10
14
|
const INSTRUCTIONS = `あなたは建築確認申請の法規照合アシスタントです。
|
|
11
15
|
|
|
12
16
|
## 利用可能なツール
|
|
@@ -16,6 +20,10 @@ const INSTRUCTIONS = `あなたは建築確認申請の法規照合アシスタ
|
|
|
16
20
|
- get_kokuji: 告示の全文を取得(例: 耐火構造の構造方法を定める件)※国土交通省の告示データベース(PDF)から取得
|
|
17
21
|
- validate_presets: 法令プリセットがe-Gov APIに存在するか検証する(グループ指定で章ごとの検証も可能)
|
|
18
22
|
- check_law_updates: 法令の改正状況を確認する(法令名指定で単体チェック、グループ指定でバッチチェック、show_historyで改正履歴表示)
|
|
23
|
+
- get_laws_batch: 複数の法令・条文を一括取得する(最大20件、同一法令は1回のAPI呼び出しで効率処理)
|
|
24
|
+
- verify_citation: AIの回答に含まれる法令引用を検証する(条文存在確認・テキスト照合、最大10件)
|
|
25
|
+
- suggest_related: 指定した条文の関連法令・委任先・同法令内参照を自動抽出して提案する
|
|
26
|
+
- analyze_article: 条文の構造解析メタデータ(項数・号数・参照統計・プレビュー)をJSON形式で返す
|
|
19
27
|
|
|
20
28
|
## 回答ルール
|
|
21
29
|
1. まず質問に対する仮回答を生成する
|
|
@@ -43,6 +51,10 @@ export function createServer() {
|
|
|
43
51
|
registerGetKokujiTool(server);
|
|
44
52
|
registerValidatePresetsTool(server);
|
|
45
53
|
registerCheckLawUpdatesTool(server);
|
|
54
|
+
registerGetLawsBatchTool(server);
|
|
55
|
+
registerVerifyCitationTool(server);
|
|
56
|
+
registerSuggestRelatedTool(server);
|
|
57
|
+
registerAnalyzeArticleTool(server);
|
|
46
58
|
return server;
|
|
47
59
|
}
|
|
48
60
|
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,WAAW,GAAG,kBAAkB,CAAC;AACvC,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;8CA0ByB,CAAC;AAE/C,MAAM,UAAU,YAAY;IAC1B,MAAM,MAAM,GAAG,IAAI,SAAS,CAC1B;QACE,IAAI,EAAE,WAAW;QACjB,OAAO,EAAE,cAAc;KACxB,EACD;QACE,YAAY,EAAE,YAAY;KAC3B,CACF,CAAC;IAEF,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAC3B,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAC/B,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,qBAAqB,CAAC,MAAM,CAAC,CAAC;IAC9B,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACpC,2BAA2B,CAAC,MAAM,CAAC,CAAC;IACpC,wBAAwB,CAAC,MAAM,CAAC,CAAC;IACjC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACnC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IACnC,0BAA0B,CAAC,MAAM,CAAC,CAAC;IAEnC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { analyzeArticle } from "../lib/article-analyzer.js";
|
|
3
|
+
const schema = {
|
|
4
|
+
law_name: z
|
|
5
|
+
.string()
|
|
6
|
+
.describe("法令名(正式名称または略称。例: 建築基準法、建基法)"),
|
|
7
|
+
article_number: z.string().describe("条文番号(例: 第20条、20)"),
|
|
8
|
+
};
|
|
9
|
+
export function registerAnalyzeArticleTool(server) {
|
|
10
|
+
server.tool("analyze_article", "条文の構造を解析し、項数・号数・参照統計・プレビューなどのメタデータをJSON形式で返す。AIが要約・解説を生成する際の素材データとして活用。", schema, async ({ law_name, article_number }) => {
|
|
11
|
+
try {
|
|
12
|
+
const analysis = await analyzeArticle(law_name, article_number);
|
|
13
|
+
return {
|
|
14
|
+
content: [
|
|
15
|
+
{
|
|
16
|
+
type: "text",
|
|
17
|
+
text: JSON.stringify(analysis, null, 2),
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
24
|
+
return {
|
|
25
|
+
content: [{ type: "text", text: `エラー: ${message}` }],
|
|
26
|
+
isError: true,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=analyze-article.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-article.js","sourceRoot":"","sources":["../../src/tools/analyze-article.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,MAAM,MAAM,GAAG;IACb,QAAQ,EAAE,CAAC;SACR,MAAM,EAAE;SACR,QAAQ,CAAC,6BAA6B,CAAC;IAC1C,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;CACxD,CAAC;AAEF,MAAM,UAAU,0BAA0B,CAAC,MAAiB;IAC1D,MAAM,CAAC,IAAI,CACT,iBAAiB,EACjB,yEAAyE,EACzE,MAAM,EACN,KAAK,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE;QACrC,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;YAEhE,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAe;wBACrB,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;qBACxC;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,QAAQ,OAAO,EAAE,EAAE,CAAC;gBAC7D,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { LawRegistry } from "../lib/law-registry.js";
|
|
3
|
+
import { getLawData } from "../lib/egov-client.js";
|
|
4
|
+
import { parseArticle, parseArticleStructured } from "../lib/egov-parser.js";
|
|
5
|
+
const registry = new LawRegistry();
|
|
6
|
+
const MAX_REQUESTS = 20;
|
|
7
|
+
const schema = {
|
|
8
|
+
requests: z
|
|
9
|
+
.array(z.object({
|
|
10
|
+
law_name: z.string().describe("法令名(正式名称または略称)"),
|
|
11
|
+
article_number: z.string().describe("条文番号(例: 第20条、20)"),
|
|
12
|
+
format: z
|
|
13
|
+
.enum(["text", "structured"])
|
|
14
|
+
.default("text")
|
|
15
|
+
.describe("出力形式"),
|
|
16
|
+
}))
|
|
17
|
+
.describe("取得対象の条文リスト(最大20件)"),
|
|
18
|
+
};
|
|
19
|
+
export function registerGetLawsBatchTool(server) {
|
|
20
|
+
server.tool("get_laws_batch", "複数の法令・条文を一括取得する。同一法令の複数条文はAPI呼び出し1回で効率的に処理。最大20件。", schema, async ({ requests }) => {
|
|
21
|
+
try {
|
|
22
|
+
if (requests.length > MAX_REQUESTS) {
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: "text",
|
|
27
|
+
text: `エラー: リクエスト数が上限(${MAX_REQUESTS}件)を超えています(${requests.length}件)。`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (requests.length === 0) {
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: "エラー: リクエストが空です。",
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
// Group requests by law_name to minimize API calls
|
|
45
|
+
const lawDataMap = new Map();
|
|
46
|
+
const presetMap = new Map();
|
|
47
|
+
for (const req of requests) {
|
|
48
|
+
if (lawDataMap.has(req.law_name))
|
|
49
|
+
continue;
|
|
50
|
+
const preset = registry.findByName(req.law_name);
|
|
51
|
+
if (!preset) {
|
|
52
|
+
lawDataMap.set(req.law_name, null);
|
|
53
|
+
presetMap.set(req.law_name, null);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
presetMap.set(req.law_name, {
|
|
57
|
+
title: preset.title,
|
|
58
|
+
law_num: preset.law_num,
|
|
59
|
+
});
|
|
60
|
+
try {
|
|
61
|
+
const data = await getLawData(preset.law_id);
|
|
62
|
+
lawDataMap.set(req.law_name, data);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
lawDataMap.set(req.law_name, null);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// Process each request
|
|
69
|
+
const results = [];
|
|
70
|
+
for (const req of requests) {
|
|
71
|
+
const preset = presetMap.get(req.law_name);
|
|
72
|
+
if (!preset) {
|
|
73
|
+
results.push({
|
|
74
|
+
law_name: req.law_name,
|
|
75
|
+
article_number: req.article_number,
|
|
76
|
+
status: "law_not_found",
|
|
77
|
+
error_message: `法令「${req.law_name}」がプリセットに見つかりませんでした。`,
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const lawData = lawDataMap.get(req.law_name);
|
|
82
|
+
if (!lawData) {
|
|
83
|
+
results.push({
|
|
84
|
+
law_name: req.law_name,
|
|
85
|
+
article_number: req.article_number,
|
|
86
|
+
status: "error",
|
|
87
|
+
error_message: `法令「${req.law_name}」のデータ取得に失敗しました。`,
|
|
88
|
+
});
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
if (req.format === "structured") {
|
|
93
|
+
const structured = parseArticleStructured(lawData.law_full_text, req.article_number);
|
|
94
|
+
if (!structured) {
|
|
95
|
+
results.push({
|
|
96
|
+
law_name: preset.title,
|
|
97
|
+
article_number: req.article_number,
|
|
98
|
+
status: "article_not_found",
|
|
99
|
+
error_message: `${preset.title}に第${req.article_number}条が見つかりませんでした。`,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
results.push({
|
|
104
|
+
law_name: preset.title,
|
|
105
|
+
article_number: req.article_number,
|
|
106
|
+
status: "success",
|
|
107
|
+
structured,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const article = parseArticle(lawData.law_full_text, req.article_number);
|
|
113
|
+
if (!article) {
|
|
114
|
+
results.push({
|
|
115
|
+
law_name: preset.title,
|
|
116
|
+
article_number: req.article_number,
|
|
117
|
+
status: "article_not_found",
|
|
118
|
+
error_message: `${preset.title}に第${req.article_number}条が見つかりませんでした。`,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
results.push({
|
|
123
|
+
law_name: preset.title,
|
|
124
|
+
article_number: req.article_number,
|
|
125
|
+
status: "success",
|
|
126
|
+
text: article.text,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
results.push({
|
|
133
|
+
law_name: preset.title,
|
|
134
|
+
article_number: req.article_number,
|
|
135
|
+
status: "error",
|
|
136
|
+
error_message: error instanceof Error ? error.message : String(error),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const successCount = results.filter((r) => r.status === "success").length;
|
|
141
|
+
const response = {
|
|
142
|
+
total: results.length,
|
|
143
|
+
success: successCount,
|
|
144
|
+
failed: results.length - successCount,
|
|
145
|
+
results,
|
|
146
|
+
};
|
|
147
|
+
return {
|
|
148
|
+
content: [
|
|
149
|
+
{
|
|
150
|
+
type: "text",
|
|
151
|
+
text: JSON.stringify(response, null, 2),
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: "text", text: `エラー: ${message}` }],
|
|
160
|
+
isError: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=get-laws-batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"get-laws-batch.js","sourceRoot":"","sources":["../../src/tools/get-laws-batch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { findRelatedLaws } from "../lib/related-law-finder.js";
|
|
3
|
+
const schema = {
|
|
4
|
+
law_name: z
|
|
5
|
+
.string()
|
|
6
|
+
.describe("法令名(正式名称または略称。例: 建築基準法、建基法)"),
|
|
7
|
+
article_number: z.string().describe("条文番号(例: 第20条、20)"),
|
|
8
|
+
};
|
|
9
|
+
function formatSuggestion(result) {
|
|
10
|
+
const lines = [];
|
|
11
|
+
lines.push(`## ${result.source_law} 第${result.source_article}条 の関連法令\n`);
|
|
12
|
+
if (result.directly_referenced.length > 0) {
|
|
13
|
+
lines.push("### 直接参照されている法令\n");
|
|
14
|
+
for (const ref of result.directly_referenced) {
|
|
15
|
+
const available = ref.preset_available ? "" : "(プリセット未登録)";
|
|
16
|
+
const article = ref.article ? ` 第${ref.article}条` : "";
|
|
17
|
+
lines.push(`- **${ref.law_name}**${article}${available}`);
|
|
18
|
+
lines.push(` - 原文: ${ref.raw_text}`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (result.delegated_to.length > 0) {
|
|
22
|
+
lines.push("\n### 委任先\n");
|
|
23
|
+
for (const del of result.delegated_to) {
|
|
24
|
+
lines.push(`- [${del.target_type}] ${del.raw_text}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (result.same_law_references.length > 0) {
|
|
28
|
+
lines.push("\n### 同法令内の参照条文\n");
|
|
29
|
+
for (const ref of result.same_law_references) {
|
|
30
|
+
lines.push(`- 第${ref.article}条 — ${ref.raw_text}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (result.same_group_laws.length > 0) {
|
|
34
|
+
lines.push("\n### 同カテゴリの法令\n");
|
|
35
|
+
for (const law of result.same_group_laws) {
|
|
36
|
+
lines.push(`- ${law.law_name}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (result.directly_referenced.length === 0 &&
|
|
40
|
+
result.delegated_to.length === 0 &&
|
|
41
|
+
result.same_law_references.length === 0) {
|
|
42
|
+
lines.push("この条文には他法令への参照が検出されませんでした。");
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
export function registerSuggestRelatedTool(server) {
|
|
47
|
+
server.tool("suggest_related", "指定した条文が参照している関連法令・委任先・同法令内参照を自動抽出して提案する。", schema, async ({ law_name, article_number }) => {
|
|
48
|
+
try {
|
|
49
|
+
const result = await findRelatedLaws(law_name, article_number);
|
|
50
|
+
const text = formatSuggestion(result);
|
|
51
|
+
return { content: [{ type: "text", text }] };
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
55
|
+
return {
|
|
56
|
+
content: [{ type: "text", text: `エラー: ${message}` }],
|
|
57
|
+
isError: true,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=suggest-related.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest-related.js","sourceRoot":"","sources":["../../src/tools/suggest-related.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { verifyCitation } from "../lib/citation-verifier.js";
|
|
3
|
+
const MAX_CITATIONS = 10;
|
|
4
|
+
const schema = {
|
|
5
|
+
citations: z
|
|
6
|
+
.array(z.object({
|
|
7
|
+
law_name: z.string().describe("法令名(正式名称または略称)"),
|
|
8
|
+
article_number: z.string().describe("条文番号(例: 第20条、20)"),
|
|
9
|
+
claimed_text: z
|
|
10
|
+
.string()
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("AIが主張する条文テキスト。省略時は条文の存在確認のみ。"),
|
|
13
|
+
}))
|
|
14
|
+
.describe("検証対象の引用リスト(最大10件)"),
|
|
15
|
+
};
|
|
16
|
+
function formatResults(results) {
|
|
17
|
+
const verified = results.filter((r) => r.status === "verified");
|
|
18
|
+
const mismatched = results.filter((r) => r.status === "mismatch");
|
|
19
|
+
const notFound = results.filter((r) => r.status === "article_not_found" || r.status === "law_not_found");
|
|
20
|
+
const errors = results.filter((r) => r.status === "error");
|
|
21
|
+
const lines = [];
|
|
22
|
+
lines.push("## 引用検証結果\n");
|
|
23
|
+
lines.push(`- 検証対象: ${results.length}件`);
|
|
24
|
+
lines.push(`- 正確: ${verified.length}件`);
|
|
25
|
+
if (mismatched.length > 0)
|
|
26
|
+
lines.push(`- **不一致: ${mismatched.length}件**`);
|
|
27
|
+
if (notFound.length > 0)
|
|
28
|
+
lines.push(`- 未発見: ${notFound.length}件`);
|
|
29
|
+
if (errors.length > 0)
|
|
30
|
+
lines.push(`- エラー: ${errors.length}件`);
|
|
31
|
+
for (const r of results) {
|
|
32
|
+
lines.push("");
|
|
33
|
+
const icon = r.status === "verified"
|
|
34
|
+
? "OK"
|
|
35
|
+
: r.status === "mismatch"
|
|
36
|
+
? "NG"
|
|
37
|
+
: r.status === "error"
|
|
38
|
+
? "ERR"
|
|
39
|
+
: "N/A";
|
|
40
|
+
lines.push(`### [${icon}] ${r.law_name} 第${r.article_number}条`);
|
|
41
|
+
if (r.match_score !== undefined) {
|
|
42
|
+
lines.push(`- 一致度: ${(r.match_score * 100).toFixed(0)}%`);
|
|
43
|
+
}
|
|
44
|
+
if (r.mismatch_detail) {
|
|
45
|
+
lines.push(`- ${r.mismatch_detail}`);
|
|
46
|
+
}
|
|
47
|
+
if (r.error_message) {
|
|
48
|
+
lines.push(`- ${r.error_message}`);
|
|
49
|
+
}
|
|
50
|
+
if (r.actual_text) {
|
|
51
|
+
lines.push(`- 実際の条文: ${r.actual_text}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return lines.join("\n");
|
|
55
|
+
}
|
|
56
|
+
export function registerVerifyCitationTool(server) {
|
|
57
|
+
server.tool("verify_citation", "AIの回答に含まれる法令引用を検証する。条文の存在確認、テキスト照合による正確性チェックが可能。最大10件。", schema, async ({ citations }) => {
|
|
58
|
+
try {
|
|
59
|
+
if (citations.length === 0) {
|
|
60
|
+
return {
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
type: "text",
|
|
64
|
+
text: "エラー: 検証対象が空です。",
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (citations.length > MAX_CITATIONS) {
|
|
71
|
+
return {
|
|
72
|
+
content: [
|
|
73
|
+
{
|
|
74
|
+
type: "text",
|
|
75
|
+
text: `エラー: 検証対象が上限(${MAX_CITATIONS}件)を超えています(${citations.length}件)。`,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
isError: true,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const results = [];
|
|
82
|
+
for (const citation of citations) {
|
|
83
|
+
const result = await verifyCitation(citation.law_name, citation.article_number, citation.claimed_text);
|
|
84
|
+
results.push(result);
|
|
85
|
+
}
|
|
86
|
+
const text = formatResults(results);
|
|
87
|
+
return { content: [{ type: "text", text }] };
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: `エラー: ${message}` }],
|
|
93
|
+
isError: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=verify-citation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify-citation.js","sourceRoot":"","sources":["../../src/tools/verify-citation.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "building-standards-act-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server for Japanese building regulations — fetches law text from e-Gov API v2 to prevent AI hallucination in building permit reviews",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|