
自作MCPサーバーを.mcpbにしてClaude Desktopにワンクリックで入れてみた
はじめに
こんにちは、AI事業本部・生成AIインテグレーション部・西日本開発チームの政岡です。
自分で書いたMCPサーバーを人に配るときに、claude_desktop_config.json の場所を説明するところから始めてげんなりしたことはないでしょうか。
パスの区切りがWindowsで違うとか、JSONのカンマが足りないとか、本題と関係ないところで時間が溶けます。
これを解消する配布形式がMCPBです。
今回は現在時刻を返すだけのMCPサーバーを作って、.mcpb ファイルにし、Claude Desktopに導入するところまで試してみました。
MCPBとは
MCPBはMCP Bundlesの略で、ローカルで動くMCPサーバーを1つのファイルにまとめて配布するための形式です。
Chrome拡張の .crx やVS Codeの .vsix と同じ発想で、対応するクライアントに .mcpb を渡すとワンクリックで導入できます。
利用者が claude_desktop_config.json を編集する必要はありません。
もともとDXT(Desktop Extensions)という名前で拡張子も .dxt でした。2025年6月に発表されましたが、2025年9月にMCPBへ改称されたみたいです。
Claude Desktopは今も .dxt を受け付けるので、既存のバンドルが急に動かなくなるわけではないみたいです。
前提
| 項目 | バージョン |
|---|---|
| OS | macOS 26.5.1 (Apple Silicon) |
| Node.js | 22.22.0 |
| pnpm | 10.29.2 |
| @anthropic-ai/mcpb | 2.1.2 |
| @modelcontextprotocol/sdk | 1.30.0 |
| zod | 4.4.3 |
| tsx | 4.23.11 |
| esbuild | 0.28.2 |
| Claude Desktop | 1.25927.0 |
セットアップ
pnpm add -g @anthropic-ai/mcpb@2.1.2
mkdir mcpb-demo && cd mcpb-demo
pnpm init
pnpm add @modelcontextprotocol/sdk@1.30.0 zod@4.4.3
pnpm add -D tsx esbuild
@modelcontextprotocol/sdk がESM専用なので、package.json に "type": "module" を足しておきます。
{
"name": "mcpb-demo",
"version": "1.0.0",
"type": "module",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "1.30.0",
"zod": "4.4.3"
},
"devDependencies": {
"esbuild": "^0.28.2",
"tsx": "^4.23.11"
}
}
やってみた
サーバーを書く
get_current_time だけを持つMCPサーバーです。
// server/index.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({ name: 'mcpb-demo', version: '1.0.0' });
server.registerTool(
'get_current_time',
{
title: '現在時刻を返す',
description: '指定したタイムゾーンの現在時刻を返します',
inputSchema: { timeZone: z.string().default('Asia/Tokyo') },
},
async ({ timeZone }) => ({
content: [{ type: 'text', text: new Date().toLocaleString('ja-JP', { timeZone }) }],
}),
);
await server.connect(new StdioServerTransport());
標準入出力にJSON-RPCを流して動作を見ておきます。
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_current_time","arguments":{}}}' \
| pnpm exec tsx server/index.ts
{"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"mcpb-demo","version":"1.0.0"}},"jsonrpc":"2.0","id":1}
{"result":{"content":[{"type":"text","text":"2026/8/11 11:20:58"}]},"jsonrpc":"2.0","id":2}
現在時刻が返ってきました。MCPサーバーとしては正しく動いています。
manifest.jsonを作る
mcpb init を実行すると、雛形の manifest.json が出力されます。-y を付けると対話をスキップします。
mcpb init -y
{
"manifest_version": "0.2",
"name": "mcpb-demo",
"version": "1.0.0",
"description": "A MCPB bundle",
"author": {
"name": "Unknown Author"
},
"server": {
"type": "node",
"entry_point": "server/index.js",
"mcp_config": {
"command": "node",
"args": [
"${__dirname}/server/index.js"
],
"env": {}
}
},
"license": "MIT"
}
entry_point はバンドル内の相対パスです。指すのは .ts ではなく変換後の .js で、Claude Desktopは node で実行するだけなので、TypeScriptのままでは動きません。
mcp_config はClaude Desktopが実際に叩くコマンドです。${__dirname} は展開先ディレクトリに置き換わります。
自動生成された manifest.json に display_name と tools、compatibility を追記し、description と author を書き換えたのがこちらです。
{
"manifest_version": "0.2",
"name": "mcpb-demo",
"display_name": "MCPB Demo",
"version": "1.0.0",
"description": "現在時刻を返すだけのサンプルMCPサーバー",
"author": { "name": "masaoka" },
"server": {
"type": "node",
"entry_point": "server/index.js",
"mcp_config": {
"command": "node",
"args": ["${__dirname}/server/index.js"],
"env": {}
}
},
"tools": [
{ "name": "get_current_time", "description": "指定したタイムゾーンの現在時刻を返します" }
],
"compatibility": { "runtimes": { "node": ">=18" } },
"license": "MIT"
}
display_name はClaude Desktopの拡張機能一覧に出る表示名です。description と author は必須項目なので、初期値のままにせず埋めておきます。
tools はこの拡張機能が提供するツールの宣言で、インストール前の確認ダイアログに表示されます。省略しても動きます。
compatibility は動作要件です。ここに node: ">=18" と書いておくと、条件を満たさない環境ではインストール時に弾かれます。
バンドルする
esbuildで .ts から依存関係ごと1つの .js にまとめ、dist/ に出力します。
manifest.json もコピーして、dist/ が .mcpb の中身そのものになるようにします。
pnpm exec esbuild server/index.ts --bundle --platform=node --format=esm \
--outfile=dist/server/index.js
cp manifest.json dist/manifest.json
dist/server/index.js 1.1mb
⚡ Done in 46ms
バンドル後も動くか、さっきと同じJSON-RPCを流して確認します。
printf '%s\n' \
'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"t","version":"1"}}}' \
'{"jsonrpc":"2.0","method":"notifications/initialized"}' \
'{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"get_current_time","arguments":{}}}' \
| node dist/server/index.js
{"result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"mcpb-demo","version":"1.0.0"}},"jsonrpc":"2.0","id":1}
{"result":{"content":[{"type":"text","text":"2026/8/11 12:02:17"}]},"jsonrpc":"2.0","id":2}
.mcpbに固める
まず mcpb validate で、manifest.json がスキーマどおりかを検査します。
mcpb validate dist/manifest.json
# Manifest schema validation passes!
問題なければ mcpb pack で、指定したディレクトリの中身をまとめて .mcpb にします。
mcpb pack dist mcpb-demo.mcpb
同梱したファイルの一覧に続けて集計が出ます。
Archive Contents
620B manifest.json
1.1MB server/index.js
Archive Details
name: mcpb-demo
version: 1.0.0
package size: 188.4kB
unpacked size: 1.1MB
total files: 2
Output: /path/to/mcpb-demo/mcpb-demo.mcpb
Outputに表示されたパスに .mcpb が作成されました。中身は manifest.json とサーバー本体の2ファイルだけです。
Claude Desktopに入れる
インストール方法は2つあります。
.mcpbをダブルクリック
ダブルクリックすると、Claude Desktopにインストールするかどうかのダイアログが出ます。
インストールを押します。

設定からインストール
左下の設定から拡張機能を選び、「詳細設定」を押します。
この画面に .mcpb をドラッグ&ドロップしても入ります。

「拡張機能をインストール」を押すとファイル選択ダイアログが開くので、入れたい .mcpb を選びます。

インストール後
拡張機能の一覧に MCPB Demo が並びました。
めちゃくちゃ簡単にMCPの設定ができましたね!

試しにチャットで時刻を聞くと、許可ダイアログが出ます。
許可を押すとツールが呼ばれて、時刻が返ってきます。


参考: バンドル化にたどり着くまで
そのままpackしてみる
やってみたの手順との違いは2つです。esbuildに --bundle を付けずに変換だけすること、dist/ ではなくプロジェクトごと固めることです。
--node-linker=hoisted を付けているのは、pnpmの既定の node_modules がシンボリックリンクで組まれていて、zipに固めた先ではリンク先を解決できないためです。
pnpm exec esbuild server/index.ts --platform=node --format=esm --outfile=server/index.js
pnpm install --prod --node-linker=hoisted
mcpb pack . mcpb-demo.mcpb
package size: 26.9MB
unpacked size: 73.4MB
total files: 9892
現在時刻を返すだけのMCPサーバーで9892ファイル、展開後73.4MBありました。配布物としては現実的ではありません。
node_modulesの中身を見る
どこが大きいのかを調べます。
du -sh node_modules/.pnpm
# 35M node_modules/.pnpm
.pnpm だけで35MBありました。
--prod はトップレベルからdevDependenciesを消しますが、.pnpm の下に置かれた実体は残ります。
tsxもesbuildも .pnpm 経由でzipに入っていました。
既存の node_modules に上書きインストールしても .pnpm は残ったままなので、いったん削除してから入れ直します。
rm -rf node_modules
pnpm install --prod --node-linker=hoisted
du -sh node_modules/.pnpm
# 24K node_modules/.pnpm
.pnpm が24KBになりました。packし直すと3.2MBです。
package size: 3.2MB
unpacked size: 10.4MB
total files: 2251
それでも2251ファイルあります。
pack が並べたファイル一覧を見ると、ajv のテストコードや各パッケージのREADME、zod に至ってはv3とv4とv4-miniとTypeScriptのソースまで全部入っていました。
mcpb cleanを試す
mcpb clean という、不要ファイルを落とすコマンドが用意されています。
mcpb clean mcpb-demo.mcpb
Clean Complete:
Before: 3.37 MB
After: 3.37 MB
サイズは変わりませんでした。
--prod でインストールした後の node_modules に対しては、このコマンドで削れるものが残っていないようです。
バンドルに切り替える
2251ファイルを減らす手立てがないので、node_modules を配るのをやめて --bundle を付けたのが、やってみたの手順です。
3.2MBで2251ファイルだったものが、188.4kBで2ファイルになりました。
後片付け
Claude Desktopの設定 > 拡張機能 から、入れた拡張機能をアンインストールすれば元に戻ります。
グローバルに入れたCLIも消しておきます。
pnpm remove -g @anthropic-ai/mcpb
まとめ
自作のMCPサーバーを .mcpb にして、Claude Desktopにワンクリックで入るところまで確認しました。
やることは manifest.json を書いて mcpb pack するだけでかなり簡単です。
また、esbuildで1ファイルにまとめると、配布するファイルをかなり小さくできることもわかりました。
同じことで悩んでいる方の参考になればうれしいです。










