@sugukuru/mcp-houjin-bangou 0.2.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/CHANGELOG.md +86 -0
- package/LICENSE +21 -0
- package/README.ja.md +196 -0
- package/README.md +198 -0
- package/dist/api/csv-parser.d.ts +37 -0
- package/dist/api/csv-parser.d.ts.map +1 -0
- package/dist/api/csv-parser.js +189 -0
- package/dist/api/csv-parser.js.map +1 -0
- package/dist/api/nta-client.d.ts +71 -0
- package/dist/api/nta-client.d.ts.map +1 -0
- package/dist/api/nta-client.js +117 -0
- package/dist/api/nta-client.js.map +1 -0
- package/dist/api/rate-limiter.d.ts +39 -0
- package/dist/api/rate-limiter.d.ts.map +1 -0
- package/dist/api/rate-limiter.js +74 -0
- package/dist/api/rate-limiter.js.map +1 -0
- package/dist/completion/handler.d.ts +19 -0
- package/dist/completion/handler.d.ts.map +1 -0
- package/dist/completion/handler.js +136 -0
- package/dist/completion/handler.js.map +1 -0
- package/dist/domain/check-digit.d.ts +47 -0
- package/dist/domain/check-digit.d.ts.map +1 -0
- package/dist/domain/check-digit.js +91 -0
- package/dist/domain/check-digit.js.map +1 -0
- package/dist/domain/close-cause-codes.d.ts +28 -0
- package/dist/domain/close-cause-codes.d.ts.map +1 -0
- package/dist/domain/close-cause-codes.js +27 -0
- package/dist/domain/close-cause-codes.js.map +1 -0
- package/dist/domain/corporate-number.d.ts +81 -0
- package/dist/domain/corporate-number.d.ts.map +1 -0
- package/dist/domain/corporate-number.js +88 -0
- package/dist/domain/corporate-number.js.map +1 -0
- package/dist/domain/invoice-codes.d.ts +142 -0
- package/dist/domain/invoice-codes.d.ts.map +1 -0
- package/dist/domain/invoice-codes.js +106 -0
- package/dist/domain/invoice-codes.js.map +1 -0
- package/dist/domain/invoice-number.d.ts +47 -0
- package/dist/domain/invoice-number.d.ts.map +1 -0
- package/dist/domain/invoice-number.js +71 -0
- package/dist/domain/invoice-number.js.map +1 -0
- package/dist/domain/kind-codes.d.ts +62 -0
- package/dist/domain/kind-codes.d.ts.map +1 -0
- package/dist/domain/kind-codes.js +68 -0
- package/dist/domain/kind-codes.js.map +1 -0
- package/dist/domain/normalizer.d.ts +50 -0
- package/dist/domain/normalizer.d.ts.map +1 -0
- package/dist/domain/normalizer.js +280 -0
- package/dist/domain/normalizer.js.map +1 -0
- package/dist/domain/prefecture-codes.d.ts +61 -0
- package/dist/domain/prefecture-codes.d.ts.map +1 -0
- package/dist/domain/prefecture-codes.js +67 -0
- package/dist/domain/prefecture-codes.js.map +1 -0
- package/dist/domain/process-codes.d.ts +52 -0
- package/dist/domain/process-codes.d.ts.map +1 -0
- package/dist/domain/process-codes.js +28 -0
- package/dist/domain/process-codes.js.map +1 -0
- package/dist/lib/env.d.ts +35 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +40 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/errors.d.ts +47 -0
- package/dist/lib/errors.d.ts.map +1 -0
- package/dist/lib/errors.js +92 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/mcp-logger.d.ts +29 -0
- package/dist/lib/mcp-logger.d.ts.map +1 -0
- package/dist/lib/mcp-logger.js +98 -0
- package/dist/lib/mcp-logger.js.map +1 -0
- package/dist/lib/pagination.d.ts +28 -0
- package/dist/lib/pagination.d.ts.map +1 -0
- package/dist/lib/pagination.js +48 -0
- package/dist/lib/pagination.js.map +1 -0
- package/dist/lib/result.d.ts +29 -0
- package/dist/lib/result.d.ts.map +1 -0
- package/dist/lib/result.js +36 -0
- package/dist/lib/result.js.map +1 -0
- package/dist/lib/trace-context.d.ts +36 -0
- package/dist/lib/trace-context.d.ts.map +1 -0
- package/dist/lib/trace-context.js +77 -0
- package/dist/lib/trace-context.js.map +1 -0
- package/dist/mcp.d.ts +9 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +54 -0
- package/dist/mcp.js.map +1 -0
- package/dist/prompts/business-card-to-database.d.ts +12 -0
- package/dist/prompts/business-card-to-database.d.ts.map +1 -0
- package/dist/prompts/business-card-to-database.js +80 -0
- package/dist/prompts/business-card-to-database.js.map +1 -0
- package/dist/prompts/customer-master-dedup.d.ts +9 -0
- package/dist/prompts/customer-master-dedup.d.ts.map +1 -0
- package/dist/prompts/customer-master-dedup.js +77 -0
- package/dist/prompts/customer-master-dedup.js.map +1 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +9 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/sales-list-enrichment.d.ts +9 -0
- package/dist/prompts/sales-list-enrichment.d.ts.map +1 -0
- package/dist/prompts/sales-list-enrichment.js +84 -0
- package/dist/prompts/sales-list-enrichment.js.map +1 -0
- package/dist/resources/attribution.d.ts +9 -0
- package/dist/resources/attribution.d.ts.map +1 -0
- package/dist/resources/attribution.js +27 -0
- package/dist/resources/attribution.js.map +1 -0
- package/dist/resources/corporate-template.d.ts +14 -0
- package/dist/resources/corporate-template.d.ts.map +1 -0
- package/dist/resources/corporate-template.js +87 -0
- package/dist/resources/corporate-template.js.map +1 -0
- package/dist/resources/index.d.ts +8 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +9 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/ui-resources.d.ts +9 -0
- package/dist/resources/ui-resources.d.ts.map +1 -0
- package/dist/resources/ui-resources.js +59 -0
- package/dist/resources/ui-resources.js.map +1 -0
- package/dist/server.d.ts +13 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +217 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/get-attribution.d.ts +25 -0
- package/dist/tools/get-attribution.d.ts.map +1 -0
- package/dist/tools/get-attribution.js +120 -0
- package/dist/tools/get-attribution.js.map +1 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/lookup-by-number.d.ts +82 -0
- package/dist/tools/lookup-by-number.d.ts.map +1 -0
- package/dist/tools/lookup-by-number.js +157 -0
- package/dist/tools/lookup-by-number.js.map +1 -0
- package/dist/tools/normalize-company-name.d.ts +60 -0
- package/dist/tools/normalize-company-name.d.ts.map +1 -0
- package/dist/tools/normalize-company-name.js +130 -0
- package/dist/tools/normalize-company-name.js.map +1 -0
- package/dist/tools/search-by-name.d.ts +96 -0
- package/dist/tools/search-by-name.d.ts.map +1 -0
- package/dist/tools/search-by-name.js +176 -0
- package/dist/tools/search-by-name.js.map +1 -0
- package/dist/tools/validate-check-digit.d.ts +35 -0
- package/dist/tools/validate-check-digit.d.ts.map +1 -0
- package/dist/tools/validate-check-digit.js +105 -0
- package/dist/tools/validate-check-digit.js.map +1 -0
- package/dist/ui/corporate-card/mcp-app.html +157 -0
- package/dist/ui/search-results/mcp-app.html +158 -0
- package/dist/version.d.ts +6 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +96 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 国税庁 Web-API Ver.4.0 の CSV レスポンスを JSON 化
|
|
3
|
+
* Parse NTA Web-API Ver.4.0 CSV responses into JSON
|
|
4
|
+
*
|
|
5
|
+
* 根拠 / Source:
|
|
6
|
+
* 国税庁仕様書 第二編 別紙1 リソース定義書 (36 項目、CSV 順序は同表に準拠)
|
|
7
|
+
* レスポンス形式: type=02 (CSV/UTF-8/JIS 第一〜第四水準)
|
|
8
|
+
*
|
|
9
|
+
* 1行目: ヘッダー (lastUpdateDate, count, divideNumber, divideSize)
|
|
10
|
+
* 2行目以降: 法人データ (30列 = 項番7〜36)
|
|
11
|
+
*
|
|
12
|
+
* CSVエスケープ:
|
|
13
|
+
* - フィールド値はダブルクォーテーション(")で囲まれる
|
|
14
|
+
* - 値内の " は "" にエスケープ
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* NTA CSV をパースして構造化
|
|
18
|
+
*
|
|
19
|
+
* @param csvText UTF-8 でデコード済みの CSV 本体
|
|
20
|
+
*/
|
|
21
|
+
export function parseNtaCsv(csvText) {
|
|
22
|
+
const lines = splitCsvLines(csvText);
|
|
23
|
+
if (lines.length === 0) {
|
|
24
|
+
throw new Error('Empty NTA CSV response');
|
|
25
|
+
}
|
|
26
|
+
const headerLine = lines[0];
|
|
27
|
+
if (headerLine === undefined) {
|
|
28
|
+
throw new Error('Missing header line in NTA CSV');
|
|
29
|
+
}
|
|
30
|
+
const headerFields = parseCsvLine(headerLine);
|
|
31
|
+
if (headerFields.length < 4) {
|
|
32
|
+
throw new Error(`Invalid header line: expected 4 fields, got ${headerFields.length}`);
|
|
33
|
+
}
|
|
34
|
+
const header = {
|
|
35
|
+
lastUpdateDate: headerFields[0] ?? '',
|
|
36
|
+
count: toInt(headerFields[1]),
|
|
37
|
+
divideNumber: toInt(headerFields[2]),
|
|
38
|
+
divideSize: toInt(headerFields[3]),
|
|
39
|
+
};
|
|
40
|
+
const corporations = [];
|
|
41
|
+
for (let i = 1; i < lines.length; i++) {
|
|
42
|
+
const line = lines[i];
|
|
43
|
+
if (line === undefined || line.trim() === '')
|
|
44
|
+
continue;
|
|
45
|
+
const fields = parseCsvLine(line);
|
|
46
|
+
if (fields.length < 30) {
|
|
47
|
+
// 項番7〜36 の 30 列を期待 (項番7=一連番号から項番36=検索対象除外まで)
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
corporations.push(rowToCorporation(fields));
|
|
51
|
+
}
|
|
52
|
+
return { header, corporations };
|
|
53
|
+
}
|
|
54
|
+
function toInt(raw) {
|
|
55
|
+
if (raw === undefined || raw === '')
|
|
56
|
+
return 0;
|
|
57
|
+
const n = Number.parseInt(raw, 10);
|
|
58
|
+
return Number.isNaN(n) ? 0 : n;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* CSV 1行をフィールド配列に分解 (RFC 4180、ダブルクォート + "" エスケープ対応)
|
|
62
|
+
*/
|
|
63
|
+
export function parseCsvLine(line) {
|
|
64
|
+
const fields = [];
|
|
65
|
+
let current = '';
|
|
66
|
+
let inQuotes = false;
|
|
67
|
+
let i = 0;
|
|
68
|
+
while (i < line.length) {
|
|
69
|
+
const ch = line[i];
|
|
70
|
+
if (inQuotes) {
|
|
71
|
+
if (ch === '"') {
|
|
72
|
+
if (line[i + 1] === '"') {
|
|
73
|
+
current += '"';
|
|
74
|
+
i += 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
inQuotes = false;
|
|
78
|
+
i += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
current += ch;
|
|
82
|
+
i += 1;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (ch === '"') {
|
|
86
|
+
inQuotes = true;
|
|
87
|
+
i += 1;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (ch === ',') {
|
|
91
|
+
fields.push(current);
|
|
92
|
+
current = '';
|
|
93
|
+
i += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
current += ch;
|
|
97
|
+
i += 1;
|
|
98
|
+
}
|
|
99
|
+
fields.push(current);
|
|
100
|
+
return fields;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* CSV 本体を行に分割。ダブルクォート内の改行を無視し、CRLF / LF を扱う。
|
|
104
|
+
*/
|
|
105
|
+
function splitCsvLines(csv) {
|
|
106
|
+
const lines = [];
|
|
107
|
+
let current = '';
|
|
108
|
+
let inQuotes = false;
|
|
109
|
+
for (let i = 0; i < csv.length; i++) {
|
|
110
|
+
const ch = csv[i];
|
|
111
|
+
if (inQuotes) {
|
|
112
|
+
if (ch === '"') {
|
|
113
|
+
if (csv[i + 1] === '"') {
|
|
114
|
+
current += '""';
|
|
115
|
+
i += 1;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
inQuotes = false;
|
|
119
|
+
}
|
|
120
|
+
current += ch;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (ch === '"') {
|
|
124
|
+
inQuotes = true;
|
|
125
|
+
current += ch;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (ch === '\r') {
|
|
129
|
+
if (csv[i + 1] === '\n')
|
|
130
|
+
i += 1;
|
|
131
|
+
if (current.length > 0) {
|
|
132
|
+
lines.push(current);
|
|
133
|
+
current = '';
|
|
134
|
+
}
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (ch === '\n') {
|
|
138
|
+
if (current.length > 0) {
|
|
139
|
+
lines.push(current);
|
|
140
|
+
current = '';
|
|
141
|
+
}
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
current += ch;
|
|
145
|
+
}
|
|
146
|
+
if (current.length > 0)
|
|
147
|
+
lines.push(current);
|
|
148
|
+
return lines;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* CSV 1行を Corporation オブジェクトに変換
|
|
152
|
+
* フィールド順は 第二編 別紙1 の項番7〜36 (30項目)
|
|
153
|
+
*/
|
|
154
|
+
function rowToCorporation(f) {
|
|
155
|
+
const g = (idx) => f[idx] ?? '';
|
|
156
|
+
return {
|
|
157
|
+
sequence_number: toInt(g(0)),
|
|
158
|
+
corporate_number: g(1),
|
|
159
|
+
process: g(2),
|
|
160
|
+
correct: (g(3) === '1' ? '1' : '0'),
|
|
161
|
+
update_date: g(4),
|
|
162
|
+
change_date: g(5),
|
|
163
|
+
name: g(6),
|
|
164
|
+
name_image_id: g(7),
|
|
165
|
+
kind: g(8),
|
|
166
|
+
prefecture_name: g(9),
|
|
167
|
+
city_name: g(10),
|
|
168
|
+
street_number: g(11),
|
|
169
|
+
address_image_id: g(12),
|
|
170
|
+
prefecture_code: g(13),
|
|
171
|
+
city_code: g(14),
|
|
172
|
+
post_code: g(15),
|
|
173
|
+
address_outside: g(16),
|
|
174
|
+
address_outside_image_id: g(17),
|
|
175
|
+
close_date: g(18),
|
|
176
|
+
close_cause: g(19),
|
|
177
|
+
successor_corporate_number: g(20),
|
|
178
|
+
change_cause: g(21),
|
|
179
|
+
assignment_date: g(22),
|
|
180
|
+
latest: (g(23) === '0' ? '0' : '1'),
|
|
181
|
+
en_name: g(24),
|
|
182
|
+
en_prefecture_name: g(25),
|
|
183
|
+
en_city_name: g(26),
|
|
184
|
+
en_address_outside: g(27),
|
|
185
|
+
furigana: g(28),
|
|
186
|
+
hihyoji: (g(29) === '1' ? '1' : '0'),
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=csv-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"csv-parser.js","sourceRoot":"","sources":["../../src/api/csv-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 国税庁 法人番号 Web-API Ver.4.0 クライアント
|
|
3
|
+
* National Tax Agency Corporate Number Web-API Ver.4.0 client
|
|
4
|
+
*
|
|
5
|
+
* 根拠 / Source:
|
|
6
|
+
* 国税庁仕様書 第二編 (概要) + 第六編 (Ver.4.0)
|
|
7
|
+
* エンドポイント: https://api.houjin-bangou.nta.go.jp/4/{num|diff|name}
|
|
8
|
+
* 応答形式: type=02 (CSV UTF-8)
|
|
9
|
+
*
|
|
10
|
+
* DI seam: fetchImpl でテスト時差替え可能
|
|
11
|
+
*/
|
|
12
|
+
import { NtaApiError } from '../lib/errors.js';
|
|
13
|
+
import { Result } from '../lib/result.js';
|
|
14
|
+
import { type ParsedNtaResponse } from './csv-parser.js';
|
|
15
|
+
export interface NtaClientConfig {
|
|
16
|
+
applicationId: string;
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
timeoutMs: number;
|
|
19
|
+
rps: number;
|
|
20
|
+
userAgent?: string;
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
/** バックオフ発動時のフック (Logging 連携用) */
|
|
23
|
+
onBackoff?: (waitMs: number) => void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 法人番号指定検索 (/4/num) のパラメータ
|
|
27
|
+
* 根拠: 第六編 §2.1, 第二編 §2.1.3
|
|
28
|
+
*/
|
|
29
|
+
export interface LookupByNumberParams {
|
|
30
|
+
/** 13桁の法人番号、最大10件 */
|
|
31
|
+
numbers: string[];
|
|
32
|
+
/** 変更履歴要否 (0=最新のみ / 1=含める)、デフォルト 0 */
|
|
33
|
+
history?: '0' | '1';
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 法人名指定検索 (/4/name) のパラメータ
|
|
37
|
+
* 根拠: 第六編 §4.1, 第二編 §4.1.3
|
|
38
|
+
*/
|
|
39
|
+
export interface SearchByNameParams {
|
|
40
|
+
/** 商号又は名称 (UTF-8、最大150文字 日本語 / 300文字 英語) */
|
|
41
|
+
name: string;
|
|
42
|
+
/** 検索方式 (1=前方一致 / 2=部分一致)、デフォルト 1 */
|
|
43
|
+
mode?: '1' | '2';
|
|
44
|
+
/** 検索対象 (1=JIS1-2水準あいまい / 2=JIS1-4水準完全 / 3=英語)、デフォルト 1 */
|
|
45
|
+
target?: '1' | '2' | '3';
|
|
46
|
+
/** 所在地 (都道府県コード2桁 or 都道府県+市区町村5桁) */
|
|
47
|
+
address?: string;
|
|
48
|
+
/** 法人種別 (01/02/03/04、最大4件カンマ区切り) */
|
|
49
|
+
kind?: string;
|
|
50
|
+
/** 変更履歴を含めるか (0/1)、デフォルト 0 */
|
|
51
|
+
change?: '0' | '1';
|
|
52
|
+
/** 登記記録の閉鎖等を含めるか (0/1)、デフォルト 1 */
|
|
53
|
+
close?: '0' | '1';
|
|
54
|
+
/** 法人番号指定年月日 開始 (YYYY-MM-DD) */
|
|
55
|
+
from?: string;
|
|
56
|
+
/** 法人番号指定年月日 終了 (YYYY-MM-DD) */
|
|
57
|
+
to?: string;
|
|
58
|
+
/** 分割番号 (1-99999)、デフォルト 1 */
|
|
59
|
+
divide?: number;
|
|
60
|
+
}
|
|
61
|
+
export interface NtaClient {
|
|
62
|
+
lookupByNumber(params: LookupByNumberParams): Promise<Result<ParsedNtaResponse, NtaApiError>>;
|
|
63
|
+
searchByName(params: SearchByNameParams): Promise<Result<ParsedNtaResponse, NtaApiError>>;
|
|
64
|
+
/** 3年無利用停止を防止するための health ping */
|
|
65
|
+
ping(): Promise<Result<true, NtaApiError>>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* NtaClient のファクトリ関数 (DI seam)
|
|
69
|
+
*/
|
|
70
|
+
export declare function createNtaClient(config: NtaClientConfig): NtaClient;
|
|
71
|
+
//# sourceMappingURL=nta-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nta-client.d.ts","sourceRoot":"","sources":["../../src/api/nta-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAe,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,iCAAiC;IACjC,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACtC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,qBAAqB;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,sCAAsC;IACtC,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,IAAI,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACjB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IACzB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,MAAM,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IACnB,kCAAkC;IAClC,KAAK,CAAC,EAAE,GAAG,GAAG,GAAG,CAAC;IAClB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACxB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC;IAC9F,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,EAAE,WAAW,CAAC,CAAC,CAAC;IAC1F,kCAAkC;IAClC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC,CAAC;CAC5C;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA8GlE"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 国税庁 法人番号 Web-API Ver.4.0 クライアント
|
|
3
|
+
* National Tax Agency Corporate Number Web-API Ver.4.0 client
|
|
4
|
+
*
|
|
5
|
+
* 根拠 / Source:
|
|
6
|
+
* 国税庁仕様書 第二編 (概要) + 第六編 (Ver.4.0)
|
|
7
|
+
* エンドポイント: https://api.houjin-bangou.nta.go.jp/4/{num|diff|name}
|
|
8
|
+
* 応答形式: type=02 (CSV UTF-8)
|
|
9
|
+
*
|
|
10
|
+
* DI seam: fetchImpl でテスト時差替え可能
|
|
11
|
+
*/
|
|
12
|
+
import { NtaApiError } from '../lib/errors.js';
|
|
13
|
+
import { Result } from '../lib/result.js';
|
|
14
|
+
import { TokenBucket } from './rate-limiter.js';
|
|
15
|
+
import { parseNtaCsv } from './csv-parser.js';
|
|
16
|
+
/**
|
|
17
|
+
* NtaClient のファクトリ関数 (DI seam)
|
|
18
|
+
*/
|
|
19
|
+
export function createNtaClient(config) {
|
|
20
|
+
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
21
|
+
const limiter = new TokenBucket({ rps: config.rps });
|
|
22
|
+
const userAgent = config.userAgent ?? '@sugukuru/mcp-houjin-bangou';
|
|
23
|
+
async function callApi(path, params) {
|
|
24
|
+
await limiter.acquire();
|
|
25
|
+
params.set('id', config.applicationId);
|
|
26
|
+
params.set('type', '02'); // ADR-0003: CSV UTF-8 固定
|
|
27
|
+
const url = `${config.baseUrl}${path}?${params.toString()}`;
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timeoutId = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
30
|
+
try {
|
|
31
|
+
const res = await fetchImpl(url, {
|
|
32
|
+
method: 'GET',
|
|
33
|
+
signal: controller.signal,
|
|
34
|
+
headers: {
|
|
35
|
+
'User-Agent': userAgent,
|
|
36
|
+
Accept: 'text/csv',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
if (res.status === 403) {
|
|
40
|
+
const waitMs = limiter.triggerBackoff();
|
|
41
|
+
if (config.onBackoff)
|
|
42
|
+
config.onBackoff(waitMs);
|
|
43
|
+
return Result.err(NtaApiError.fromHttpStatus(403, `Rate limited by NTA. Backoff ${waitMs}ms applied. (別添1 第9条第1項三号)`));
|
|
44
|
+
}
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const body = await res.text().catch(() => '');
|
|
47
|
+
return Result.err(NtaApiError.fromHttpStatus(res.status, body));
|
|
48
|
+
}
|
|
49
|
+
limiter.resetBackoff();
|
|
50
|
+
const csvText = await res.text();
|
|
51
|
+
try {
|
|
52
|
+
const parsed = parseNtaCsv(csvText);
|
|
53
|
+
return Result.ok(parsed);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return Result.err(new NtaApiError(`Failed to parse NTA CSV: ${err instanceof Error ? err.message : String(err)}`, 'parse_error'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
return Result.err(NtaApiError.fromUnknown(err));
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
clearTimeout(timeoutId);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
async lookupByNumber(params) {
|
|
68
|
+
if (params.numbers.length === 0 || params.numbers.length > 10) {
|
|
69
|
+
return Result.err(new NtaApiError('numbers must contain 1-10 corporate numbers (第二編 §2.1.3)', 'http_400'));
|
|
70
|
+
}
|
|
71
|
+
const search = new URLSearchParams({
|
|
72
|
+
number: params.numbers.join(','),
|
|
73
|
+
history: params.history ?? '0',
|
|
74
|
+
});
|
|
75
|
+
return callApi('/num', search);
|
|
76
|
+
},
|
|
77
|
+
async searchByName(params) {
|
|
78
|
+
if (params.name.length === 0) {
|
|
79
|
+
return Result.err(new NtaApiError('name parameter is required (第二編 §4.1.3)', 'http_400'));
|
|
80
|
+
}
|
|
81
|
+
const search = new URLSearchParams();
|
|
82
|
+
search.set('name', params.name);
|
|
83
|
+
if (params.mode !== undefined)
|
|
84
|
+
search.set('mode', params.mode);
|
|
85
|
+
if (params.target !== undefined)
|
|
86
|
+
search.set('target', params.target);
|
|
87
|
+
if (params.address !== undefined)
|
|
88
|
+
search.set('address', params.address);
|
|
89
|
+
if (params.kind !== undefined)
|
|
90
|
+
search.set('kind', params.kind);
|
|
91
|
+
if (params.change !== undefined)
|
|
92
|
+
search.set('change', params.change);
|
|
93
|
+
if (params.close !== undefined)
|
|
94
|
+
search.set('close', params.close);
|
|
95
|
+
if (params.from !== undefined)
|
|
96
|
+
search.set('from', params.from);
|
|
97
|
+
if (params.to !== undefined)
|
|
98
|
+
search.set('to', params.to);
|
|
99
|
+
if (params.divide !== undefined)
|
|
100
|
+
search.set('divide', String(params.divide));
|
|
101
|
+
return callApi('/name', search);
|
|
102
|
+
},
|
|
103
|
+
async ping() {
|
|
104
|
+
// ダミー番号 0000000000000 → 404 相当の応答が返る (または 200 空結果)
|
|
105
|
+
// 目的は ID の active 状態維持 (第一編 §5.3、3年無利用停止防止)
|
|
106
|
+
const result = await this.lookupByNumber({ numbers: ['0000000000000'] });
|
|
107
|
+
if (Result.isOk(result))
|
|
108
|
+
return Result.ok(true);
|
|
109
|
+
const err = result.error;
|
|
110
|
+
// 404 は「該当なし」で OK、403 は rate limit、その他は真のエラー
|
|
111
|
+
if (err.code === 'http_404')
|
|
112
|
+
return Result.ok(true);
|
|
113
|
+
return Result.err(err);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=nta-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nta-client.js","sourceRoot":"","sources":["../../src/api/nta-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token bucket レートリミッター
|
|
3
|
+
* Token bucket rate limiter for NTA API
|
|
4
|
+
*
|
|
5
|
+
* 根拠 / Source:
|
|
6
|
+
* 国税庁仕様書 第二編 別紙2: HTTP 403 は「同一アプリケーションIDで一定期間内に多数アクセス」で発動
|
|
7
|
+
* 別添1 第9条第1項三号: 短時間における大量アクセスは禁止
|
|
8
|
+
* → 保守的に 1 RPS デフォルト、403 受信時は指数バックオフ
|
|
9
|
+
*/
|
|
10
|
+
export interface RateLimiterOptions {
|
|
11
|
+
/** リクエスト毎秒 (requests per second) */
|
|
12
|
+
rps: number;
|
|
13
|
+
/** バケット最大容量 (burst 許容量) */
|
|
14
|
+
maxBurst?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare class TokenBucket {
|
|
17
|
+
private tokens;
|
|
18
|
+
private readonly capacity;
|
|
19
|
+
private readonly refillPerMs;
|
|
20
|
+
private lastRefill;
|
|
21
|
+
private backoffUntil;
|
|
22
|
+
private backoffLevel;
|
|
23
|
+
constructor(options: RateLimiterOptions);
|
|
24
|
+
private refill;
|
|
25
|
+
/**
|
|
26
|
+
* 1トークン獲得するまで待機
|
|
27
|
+
* Awaits until a token is available (resolves immediately if bucket full)
|
|
28
|
+
*/
|
|
29
|
+
acquire(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* 403 受信時の指数バックオフを発動 (30s → 1m → 5m → 30m)
|
|
32
|
+
*/
|
|
33
|
+
triggerBackoff(): number;
|
|
34
|
+
/** 正常完了時にバックオフをリセット */
|
|
35
|
+
resetBackoff(): void;
|
|
36
|
+
/** テスト・観測用: 現在のトークン数を返す */
|
|
37
|
+
getTokens(): number;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/api/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,kBAAkB;IACjC,oCAAoC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAAK;gBAEb,OAAO,EAAE,kBAAkB;IAQvC,OAAO,CAAC,MAAM;IASd;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAe9B;;OAEG;IACH,cAAc,IAAI,MAAM;IAQxB,uBAAuB;IACvB,YAAY,IAAI,IAAI;IAKpB,2BAA2B;IAC3B,SAAS,IAAI,MAAM;CAIpB"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token bucket レートリミッター
|
|
3
|
+
* Token bucket rate limiter for NTA API
|
|
4
|
+
*
|
|
5
|
+
* 根拠 / Source:
|
|
6
|
+
* 国税庁仕様書 第二編 別紙2: HTTP 403 は「同一アプリケーションIDで一定期間内に多数アクセス」で発動
|
|
7
|
+
* 別添1 第9条第1項三号: 短時間における大量アクセスは禁止
|
|
8
|
+
* → 保守的に 1 RPS デフォルト、403 受信時は指数バックオフ
|
|
9
|
+
*/
|
|
10
|
+
export class TokenBucket {
|
|
11
|
+
tokens;
|
|
12
|
+
capacity;
|
|
13
|
+
refillPerMs;
|
|
14
|
+
lastRefill;
|
|
15
|
+
backoffUntil = 0;
|
|
16
|
+
backoffLevel = 0;
|
|
17
|
+
constructor(options) {
|
|
18
|
+
const capacity = options.maxBurst ?? Math.max(1, Math.ceil(options.rps));
|
|
19
|
+
this.capacity = capacity;
|
|
20
|
+
this.tokens = capacity;
|
|
21
|
+
this.refillPerMs = options.rps / 1000;
|
|
22
|
+
this.lastRefill = Date.now();
|
|
23
|
+
}
|
|
24
|
+
refill() {
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const elapsed = now - this.lastRefill;
|
|
27
|
+
if (elapsed > 0) {
|
|
28
|
+
this.tokens = Math.min(this.capacity, this.tokens + elapsed * this.refillPerMs);
|
|
29
|
+
this.lastRefill = now;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 1トークン獲得するまで待機
|
|
34
|
+
* Awaits until a token is available (resolves immediately if bucket full)
|
|
35
|
+
*/
|
|
36
|
+
async acquire() {
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
if (now < this.backoffUntil) {
|
|
39
|
+
const waitMs = this.backoffUntil - now;
|
|
40
|
+
await sleep(waitMs);
|
|
41
|
+
}
|
|
42
|
+
this.refill();
|
|
43
|
+
while (this.tokens < 1) {
|
|
44
|
+
const neededMs = Math.ceil((1 - this.tokens) / this.refillPerMs);
|
|
45
|
+
await sleep(neededMs);
|
|
46
|
+
this.refill();
|
|
47
|
+
}
|
|
48
|
+
this.tokens -= 1;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 403 受信時の指数バックオフを発動 (30s → 1m → 5m → 30m)
|
|
52
|
+
*/
|
|
53
|
+
triggerBackoff() {
|
|
54
|
+
const schedule = [30_000, 60_000, 5 * 60_000, 30 * 60_000];
|
|
55
|
+
const waitMs = schedule[Math.min(this.backoffLevel, schedule.length - 1)] ?? 30_000;
|
|
56
|
+
this.backoffLevel += 1;
|
|
57
|
+
this.backoffUntil = Date.now() + waitMs;
|
|
58
|
+
return waitMs;
|
|
59
|
+
}
|
|
60
|
+
/** 正常完了時にバックオフをリセット */
|
|
61
|
+
resetBackoff() {
|
|
62
|
+
this.backoffLevel = 0;
|
|
63
|
+
this.backoffUntil = 0;
|
|
64
|
+
}
|
|
65
|
+
/** テスト・観測用: 現在のトークン数を返す */
|
|
66
|
+
getTokens() {
|
|
67
|
+
this.refill();
|
|
68
|
+
return this.tokens;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function sleep(ms) {
|
|
72
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=rate-limiter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/api/rate-limiter.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH,MAAM,OAAO,WAAW;IACd,MAAM,CAAS;IACN,QAAQ,CAAS;IACjB,WAAW,CAAS;IAC7B,UAAU,CAAS;IACnB,YAAY,GAAG,CAAC,CAAC;IACjB,YAAY,GAAG,CAAC,CAAC;IAEzB,YAAY,OAA2B;QACrC,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QACzE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QACvB,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;QACtC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC/B,CAAC;IAEO,MAAM;QACZ,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;QACtC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;YAChF,IAAI,CAAC,UAAU,GAAG,GAAG,CAAC;QACxB,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,GAAG,GAAG,CAAC;YACvC,MAAM,KAAK,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;YACjE,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;YACtB,IAAI,CAAC,MAAM,EAAE,CAAC;QAChB,CAAC;QACD,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,cAAc;QACZ,MAAM,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,EAAE,EAAE,GAAG,MAAM,CAAC,CAAC;QAC3D,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;QACpF,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC;QACxC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,uBAAuB;IACvB,YAAY;QACV,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;QACtB,IAAI,CAAC,YAAY,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,2BAA2B;IAC3B,SAAS;QACP,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Completion handler (completion/complete)
|
|
3
|
+
*
|
|
4
|
+
* Resource Template `corp://{corporate_number}` の引数補完では、
|
|
5
|
+
* normalizeCompanyName (T7) を裏で駆動して会社名ベースの候補を返す実装も可能だが、
|
|
6
|
+
* corporate_number は数字13桁なので、国税庁 API を叩かず「ローカルでのチェックデジット
|
|
7
|
+
* 検証済み候補」を補完対象とする。
|
|
8
|
+
*
|
|
9
|
+
* 根拠 / Source: MCP 公式仕様 server/utilities/completion
|
|
10
|
+
* values: max 100 items
|
|
11
|
+
* total: optional
|
|
12
|
+
* hasMore: boolean
|
|
13
|
+
*/
|
|
14
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
15
|
+
import type { McpLogger } from '../lib/mcp-logger.js';
|
|
16
|
+
export declare function registerCompletionHandler(server: Server, deps: {
|
|
17
|
+
logger: McpLogger;
|
|
18
|
+
}): void;
|
|
19
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/completion/handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AAOxE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,MAAM,EAAE,SAAS,CAAA;CAAE,GAAG,IAAI,CAkC3F"}
|