bizgate-mcp-server 0.1.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 +161 -0
- package/dist/bizgate-client.d.ts +68 -0
- package/dist/bizgate-client.js +140 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +401 -0
- package/dist/types.d.ts +90 -0
- package/dist/types.js +49 -0
- package/dist/usage-tracker.d.ts +15 -0
- package/dist/usage-tracker.js +48 -0
- package/package.json +26 -0
package/README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# bizgate-mcp-server
|
|
2
|
+
|
|
3
|
+
BizGate API と Claude を接続する MCP サーバーです。
|
|
4
|
+
会社名・法人番号で企業情報・部署・人事情報を検索し、Claude が自然言語で回答できるようにします。
|
|
5
|
+
|
|
6
|
+
## 仕組み
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
[ユーザー] → [Claude] ←── MCP (stdio) ──→ [本サーバー] ──→ [BizGate API]
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
1. ユーザーが Claude に「○○の人事部の電話番号を教えて」と依頼
|
|
13
|
+
2. Claude が MCP プロトコル経由で本サーバーのツールを呼び出す
|
|
14
|
+
3. 本サーバーが BizGate API に問い合わせ → JSON レスポンスを解析
|
|
15
|
+
4. 構造化された情報を Claude に返却
|
|
16
|
+
5. Claude がユーザーに自然言語で回答(スプレッドシートへの入力も可能)
|
|
17
|
+
|
|
18
|
+
## 提供ツール
|
|
19
|
+
|
|
20
|
+
| ツール名 | 説明 | API消費 |
|
|
21
|
+
|---------|------|--------|
|
|
22
|
+
| `bizgate__company_search` | 企業の基本情報(住所・電話・代表者・業種・資本金 等) | 1回(結果なしでも課金) |
|
|
23
|
+
| `bizgate__department_search` | 部署情報(部署名・住所・電話番号)最大500件 | 1回(データなしは課金なし) |
|
|
24
|
+
| `bizgate__marketing_tags` | マーケティングタグ(SNS・MA・サービス・活動) | 1回(データなしは課金なし) |
|
|
25
|
+
| `bizgate__keyman_search` | 人事情報(部署・役職・発令日)最大500件 | 1回(データなしは課金なし) |
|
|
26
|
+
| `bizgate__company_full` | 企業+部署+人事を一括取得 | 3回 |
|
|
27
|
+
| `bizgate__usage_status` | 本日の残りAPI回数を確認 | 0回 |
|
|
28
|
+
|
|
29
|
+
## セットアップ
|
|
30
|
+
|
|
31
|
+
### 前提条件
|
|
32
|
+
|
|
33
|
+
- Node.js 18 以上
|
|
34
|
+
- Claude Code がインストール済み
|
|
35
|
+
- BizGate API のアカウント(ユーザー名・パスワード・サービスキー)
|
|
36
|
+
|
|
37
|
+
### 1. リポジトリをクローン&ビルド
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
git clone https://github.com/digiman-hq/bizgate-mcp-server.git
|
|
41
|
+
cd bizgate-mcp-server
|
|
42
|
+
npm install
|
|
43
|
+
npm run build
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Claude Code に MCP サーバーを登録
|
|
47
|
+
|
|
48
|
+
以下のコマンドを **1行ずつ** 実行してください。サービスキーの値は管理者から受け取ってください。
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude mcp add --scope user bizgate \
|
|
52
|
+
--transport stdio \
|
|
53
|
+
-- node /path/to/bizgate-mcp-server/dist/index.js
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
> **注意**: 上記コマンドでは環境変数が設定されません。次のステップで手動設定が必要です。
|
|
57
|
+
|
|
58
|
+
### 3. 環境変数を設定
|
|
59
|
+
|
|
60
|
+
`~/.claude.json` を開き、`mcpServers.bizgate` の `env` セクションに以下を設定します。
|
|
61
|
+
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"mcpServers": {
|
|
65
|
+
"bizgate": {
|
|
66
|
+
"type": "stdio",
|
|
67
|
+
"command": "node",
|
|
68
|
+
"args": ["/path/to/bizgate-mcp-server/dist/index.js"],
|
|
69
|
+
"env": {
|
|
70
|
+
"BIZGATE_USERNAME": "あなたのユーザー名",
|
|
71
|
+
"BIZGATE_PASSWORD": "あなたのパスワード",
|
|
72
|
+
"BIZGATE_AUTH_MODE": "basic",
|
|
73
|
+
"BIZGATE_DAILY_LIMIT": "200",
|
|
74
|
+
"BIZGATE_SKEY_COMPANY": "企業APIのサービスキー",
|
|
75
|
+
"BIZGATE_SKEY_DEPARTMENT": "部署APIのサービスキー",
|
|
76
|
+
"BIZGATE_SKEY_MARKETING": "マーケAPIのサービスキー(任意)",
|
|
77
|
+
"BIZGATE_SKEY_KEYMAN": "キーマンAPIのサービスキー(任意)"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
> `/path/to/` はクローンした実際のパスに置き換えてください。
|
|
85
|
+
|
|
86
|
+
### 4. Claude Code を再起動して確認
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
claude
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
起動後、`/mcp` コマンドで `bizgate` が **connected** になっていれば成功です。
|
|
93
|
+
|
|
94
|
+
## 使い方
|
|
95
|
+
|
|
96
|
+
Claude Code で自然言語で聞くだけです。
|
|
97
|
+
|
|
98
|
+
### 基本的な使い方
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
エージェントの企業情報を教えて
|
|
102
|
+
|
|
103
|
+
富士フイルムBIの営業部署を調べて
|
|
104
|
+
|
|
105
|
+
GMOインターネットのマーケティングタグを教えて
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 部署の絞り込み
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
富士フイルムBIの東京都にある人事部を調べて
|
|
112
|
+
|
|
113
|
+
トヨタ自動車の経理と総務の部署情報を教えて
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 一括取得
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
株式会社エージェントの全情報を調べて
|
|
120
|
+
(→ 企業情報・部署・人事情報をまとめて取得)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### スプレッドシートとの連携
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
エージェントの企業情報を調べて、Excelに入力して
|
|
127
|
+
|
|
128
|
+
このリストの会社の電話番号をスプレッドシートのB列に入れて
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## 環境変数一覧
|
|
132
|
+
|
|
133
|
+
| 変数名 | 必須 | 説明 |
|
|
134
|
+
|-------|------|------|
|
|
135
|
+
| `BIZGATE_USERNAME` | ○(basic認証時) | BizGate ユーザー名 |
|
|
136
|
+
| `BIZGATE_PASSWORD` | ○(basic認証時) | BizGate パスワード |
|
|
137
|
+
| `BIZGATE_AUTH_MODE` | | `basic`(デフォルト)または `ip` |
|
|
138
|
+
| `BIZGATE_APP` | ○(IP認証時) | アプリ識別子 |
|
|
139
|
+
| `BIZGATE_SKEY_COMPANY` | ○ | 企業APIのサービスキー |
|
|
140
|
+
| `BIZGATE_SKEY_DEPARTMENT` | ○ | 部署APIのサービスキー |
|
|
141
|
+
| `BIZGATE_SKEY_MARKETING` | | マーケティングタグAPIのサービスキー |
|
|
142
|
+
| `BIZGATE_SKEY_KEYMAN` | | キーマンAPIのサービスキー |
|
|
143
|
+
| `BIZGATE_DAILY_LIMIT` | | 1日のAPI上限(デフォルト: 200) |
|
|
144
|
+
| `BIZGATE_BASE_URL` | | APIエンドポイント(通常変更不要) |
|
|
145
|
+
|
|
146
|
+
## API利用上の注意
|
|
147
|
+
|
|
148
|
+
- **企業検索は結果なしでも課金**されます。会社名は正確に入力してください
|
|
149
|
+
- **部署・マーケ・キーマンはデータなしなら課金なし**です
|
|
150
|
+
- 1日のAPI上限はエンリッチメントパイプラインと共有です(全体900回中、MCP用に200回を確保)
|
|
151
|
+
- すべてのレスポンスに残りAPI回数が表示されます
|
|
152
|
+
|
|
153
|
+
## トラブルシューティング
|
|
154
|
+
|
|
155
|
+
| 症状 | 原因 | 対処法 |
|
|
156
|
+
|------|------|--------|
|
|
157
|
+
| `/mcp` で `failed` | 環境変数の設定ミス | `~/.claude.json` の env を確認 |
|
|
158
|
+
| エラーコード 102 | サービスキーが無効 | 管理者に有効なサービスキーを確認 |
|
|
159
|
+
| エラーコード 112 | 1日の上限超過 | 翌日まで待つ、または上限を確認 |
|
|
160
|
+
| エラーコード 204 | 企業が見つからない | 正式名称(株式会社○○)で再検索 |
|
|
161
|
+
| エラーコード 205 | 複数企業がマッチ | 法人番号やメールアドレスを追加で指定 |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { BizGateConfig, BizGateDoc } from "./types.js";
|
|
2
|
+
import { UsageTracker } from "./usage-tracker.js";
|
|
3
|
+
/** Extract first element from a string array field, or return empty string. */
|
|
4
|
+
export declare function first(field: string[] | string | undefined): string;
|
|
5
|
+
export declare class BizGateClient {
|
|
6
|
+
private config;
|
|
7
|
+
private usageTracker;
|
|
8
|
+
constructor(config: BizGateConfig, usageTracker: UsageTracker);
|
|
9
|
+
private buildUrl;
|
|
10
|
+
private getHeaders;
|
|
11
|
+
/** Send GET request and return parsed response. */
|
|
12
|
+
private request;
|
|
13
|
+
/**
|
|
14
|
+
* Company lookup (企業).
|
|
15
|
+
* Billed even on no-result — usage tracker is incremented BEFORE the call.
|
|
16
|
+
*/
|
|
17
|
+
searchCompany(params: {
|
|
18
|
+
shogo?: string;
|
|
19
|
+
compno?: string;
|
|
20
|
+
hpurl?: string;
|
|
21
|
+
email?: string;
|
|
22
|
+
tel?: string;
|
|
23
|
+
}): Promise<{
|
|
24
|
+
matchPattern: string;
|
|
25
|
+
docs: BizGateDoc[];
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Department lookup (部署).
|
|
29
|
+
* Not billed on failure, but we count conservatively.
|
|
30
|
+
*/
|
|
31
|
+
searchDepartments(params: {
|
|
32
|
+
shogo?: string;
|
|
33
|
+
compno?: string;
|
|
34
|
+
pList?: string;
|
|
35
|
+
cList?: string;
|
|
36
|
+
bKwd?: string;
|
|
37
|
+
bKOpr?: string;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
docs: BizGateDoc[];
|
|
40
|
+
numFound: number;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* Marketing tags lookup (マーケティングタグ).
|
|
44
|
+
* Not billed on failure (company not found or no tags).
|
|
45
|
+
*/
|
|
46
|
+
searchMarketingTags(params: {
|
|
47
|
+
shogo?: string;
|
|
48
|
+
compno?: string;
|
|
49
|
+
}): Promise<{
|
|
50
|
+
docs: BizGateDoc[];
|
|
51
|
+
}>;
|
|
52
|
+
/**
|
|
53
|
+
* Keyman lookup (キーマン・人名なし).
|
|
54
|
+
* Returns personnel records (department + title, announcement date, etc.).
|
|
55
|
+
* Not billed on failure.
|
|
56
|
+
*/
|
|
57
|
+
searchKeyman(params: {
|
|
58
|
+
shogo?: string;
|
|
59
|
+
compno?: string;
|
|
60
|
+
pList?: string;
|
|
61
|
+
cList?: string;
|
|
62
|
+
bKwd?: string;
|
|
63
|
+
bKOpr?: string;
|
|
64
|
+
}): Promise<{
|
|
65
|
+
docs: BizGateDoc[];
|
|
66
|
+
numFound: number;
|
|
67
|
+
}>;
|
|
68
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { BizGateApiError, BIZGATE_ERROR_CODES } from "./types.js";
|
|
2
|
+
/** Extract first element from a string array field, or return empty string. */
|
|
3
|
+
export function first(field) {
|
|
4
|
+
if (Array.isArray(field))
|
|
5
|
+
return field[0] ?? "";
|
|
6
|
+
return field ?? "";
|
|
7
|
+
}
|
|
8
|
+
export class BizGateClient {
|
|
9
|
+
config;
|
|
10
|
+
usageTracker;
|
|
11
|
+
constructor(config, usageTracker) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
this.usageTracker = usageTracker;
|
|
14
|
+
}
|
|
15
|
+
buildUrl(skey, params) {
|
|
16
|
+
const searchParams = new URLSearchParams({ skey, ...params });
|
|
17
|
+
if (this.config.authMode === "ip" && this.config.app) {
|
|
18
|
+
searchParams.set("app", this.config.app);
|
|
19
|
+
}
|
|
20
|
+
return `${this.config.baseUrl}?${searchParams.toString()}`;
|
|
21
|
+
}
|
|
22
|
+
getHeaders() {
|
|
23
|
+
if (this.config.authMode === "basic" &&
|
|
24
|
+
this.config.username &&
|
|
25
|
+
this.config.password) {
|
|
26
|
+
const encoded = Buffer.from(`${this.config.username}:${this.config.password}`).toString("base64");
|
|
27
|
+
return { Authorization: `Basic ${encoded}` };
|
|
28
|
+
}
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
/** Send GET request and return parsed response. */
|
|
32
|
+
async request(skey, params) {
|
|
33
|
+
const url = this.buildUrl(skey, params);
|
|
34
|
+
const response = await fetch(url, { headers: this.getHeaders() });
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new BizGateApiError(String(response.status), `HTTP ${response.status}: ${response.statusText}`);
|
|
37
|
+
}
|
|
38
|
+
const json = (await response.json());
|
|
39
|
+
// Error response: status=999, error.code + error.msg
|
|
40
|
+
if (json.responseHeader.status === 999) {
|
|
41
|
+
const err = json.responseHeader.error;
|
|
42
|
+
const code = err?.code ?? "unknown";
|
|
43
|
+
const message = err?.msg ??
|
|
44
|
+
BIZGATE_ERROR_CODES[code] ??
|
|
45
|
+
`不明なエラー(コード: ${code})`;
|
|
46
|
+
throw new BizGateApiError(code, message);
|
|
47
|
+
}
|
|
48
|
+
return json;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Company lookup (企業).
|
|
52
|
+
* Billed even on no-result — usage tracker is incremented BEFORE the call.
|
|
53
|
+
*/
|
|
54
|
+
async searchCompany(params) {
|
|
55
|
+
this.usageTracker.increment();
|
|
56
|
+
const queryParams = {};
|
|
57
|
+
if (params.shogo)
|
|
58
|
+
queryParams.shogo = params.shogo;
|
|
59
|
+
if (params.compno)
|
|
60
|
+
queryParams.compno = params.compno;
|
|
61
|
+
if (params.hpurl)
|
|
62
|
+
queryParams.hpurl = params.hpurl;
|
|
63
|
+
if (params.email)
|
|
64
|
+
queryParams.email = params.email;
|
|
65
|
+
if (params.tel)
|
|
66
|
+
queryParams.tel = params.tel;
|
|
67
|
+
const result = await this.request(this.config.skeyCompany, queryParams);
|
|
68
|
+
return {
|
|
69
|
+
matchPattern: result.responseHeader.matchpatern ?? "",
|
|
70
|
+
docs: result.response?.docs ?? [],
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Department lookup (部署).
|
|
75
|
+
* Not billed on failure, but we count conservatively.
|
|
76
|
+
*/
|
|
77
|
+
async searchDepartments(params) {
|
|
78
|
+
this.usageTracker.increment();
|
|
79
|
+
const queryParams = {};
|
|
80
|
+
if (params.shogo)
|
|
81
|
+
queryParams.shogo = params.shogo;
|
|
82
|
+
if (params.compno)
|
|
83
|
+
queryParams.compno = params.compno;
|
|
84
|
+
if (params.pList)
|
|
85
|
+
queryParams.pList = params.pList;
|
|
86
|
+
if (params.cList)
|
|
87
|
+
queryParams.cList = params.cList;
|
|
88
|
+
if (params.bKwd)
|
|
89
|
+
queryParams.bKwd = params.bKwd;
|
|
90
|
+
if (params.bKOpr)
|
|
91
|
+
queryParams.bKOpr = params.bKOpr;
|
|
92
|
+
const result = await this.request(this.config.skeyDepartment, queryParams);
|
|
93
|
+
return {
|
|
94
|
+
docs: result.response?.docs ?? [],
|
|
95
|
+
numFound: result.response?.numFound ?? 0,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Marketing tags lookup (マーケティングタグ).
|
|
100
|
+
* Not billed on failure (company not found or no tags).
|
|
101
|
+
*/
|
|
102
|
+
async searchMarketingTags(params) {
|
|
103
|
+
this.usageTracker.increment();
|
|
104
|
+
const queryParams = {};
|
|
105
|
+
if (params.shogo)
|
|
106
|
+
queryParams.shogo = params.shogo;
|
|
107
|
+
if (params.compno)
|
|
108
|
+
queryParams.compno = params.compno;
|
|
109
|
+
const result = await this.request(this.config.skeyMarketing, queryParams);
|
|
110
|
+
return {
|
|
111
|
+
docs: result.response?.docs ?? [],
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Keyman lookup (キーマン・人名なし).
|
|
116
|
+
* Returns personnel records (department + title, announcement date, etc.).
|
|
117
|
+
* Not billed on failure.
|
|
118
|
+
*/
|
|
119
|
+
async searchKeyman(params) {
|
|
120
|
+
this.usageTracker.increment();
|
|
121
|
+
const queryParams = {};
|
|
122
|
+
if (params.shogo)
|
|
123
|
+
queryParams.shogo = params.shogo;
|
|
124
|
+
if (params.compno)
|
|
125
|
+
queryParams.compno = params.compno;
|
|
126
|
+
if (params.pList)
|
|
127
|
+
queryParams.pList = params.pList;
|
|
128
|
+
if (params.cList)
|
|
129
|
+
queryParams.cList = params.cList;
|
|
130
|
+
if (params.bKwd)
|
|
131
|
+
queryParams.bKwd = params.bKwd;
|
|
132
|
+
if (params.bKOpr)
|
|
133
|
+
queryParams.bKOpr = params.bKOpr;
|
|
134
|
+
const result = await this.request(this.config.skeyKeyman, queryParams);
|
|
135
|
+
return {
|
|
136
|
+
docs: result.response?.docs ?? [],
|
|
137
|
+
numFound: result.response?.numFound ?? 0,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { BizGateClient, first as f } from "./bizgate-client.js";
|
|
6
|
+
import { BizGateApiError, DEPARTMENT_CATEGORIES } from "./types.js";
|
|
7
|
+
import { UsageTracker } from "./usage-tracker.js";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
// ---------- 環境変数読み込み ----------
|
|
11
|
+
const skeyCompany = process.env.BIZGATE_SKEY_COMPANY;
|
|
12
|
+
const skeyDepartment = process.env.BIZGATE_SKEY_DEPARTMENT;
|
|
13
|
+
const skeyMarketing = process.env.BIZGATE_SKEY_MARKETING ?? "";
|
|
14
|
+
const skeyKeyman = process.env.BIZGATE_SKEY_KEYMAN ?? "";
|
|
15
|
+
if (!skeyCompany || !skeyDepartment) {
|
|
16
|
+
console.error("Error: BIZGATE_SKEY_COMPANY and BIZGATE_SKEY_DEPARTMENT environment variables are required");
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const authMode = (process.env.BIZGATE_AUTH_MODE ?? "basic");
|
|
20
|
+
if (authMode === "basic") {
|
|
21
|
+
if (!process.env.BIZGATE_USERNAME || !process.env.BIZGATE_PASSWORD) {
|
|
22
|
+
console.error("Error: BIZGATE_USERNAME and BIZGATE_PASSWORD are required when BIZGATE_AUTH_MODE=basic");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (authMode === "ip" && !process.env.BIZGATE_APP) {
|
|
27
|
+
console.error("Error: BIZGATE_APP is required when BIZGATE_AUTH_MODE=ip");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
const dailyLimit = Number(process.env.BIZGATE_DAILY_LIMIT ?? "200");
|
|
31
|
+
const usageFile = process.env.BIZGATE_USAGE_FILE ??
|
|
32
|
+
join(homedir(), ".bizgate-mcp-usage.json");
|
|
33
|
+
const baseUrl = process.env.BIZGATE_BASE_URL ??
|
|
34
|
+
"https://target.papatto.info/bizgate/dictionary.php";
|
|
35
|
+
// ---------- クライアント初期化 ----------
|
|
36
|
+
const config = {
|
|
37
|
+
baseUrl,
|
|
38
|
+
skeyCompany,
|
|
39
|
+
skeyDepartment,
|
|
40
|
+
skeyMarketing,
|
|
41
|
+
skeyKeyman,
|
|
42
|
+
authMode,
|
|
43
|
+
username: process.env.BIZGATE_USERNAME,
|
|
44
|
+
password: process.env.BIZGATE_PASSWORD,
|
|
45
|
+
app: process.env.BIZGATE_APP,
|
|
46
|
+
dailyLimit,
|
|
47
|
+
};
|
|
48
|
+
const usageTracker = new UsageTracker(usageFile, dailyLimit);
|
|
49
|
+
const client = new BizGateClient(config, usageTracker);
|
|
50
|
+
// ---------- MCPサーバー ----------
|
|
51
|
+
const server = new McpServer({
|
|
52
|
+
name: "bizgate-mcp-server",
|
|
53
|
+
version: "0.3.0",
|
|
54
|
+
});
|
|
55
|
+
// ---------- ヘルパー ----------
|
|
56
|
+
function usageFooter() {
|
|
57
|
+
const remaining = usageTracker.remaining();
|
|
58
|
+
const count = usageTracker.currentCount();
|
|
59
|
+
return `\n\n---\n残りAPI回数: ${remaining}回(本日使用: ${count}/${dailyLimit})`;
|
|
60
|
+
}
|
|
61
|
+
function errorText(err) {
|
|
62
|
+
if (err instanceof BizGateApiError) {
|
|
63
|
+
return `エラー: ${err.message}(コード: ${err.code})${usageFooter()}`;
|
|
64
|
+
}
|
|
65
|
+
if (err instanceof Error) {
|
|
66
|
+
return `エラー: ${err.message}`;
|
|
67
|
+
}
|
|
68
|
+
return "不明なエラーが発生しました";
|
|
69
|
+
}
|
|
70
|
+
function formatCompanyDoc(doc) {
|
|
71
|
+
const lines = [`## 企業情報: ${f(doc.shogo)}`];
|
|
72
|
+
if (doc.compno)
|
|
73
|
+
lines.push(`法人番号: ${doc.compno}`);
|
|
74
|
+
if (f(doc.zip))
|
|
75
|
+
lines.push(`郵便番号: ${f(doc.zip)}`);
|
|
76
|
+
if (f(doc.add))
|
|
77
|
+
lines.push(`住所: ${f(doc.add)}`);
|
|
78
|
+
if (f(doc.pref))
|
|
79
|
+
lines.push(`都道府県: ${f(doc.pref)}`);
|
|
80
|
+
if (f(doc.tel))
|
|
81
|
+
lines.push(`電話: ${f(doc.tel)}`);
|
|
82
|
+
if (f(doc.fax))
|
|
83
|
+
lines.push(`FAX: ${f(doc.fax)}`);
|
|
84
|
+
if (f(doc.mail))
|
|
85
|
+
lines.push(`メール: ${f(doc.mail)}`);
|
|
86
|
+
if (f(doc.ceo))
|
|
87
|
+
lines.push(`代表者: ${f(doc.ceo)}`);
|
|
88
|
+
if (f(doc.gyoshu_facet))
|
|
89
|
+
lines.push(`業種: ${f(doc.gyoshu_facet)}`);
|
|
90
|
+
if (doc.shihon)
|
|
91
|
+
lines.push(`資本金: ${doc.shihon}`);
|
|
92
|
+
if (doc.revenue)
|
|
93
|
+
lines.push(`売上: ${doc.revenue}`);
|
|
94
|
+
if (doc.emp)
|
|
95
|
+
lines.push(`従業員: ${doc.emp}`);
|
|
96
|
+
if (doc.pub)
|
|
97
|
+
lines.push(`上場区分: ${doc.pub}`);
|
|
98
|
+
if (f(doc.hpurl))
|
|
99
|
+
lines.push(`HP: ${f(doc.hpurl)}`);
|
|
100
|
+
if (doc.seturitu)
|
|
101
|
+
lines.push(`設立: ${doc.seturitu}`);
|
|
102
|
+
if (doc.kessanm)
|
|
103
|
+
lines.push(`決算月: ${doc.kessanm}`);
|
|
104
|
+
if (doc.invoiceno)
|
|
105
|
+
lines.push(`インボイス番号: ${doc.invoiceno}`);
|
|
106
|
+
if (f(doc.bumon))
|
|
107
|
+
lines.push(`拠点名: ${f(doc.bumon)}`);
|
|
108
|
+
return lines.join("\n");
|
|
109
|
+
}
|
|
110
|
+
function formatDepartmentDoc(doc, index) {
|
|
111
|
+
const deptName = f(doc.bumon) || "(不明)";
|
|
112
|
+
const lines = [`${index}. ${deptName}`];
|
|
113
|
+
if (f(doc.add))
|
|
114
|
+
lines.push(` 住所: ${f(doc.add)}`);
|
|
115
|
+
if (f(doc.tel))
|
|
116
|
+
lines.push(` 電話: ${f(doc.tel)}`);
|
|
117
|
+
if (f(doc.pref))
|
|
118
|
+
lines.push(` 都道府県: ${f(doc.pref)}`);
|
|
119
|
+
const categories = [];
|
|
120
|
+
for (const [key, label] of Object.entries(DEPARTMENT_CATEGORIES)) {
|
|
121
|
+
if (doc[key] === "true") {
|
|
122
|
+
categories.push(label);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (categories.length > 0) {
|
|
126
|
+
lines.push(` カテゴリ: ${categories.join(", ")}`);
|
|
127
|
+
}
|
|
128
|
+
return lines.join("\n");
|
|
129
|
+
}
|
|
130
|
+
function formatKeymanDoc(doc, index) {
|
|
131
|
+
const role = f(doc.bumon) || "(不明)";
|
|
132
|
+
const lines = [`${index}. ${role}`];
|
|
133
|
+
if (doc.pagedate)
|
|
134
|
+
lines.push(` 発表日: ${doc.pagedate}`);
|
|
135
|
+
if (doc.haturei)
|
|
136
|
+
lines.push(` 発令日: ${doc.haturei}`);
|
|
137
|
+
if (doc.bikou)
|
|
138
|
+
lines.push(` 備考: ${doc.bikou}`);
|
|
139
|
+
if (doc.compno)
|
|
140
|
+
lines.push(` 法人番号: ${doc.compno}`);
|
|
141
|
+
const categories = [];
|
|
142
|
+
for (const [key, label] of Object.entries(DEPARTMENT_CATEGORIES)) {
|
|
143
|
+
if (doc[key] === "true") {
|
|
144
|
+
categories.push(label);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (categories.length > 0) {
|
|
148
|
+
lines.push(` カテゴリ: ${categories.join(", ")}`);
|
|
149
|
+
}
|
|
150
|
+
return lines.join("\n");
|
|
151
|
+
}
|
|
152
|
+
// ---------- 共通バリデーション ----------
|
|
153
|
+
function validateInput(shogo, compno) {
|
|
154
|
+
if (!shogo && !compno) {
|
|
155
|
+
return "エラー: 会社名(shogo)または法人番号(compno)のいずれかを入力してください。";
|
|
156
|
+
}
|
|
157
|
+
if (compno && !/^\d{13}$/.test(compno)) {
|
|
158
|
+
return "エラー: 法人番号は13桁の数字で入力してください。";
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
// ---------- Tool 1: 企業検索 ----------
|
|
163
|
+
server.tool("bizgate__company_search", "会社名または法人番号で企業の基本情報(住所・電話・FAX・代表者・業種・資本金・売上・HP)を検索する。1回の検索で課金されるため、会社名は正確に入力してください。", {
|
|
164
|
+
shogo: z.string().optional().describe("会社名(例:株式会社○○)"),
|
|
165
|
+
compno: z.string().optional().describe("法人番号(13桁)"),
|
|
166
|
+
hpurl: z.string().optional().describe("ホームページURL(マッチング精度向上)"),
|
|
167
|
+
email: z.string().optional().describe("メールアドレス(マッチング精度向上)"),
|
|
168
|
+
tel: z.string().optional().describe("電話番号(マッチング精度向上)"),
|
|
169
|
+
}, async ({ shogo, compno, hpurl, email, tel }) => {
|
|
170
|
+
const err = validateInput(shogo, compno);
|
|
171
|
+
if (err)
|
|
172
|
+
return { content: [{ type: "text", text: err }] };
|
|
173
|
+
try {
|
|
174
|
+
const { matchPattern, docs } = await client.searchCompany({
|
|
175
|
+
shogo, compno, hpurl, email, tel,
|
|
176
|
+
});
|
|
177
|
+
if (docs.length === 0) {
|
|
178
|
+
return {
|
|
179
|
+
content: [{
|
|
180
|
+
type: "text",
|
|
181
|
+
text: `該当する企業が見つかりませんでした。(マッチパターン: ${matchPattern})${usageFooter()}`,
|
|
182
|
+
}],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const text = docs.map(formatCompanyDoc).join("\n\n");
|
|
186
|
+
return {
|
|
187
|
+
content: [{ type: "text", text: text + `\nマッチパターン: ${matchPattern}${usageFooter()}` }],
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
return { content: [{ type: "text", text: errorText(e) }] };
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
// ---------- Tool 2: 部署検索 ----------
|
|
195
|
+
server.tool("bizgate__department_search", "会社名または法人番号で部署情報(部署名・住所・電話番号・カテゴリ)を検索する。最大500件。都道府県・カテゴリ・部署名キーワードで絞り込み可能。", {
|
|
196
|
+
shogo: z.string().optional().describe("会社名(例:株式会社○○)"),
|
|
197
|
+
compno: z.string().optional().describe("法人番号(13桁)"),
|
|
198
|
+
pList: z.string().optional().describe("都道府県コード(カンマ区切り。例: 13,14 = 東京都,神奈川県)"),
|
|
199
|
+
cList: z.string().optional().describe("部署カテゴリ番号(カンマ区切り。1=経営企画,2=営業企画,3=人事,4=経理,5=総務,6=広報IR,7=法務,8=研究,9=購買,10=システム,11=海外,12=環境CSR,13=営業,14=製造工場,15=その他)"),
|
|
200
|
+
bKwd: z.string().optional().describe("部署名キーワード(カンマ区切りで複数可。例: 人事部,経理部)"),
|
|
201
|
+
bKOpr: z.string().optional().describe("キーワード結合演算子(0=OR(デフォルト), 1=AND)"),
|
|
202
|
+
}, async ({ shogo, compno, pList, cList, bKwd, bKOpr }) => {
|
|
203
|
+
const err = validateInput(shogo, compno);
|
|
204
|
+
if (err)
|
|
205
|
+
return { content: [{ type: "text", text: err }] };
|
|
206
|
+
try {
|
|
207
|
+
const { docs, numFound } = await client.searchDepartments({
|
|
208
|
+
shogo, compno, pList, cList, bKwd, bKOpr,
|
|
209
|
+
});
|
|
210
|
+
if (docs.length === 0) {
|
|
211
|
+
return {
|
|
212
|
+
content: [{ type: "text", text: `部署情報が見つかりませんでした。${usageFooter()}` }],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
const companyName = f(docs[0].shogo);
|
|
216
|
+
const header = `## 部署情報${companyName ? `: ${companyName}` : ""}(全${numFound}件)\n`;
|
|
217
|
+
const text = docs.map((d, i) => formatDepartmentDoc(d, i + 1)).join("\n\n");
|
|
218
|
+
return {
|
|
219
|
+
content: [{ type: "text", text: header + text + usageFooter() }],
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
return { content: [{ type: "text", text: errorText(e) }] };
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
// ---------- Tool 3: マーケティングタグ ----------
|
|
227
|
+
server.tool("bizgate__marketing_tags", "会社名または法人番号で企業のマーケティングタグ(SNS導入、MAツール、事業サービス、活動状況)を検索する。プロスペクトリストのフィルタリングや営業優先度判断に活用。課金なし(企業特定不可・タグなしの場合)。", {
|
|
228
|
+
shogo: z.string().optional().describe("会社名(例:株式会社○○)"),
|
|
229
|
+
compno: z.string().optional().describe("法人番号(13桁)"),
|
|
230
|
+
}, async ({ shogo, compno }) => {
|
|
231
|
+
const err = validateInput(shogo, compno);
|
|
232
|
+
if (err)
|
|
233
|
+
return { content: [{ type: "text", text: err }] };
|
|
234
|
+
if (!skeyMarketing) {
|
|
235
|
+
return {
|
|
236
|
+
content: [{ type: "text", text: "エラー: BIZGATE_SKEY_MARKETING が設定されていません。" }],
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
try {
|
|
240
|
+
const { docs } = await client.searchMarketingTags({ shogo, compno });
|
|
241
|
+
if (docs.length === 0) {
|
|
242
|
+
return {
|
|
243
|
+
content: [{ type: "text", text: `マーケティングタグが見つかりませんでした。${usageFooter()}` }],
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const doc = docs[0];
|
|
247
|
+
const lines = [`## マーケティングタグ: ${f(doc.shogo)}`];
|
|
248
|
+
if (doc.compno)
|
|
249
|
+
lines.push(`法人番号: ${doc.compno}`);
|
|
250
|
+
if (doc.snstag)
|
|
251
|
+
lines.push(`\n### SNSタグ\n${doc.snstag.replace(/¥\//g, "\n- ").replace(/^/, "- ")}`);
|
|
252
|
+
if (doc.matag)
|
|
253
|
+
lines.push(`\n### MAツール\n${doc.matag.replace(/¥\//g, "\n- ").replace(/^/, "- ")}`);
|
|
254
|
+
if (doc.servicetag)
|
|
255
|
+
lines.push(`\n### 事業サービス\n${doc.servicetag.replace(/¥\//g, "\n- ").replace(/^/, "- ")}`);
|
|
256
|
+
if (doc.activtag)
|
|
257
|
+
lines.push(`\n### 活動タグ\n${doc.activtag.replace(/¥\//g, "\n- ").replace(/^/, "- ")}`);
|
|
258
|
+
return {
|
|
259
|
+
content: [{ type: "text", text: lines.join("\n") + usageFooter() }],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
return { content: [{ type: "text", text: errorText(e) }] };
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
// ---------- Tool 4: キーマン検索 ----------
|
|
267
|
+
server.tool("bizgate__keyman_search", "会社名または法人番号で人事情報(部署・役職、発表日、発令日)を検索する。最大500件。部署カテゴリやキーワードで絞り込み可能。人名は含まれない。課金なし(企業特定不可・データなしの場合)。", {
|
|
268
|
+
shogo: z.string().optional().describe("会社名(例:株式会社○○)"),
|
|
269
|
+
compno: z.string().optional().describe("法人番号(13桁)"),
|
|
270
|
+
pList: z.string().optional().describe("都道府県コード(カンマ区切り)"),
|
|
271
|
+
cList: z.string().optional().describe("部署カテゴリ番号(カンマ区切り。3=人事,13=営業 など)"),
|
|
272
|
+
bKwd: z.string().optional().describe("部署・役職キーワード(カンマ区切り。例: 役員,部長,ソリューション)"),
|
|
273
|
+
bKOpr: z.string().optional().describe("キーワード結合演算子(0=OR(デフォルト), 1=AND)"),
|
|
274
|
+
}, async ({ shogo, compno, pList, cList, bKwd, bKOpr }) => {
|
|
275
|
+
const err = validateInput(shogo, compno);
|
|
276
|
+
if (err)
|
|
277
|
+
return { content: [{ type: "text", text: err }] };
|
|
278
|
+
if (!skeyKeyman) {
|
|
279
|
+
return {
|
|
280
|
+
content: [{ type: "text", text: "エラー: BIZGATE_SKEY_KEYMAN が設定されていません。" }],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
const { docs, numFound } = await client.searchKeyman({
|
|
285
|
+
shogo, compno, pList, cList, bKwd, bKOpr,
|
|
286
|
+
});
|
|
287
|
+
if (docs.length === 0) {
|
|
288
|
+
return {
|
|
289
|
+
content: [{ type: "text", text: `人事情報が見つかりませんでした。${usageFooter()}` }],
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
const companyName = f(docs[0].shogo);
|
|
293
|
+
const header = `## 人事情報${companyName ? `: ${companyName}` : ""}(全${numFound}件)\n`;
|
|
294
|
+
const text = docs.map((d, i) => formatKeymanDoc(d, i + 1)).join("\n\n");
|
|
295
|
+
return {
|
|
296
|
+
content: [{ type: "text", text: header + text + usageFooter() }],
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
catch (e) {
|
|
300
|
+
return { content: [{ type: "text", text: errorText(e) }] };
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
// ---------- Tool 5: 企業総合検索(企業+部署+キーマンを一括取得) ----------
|
|
304
|
+
server.tool("bizgate__company_full", "会社名または法人番号で企業情報・部署情報・人事情報を一括取得する。個別ツールを3回呼ぶ代わりに1回で全情報を取得可能。API 3回分を消費。部署やキーワードで絞り込みも可能。", {
|
|
305
|
+
shogo: z.string().optional().describe("会社名(例:株式会社○○)"),
|
|
306
|
+
compno: z.string().optional().describe("法人番号(13桁)"),
|
|
307
|
+
hpurl: z.string().optional().describe("ホームページURL(企業特定精度向上)"),
|
|
308
|
+
email: z.string().optional().describe("メールアドレス(企業特定精度向上)"),
|
|
309
|
+
bKwd: z.string().optional().describe("部署名キーワード(部署・キーマン絞り込み。例: 人事部,営業部)"),
|
|
310
|
+
cList: z.string().optional().describe("部署カテゴリ番号(例: 3=人事, 13=営業)"),
|
|
311
|
+
}, async ({ shogo, compno, hpurl, email, bKwd, cList }) => {
|
|
312
|
+
const err = validateInput(shogo, compno);
|
|
313
|
+
if (err)
|
|
314
|
+
return { content: [{ type: "text", text: err }] };
|
|
315
|
+
const sections = [];
|
|
316
|
+
// 1. Company info
|
|
317
|
+
try {
|
|
318
|
+
const { matchPattern, docs } = await client.searchCompany({
|
|
319
|
+
shogo, compno, hpurl, email,
|
|
320
|
+
});
|
|
321
|
+
if (docs.length > 0) {
|
|
322
|
+
sections.push(docs.map(formatCompanyDoc).join("\n\n"));
|
|
323
|
+
sections.push(`マッチパターン: ${matchPattern}`);
|
|
324
|
+
}
|
|
325
|
+
else {
|
|
326
|
+
sections.push(`企業情報: 該当なし(マッチパターン: ${matchPattern})`);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
sections.push(`企業情報: ${e instanceof Error ? e.message : "取得失敗"}`);
|
|
331
|
+
}
|
|
332
|
+
// 2. Department info
|
|
333
|
+
try {
|
|
334
|
+
const { docs, numFound } = await client.searchDepartments({
|
|
335
|
+
shogo, compno, bKwd, cList,
|
|
336
|
+
});
|
|
337
|
+
if (docs.length > 0) {
|
|
338
|
+
const companyName = f(docs[0].shogo);
|
|
339
|
+
sections.push(`\n## 部署情報${companyName ? `: ${companyName}` : ""}(全${numFound}件)`);
|
|
340
|
+
sections.push(docs.map((d, i) => formatDepartmentDoc(d, i + 1)).join("\n\n"));
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
sections.push("\n## 部署情報\n該当なし");
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
catch (e) {
|
|
347
|
+
if (e instanceof BizGateApiError && e.code === "504") {
|
|
348
|
+
sections.push("\n## 部署情報\nデータなし");
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
sections.push(`\n## 部署情報\n${e instanceof Error ? e.message : "取得失敗"}`);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
// 3. Keyman info
|
|
355
|
+
if (skeyKeyman) {
|
|
356
|
+
try {
|
|
357
|
+
const { docs, numFound } = await client.searchKeyman({
|
|
358
|
+
shogo, compno, bKwd, cList,
|
|
359
|
+
});
|
|
360
|
+
if (docs.length > 0) {
|
|
361
|
+
const companyName = f(docs[0].shogo);
|
|
362
|
+
sections.push(`\n## 人事情報${companyName ? `: ${companyName}` : ""}(全${numFound}件)`);
|
|
363
|
+
sections.push(docs.map((d, i) => formatKeymanDoc(d, i + 1)).join("\n\n"));
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
sections.push("\n## 人事情報\n該当なし");
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
catch (e) {
|
|
370
|
+
if (e instanceof BizGateApiError && e.code === "604") {
|
|
371
|
+
sections.push("\n## 人事情報\nデータなし");
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
sections.push(`\n## 人事情報\n${e instanceof Error ? e.message : "取得失敗"}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
content: [{ type: "text", text: sections.join("\n") + usageFooter() }],
|
|
380
|
+
};
|
|
381
|
+
});
|
|
382
|
+
// ---------- Tool 6: 利用状況確認 ----------
|
|
383
|
+
server.tool("bizgate__usage_status", "本日のBizGate API残り利用回数を確認する(APIコールなし)", {}, async () => {
|
|
384
|
+
const remaining = usageTracker.remaining();
|
|
385
|
+
const count = usageTracker.currentCount();
|
|
386
|
+
return {
|
|
387
|
+
content: [
|
|
388
|
+
{
|
|
389
|
+
type: "text",
|
|
390
|
+
text: `本日のAPI利用状況: ${count}回使用済み / 残り${remaining}回(上限: ${dailyLimit}回)`,
|
|
391
|
+
},
|
|
392
|
+
],
|
|
393
|
+
};
|
|
394
|
+
});
|
|
395
|
+
// ---------- 起動 ----------
|
|
396
|
+
async function main() {
|
|
397
|
+
const transport = new StdioServerTransport();
|
|
398
|
+
await server.connect(transport);
|
|
399
|
+
console.error("BizGate MCP Server running on stdio");
|
|
400
|
+
}
|
|
401
|
+
main().catch(console.error);
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export interface BizGateConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
skeyCompany: string;
|
|
4
|
+
skeyDepartment: string;
|
|
5
|
+
skeyMarketing: string;
|
|
6
|
+
skeyKeyman: string;
|
|
7
|
+
authMode: "basic" | "ip";
|
|
8
|
+
username?: string;
|
|
9
|
+
password?: string;
|
|
10
|
+
app?: string;
|
|
11
|
+
dailyLimit: number;
|
|
12
|
+
}
|
|
13
|
+
export interface BizGateRawResponse {
|
|
14
|
+
responseHeader: {
|
|
15
|
+
status: number;
|
|
16
|
+
QTime: number;
|
|
17
|
+
params: Record<string, string>;
|
|
18
|
+
matchpatern?: string;
|
|
19
|
+
warning?: string;
|
|
20
|
+
querystring?: string;
|
|
21
|
+
error?: {
|
|
22
|
+
code: string;
|
|
23
|
+
msg: string;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
response?: {
|
|
27
|
+
numFound: number;
|
|
28
|
+
start: number;
|
|
29
|
+
docs: BizGateDoc[];
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** A single document from the BizGate response. */
|
|
33
|
+
export interface BizGateDoc {
|
|
34
|
+
orgid?: string;
|
|
35
|
+
compno?: string;
|
|
36
|
+
shogo?: string[];
|
|
37
|
+
update?: string;
|
|
38
|
+
pageitem?: string;
|
|
39
|
+
pagecat?: string[];
|
|
40
|
+
add?: string[];
|
|
41
|
+
pref?: string[];
|
|
42
|
+
citycode?: string[];
|
|
43
|
+
zip?: string[];
|
|
44
|
+
tel?: string[];
|
|
45
|
+
fax?: string[];
|
|
46
|
+
mail?: string[];
|
|
47
|
+
hpurl?: string[];
|
|
48
|
+
ceo?: string[];
|
|
49
|
+
gyoshu_facet?: string[];
|
|
50
|
+
gyoshu_dcat?: string;
|
|
51
|
+
shihon?: string;
|
|
52
|
+
emp?: string;
|
|
53
|
+
revenue?: string;
|
|
54
|
+
pub?: string;
|
|
55
|
+
bumon?: string[];
|
|
56
|
+
kessanm?: string;
|
|
57
|
+
seturitu?: string;
|
|
58
|
+
invoiceno?: string;
|
|
59
|
+
inquryurl?: string[];
|
|
60
|
+
id?: string;
|
|
61
|
+
pagedate?: string;
|
|
62
|
+
haturei?: string;
|
|
63
|
+
bikou?: string;
|
|
64
|
+
snstag?: string;
|
|
65
|
+
matag?: string;
|
|
66
|
+
servicetag?: string;
|
|
67
|
+
activtag?: string;
|
|
68
|
+
ctg_01?: string;
|
|
69
|
+
ctg_02?: string;
|
|
70
|
+
ctg_03?: string;
|
|
71
|
+
ctg_04?: string;
|
|
72
|
+
ctg_05?: string;
|
|
73
|
+
ctg_06?: string;
|
|
74
|
+
ctg_07?: string;
|
|
75
|
+
ctg_08?: string;
|
|
76
|
+
ctg_09?: string;
|
|
77
|
+
ctg_10?: string;
|
|
78
|
+
ctg_11?: string;
|
|
79
|
+
ctg_12?: string;
|
|
80
|
+
ctg_13?: string;
|
|
81
|
+
ctg_14?: string;
|
|
82
|
+
ctg_15?: string;
|
|
83
|
+
[key: string]: unknown;
|
|
84
|
+
}
|
|
85
|
+
export declare const DEPARTMENT_CATEGORIES: Record<string, string>;
|
|
86
|
+
export declare const BIZGATE_ERROR_CODES: Record<string, string>;
|
|
87
|
+
export declare class BizGateApiError extends Error {
|
|
88
|
+
readonly code: string;
|
|
89
|
+
constructor(code: string, message: string);
|
|
90
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// --- Department category labels ---
|
|
2
|
+
export const DEPARTMENT_CATEGORIES = {
|
|
3
|
+
ctg_01: "経営企画",
|
|
4
|
+
ctg_02: "営業企画・マーケティング",
|
|
5
|
+
ctg_03: "人事",
|
|
6
|
+
ctg_04: "経理",
|
|
7
|
+
ctg_05: "総務管理",
|
|
8
|
+
ctg_06: "広報IR",
|
|
9
|
+
ctg_07: "法務リスク",
|
|
10
|
+
ctg_08: "研究",
|
|
11
|
+
ctg_09: "購買資材",
|
|
12
|
+
ctg_10: "システム",
|
|
13
|
+
ctg_11: "海外",
|
|
14
|
+
ctg_12: "環境CSR",
|
|
15
|
+
ctg_13: "営業",
|
|
16
|
+
ctg_14: "製造工場",
|
|
17
|
+
ctg_15: "その他",
|
|
18
|
+
};
|
|
19
|
+
// --- Error codes → Japanese messages ---
|
|
20
|
+
export const BIZGATE_ERROR_CODES = {
|
|
21
|
+
"101": "認証コードを指定してください",
|
|
22
|
+
"102": "問合せパラメータに有効なサービスキーを設定してください",
|
|
23
|
+
"111": "有効な認証コードを指定してください",
|
|
24
|
+
"112": "1日の利用出来るリクエストの上限を超えています",
|
|
25
|
+
"113": "リクエストのカウントアップに失敗しました",
|
|
26
|
+
"201": "検索に使用できない文字を除いて、再度リクエストを送信してください",
|
|
27
|
+
"202": "企業を特定するために、商号若しくは法人番号のいずれかを指定してください",
|
|
28
|
+
"203": "有効な認証コードを指定してください",
|
|
29
|
+
"204": "検索の結果、一致する企業が存在しませんでした",
|
|
30
|
+
"205": "検索の結果、複数の企業が存在します",
|
|
31
|
+
"206": "検索の結果、商号を得ることができませんでした",
|
|
32
|
+
"207": "検索の結果、古い登記情報と一致しました",
|
|
33
|
+
"404": "検索の結果、該当企業にはタグデータが付加されていませんでした",
|
|
34
|
+
"501": "検索に使用できない文字を除いて、再度リクエストを送信してください",
|
|
35
|
+
"504": "検索の結果、該当企業の部署データは0件でした",
|
|
36
|
+
"601": "検索に使用できない文字を除いて、再度リクエストを送信してください",
|
|
37
|
+
"604": "検索の結果、該当企業の人事情報データは0件でした",
|
|
38
|
+
"1001": "データベースへの接続が確立しませんでした",
|
|
39
|
+
"1002": "SQLステートメントの実行に失敗しました",
|
|
40
|
+
};
|
|
41
|
+
// --- Custom error class ---
|
|
42
|
+
export class BizGateApiError extends Error {
|
|
43
|
+
code;
|
|
44
|
+
constructor(code, message) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.code = code;
|
|
47
|
+
this.name = "BizGateApiError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare class UsageTracker {
|
|
2
|
+
private filePath;
|
|
3
|
+
private limit;
|
|
4
|
+
constructor(filePath: string, limit: number);
|
|
5
|
+
/** Get today's date string in JST (Asia/Tokyo) */
|
|
6
|
+
private todayJST;
|
|
7
|
+
private readState;
|
|
8
|
+
private writeState;
|
|
9
|
+
/** Increment counter before making an API call. Throws if limit exceeded. */
|
|
10
|
+
increment(cost?: number): number;
|
|
11
|
+
/** Remaining requests for today. */
|
|
12
|
+
remaining(): number;
|
|
13
|
+
/** Current usage count for today. */
|
|
14
|
+
currentCount(): number;
|
|
15
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
export class UsageTracker {
|
|
3
|
+
filePath;
|
|
4
|
+
limit;
|
|
5
|
+
constructor(filePath, limit) {
|
|
6
|
+
this.filePath = filePath;
|
|
7
|
+
this.limit = limit;
|
|
8
|
+
}
|
|
9
|
+
/** Get today's date string in JST (Asia/Tokyo) */
|
|
10
|
+
todayJST() {
|
|
11
|
+
return new Date().toLocaleDateString("en-CA", { timeZone: "Asia/Tokyo" });
|
|
12
|
+
}
|
|
13
|
+
readState() {
|
|
14
|
+
try {
|
|
15
|
+
const raw = readFileSync(this.filePath, "utf-8");
|
|
16
|
+
const state = JSON.parse(raw);
|
|
17
|
+
if (state.date === this.todayJST()) {
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// File missing or corrupted — start fresh
|
|
23
|
+
}
|
|
24
|
+
return { date: this.todayJST(), count: 0 };
|
|
25
|
+
}
|
|
26
|
+
writeState(state) {
|
|
27
|
+
writeFileSync(this.filePath, JSON.stringify(state), "utf-8");
|
|
28
|
+
}
|
|
29
|
+
/** Increment counter before making an API call. Throws if limit exceeded. */
|
|
30
|
+
increment(cost = 1) {
|
|
31
|
+
const state = this.readState();
|
|
32
|
+
if (state.count + cost > this.limit) {
|
|
33
|
+
throw new Error(`本日のAPI利用上限(${this.limit}回)に達しました。明日以降に再度お試しください。`);
|
|
34
|
+
}
|
|
35
|
+
state.count += cost;
|
|
36
|
+
this.writeState(state);
|
|
37
|
+
return state.count;
|
|
38
|
+
}
|
|
39
|
+
/** Remaining requests for today. */
|
|
40
|
+
remaining() {
|
|
41
|
+
const state = this.readState();
|
|
42
|
+
return Math.max(0, this.limit - state.count);
|
|
43
|
+
}
|
|
44
|
+
/** Current usage count for today. */
|
|
45
|
+
currentCount() {
|
|
46
|
+
return this.readState().count;
|
|
47
|
+
}
|
|
48
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bizgate-mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "BizGate APIとClaudeを連携するMCPサーバー",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"bizgate-mcp-server": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"start": "node dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
20
|
+
"zod": "^3.24.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^22.0.0",
|
|
24
|
+
"typescript": "^5.8.3"
|
|
25
|
+
}
|
|
26
|
+
}
|