In this article
MCPサーバー構築のベストプラクティス5選
MCPサーバーの構築は、AIアプリケーションやAI主導のワークフローに製品機能を提供するための一般的な手段になっています。MCPサーバーの構築が初めての場合、ベストプラクティスや慣習に従い、MCPサーバーを最大限に活用するにはどうすればよいでしょうか。Node.jsでいくつか構築した経験をもとに、ベストプラクティスをまとめました。
Snykリソースハブの過去の記事をご覧になった方は、プロダクトマネージャー向けの7つのMCPサーバーを紹介した記事や、開発者向けのCursorにMCPサーバーを追加する方法、Node.jsアプリケーションのセキュリティ脆弱性につながるバイブコーディングのセキュリティリスクについての記事をご覧になったかもしれません。
このガイドでは、MCPサーバーのパフォーマンスを高めるパターンの微妙なポイントや構成要素を整理し、MCPクライアントの開発者体験やデバッグ機能についても触れます。
MCPサーバーのベストプラクティス1:ツールの命名規則に従う
MCPサーバーは、名前と説明を付けてツールを公開し、MCPクライアントからのリクエストに応じてツールの一覧を提供します。
以下は、ツール名をgetNpmPackageInfoと定義する簡単なツールの例です。
server.tool(
"getNpmPackageInfo",
"Get information about an npm package",
{
packageName: z.string()
},
async ({ packageName }) => {
const output = execSync(`npm view ${packageName}`, {
encoding: "utf-8",
});
return {
content: [
{
type: "text",
text: output
},
],
};
}
);
セキュリティに関する免責事項:上記のコード例は、MCPサーバーのセキュリティ脆弱性に関する教育記事の一環として、意図的にコマンドインジェクションに脆弱なMCPサーバーになっています。
たとえば、ツール名には次のような選択肢があります。
getNpmPackageInfo(上記の例で使用した名前)get-Npm-Package-Infoget.Npm.Package.Infoget Npm Package Information
実感として、ハイフン(-)、アンダースコア(_)、スネークケース(getNpm…)を使う一般的なツール命名規則から外れると、MCPクライアントがツールを利用可能な状態で表示できないことがあります。その結果、エンドユーザーからはツールが呼び出されていないように見えます。
おすすめ:ツール名にはスペースやドット(.)、丸括弧や角括弧((や))を使わないでください。これらは混乱を招き、MCPツールの呼び出しを妨げます。ツール名には必ずスネークケースを使いましょう。GPT-4oのトークン化では、この形式が最も適切に処理されます。区切り文字としてハイフンやアンダースコアを使う方法もあります。
ツール名にはLLMのトークン化が重要
こうした問題が起きる理由は、使用するモデルやMCPクライアントの実装、利用するMCPホストによって異なりますが、LLMのトークン化処理も原因の一つです。
たとえば、GPT-4oなどのOpenAIモデルがテキストをどのようにトークン化するか、次の例を見てみましょう。
nodeCryptoは2つのトークンとして解析されます。
node.Cryptoは3つのトークンとして解析されます。
node_cryptoは2つのトークンとして解析されます。
npm_Utilsは3つのトークンとして解析されます。
npm_Get_Package_Infoは5つのトークンとして解析されます。
MCPサーバーのベストプラクティス2:ログを記録する
MCPクライアントとサーバー間のやり取りがうまくいっていないとき、どうすればそれを把握できるでしょうか。ツールは呼び出されているでしょうか。
MCPサーバー、特にプロセスのSTDIO(標準入出力)に依存するシンプルなサーバーを構築したことがあれば、console.log()がうまく機能しないことに気づいたでしょう。
STDIOを使用するMCPサーバーでは、コンソールやターミナルへのログ出力は簡単ではありません。MCPクライアントとMCPサーバー間の情報交換に、その通信経路が使われるためです。
とはいえ、ログをまったく記録しないわけにはいきません。効果的なログはMCPサーバーに不可欠です。ツール呼び出しの解決や、ツール定義ロジック内のその他の処理におけるプログラムの動作を可視化するうえで重要な役割を果たします。MCPサーバーの問題を効率的にトラブルシューティングし、AIワークフローの実行に関する重要な情報を把握する必要があるでしょう。
そのため、MCP仕様に従いプロセスのSTDIOトランスポートを使用するMCPサーバーでは、ファイルにログを記録する方法が最も簡単です。出力先をログファイルにし、指定したファイルにログメッセージを書き込めば、後から分析できます。
おすすめ:Node.jsでは、pinoなどのロギングライブラリを使うと、実装を効率化できます。Node.jsで長年信頼されてきたコラボレーターや、Fastifyエコシステムのライブラリ作者が開発に携わっているため、優れた選択肢です。
pinoの使用例を見てみましょう。
// import and initialize the logger
import pino from 'pino';
const logger = pino('/tmp/mcp-server.log');
// log data
logger.info(`Logger initialized`);この例では、pinoを使ってロガーを生成し、ログを/tmp/mcp-server.logに出力しています。
ロガーの初期化などのログエントリは、このあと体系的に記録できます。これにより、MCPサーバーのプログラムフローを綿密に監視し、可視化できます。
HTTPトランスポートを使用するMCPサーバーでは、ログの扱い方が異なる場合があります。このベストプラクティスの推奨事項では対象としていません。
MCPサーバーのベストプラクティス3:「見つからない」という応答テキストを避ける
この推奨事項は、ツール呼び出しの応答テキストとロジックの実装に関するもので、私の限られた経験に基づいています。それでも、観察や実験の結果と一致しているようです。
基本的な考え方は次のとおりです。「検索」のようなツール呼び出しを実装する場合、「見つかりませんでした」というテキストを返すのは避けましょう。代わりに、LLMにできるだけ汎用的なデータを提供してください。
実際にこのことを体験したのは、Node.js APIドキュメント用のMCPサーバーを構築し、nodeSearchツールを実装したときです。ユーザーがNode.jsランタイムのメソッド関連APIを尋ねても、最初は見つからない場合があります。nodeSearchの実装では、基盤となるAPIの実際のメソッドではなく、名前でNode.jsのコアモジュールを検索するためです。
当初、該当する場合に返す応答としてModule <xyz> not found. <here is everything I have>を選びました。しかし、すべてのモジュールとメソッドを提供しても、応答冒頭の「見つかりませんでした」というテキストにLLMが引きずられ、提供したデータの中から対応するNode.jsコアモジュールのメソッドを見つけようとしないことがわかりました。
おすすめ:「見つかりませんでした」というテキストは返さず、代わりに関連する他のデータを提供してください。もちろん、すべてのデータを返せるとは限らないため、例外もあります。たとえば、ユーザーを検索する場合に、すべてのデータを返すのは不適切であり、セキュリティ上の問題やプライバシー侵害につながるおそれがあります。
この方法の実施前と実施後の違いを見てみませんか?以下をご覧ください。
以下は、「見つかりませんでした」というテキストを返すツール呼び出しを含むMCPサーバーを使った、IDEチャットのやり取りの例です。ご覧のとおり、正しい回答を得られていません。

しかし、テキスト応答の先頭にある# Module “color” not foundを削除し、利用可能なNode.jsコアモジュールとそのメソッドをすべて返すと、LLMは考えられるモジュールを順に調べ、より適切な解決策を見つけられます。
うまくいきます。

MCPサーバーのベストプラクティス4:脆弱なサードパーティコンポーネントを避ける
AIセキュリティ企業のSnykが、ベストプラクティスシリーズでセキュリティをおろそかにするわけにはいきませんよね。
MCPサーバーが組織内で広く採用され、社内のインフラチームに受け入れられるには、厳格なセキュリティ要件とコンプライアンス要件を満たす必要があります。ITチームやプロダクトセキュリティチームは、ワークフローに脆弱な依存関係を持ち込むリスクを十分に認識しています。これは新しい問題ではありません。SolarWinds攻撃の影響を受けて発令されたバイデン大統領令で定められたSBOM要件にも含まれています。
脆弱なサードパーティコンポーネントを避けることは非常に重要です。サードパーティライブラリに依存するソフトウェアや、既知の脆弱性を含むコードは、悪意ある攻撃者の侵入口になり得ます。MCPサーバーは、その機能上、幅広いアクセス権限や連携機能を持つことが多いため、脆弱性があれば重大なリスクにつながります。
したがって、MCPサーバーに脆弱性がないことを確認するのは、単なるベストプラクティスではなく、導入を成功させるために不可欠です。
おすすめはSnykを使うことです。では、MCPサーバーのセキュリティを確保するには、何から始め、どこを確認すればよいでしょうか。
脆弱なコードをスキャンする:Snykはコードベースを分析して、セキュリティ上の欠陥や安全でないコードパターンを検出し、IDEやその他の開発者ワークフローで修正案を提示できます。
脆弱な依存関係を確認する:オープンソースソフトウェアのセキュリティ確保は、Snykの基盤となる強みの一つです。Snykはプロジェクトの依存関係を既知の脆弱性に関する包括的なデータベースと照合し、リスクとアップグレードが必要なバージョンを通知します。
コンプライアンスを確保する:開発者にとって法的な問題は退屈に感じるかもしれませんが、企業にとって非常に重要です。Snykは、セキュリティ基準やライセンス基準に違反する脆弱性を特定し、開発者が法的責任を問われるリスクを回避できるよう支援します。
Snykを使って、脆弱なオープンソース依存関係を見つける実践的なデモをご覧ください。

Snykは、コマンドインジェクションやパストラバーサルなど、自分で書いたコードやLLMが生成したコードに含まれる脆弱性も検出します。どちらの場合も、Snykがコードをスキャンします。
MCPサーバーのベストプラクティス5:MCPサーバーをDockerコンテナとしてパッケージ化する
MCPサーバーの構築には多くのメリットがありますが、デプロイや管理によってエンドユーザー側に複雑さが生じることがあります。
多くの場合、MCPサーバーはuvを使うPythonやnpmを使うNode.jsなど、特定のランタイム環境を必要とします。特定の言語環境への依存は、サーバーを利用するエンドユーザーの障壁になり得ます。ユーザーはMCPサーバーを正しく実行するために環境を正確に構成しなければならず、環境の不整合やバージョンの競合、エラー、複雑なセットアップにつながる可能性があります。
この問題を解決する効果的な方法の一つが、MCPサーバーをDockerコンテナとしてパッケージ化することです。Dockerコンテナは、MCPサーバーの正常な動作に必要な依存関係、ライブラリ、ランタイムコンポーネントをすべて含む、標準化された分離環境を提供します。MCPサーバーをコンテナイメージとして配布すれば、ユーザーはDockerをインストールするだけで実行できます。
Dockerは、人気のあるMCPサーバーをコンテナイメージとして集めた一元管理型のハブも作成しています。

Dockerは、アプリケーションとその依存関係をポータブルなコンテナにまとめる、業界で広く認められた抽象化レイヤーとして機能します。この方法によりデプロイが簡素化され、異なる環境間で一貫性を保てるほか、「自分のマシンでは動くのに」という問題も解消できます。
構築するMCPサーバーのDockerイメージを提供するメリットは次のとおりです。
一貫性:開発、テスト、本番の各環境で、MCPサーバーが同じように動作することを保証します。
分離:MCPサーバーの依存関係と、ホストシステム上の他のアプリケーションとの競合を防ぎます。
ポータビリティ:Dockerに対応するあらゆるシステムに、MCPサーバーを簡単にデプロイできます。
デプロイの簡素化:エンドユーザーがMCPサーバーを起動して使える状態にするまでの手順を減らします。
リソース管理:Dockerが提供するツールでCPU、メモリ、ネットワークなどのリソースを管理し、MCPサーバーを効率的に実行できます。
AIを活用した開発を安全に進めるためのベストプラクティス
AIを活用した開発のメリットを最大限に生かしながら、潜在的なリスクを効果的に軽減するための、開発者やセキュリティ担当者向けの10のヒントをご紹介します。