📦

manifest.jsonと環境変数をいい感じにしてClaude Desktop Extensions (DXT) を構築してみる

に公開

はじめに

以前 
https://zenn.dev/akizarashi/articles/02783c2b0133f5
で社内の日常的な業務やツールの横断をMCPサーバーを使って楽をしていく、という記事を書きました。

実際業務は捗りclaude code経由で呼んだりすることで普段の開発のオーバーヘッドや連携コストは爆裂に軽減されていると感じています。
他方で、目を背けてきた課題感というのも感じていました。そんな課題感をDXTが解決してくれそうだったので実際にDXTをためしてみました

紹介すること

MCPをDXT化する手順

割愛すること

DXT自体の詳細な仕様説明など

自分がMCPで抱えていた課題感

当然いくつかの課題や”もっとこうだったらいいのに”などいろいろなことは出てくるものですが、直近よく遭遇したテーマとして「他人のPC(のclaudeなど)にMCPを適用するハードルが高い」ということです。

特にプログラミングやそれに準ずる業務に普段触れない人のPCに適用するとなると、これまではxxxx.jsonをいじって〜、nodeがないからいれて〜、PATHを通して〜といった付帯する作業がびっくりするくらい同梱されてきます。

DXTについて言及しているブログ
https://fastmcp.me/blog/claude-desktop-extensions-dxt-making-mcp-servers-actually-usable
や公式の解説
https://www.anthropic.com/engineering/desktop-extensions
にもある通り、実際にPC上で動作する、までの障壁が高すぎたのです。

DXTが解決してくれる課題

めちゃくちゃ参考になりました。
https://zenn.dev/cadp/articles/6d9dd374fd3d32

そのなかでも、インストールが簡単でセキュアである、ということが上述していた課題感をなんとかしてくれるのではないかとモチベになります。
弊社はフルリモートであることもあり、直接誰かのデスクに行ってPCを操作して導入をサポートする、というようなことがなかなかしづらい、というのもあります。

## やってみる

さて、長くなってしまいましたが本題に入りましょう。今回もサンプルとして前回の記事でも使用した

https://github.com/Akito-n/use-mcp-on-claude
を使っていきます。

パッケージのインストール

まずdxt化するためには、それを行うパッケージをインストールする必要があります。npm経由で今回installします。

npm install -g @anthropic-ai/dxt

dxt initする

パッケージがインストールできたら、MCPのプロジェクトルートに移動し、そこで

dxt init

を実行しましょう。

実行すると、このように対話形式でそれぞれどのようにしていくかを聞かれます。
これは後でmanifest.jsonというファイルにまとめて記載されるので、修正可能です。軽い気持ちで進めて大丈夫です

各項目の説明などはmanifest.jsonの方でまとめるので詳細は後回し。一旦進めましょう。

ここでちゃんと選択しておきたいのは2つで、

Server type: Node.js
Entry point: build/index.js

の2つです。Entry pointは空でエンターを押すとデフォルトでbuild/index.jsになりますがビルド先が異なる方は適宜変更を行なってください。

dxt packする

initで対話形式のやりとりがひと段落すると、

Next steps:
1. Ensure all your production dependencies are in this directory
2. Run 'dxt pack' to create your .dxt file

と出てくると思います。
1には
本番環境で必要な依存関係がすべてこのディレクトリ内にあることを確認してください
とあります。

これは本番環境で動くように諸々揃えておいてね、ということですね。今回使っていくソースコードはjavascriptSDKを使用したもので、Typescriptで記載されているので、依存関係のインストールやビルドが必要になっています。 公式を見るとPythonやバイナリなどの選択肢があるため、包括的に1のような記載になっていると思います。

今回だと1を満たすために下記コマンドを実行します。

npm install # 必要であれば。node_modules。
npm run build # package.jsonに記載。ビルドする。

ご自身のMCPを使用される場合は環境ごとに読み替えてください。

完了したら、2に移ります。2はそのままなので、

dxt pack

を実行しましょう。実行すると、{プロジェクト名}.dxtというファイルが出来上がります。
ここまでで一旦console上の作業は完了です。

Claude Desctopに適用する

今回の目標はClaudeDesctopにextentionを読み込ませることなのでClaudeのデスクトップアプリを開きます。
開いたら、”設定”を押して「エクステンション」の項目を選んでください。

エクステンションの画面が開いたら、先ほど作成したdxtファイルを選択し、ドラッグ&ドロップでエクステンションの画面に渡してあげます

要件を満たしている場合、このように作成したdxtが表示され、インストールするかどうかを聞かれます。今回自作のMCPをdxt化したものなのでインストールを押します。

インストールできると、このようになります。

この段階で、完了です!外部依存のないMCPサーバーや固有の情報を必要としないものであれば動作すると思います。

manifest.jsonの設定と環境変数をいい感じに渡せるようにする

試してみた、ということであればここまででクリアなのですが、今回使っているMCPサーバーには

  • 手元のObsidianのValutのパス
  • asana,kiberaなどへのアクセストークンなどの機密情報

など、以前のjsonファイルではenvとして渡していたものをdxtにも適用しなければ動作できません。
そのため、manifest.jsonを見直していい感じにしてあげる必要があります。

適当に対話して作成されたmanifest.jsonはこのようになっています。

{
  "dxt_version": "0.1",
  "name": "sample-mcp-tools",
  "version": "1.0.0",
  "description": "my sample extention",
  "author": {
    "name": "akito-n"
  },
  "server": {
    "type": "node",
    "entry_point": "build/index.js",
    "mcp_config": {
      "command": "node",
      "args": [
        "${__dirname}/build/index.js"
      ],
      "env": {}
    }
  },
  "tools": [
    {
      "name": "multi-tools"
    }
  ],
  "license": "ISC"
}

ここにenvという項目があり、これは以前の記事で見たcalude_desctop.jsonのファイルから見て既視感がありますね。
ここに値を入れるのかな、と思うのですがよく考えると(今回は個人利用の範囲ですが)extentionの性質として配布することを考えるとここに固定値や、自分のkeyを入れるのは違和感です。
それにここをいじるのであれば結局冒頭のjsonを慣れてない人が触らなくちゃいけません。

DXT,ちゃんといい感じの設定方法があります!

まず、envの部分は下記のようにしてあげましょう

  "env": {
        "OBSIDIAN_VAULT_PATH": "${user_config.obsidian_vault_path}",
        "KIBELA_TEAM_NAME": "${user_config.kibela_team_name}",
        "KIBELA_ACCESS_TOKEN": "${user_config.kibela_access_token}",
        "BRAVE_API_KEY": "${user_config.brave_api_key}",
        "SLACK_BOT_TOKEN": "${user_config.slack_bot_token}",
        "SLACK_TEAM_ID": "${user_config.slack_team_id}",
        "GDRIVE_TOKEN_PATH": "${user_config.gdrive_token_path}",
        "GDRIVE_CREDENTIALS_PATH": "${user_config.gdrive_credentials_path}"
      }

"${user_config.obsidian_vault_path}"という記載が重要です。記載できたら、今度は”server”や"dxt_version"と同階層に”user_config”を配置してあげましょう。下記のような記載になります。

{
  "dxt_version": "0.1",
  "name": "sample-mcp-tools",
  "version": "1.0.0",
  "description": "my sample extention",
  "author": {
    "name": "akito-n"
  },
  "server": {
    "type": "node",
    "entry_point": "build/index.js",
    "mcp_config": {
      "command": "node",
      "args": [
        "${__dirname}/build/index.js"
      ],
       "env": {
        "OBSIDIAN_VAULT_PATH": "${user_config.obsidian_vault_path}",
        "KIBELA_TEAM_NAME": "${user_config.kibela_team_name}",
        "KIBELA_ACCESS_TOKEN": "${user_config.kibela_access_token}",
        "BRAVE_API_KEY": "${user_config.brave_api_key}",
        "SLACK_BOT_TOKEN": "${user_config.slack_bot_token}",
        "SLACK_TEAM_ID": "${user_config.slack_team_id}",
        "GDRIVE_TOKEN_PATH": "${user_config.gdrive_token_path}",
        "GDRIVE_CREDENTIALS_PATH": "${user_config.gdrive_credentials_path}"
      }
    }
  },
  "user_config": {
    "obsidian_vault_path": {
      "type": "directory",
      "title": "Obsidian Vault Path",
      "description": "Path to your Obsidian vault directory",
      "required": true,
      "default": "~/Documents/Obsidian Vault"
    },
    "kibela_team_name": {
      "type": "string",
      "title": "Kibela Team Name",
      "description": "Your Kibela team name (e.g., 'kibera')",
      "required": false,
      "default": ""
    },
    "kibela_access_token": {
      "type": "string",
      "title": "Kibela Access Token",
      "description": "Your Kibela API access token",
      "required": false,
      "sensitive": true
    },
    "brave_api_key": {
      "type": "string",
      "title": "Brave Search API Key",
      "description": "Get your API key from https://api.search.brave.com/",
      "required": false,
      "sensitive": true
    },
    "slack_bot_token": {
      "type": "string",
      "title": "Slack Bot Token",
      "description": "Your Slack bot token (starts with xoxb-)",
      "required": false,
      "sensitive": true
    },
    "slack_team_id": {
      "type": "string",
      "title": "Slack Team ID",
      "description": "Your Slack team/workspace ID",
      "required": false
    },
    "gdrive_token_path": {
      "type": "string",
      "title": "Google Drive Token File Path",
      "description": "Path to your Google Drive OAuth token JSON file",
      "required": false,
      "default": ""
    },
    "gdrive_credentials_path": {
      "type": "string",
      "title": "Google Drive Credentials File Path",
      "description": "Path to your Google Drive credentials JSON file",
      "required": false,
      "default": ""
    }
  },
  "tools": [
    {
      "name": "multi-tools"
    }
  ],
  "license": "ISC"
}

このようにしてuser_configにどんな型が入るのか、必須かどうか、デフォルト値などを設定することができます。
このようにできたら、以前のdxtを削除して、再度dxt packを行いましょう。できたファイルをドラッグ&ドロップして再度エクステンションに読み込んでもらいます。

インストールするとこのようにユーザーごとに設定することのできるUIが出てきます。

ここに今まで使用してた値を入れたり、各々のlocalのpathをユーザーごとに設定することでjsonを見ることなくdxtが使えるということですね!すごい。

最後に、manifest.jsonについても見ておきましょう。
基本的には
https://github.com/anthropics/dxt/blob/main/MANIFEST.md
ここに必要な情報は揃っていると思います。
例えば先ほどのuser_configに使用できる入力のtypeなんかは

type: The data type of the configuration
"string": Text input
"number": Numeric input
"boolean": Checkbox/toggle
"directory": Directory picker
"file": File picker

このような入力形式をサポートしているみたいです。
将来的にDXTを配布するようにするためにはここに記載しているオプションフィールドなども充実させる必要がありそうです。

MANIFEST.mdをみると下記のような設定が可能なよう。

# 必須なやつ
"dxt_version": 'DXTの仕様バージョン。現在0.1が最新のよう。',
"name": '機械が読み取る識別名。CLIやAPIで使用。display_nameが未設定ならextensionの名前にもなる',
"version": 'このMCP拡張のバージョン。配布なら自動更新らしいけど検証できてない',
"description": 'MCPの説明。エクステンションの画面に表示される',
"author": "著者",
"server": "サーバー情報。node使うとかエントリーポイントどことか。"
# オプション
"icon": "アイコンファイルへのパスかURLでもいいらしい",
"display_name": "エクステンションに表示したい名前",
"long_description": ”ストアに並ぶ時の詳細説明”,
"repository": "ソースコードのurl",
"homepage": "拡張機能に関するhomepageのurl",
"documentation": "仕様書のurl",
"support": 'サポート窓口?のurl',
"screenshots": 'スクリーンショットの配列のパス',
"tools": "提供機能の名称と説明。この静的宣言とMCPサーバーでの動的処理の名称が一致していると動作がスムーズになるとのこと。",
"prompts": "既存のプロンプトと一緒。画一的なテンプレート指示を送るためのもの",
"keywords": "ストアなどでヒットさせる時のキーワード?未検証"
"compatibility": "互換性の設定やサポート対象の制限など",
"user_config": "ユーザー固有の設定"

おわりに

読んでいただいてありがとうございます。

今後DXTやAIの利用が活発になってくると、新入社員のオンボーディングなどに自社のDXTをインストールする、というようなステップが一般化してくるかもしれませんね。開発ではClaude Codeなどを使い倒してCLIにどっぷり使っている日々ではありますが、こっちも面白いので機会を見て触っていこうと思いました。

Discussion