bizgate-mcp-server 0.3.4 → 0.3.6
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/dist/index.js +135 -0
- package/dist/install-skill.js +17 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ if (authMode === "ip" && !process.env.BIZGATE_APP) {
|
|
|
37
37
|
process.exit(1);
|
|
38
38
|
}
|
|
39
39
|
const dailyLimit = Number(process.env.BIZGATE_DAILY_LIMIT ?? "200");
|
|
40
|
+
const seedApiUrl = process.env.BIZGATE_SEED_API_URL ?? "";
|
|
40
41
|
const usageFile = process.env.BIZGATE_USAGE_FILE ??
|
|
41
42
|
join(homedir(), ".bizgate-mcp-usage.json");
|
|
42
43
|
const baseUrl = process.env.BIZGATE_BASE_URL ??
|
|
@@ -420,6 +421,140 @@ server.tool("bizgate__usage_status", "本日のBizGate API残り利用回数を
|
|
|
420
421
|
],
|
|
421
422
|
};
|
|
422
423
|
});
|
|
424
|
+
// ---------- Tool 7: プロスペクトリスト(シードAPI連携) ----------
|
|
425
|
+
const REVENUE_CATEGORIES = {
|
|
426
|
+
1: "1. 1000万未満",
|
|
427
|
+
2: "2. 1000万-3000万未満",
|
|
428
|
+
3: "3. 3000万-1億未満",
|
|
429
|
+
4: "4. 1億-5億未満",
|
|
430
|
+
5: "5. 5億-10億未満",
|
|
431
|
+
6: "6. 10億-20億未満",
|
|
432
|
+
7: "7. 20億-100億未満",
|
|
433
|
+
8: "8. 100億以上",
|
|
434
|
+
};
|
|
435
|
+
const MAX_API_CALLS_HARD_LIMIT = 150;
|
|
436
|
+
async function fetchSeeds(params) {
|
|
437
|
+
const url = new URL("/search", seedApiUrl);
|
|
438
|
+
for (const [k, v] of Object.entries(params))
|
|
439
|
+
url.searchParams.set(k, v);
|
|
440
|
+
const res = await fetch(url);
|
|
441
|
+
if (!res.ok)
|
|
442
|
+
throw new Error(`seed-api エラー: HTTP ${res.status}`);
|
|
443
|
+
return (await res.json());
|
|
444
|
+
}
|
|
445
|
+
function revenueMatchesMin(revenue, minCategory) {
|
|
446
|
+
if (!revenue)
|
|
447
|
+
return false;
|
|
448
|
+
const match = revenue.match(/^(\d+)\./);
|
|
449
|
+
if (!match)
|
|
450
|
+
return false;
|
|
451
|
+
return Number(match[1]) >= minCategory;
|
|
452
|
+
}
|
|
453
|
+
server.tool("bizgate__prospect_list", "シードリスト(国税庁法人データ)から条件に合う企業を抽出し、BizGate APIで詳細を照会してプロスペクトリストを作成する。BIZGATE_SEED_API_URLの設定が必要。", {
|
|
454
|
+
pref: z.string().optional().describe("都道府県(例: 東京都)"),
|
|
455
|
+
keyword: z.string().optional().describe("会社名キーワード(部分一致)"),
|
|
456
|
+
industry: z.string().optional().describe("業種キーワード(例: IT,製造)。BizGateの業種情報でフィルタリング"),
|
|
457
|
+
minRevenue: z.number().optional().describe("最低売上カテゴリ番号(1=1000万未満, 2=1000万-3000万, 3=3000万-1億, 4=1億-5億, 5=5億-10億, 6=10億-20億, 7=20億-100億, 8=100億以上)"),
|
|
458
|
+
targetCount: z.number().optional().describe("取得したい企業数(デフォルト: 10、最大: 20)"),
|
|
459
|
+
maxApiCalls: z.number().optional().describe("このツール1回で使うAPI上限(デフォルト: 50、最大: 150)"),
|
|
460
|
+
}, async ({ pref, keyword, industry, minRevenue, targetCount: rawTarget, maxApiCalls: rawMax }) => {
|
|
461
|
+
if (!seedApiUrl) {
|
|
462
|
+
return {
|
|
463
|
+
content: [{ type: "text", text: "エラー: BIZGATE_SEED_API_URL が設定されていません。管理者に連絡してください。" }],
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const targetCount = Math.min(rawTarget ?? 10, 20);
|
|
467
|
+
const apiLimit = Math.min(rawMax ?? 50, MAX_API_CALLS_HARD_LIMIT);
|
|
468
|
+
const remaining = usageTracker.remaining();
|
|
469
|
+
const effectiveLimit = Math.min(apiLimit, remaining);
|
|
470
|
+
if (effectiveLimit < 5) {
|
|
471
|
+
return {
|
|
472
|
+
content: [{ type: "text", text: `エラー: 残りAPI回数が${remaining}回しかありません。明日以降にお試しください。${usageFooter()}` }],
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
// Step 1: シードAPIから候補取得
|
|
476
|
+
const seedParams = {
|
|
477
|
+
limit: String(targetCount * 5),
|
|
478
|
+
shuffle: "true",
|
|
479
|
+
};
|
|
480
|
+
if (pref)
|
|
481
|
+
seedParams.pref = pref;
|
|
482
|
+
if (keyword)
|
|
483
|
+
seedParams.keyword = keyword;
|
|
484
|
+
let seeds;
|
|
485
|
+
let seedTotal;
|
|
486
|
+
try {
|
|
487
|
+
const result = await fetchSeeds(seedParams);
|
|
488
|
+
seeds = result.results;
|
|
489
|
+
seedTotal = result.total;
|
|
490
|
+
}
|
|
491
|
+
catch (e) {
|
|
492
|
+
return {
|
|
493
|
+
content: [{ type: "text", text: `シードAPIへの接続に失敗しました: ${e instanceof Error ? e.message : "不明なエラー"}` }],
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
if (seeds.length === 0) {
|
|
497
|
+
return {
|
|
498
|
+
content: [{ type: "text", text: `条件に合う企業がシードリストに見つかりませんでした。${usageFooter()}` }],
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
// Step 2: BizGate APIで詳細照会 + フィルタリング
|
|
502
|
+
const matched = [];
|
|
503
|
+
let apiCalls = 0;
|
|
504
|
+
const batchSize = 5;
|
|
505
|
+
for (let i = 0; i < seeds.length && matched.length < targetCount && apiCalls < effectiveLimit; i += batchSize) {
|
|
506
|
+
const batch = seeds.slice(i, Math.min(i + batchSize, seeds.length));
|
|
507
|
+
const remaining2 = effectiveLimit - apiCalls;
|
|
508
|
+
const actualBatch = batch.slice(0, remaining2);
|
|
509
|
+
const results = await Promise.all(actualBatch.map(async (seed) => {
|
|
510
|
+
try {
|
|
511
|
+
const { docs } = await client.searchCompany({ compno: seed.compno });
|
|
512
|
+
apiCalls++;
|
|
513
|
+
return { seed, doc: docs[0] ?? null };
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
apiCalls++;
|
|
517
|
+
return { seed, doc: null };
|
|
518
|
+
}
|
|
519
|
+
}));
|
|
520
|
+
for (const { seed, doc } of results) {
|
|
521
|
+
if (!doc)
|
|
522
|
+
continue;
|
|
523
|
+
// 売上フィルタ
|
|
524
|
+
if (minRevenue && !revenueMatchesMin(doc.revenue, minRevenue))
|
|
525
|
+
continue;
|
|
526
|
+
// 業種フィルタ
|
|
527
|
+
if (industry) {
|
|
528
|
+
const docIndustry = f(doc.gyoshu_facet);
|
|
529
|
+
const keywords = industry.split(",").map((k) => k.trim());
|
|
530
|
+
if (!keywords.some((k) => docIndustry.includes(k)))
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
matched.push({ doc, seed });
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
// Step 3: 結果フォーマット
|
|
537
|
+
if (matched.length === 0) {
|
|
538
|
+
return {
|
|
539
|
+
content: [{
|
|
540
|
+
type: "text",
|
|
541
|
+
text: `条件に合う企業が見つかりませんでした。\n(シード候補: ${seeds.length}社 / API照会: ${apiCalls}回 / シード全体: ${seedTotal}社)${usageFooter()}`,
|
|
542
|
+
}],
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
const header = `## プロスペクトリスト(${matched.length}社)\n\n`;
|
|
546
|
+
const table = `| # | 会社名 | 業種 | 売上 | 従業員 | 所在地 | HP |\n|---|--------|------|------|--------|--------|----|\n` +
|
|
547
|
+
matched
|
|
548
|
+
.map((m, i) => {
|
|
549
|
+
const d = m.doc;
|
|
550
|
+
return `| ${i + 1} | ${f(d.shogo)} | ${f(d.gyoshu_facet)} | ${d.revenue ?? "-"} | ${d.emp ?? "-"} | ${f(d.add)} | ${f(d.hpurl) || "-"} |`;
|
|
551
|
+
})
|
|
552
|
+
.join("\n");
|
|
553
|
+
const stats = `\n\n---\nシード候補: ${seeds.length}社(全${seedTotal}社中) / API照会: ${apiCalls}回 / マッチ: ${matched.length}社`;
|
|
554
|
+
return {
|
|
555
|
+
content: [{ type: "text", text: header + table + stats + usageFooter() }],
|
|
556
|
+
};
|
|
557
|
+
});
|
|
423
558
|
// ---------- 起動 ----------
|
|
424
559
|
async function main() {
|
|
425
560
|
const transport = new StdioServerTransport();
|
package/dist/install-skill.js
CHANGED
|
@@ -43,19 +43,23 @@ allowed-tools: mcp__bizgate__bizgate__department_search, mcp__bizgate__bizgate__
|
|
|
43
43
|
- サービスが指定されていない場合は「全部」として3サービスすべてをマッチングする
|
|
44
44
|
- 会社名が不明な場合は \`AskUserQuestion\` で確認する
|
|
45
45
|
|
|
46
|
-
### Step 2:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
### Step 2: サービスごとに bKwd で部署を検索
|
|
47
|
+
\`bizgate__department_search\` の \`bKwd\`(フリーワード部分一致)を使い、サービスごとに関連部署だけを取得する。
|
|
48
|
+
**3サービスの検索は並列で実行すること。**
|
|
49
|
+
|
|
50
|
+
#### 営業代行の検索
|
|
51
|
+
bKwd: "営業,販売,販促,マーケティング,事業開発,顧客"
|
|
52
|
+
|
|
53
|
+
#### Solutionの検索
|
|
54
|
+
bKwd: "ICT,システム,DX,情報,企画,総務,イノベーション,デジタル"
|
|
55
|
+
|
|
56
|
+
#### AIの検索
|
|
57
|
+
bKwd: "戦略,成長,事業創造,イノベーション,企画,開発,推進,DX,AI,ICT"
|
|
58
|
+
|
|
59
|
+
### Step 3: 結果を精査
|
|
60
|
+
- 各検索結果から **明らかに関連のない部署を除外** する(例:「食品企画部」は営業代行には不適切)
|
|
61
|
+
- 重複する部署は統合し、どのサービスに関連するか整理する
|
|
62
|
+
- 各サービスにつき **最大5部署** に絞る
|
|
59
63
|
|
|
60
64
|
### Step 4: 結果を出力
|
|
61
65
|
以下のフォーマットで出力する:
|