はじめに: MCP サーバーの通信方法
エコシステムの中で 22 台の MCP サーバー アクティブな場合、サーバー間のコラボレーションが不可欠になります。 単一サーバーは特定のドメイン (スクラム、時間追跡、CI/CD) を管理しますが、ワークフローは 実際には複数のドメインにまたがります: スプリントが終了すると、メトリクスを更新する必要があり、コストも を計算する必要がある場合は、ふりかえりを自動的に作成する必要があります。
テックMCP 実装する サーバー間通信のための 2 つの補完的なメカニズム:イベントバス (パブ/サブ 非同期ファイアアンドフォーゲット) クライアントマネージャー (同期 RPC 呼び出しと呼ばれます)。一緒に、 直接結合せずにスイート全体を横断するエンドツーエンドのフローを構築できます。 サーバー間。
この記事で学べること
- MCP Suite の EventBus アーキテクチャとパブリッシュ/サブスクライブ パターン
- 29 種類のイベントを 11 のドメインに編成
- コラボレーション マトリックス: 誰が公開し、誰が購読するのか
- サーバー間の同期 RPC 呼び出し用の ClientManager
- サーバーの分類: 双方向、パブリッシャのみ、サブスクライバのみ、パススルー
- エンドツーエンドのフロー: スプリントのライフサイクル、時間とコスト、DevOps、品質
- ワイルドカード サブスクリプションのマイクロマッチを使用したパターン マッチング
EventBus: 非同期コラボレーションの中心
L'イベントバス MCP Suite サーバーがシームレスに連携できるようにするメカニズム 非同期かつ分離された。サーバーが重要なアクション (スプリントの作成、 ログ時間、ビルド完了など)、他のサーバーがサブスクライブできる型指定されたイベントを発行します 自動的に反応するようにします。
Server A EventBus Server B
| | |
|-- publish(evento) ------->| |
| |-- handler(payload) ------>|
| | |
| (fire-and-forget) | (delivery garantita |
| | in-process) |
このパターンは次のように知られています 発行/購読 そして次のような基本的な利点を提供します。
- デカップリング: 発行者は購読者を知りません
- 拡張性: 新しいサブスクライバーを追加する場合、パブリッシャーを変更する必要はありません。
- 任意性: EventBus は常にオプションです。すべてのサーバーはそれなしでも完璧に動作します
イベントバスインターフェース
インターフェース EventBus イベント バス実装のコントラクトを定義します。
interface EventBus {
publish<E extends EventName>(event: E, payload: EventPayload<E>): Promise<void>;
subscribe<E extends EventName>(event: E, handler: EventHandler<E>): () => void;
subscribePattern(pattern: string, handler: PatternHandler): () => void;
clear(): void;
}
EventBus メソッド
| 方法 | 説明 |
|---|---|
publish |
型指定されたイベントとそのペイロードを発行します |
subscribe |
特定のイベントを購読します。購読解除関数が返す |
subscribePattern |
glob パターンに一致するイベントをサブスクライブします (例: scrum:*) |
clear |
すべてのサブスクリプションを削除します |
LocalEventBus: インプロセス実装
クラス LocalEventBus Node.js に基づくデフォルトの実装
EventEmitter パターンサポート付き micromatch:
import { EventEmitter } from "node:events";
import micromatch from "micromatch";
interface PatternSubscription {
pattern: string;
handler: PatternHandler;
}
export class LocalEventBus implements EventBus {
private emitter = new EventEmitter();
private patternSubs: PatternSubscription[] = [];
constructor() {
this.emitter.setMaxListeners(100);
}
async publish(event: string, payload: unknown): Promise<void> {
// Notifica subscriber esatti
this.emitter.emit(event, payload);
// Notifica subscriber pattern
for (const sub of this.patternSubs) {
if (micromatch.isMatch(event, sub.pattern)) {
try {
await sub.handler(event, payload);
} catch {
// Errori nei subscriber non bloccano la pubblicazione
}
}
}
}
subscribe(event: string, handler: EventHandler): () => void {
this.emitter.on(event, handler);
return () => this.emitter.off(event, handler);
}
subscribePattern(pattern: string, handler: PatternHandler): () => void {
const sub: PatternSubscription = { pattern, handler };
this.patternSubs.push(sub);
return () => {
const index = this.patternSubs.indexOf(sub);
if (index >= 0) this.patternSubs.splice(index, 1);
};
}
clear(): void {
this.emitter.removeAllListeners();
this.patternSubs = [];
}
}
主な特徴: イベントごとに最大 100 人のリスナー、パターン マッチングによる
micromatch ワイルドカード (scrum:*, *:completed)、エラー
パブリッシュを中断しないようにサイレントにパターン ハンドラーをキャプチャし、依存関係をゼロにします。
外部を超えて micromatch.
EventMap を使用して入力を完了する
マップのおかげで、各イベントは名前とペイロードの両方で型付けされます EventMap。
TypeScript コンパイラは、ペイロードが正しいかどうかをコンパイル時にチェックします。
// Il compilatore TypeScript verifica che il payload sia corretto
eventBus.publish('scrum:sprint-started', {
sprintId: '42',
name: 'Sprint 15',
startDate: '2025-01-01',
endDate: '2025-01-14',
});
// Errore di compilazione: manca il campo 'name'
eventBus.publish('scrum:sprint-started', {
sprintId: '42',
});
11 のドメインに分類された 29 のイベント
MCP スイートの定義 29 の典型的なイベント に組織される 11 ドメイン。毎
イベントはフォーマットに従います dominio:azione-in-kebab-case TypeScript ペイロードを持っています
強く類型化されている。
ドメインマップ
| ドメイン | 番号 イベント | 機能エリア |
|---|---|---|
code:* |
3 | コードとGit |
cicd:* |
2 | CI/CD |
scrum:* |
4 | プロジェクト管理 |
time:* |
2 | 時間の追跡 |
db:* |
2 | データベース |
test:* |
2 | テスト |
docs:* |
2 | ドキュメント |
perf:* |
2 | パフォーマンス |
retro:* |
2 | 回顧展 |
economics:* |
2 | プロジェクトエコノミー |
standup:* |
1 | スタンドアップ |
| 合計 | 29 |
イベントの完全なリファレンス
サーバーとトリガーを備えた 29 のイベントすべて
| ドメイン | イベント | 出版社 | トリガー (ツール) |
|---|---|---|---|
| キュー | code:commit-analyzed |
コードレビュー | analyze-diff |
code:review-completed |
コードレビュー | suggest-improvements |
|
code:dependency-alert |
依存関係マネージャー | check-vulnerabilities |
|
| シコッド | cicd:pipeline-completed |
cicd-モニター | get-pipeline-status |
cicd:build-failed |
cicd-モニター | get-pipeline-status |
|
| スクラム | scrum:sprint-started |
スクラムボード | create-sprint |
scrum:sprint-completed |
スクラムボード | close-sprint |
|
scrum:task-updated |
スクラムボード | update-task-status |
|
scrum:story-completed |
スクラムボード | close-sprint |
|
| 時間 | time:entry-logged |
時間の追跡 | log-time, stop-timer |
time:timesheet-generated |
時間の追跡 | get-timesheet |
|
| db | db:schema-changed |
データベーススキーマエクスプローラー | explore-schema |
db:index-suggestion |
データベーススキーマエクスプローラー | suggest-indexes |
|
| テスト | test:generated |
テストジェネレーター | generate-unit-tests |
test:coverage-report |
テストジェネレーター | analyze-coverage |
|
| ドキュメント | docs:api-updated |
APIドキュメント | extract-endpoints |
docs:stale-detected |
APIドキュメント | find-undocumented |
|
| 完璧 | perf:bottleneck-found |
パフォーマンスプロファイラー | find-bottlenecks |
perf:profile-completed |
パフォーマンスプロファイラー | benchmark-compare |
|
| 戻る | retro:action-item-created |
振り返りマネージャー | generate-action-items |
retro:completed |
振り返りマネージャー | (遡及終了) | |
| 経済 | economics:budget-alert |
プロジェクトの経済学 | get-budget-status |
economics:cost-updated |
プロジェクトの経済学 | log-cost |
|
| 立ち上がる | standup:report-generated |
スタンドアップノート | log-standup |
型付きペイロードの例
各イベントには特定の TypeScript ペイロードがあります。それぞれの代表的な例をいくつか挙げておきます メインドメイン:
// scrum:sprint-completed
{
sprintId: string;
name: string;
velocity: number;
completedStories: number;
incompleteStories: number;
}
// time:entry-logged
{
taskId: string;
userId: string;
minutes: number;
date: string; // ISO 8601
}
// cicd:build-failed
{
pipelineId: string;
error: string;
stage: string;
branch: string;
}
// economics:budget-alert
{
project: string;
percentUsed: number;
threshold: number; // es. 80
remaining: number;
}
// code:dependency-alert
{
package: string;
severity: 'critical' | 'high' | 'medium' | 'low';
advisory: string;
}
サーバー統合パターン
EventBus のサーバーへの統合は、標準化された 4 ステップのパターンに従います。 責任の分離と完全な任意性。
1. EventBus (index.ts) の作成
// servers/<nome>/src/index.ts
import { LocalEventBus } from '@mcp-suite/event-bus';
const eventBus = new LocalEventBus();
const suite = createMyServer(eventBus);
2. サーバー (server.ts) へのインジェクション
export function createMyServer(eventBus?: EventBus): McpSuiteServer {
const suite = createMcpServer({
name: 'my-server',
version: '0.1.0',
eventBus,
});
const store = new MyStore();
// Tool che pubblica eventi
registerCreateItem(suite.server, store, suite.eventBus);
// Tool read-only: non serve l'eventBus
registerListItems(suite.server, store);
// Collaboration handlers
if (suite.eventBus) {
setupCollaborationHandlers(suite.eventBus, store);
}
return suite;
}
3. ツール ハンドラーでの公開 (tools/*.ts)
export function registerCreateItem(
server: McpServer,
store: MyStore,
eventBus?: EventBus,
): void {
server.tool('create-item', 'Create a new item', schema, async (args) => {
const item = store.create(args);
// Fire-and-forget: eventBus può essere undefined
eventBus?.publish('domain:item-created', {
itemId: item.id,
// ...payload tipizzato
});
return { content: [{ type: 'text', text: JSON.stringify(item) }] };
});
}
4. サブスクリプション (collaboration.ts)
export function setupCollaborationHandlers(
eventBus: EventBus,
store: MyStore,
): void {
eventBus.subscribe('other-domain:event', (payload) => {
// Reagisci all'evento
store.updateSomething(payload);
});
}
EventBus の設計原則
- ファイアアンドフォーゲット:
eventBus?.publish()オプションのチェーンを使用すると、未定義のケースが処理されます。使用されていませんawait - ミュータントツールのみが公開されます: 作成、更新、削除ツールはイベントを公開します。読み取りツールなし
- 孤立したコラボレーション: イベントに対する反応のロジックは常に存在します。
collaboration.ts、ツール ハンドラーでは決して使用しない - コンテンツエラー: サブスクライバーの障害がパブリッシャーに影響を与えることはありません。
マイクロマッチによるパターンマッチング
方法 subscribePattern を使用してイベントのグループを購読できます。
glob パターン、ライブラリのおかげで micromatch:
// Tutti gli eventi del dominio scrum
eventBus.subscribePattern('scrum:*', (event, payload) => {
console.log(`Scrum event: ${event}`, payload);
});
// Tutti gli eventi di completamento
eventBus.subscribePattern('*:*-completed', (event, payload) => {
console.log(`Completed: ${event}`, payload);
});
// Workflow orchestrator: intercetta TUTTI gli eventi
eventBus.subscribePattern('*', async (event, payload) => {
const workflows = store.getActiveWorkflowsByTrigger(event);
for (const workflow of workflows) {
if (matchesConditions(workflow.triggerConditions, payload)) {
await executeWorkflow(workflow, payload);
}
}
});
コラボレーション マトリックス: パブリッシャーとサブスクライバー
コラボレーション マトリックスには、スイート内の 22 台のサーバー間のすべてのイベント フローが文書化されています。毎 サーバーに分類され、イベント ネットワーク内での役割に応じて分類されます。
誰が何に署名するか
アクティブなサブスクリプション
| サブスクライバサーバー | サブスクライブされたイベント | 反応 |
|---|---|---|
| アジャイルメトリクス | scrum:sprint-completed |
速度データキャッシュ |
scrum:task-updated |
サイクルタイムによる遷移の追跡 | |
scrum:story-completed |
スループットの更新 | |
| 時間の追跡 | scrum:task-updated |
自動スタート/ストップタイマー (将来) |
| プロジェクトの経済学 | time:entry-logged |
時間をコストに変換する |
scrum:sprint-completed |
スプリントコストのスナップショット | |
| スクラムボード | retro:action-item-created |
自動作成タスク (将来) |
| 振り返りマネージャー | scrum:sprint-completed |
自作バックペンダント(未来) |
cicd:build-failed |
議論事項の追加(今後) | |
| スタンドアップノート | scrum:task-updated |
スタンドアップの進捗状況(将来) |
cicd:build-failed |
潜在的なブロッカーを追加しました (将来) |
サーバーの分類
スイート内の 22 台のサーバーは、ネットワーク内での役割に基づいて 4 つのカテゴリに分類されます。 コラボレーション:
双方向サーバー (パブリッシュ + サブスクライブ)
これら 5 つのサーバーはイベント ネットワークに両方向で積極的に参加します。
双方向サーバー
| サーバ | 公開 | 購読する | コラボレーション.ts ファイル |
|---|---|---|---|
| スクラムボード | 4つのイベント | 1件のイベント | Si |
| 時間の追跡 | 1件のイベント | 1件のイベント | Si |
| プロジェクトの経済学 | 2つのイベント | 2つのイベント | Si |
| 振り返りマネージャー | 1件のイベント | 2つのイベント | Si |
| スタンドアップノート | 1件のイベント | 2つのイベント | Si |
パブリッシャー専用サーバー (7 サーバー)
これらはイベントを生成しますが、外部イベントには反応しません。
- cicd-モニター (2 イベント): DevOps イベント ハブ
- コードレビュー (2 イベント): コード分析通知
- 依存関係マネージャー (1 イベント): 脆弱性警告
- データベーススキーマエクスプローラー (1 イベント): インデックスの提案
- テストジェネレーター (2 イベント): テスト生成通知
- APIドキュメント (2 イベント): ドキュメントの更新
- パフォーマンスプロファイラー (2イベント): パフォーマンスレポート
加入者専用サーバー (1 サーバー)
アジャイルメトリクス (3 つの受信イベント): スクラム イベントからメトリクスを計算します。 独自のイベントをツールから直接公開します。
パススルーサーバー (9 サーバー)
これらのサーバーは工場で EventBus を受け入れますが、現在はパブリッシュまたはサブスクライブしません。 将来の拡張に備えて準備が整っています。
- docker-compose、ログアナライザー、データモックジェネレーター
- コードベースの知識、正規表現ビルダー、http クライアント
- スニペットマネージャー、プロジェクト足場、環境マネージャー
ClientManager: サーバー間の同期 RPC 呼び出し
Il クライアントマネージャー そして 2 番目のサーバー間通信メカニズムです。とは異なります EventBus (非同期ファイアアンドフォーゲット) の ClientManager により、MCP サーバーは次のことを行うことができます。 別のサーバー上のツールを呼び出す そして結果を同期的に取得します (リクエスト/レスポンス)。
Server Chiamante Client Manager Server Target
| | |
|-- callTool('target', | |
| 'tool') ------------>| |
| |-- MCP JSON-RPC ------>|
| | |
| |<-- JSON-RPC response--|
|<-- risultato ----------| |
| | |
| (sincrono: il chiamante attende il risultato) |
EventBus と ClientManager の比較
| 特性 | EventBus (Pub/Sub) | クライアントマネージャー (RPC) |
|---|---|---|
| コミュニケーション | 非同期、ファイアアンドフォーゲット | 同期、リクエスト/レスポンス |
| 方向 | 1 対多 (ブロードキャスト) | 1対1(直接通話) |
| カップリング | なし (パブリッシャはサブスクライバを無視します) | 低 (サーバー名とツールを知っている) |
| 一般的な使用方法 | 通知、自動アップデート | データクエリ、結果の強化 |
| エラー | 無視 (発行者には影響しません) | グレースフル デグラデーションで管理 |
| レイテンシ | マイクロ秒 (プロセス中) | ミリ秒 |
| Esempio | 「スプリントが作成されました」 -> メトリクスを更新 | 「スプリントデータをください」 -> 速度を計算します |
経験則: いつでも EventBus を使用できます 通知する 他のサーバー、 ClientManager を持っている場合はそれを使用します データが必要です 別のサーバーから。
ClientManager を使用したクロスサーバー呼び出しの例
export function registerForecastBudget(
server: McpServer,
store: EconomicsStore,
clientManager?: McpClientManager,
): void {
server.tool(
'forecast-budget',
'Prevedi quando il budget si esaurira',
{
projectName: z.string(),
includeTimeData: z.boolean().optional().default(false),
},
async ({ projectName, includeTimeData }) => {
const forecast = store.forecastBudget(projectName);
const result: Record<string, unknown> = { ...forecast };
// Cross-server call: recupera dati dal time-tracking
if (includeTimeData && clientManager) {
const timeResult = await clientManager.callTool(
'time-tracking', // Server target
'get-timesheet', // Tool da invocare
{}, // Argomenti
);
const content = (timeResult as {
content: Array<{ type: string; text: string }>;
}).content;
const timesheet = JSON.parse(content[0].text);
result.laborAnalysis = {
trackedHours: timesheet.totalMinutes / 60,
estimatedLaborCost: (timesheet.totalMinutes / 60) * 50,
};
}
return {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
};
},
);
}
実装された 6 つの配線シナリオ
MCP Suite は、5 つの呼び出しサーバーと 3 つのサーバーを含む 6 つのクロスサーバー通信シナリオを実装します。 ターゲットサーバー:
クロスサーバー配線のシナリオ
| # | 発信者 | ターゲット | ツールターゲット | 範囲 |
|---|---|---|---|---|
| 1 | アジャイルメトリクス | スクラムボード | get-sprint |
実際のスプリントデータを使用した速度計算 |
| 2 | アジャイルメトリクス | 時間の追跡 | get-timesheet |
時間の追跡によるサイクルタイムの強化 |
| 3 | プロジェクトの経済学 | 時間の追跡 | get-timesheet |
タイムシートからの作業コストの計算 |
| 4 | データモックジェネレーター | データベーススキーマエクスプローラー | explore-schema |
実際のDBスキーマからのモック生成 |
| 5 | テストジェネレーター | コードベースの知識 | explain-module |
フォーム分析からのテスト生成 |
| 6 | スタンドアップノート | スクラムボード | sprint-board |
スプリントデータを含むステータスレポート |
グレースフルデグラデーション
配線の基本原理は、 優雅な劣化: すべてのツールが機能します ClientManager がなくても完全に機能します。配線は結果を豊かにしますが、決して必須ではありません。
// Il pattern if (param && clientManager) garantisce:
// 1. Senza clientManager: il tool funziona normalmente
// 2. Senza il parametro trigger: il wiring non viene attivato
// 3. Con entrambi: il risultato viene arricchito con dati esterni
if (enrichFromExternal && clientManager) {
// cross-server call
}
エンドツーエンドのフロー
コラボレーションの真の力は、複数のサーバーをまたぐエンドツーエンドのフローで現れます。 これらのフローは、一元的なオーケストレーションを必要とせず、イベントの自然な構成から生じます。
フロー 1: スプリント ライフ サイクル
[Creazione Sprint]
scrum-board:create-sprint
|
+-- scrum:sprint-started -----> (informativo, espansione futura)
[Lavoro durante lo Sprint]
scrum-board:update-task-status
|
+-- scrum:task-updated -------> agile-metrics (cycle time)
| +-> time-tracking (auto-timer futuro)
| +-> standup-notes (contesto standup)
[Chiusura Sprint]
scrum-board:close-sprint
|
+-- scrum:sprint-completed ---> agile-metrics (velocity)
| +-> project-economics (snapshot costo)
| +-> retrospective-mgr (auto-crea retro)
|
+-- scrum:story-completed ----> agile-metrics (throughput)
フロー 2: 時間とコストの追跡
[Registrazione Tempo]
time-tracking:log-time / stop-timer
|
+-- time:entry-logged --------> project-economics (conversione costo)
[Registrazione Costo Diretto]
project-economics:log-cost
|
+-- economics:cost-updated ---> (informativo)
[Verifica Budget]
project-economics:get-budget-status
|
+-- economics:budget-alert ---> (notifica se >80%)
フロー 3: DevOps サイクル
[Monitoraggio Pipeline]
cicd-monitor:get-pipeline-status
|
+-- cicd:pipeline-completed --> (informativo)
|
+-- cicd:build-failed -------> retrospective-mgr (item discussione)
+-> standup-notes (blocker)
フロー 4: 品質とレビュー
[Analisi Codice]
code-review:analyze-diff
|
+-- code:commit-analyzed -----> (informativo)
code-review:suggest-improvements
|
+-- code:review-completed ----> (informativo, codebase-knowledge futuro)
[Vulnerabilità]
dependency-manager:check-vulnerabilities
|
+-- code:dependency-alert ----> (informativo)
フロー 5: 遡及的および継続的改善
[Sprint Completato]
scrum:sprint-completed -----------> retrospective-mgr (auto-crea retro)
[Generazione Action Items]
retrospective-mgr:generate-action-items
|
+-- retro:action-item-created -> scrum-board (auto-crea task futuro)
完全なネットワーク図
完全なフローはネットワークを形成します。 スクラムボード 4 つのイベントを備えた中央ハブ
4 つの異なるサーバーに到達する送信メッセージ。 アジャイルメトリクス そして最大の加入者
3 つの入力イベントを使用します。 時間の追跡 e プロジェクトの経済学 彼らは形成します
チェーン: 記録された時間はイベントを通じて自動的にコストになります
time:entry-logged.
EventBus と配線のテスト
コラボレーションをテストするために、Tech-MCP は MockEventBus イベントを記録するもの
副作用なしで公開されました:
export class MockEventBus implements EventBus {
public published: Array<{ event: string; payload: unknown }> = [];
async publish(event: string, payload: unknown): Promise<void> {
this.published.push({ event, payload });
}
subscribe(): () => void { return () => {}; }
subscribePattern(): () => void { return () => {}; }
clear(): void { this.published = []; }
// Utility per asserzioni
wasPublished(eventName: string): boolean {
return this.published.some((e) => e.event === eventName);
}
}
// Uso nei test
it('should publish time:entry-logged event', async () => {
const eventBus = new MockEventBus();
const { server } = createTimeTrackingServer({
eventBus,
storeOptions: { inMemory: true },
});
const harness = await createTestHarness(server);
await harness.client.callTool({
name: 'log-time',
arguments: { taskId: 'TASK-1', durationMinutes: 60 },
});
expect(eventBus.wasPublished('time:entry-logged')).toBe(true);
});
サーバー間の配線の場合、テストでは次を使用します。 InMemoryTransport サーバーに接続するには
ネットワークなしでインプロセス。順序は重要です。ターゲット サーバーはトランスポートに接続する必要があります。
前に クライアントはすぐにメッセージを送信するため、
initialize 接続するとき。
今後の展開
コラボレーション ネットワークは成長するように設計されています。計画されている開発の中には、次のようなものがあります。
- Redis イベントバス: マルチプロセス展開の場合、別々のプロセスのサーバー間のコラボレーションが可能になります。
- 新しいイベント:
scrum:backlog-prioritized,time:timer-started/time:timer-stopped,deploy:release-created - コラボレーションハンドラーの完全な実装: 現在、多くのハンドラーには実装を待つプレースホルダーが含まれています
- ワークフロー オーケストレーター: を使用するサーバー
subscribePattern('*')複雑な複数ステップのフローを調整するため
結論
サーバー間のコラボレーションは、サーバーのコレクションを変換するアーキテクチャ層です。 独立したサーバーを 1 つにまとめた 統合されたエコシステム。 EventBus はデカップリングを提供します ClientManager によりデータの強化が可能になる一方で、自動通知と反応が可能になります。 同期クロスサーバークエリを通じて。
11 のドメインに編成された 29 の型別イベントにより、あらゆるアクションが連携できる協力的なネットワークが形成されます。 重要な場合、複数のサーバーで自動反応がトリガーされる可能性があります。サーバーの分類 双方向、パブリッシャ専用、サブスクライバ専用、およびパススルーでアーキテクチャを明確にします 将来の拡張を容易にします。
次の記事では、 専門的なテストと本番環境への展開、 テスト ハーネス、InMemoryTransport を使用して MCP サーバーをテストする方法、およびそれらを準備する方法を分析する 構成、ロギング、モニタリングを備えた運用環境。
完全なサーバー間コラボレーション コードはリポジトリで入手できます。 GitHub 上の Tech-MCP.







