bizgate-mcp-server 0.3.1 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -58,17 +58,27 @@ Mac の場合:
58
58
 
59
59
  ターミナルに以下を **そのままコピーして貼り付け** → Enter を押してください。
60
60
 
61
+ ```smalltalk
62
+ claude mcp add bizgate --scope user \
63
+ -e "BIZGATE_USERNAME=digi-man_bizg1" \
64
+ -e "BIZGATE_PASSWORD=digi-man_bizg1" \
65
+ -e "BIZGATE_AUTH_MODE=basic" \
66
+ -e "BIZGATE_DAILY_LIMIT=200" \
67
+ -e "BIZGATE_SKEY_COMPANY=/EhMJ9YMCJtJgo73.DjLuew8rnnTlb.F6/MuiESFXmZwlCKvG8bMm" \
68
+ -e "BIZGATE_SKEY_DEPARTMENT=i08d/E8OwOtb2L0cdzbh1.Y5LvlTwpvZ49o3rBFVOnzRL7lDvR4Om" \
69
+ -e "BIZGATE_SKEY_MARKETING=X96mHxuTt4RExyo94/eGIOJwsFKr6aVlYQ80UozjTZaUVf9t/1CDu" \
70
+ -e "BIZGATE_SKEY_KEYMAN=3Rc3.RMt53yzVJ5zv28bjOIZVx8KjfIfo2PiCryGtF81feOv3hPZ." \
71
+ -e "BIZGATE_SKEY_KEYMAN_NAME=qT42YqynPg0uhPoGvquwbOUAYCotYSzxHhwiCHG5gZbTFMk75waAG" \
72
+ -- npx bizgate-mcp-server
61
73
  ```
62
- bash <(curl -sL https://raw.githubusercontent.com/digiman-hq/bizgate-mcp-server/dev/install.sh)
63
- ```
64
-
65
- 画面の案内に従って、管理者から受け取った認証情報を入力してください。
66
74
 
67
- - ユーザー名
68
- - パスワード(入力しても画面に表示されません)
69
- - サービスキー
70
-
71
- すべて入力すると、自動でセットアップが完了します。
75
+ - サービスキーを誤って入力した場合
76
+
77
+ ```bash
78
+ claude mcp remove bizgate -s user
79
+ ```
80
+
81
+ その後、再度入力してください。
72
82
 
73
83
  ---
74
84
 
@@ -108,6 +118,33 @@ claude
108
118
  | 採用活動をしている会社か確認 | 「○○のマーケティングタグを教えて」 |
109
119
  | 全情報をまとめて見たい | 「○○の全情報を調べて」 |
110
120
  | Excelに入力したい | 「○○の情報を調べてExcelに入れて」 |
121
+ | DigiManのサービスに合う部署を探したい | 「○○にDigiManのサービスが売れそうな部署を探して」 |
122
+
123
+ ### prospect-match スキル(部署マッチング)
124
+
125
+ 企業の部署一覧から、DigiManの各サービス(営業代行・Solution・AI)に適した営業ターゲット部署を自動で提案するスキルです。
126
+
127
+ ```
128
+ /prospect-match 兼松株式会社
129
+ ```
130
+
131
+ または自然言語でもOK:
132
+ ```
133
+ 「兼松株式会社にDigiManのサービスが売れそうな部署を探して」
134
+ ```
135
+
136
+ **出力内容:**
137
+ - サービスごとの推薦部署(最大5件ずつ)+ 電話番号
138
+ - マッチ理由
139
+ - 最優先アプローチ先
140
+
141
+ **すでにBizGate MCPをインストール済みの方** は、以下でスキルだけ追加できます:
142
+
143
+ ```bash
144
+ bash install-skill.sh
145
+ ```
146
+
147
+ 新規インストールの場合は `install.sh` にスキルも含まれています。
111
148
 
112
149
  ### 注意点
113
150
 
@@ -124,7 +161,7 @@ claude
124
161
  本サーバーには以下の最適化が組み込まれています。
125
162
 
126
163
  ### APIコール節約
127
- - **60秒キャッシュ**: 同じ会社を60秒以内に再検索した場合、APIを消費しません
164
+ - **5分キャッシュ**: 同じ会社を5分以内に再検索した場合、APIを消費しません
128
165
  - **キーマン詳細の並列取得**: 5件ずつ並列リクエストで高速化
129
166
 
130
167
  ### LLMトークン節約
@@ -32,7 +32,7 @@ export declare class BizGateClient {
32
32
  private cacheKey;
33
33
  /**
34
34
  * Company lookup (企業).
35
- * Cached for 60s. Billed even on no-result.
35
+ * Cached for 5 minutes. Billed even on no-result.
36
36
  */
37
37
  searchCompany(params: {
38
38
  shogo?: string;
@@ -43,7 +43,7 @@ export declare class BizGateClient {
43
43
  }): Promise<CompanyResult>;
44
44
  /**
45
45
  * Department lookup (部署).
46
- * Cached for 60s. Not billed on failure.
46
+ * Cached for 5 minutes. Not billed on failure.
47
47
  */
48
48
  searchDepartments(params: {
49
49
  shogo?: string;
@@ -55,7 +55,7 @@ export declare class BizGateClient {
55
55
  }): Promise<DepartmentResult>;
56
56
  /**
57
57
  * Marketing tags lookup (マーケティングタグ).
58
- * Cached for 60s. Not billed on failure.
58
+ * Cached for 5 minutes. Not billed on failure.
59
59
  */
60
60
  searchMarketingTags(params: {
61
61
  shogo?: string;
@@ -63,7 +63,7 @@ export declare class BizGateClient {
63
63
  }): Promise<MarketingResult>;
64
64
  /**
65
65
  * Keyman lookup (キーマン・人名なし).
66
- * Cached for 60s. Not billed on failure.
66
+ * Cached for 5 minutes. Not billed on failure.
67
67
  */
68
68
  searchKeyman(params: {
69
69
  shogo?: string;
@@ -8,6 +8,12 @@ export function first(field) {
8
8
  }
9
9
  // Cache TTL: 5 minutes — same company searched within 5min won't hit the API again
10
10
  const CACHE_TTL = 300_000;
11
+ /** BizGate error codes that do NOT consume a billable request. */
12
+ const NON_BILLABLE_ERRORS = new Set(["404", "504", "604"]);
13
+ /** Pick only defined (non-null, non-undefined) string values from an object. */
14
+ function pickDefined(obj) {
15
+ return Object.fromEntries(Object.entries(obj).filter((entry) => entry[1] != null));
16
+ }
11
17
  export class BizGateClient {
12
18
  config;
13
19
  usageTracker;
@@ -62,7 +68,7 @@ export class BizGateClient {
62
68
  }
63
69
  /**
64
70
  * Company lookup (企業).
65
- * Cached for 60s. Billed even on no-result.
71
+ * Cached for 5 minutes. Billed even on no-result.
66
72
  */
67
73
  async searchCompany(params) {
68
74
  const key = this.cacheKey(params);
@@ -70,17 +76,7 @@ export class BizGateClient {
70
76
  if (cached)
71
77
  return cached;
72
78
  this.usageTracker.increment();
73
- const queryParams = {};
74
- if (params.shogo)
75
- queryParams.shogo = params.shogo;
76
- if (params.compno)
77
- queryParams.compno = params.compno;
78
- if (params.hpurl)
79
- queryParams.hpurl = params.hpurl;
80
- if (params.email)
81
- queryParams.email = params.email;
82
- if (params.tel)
83
- queryParams.tel = params.tel;
79
+ const queryParams = pickDefined(params);
84
80
  const result = await this.request(this.config.skeyCompany, queryParams);
85
81
  const data = {
86
82
  matchPattern: result.responseHeader.matchpatern ?? "",
@@ -91,7 +87,7 @@ export class BizGateClient {
91
87
  }
92
88
  /**
93
89
  * Department lookup (部署).
94
- * Cached for 60s. Not billed on failure.
90
+ * Cached for 5 minutes. Not billed on failure.
95
91
  */
96
92
  async searchDepartments(params) {
97
93
  const key = this.cacheKey(params);
@@ -99,30 +95,26 @@ export class BizGateClient {
99
95
  if (cached)
100
96
  return cached;
101
97
  this.usageTracker.increment();
102
- const queryParams = {};
103
- if (params.shogo)
104
- queryParams.shogo = params.shogo;
105
- if (params.compno)
106
- queryParams.compno = params.compno;
107
- if (params.pList)
108
- queryParams.pList = params.pList;
109
- if (params.cList)
110
- queryParams.cList = params.cList;
111
- if (params.bKwd)
112
- queryParams.bKwd = params.bKwd;
113
- if (params.bKOpr)
114
- queryParams.bKOpr = params.bKOpr;
115
- const result = await this.request(this.config.skeyDepartment, queryParams);
116
- const data = {
117
- docs: result.response?.docs ?? [],
118
- numFound: result.response?.numFound ?? 0,
119
- };
120
- this.departmentCache.set(key, data);
121
- return data;
98
+ try {
99
+ const queryParams = pickDefined(params);
100
+ const result = await this.request(this.config.skeyDepartment, queryParams);
101
+ const data = {
102
+ docs: result.response?.docs ?? [],
103
+ numFound: result.response?.numFound ?? 0,
104
+ };
105
+ this.departmentCache.set(key, data);
106
+ return data;
107
+ }
108
+ catch (e) {
109
+ if (e instanceof BizGateApiError && NON_BILLABLE_ERRORS.has(e.code)) {
110
+ this.usageTracker.decrement();
111
+ }
112
+ throw e;
113
+ }
122
114
  }
123
115
  /**
124
116
  * Marketing tags lookup (マーケティングタグ).
125
- * Cached for 60s. Not billed on failure.
117
+ * Cached for 5 minutes. Not billed on failure.
126
118
  */
127
119
  async searchMarketingTags(params) {
128
120
  const key = this.cacheKey(params);
@@ -130,21 +122,25 @@ export class BizGateClient {
130
122
  if (cached)
131
123
  return cached;
132
124
  this.usageTracker.increment();
133
- const queryParams = {};
134
- if (params.shogo)
135
- queryParams.shogo = params.shogo;
136
- if (params.compno)
137
- queryParams.compno = params.compno;
138
- const result = await this.request(this.config.skeyMarketing, queryParams);
139
- const data = {
140
- docs: result.response?.docs ?? [],
141
- };
142
- this.marketingCache.set(key, data);
143
- return data;
125
+ try {
126
+ const queryParams = pickDefined(params);
127
+ const result = await this.request(this.config.skeyMarketing, queryParams);
128
+ const data = {
129
+ docs: result.response?.docs ?? [],
130
+ };
131
+ this.marketingCache.set(key, data);
132
+ return data;
133
+ }
134
+ catch (e) {
135
+ if (e instanceof BizGateApiError && NON_BILLABLE_ERRORS.has(e.code)) {
136
+ this.usageTracker.decrement();
137
+ }
138
+ throw e;
139
+ }
144
140
  }
145
141
  /**
146
142
  * Keyman lookup (キーマン・人名なし).
147
- * Cached for 60s. Not billed on failure.
143
+ * Cached for 5 minutes. Not billed on failure.
148
144
  */
149
145
  async searchKeyman(params) {
150
146
  const key = this.cacheKey(params);
@@ -152,26 +148,22 @@ export class BizGateClient {
152
148
  if (cached)
153
149
  return cached;
154
150
  this.usageTracker.increment();
155
- const queryParams = {};
156
- if (params.shogo)
157
- queryParams.shogo = params.shogo;
158
- if (params.compno)
159
- queryParams.compno = params.compno;
160
- if (params.pList)
161
- queryParams.pList = params.pList;
162
- if (params.cList)
163
- queryParams.cList = params.cList;
164
- if (params.bKwd)
165
- queryParams.bKwd = params.bKwd;
166
- if (params.bKOpr)
167
- queryParams.bKOpr = params.bKOpr;
168
- const result = await this.request(this.config.skeyKeyman, queryParams);
169
- const data = {
170
- docs: result.response?.docs ?? [],
171
- numFound: result.response?.numFound ?? 0,
172
- };
173
- this.keymanCache.set(key, data);
174
- return data;
151
+ try {
152
+ const queryParams = pickDefined(params);
153
+ const result = await this.request(this.config.skeyKeyman, queryParams);
154
+ const data = {
155
+ docs: result.response?.docs ?? [],
156
+ numFound: result.response?.numFound ?? 0,
157
+ };
158
+ this.keymanCache.set(key, data);
159
+ return data;
160
+ }
161
+ catch (e) {
162
+ if (e instanceof BizGateApiError && NON_BILLABLE_ERRORS.has(e.code)) {
163
+ this.usageTracker.decrement();
164
+ }
165
+ throw e;
166
+ }
175
167
  }
176
168
  /**
177
169
  * Keyman with name lookup (キーマン・人名あり).
@@ -186,7 +178,10 @@ export class BizGateClient {
186
178
  });
187
179
  return result.response?.docs?.[0] ?? null;
188
180
  }
189
- catch {
181
+ catch (e) {
182
+ if (e instanceof BizGateApiError && NON_BILLABLE_ERRORS.has(e.code)) {
183
+ this.usageTracker.decrement();
184
+ }
190
185
  return null;
191
186
  }
192
187
  };
package/dist/index.js CHANGED
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ // ---------- --install-skill サブコマンド ----------
3
+ if (process.argv.includes("--install-skill")) {
4
+ const { main: installSkill } = await import("./install-skill.js");
5
+ installSkill();
6
+ process.exit(0);
7
+ }
2
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
10
  import { z } from "zod";
@@ -6,7 +12,9 @@ import { BizGateClient, first as f } from "./bizgate-client.js";
6
12
  import { BizGateApiError, DEPARTMENT_CATEGORIES } from "./types.js";
7
13
  import { UsageTracker } from "./usage-tracker.js";
8
14
  import { homedir } from "node:os";
9
- import { join } from "node:path";
15
+ import { join, dirname } from "node:path";
16
+ import { readFileSync } from "node:fs";
17
+ import { fileURLToPath } from "node:url";
10
18
  // ---------- 環境変数読み込み ----------
11
19
  const skeyCompany = process.env.BIZGATE_SKEY_COMPANY;
12
20
  const skeyDepartment = process.env.BIZGATE_SKEY_DEPARTMENT;
@@ -50,9 +58,11 @@ const config = {
50
58
  const usageTracker = new UsageTracker(usageFile, dailyLimit);
51
59
  const client = new BizGateClient(config, usageTracker);
52
60
  // ---------- MCPサーバー ----------
61
+ const __dirname = dirname(fileURLToPath(import.meta.url));
62
+ const pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8"));
53
63
  const server = new McpServer({
54
64
  name: "bizgate-mcp-server",
55
- version: "0.3.0",
65
+ version: pkg.version,
56
66
  });
57
67
  // ---------- ヘルパー ----------
58
68
  function usageFooter() {
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export declare function main(): void;
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ import { mkdirSync, writeFileSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { homedir } from "node:os";
5
+ const SKILL_DIR = join(homedir(), ".claude", "skills", "prospect-match");
6
+ const SKILL_MD = `---
7
+ name: prospect-match
8
+ description: |
9
+ 企業の部署リストとDigiManサービスをマッチングし、営業ターゲット部署を提案する。
10
+ "売れそうな部署", "部署マッチング", "prospect", "ターゲット部署", "영업 타겟",
11
+ "DigiManのサービスが売れそうな", "サービスが売れそう", "営業代行", "Solution", "AI",
12
+ "どの部署に売れる", "ターゲット探して", "部署を探して"
13
+ user-invocable: true
14
+ argument-hint: "[会社名] [サービス種別(営業代行/Solution/AI/全部)]"
15
+ allowed-tools: mcp__bizgate__bizgate__department_search, mcp__bizgate__bizgate__keyman_search, mcp__bizgate__bizgate__company_search, Read, Grep, Glob, AskUserQuestion
16
+ ---
17
+
18
+ # Prospect Match: 企業部署 × DigiManサービス マッチング
19
+
20
+ 企業名を受け取り、BizGate APIで部署情報を取得し、DigiManのサービスに適した営業ターゲット部署を提案するスキルです。
21
+
22
+ ## DigiManサービス定義
23
+
24
+ ### 1. 営業代行
25
+ - **内容**: 営業チームのKPIダッシュボード、架電/アポ進捗管理、担当者別パフォーマンス分析
26
+ - **ターゲット部署の特徴**: 営業組織を持つ部署、営業企画、営業管理、営業推進
27
+ - **マッチキーワード**: 営業, 販売, 販促, 商品, 事業開発, マーケティング, 顧客
28
+
29
+ ### 2. Solution
30
+ - **内容**: 内部管理システム(DB・データ分析・ダッシュボード構築)
31
+ - **ターゲット部署の特徴**: DX推進、経営企画、総務、システム、業務改善担当
32
+ - **マッチキーワード**: ICT, システム, DX, 情報, 企画, 経営企画, 総務, 管理, イノベーション, デジタル
33
+
34
+ ### 3. AI
35
+ - **内容**: Claude × BizGate連携MCP。自然言語で企業情報を検索・取得
36
+ - **ターゲット部署の特徴**: 新規事業、事業創造、戦略推進、営業企画
37
+ - **マッチキーワード**: 戦略, 成長, 事業創造, イノベーション, 企画, 開発, 推進, DX, AI, ICT
38
+
39
+ ## プロセス
40
+
41
+ ### Step 1: 入力を解析
42
+ - ユーザーの入力から **会社名** と **対象サービス** を特定する
43
+ - サービスが指定されていない場合は「全部」として3サービスすべてをマッチングする
44
+ - 会社名が不明な場合は \`AskUserQuestion\` で確認する
45
+
46
+ ### Step 2: 部署情報を取得
47
+ - \`bizgate__department_search\` で部署一覧を取得する(limit: 200)
48
+ - 電話番号がある部署は電話番号も含める
49
+
50
+ ### Step 3: サービスとマッチング
51
+ 各サービスについて、以下の基準で部署をマッチングする:
52
+
53
+ 1. **部署名にマッチキーワードが含まれるか** を確認
54
+ 2. **部署のカテゴリ(ctg_01〜15)** も参考にする:
55
+ - 営業代行 → ctg_02(営業企画), ctg_13(営業)
56
+ - Solution → ctg_01(経営企画), ctg_05(総務), ctg_10(システム)
57
+ - AI → ctg_01(経営企画), ctg_02(営業企画), ctg_10(システム)
58
+ 3. キーワードマッチだけでなく、**部署の役割から判断** して適切な部署を選ぶ
59
+
60
+ ### Step 4: 結果を出力
61
+ 以下のフォーマットで出力する:
62
+
63
+ ## [会社名] × DigiMan サービスマッチング
64
+
65
+ ### 1. 営業代行
66
+ | 部署名 | 電話番号 | マッチ理由 |
67
+ |--------|---------|----------|
68
+
69
+ ### 2. Solution
70
+ | 部署名 | 電話番号 | マッチ理由 |
71
+ |--------|---------|----------|
72
+
73
+ ### 3. AI
74
+ | 部署名 | 電話番号 | マッチ理由 |
75
+ |--------|---------|----------|
76
+
77
+ ### 最優先アプローチ先
78
+ [3サービスに共通して関連性が高い部署をピックアップ]
79
+
80
+ ## 注意事項
81
+ - 電話番号がない部署は「-」と表示する
82
+ - 各サービスにつき最大5部署まで提案する(多すぎると選べない)
83
+ - 明らかに関連のない部署(畜産、食品など事業部門)はスキップする
84
+ - マッチ理由は1行で簡潔に書く
85
+ - ユーザーの言語(日本語 or 韓国語)に合わせて出力する
86
+
87
+ ---
88
+
89
+ $ARGUMENTS
90
+ `;
91
+ export function main() {
92
+ const existed = existsSync(join(SKILL_DIR, "SKILL.md"));
93
+ mkdirSync(SKILL_DIR, { recursive: true });
94
+ writeFileSync(join(SKILL_DIR, "SKILL.md"), SKILL_MD, "utf-8");
95
+ if (existed) {
96
+ console.log("✔ prospect-match スキルを更新しました");
97
+ }
98
+ else {
99
+ console.log("✔ prospect-match スキルをインストールしました");
100
+ }
101
+ console.log(` 場所: ${SKILL_DIR}/SKILL.md`);
102
+ console.log("");
103
+ console.log("使い方:");
104
+ console.log(" /prospect-match 会社名");
105
+ console.log(' または「○○にDigiManのサービスが売れそうな部署を探して」');
106
+ }
107
+ // 直接実行された場合のみ実行
108
+ const isDirectRun = process.argv[1]?.endsWith("install-skill.js");
109
+ if (isDirectRun) {
110
+ main();
111
+ }
@@ -8,6 +8,10 @@ export declare class UsageTracker {
8
8
  private writeState;
9
9
  /** Increment counter before making an API call. Throws if limit exceeded. */
10
10
  increment(cost?: number): number;
11
+ /** Check if limit allows the call, but don't increment yet. Throws if limit exceeded. */
12
+ checkLimit(cost?: number): void;
13
+ /** Decrement counter (rollback on API failure). */
14
+ decrement(cost?: number): void;
11
15
  /** Remaining requests for today. */
12
16
  remaining(): number;
13
17
  /** Current usage count for today. */
@@ -36,6 +36,19 @@ export class UsageTracker {
36
36
  this.writeState(state);
37
37
  return state.count;
38
38
  }
39
+ /** Check if limit allows the call, but don't increment yet. Throws if limit exceeded. */
40
+ checkLimit(cost = 1) {
41
+ const state = this.readState();
42
+ if (state.count + cost > this.limit) {
43
+ throw new Error(`本日のAPI利用上限(${this.limit}回)に達しました。明日以降に再度お試しください。`);
44
+ }
45
+ }
46
+ /** Decrement counter (rollback on API failure). */
47
+ decrement(cost = 1) {
48
+ const state = this.readState();
49
+ state.count = Math.max(0, state.count - cost);
50
+ this.writeState(state);
51
+ }
39
52
  /** Remaining requests for today. */
40
53
  remaining() {
41
54
  const state = this.readState();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bizgate-mcp-server",
3
- "version": "0.3.1",
3
+ "version": "0.3.4",
4
4
  "description": "BizGate APIとClaudeを連携するMCPサーバー",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",