◉CLAUDE LABEN
●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/API & SDK
⬡ API & SDK/2026-06-02上級

MCP の tools 以外の3つの能力を使い分ける:resources・prompts・sampling の設計

MCP サーバーで何でも tools にしてしまう設計の限界を、resources・prompts・sampling という3つの primitive で解きほぐします。壁紙アプリのアセット管理サーバーを題材に、判断基準と実装、クライアント対応の現実的な制約までまとめました。

MCP54Claude API128TypeScript24サーバー設計個人開発130

✦ プレミアム記事

アーティスト・クリエイターの廣川政樹です。壁紙アプリのアセットを Claude から直接扱えるようにしたくて、MCP サーバーを書き始めたときのことを振り返ります。最初に作ったサーバーは、機能をすべて tool として実装していました。アセット一覧の取得も、メタデータの読み取りも、定型のレビュー手順も、キャプション生成も、全部 tool です。動きはしたのですが、tool の数が14個まで膨らんだあたりで、Claude がどれを呼べばよいか迷い始め、私自身もどこに何があるのか把握しきれなくなりました。

このとき気づいたのは、MCP には tool のほかに resource・prompt・sampling という能力が用意されていて、本来そちらで表現すべきものまで tool に押し込んでいた、ということでした。14個あった tool を5個まで減らした実際のリファクタを下敷きに、3つの primitive をどう使い分けるかを順に整理します。題材は私が2014年から個人開発を続けている壁紙アプリ群(iOS / Android 合わせて累計5,000万ダウンロードを超えました)のアセット管理サーバーです。

tool に何でも詰め込むと何が起きるか

MCP を学び始めると、最初に手に取るのはほぼ確実に tool です。server.tool() で関数を登録すれば Claude がそれを呼べる、という分かりやすさがあるからです。私もそうでした。ただ、tool だけで全部を表現しようとすると、いくつかの具体的な問題が出てきます。

ひとつ目は、Claude の選択精度が落ちることです。tool は「副作用のある操作」としてモデルに提示されます。読み取り専用のデータ取得まで tool にすると、モデルは「これは呼んでも安全なのか」を毎回判断する必要が生まれ、似た名前の tool が増えるほど取り違えが起きます。実際、私のサーバーでは get_wallpaper_meta と get_wallpaper_info という似た tool を両方残してしまい、Claude がしばしば逆を呼ぶ事故が起きていました。

ふたつ目は、定型ワークフローが再利用できないことです。「この壁紙のメタデータを App Store 向けにレビューして」という作業は、毎回ほぼ同じ手順を踏みます。これを tool にすると、手順そのものはプロンプトとして私が毎回書き直すことになり、サーバー側に蓄積されません。

みっつ目は、サーバー側で軽い推論をしたいときに行き場がないことです。たとえば壁紙のファイル名から人間向けのキャプションを生成したい場合、サーバー自身が API キーを持って Claude を呼ぶ実装も可能ですが、それだとサーバーが課金主体になり、個人開発のコスト管理が一気に複雑になります。

下記は、リファクタ前の「何でも tool」状態を簡略化したものです。

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: "wallpaper-assets", version: "1.0.0" });
 
// 読み取り専用のはずなのに、すべて tool
server.tool("list_wallpapers", {}, async () => ({
  content: [{ type: "text", text: JSON.stringify(await db.listAll()) }],
}));
 
server.tool("get_wallpaper_meta", { id: z.string() }, async ({ id }) => ({
  content: [{ type: "text", text: JSON.stringify(await db.meta(id)) }],
}));
 
// 定型レビュー手順も tool(毎回プロンプトを呼び出し側で書くことになる)
server.tool("review_metadata", { id: z.string() }, async ({ id }) => ({
  content: [{ type: "text", text: `次のメタデータをレビュー: ${JSON.stringify(await db.meta(id))}` }],
}));
 
// ……このあと 11 個の tool が続く

この review_metadata が象徴的です。やっていることは「レビュー用のプロンプトを組み立てて返す」だけで、実体は操作ではなくテンプレートです。本来これは prompt として表現すべきものでした。

resources:読み取り専用のデータは「アドレス」で渡す

resource は、サーバーが持つ読み取り可能なデータに URI を与えて公開する仕組みです。wallpaper://1024 のようなアドレスでアクセスでき、副作用がないことが型として表現されます。Claude から見ると「呼ぶかどうか判断する操作」ではなく「参照できる素材」になるため、tool の選択ノイズから切り離せます。

壁紙アセットの一覧と個別メタデータは、まさに読み取り専用データです。これを resource に移します。

import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
 
// 一覧は固定 URI の resource
server.resource("wallpaper-list", "wallpaper://list", async (uri) => ({
  contents: [{
    uri: uri.href,
    mimeType: "application/json",
    text: JSON.stringify(await db.listAll()),
  }],
}));
 
// 個別メタデータはテンプレート URI で動的に
server.resource(
  "wallpaper",
  new ResourceTemplate("wallpaper://{id}", { list: undefined }),
  async (uri, { id }) => ({
    contents: [{
      uri: uri.href,
      mimeType: "application/json",
      text: JSON.stringify(await db.meta(String(id))),
    }],
  })
);

この変更だけで list_wallpapers・get_wallpaper_meta・get_wallpaper_info の3つの tool が消えました。データの取得は resource に一本化され、似た名前の tool による取り違えも自然に解消しました。私はこの「読み取りは resource、書き込み・操作は tool」という線引きを設計の最初の分岐点として置くことを強く推奨します。tool に残すべきは、分類の再実行やサムネイルの再生成のように、状態を変える操作だけです。

ひとつ注意点があります。resource は「クライアントが明示的に読みに来る」モデルです。Claude が会話の流れで勝手に全 resource を読むわけではありません。会話の中で特定の壁紙を扱わせたいときは、クライアント側でその resource を文脈に添付するか、resource を参照するよう促す必要があります。ここを誤解すると「resource にしたのに Claude が見てくれない」と感じますが、それは設計通りの挙動です。

✦

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
✦resources・prompts・sampling をいつ使い分けるかの具体的な判断基準(読み取りは resource、定型操作は prompt、サーバー内推論は sampling)
✦壁紙アプリ6本のアセット管理サーバーで tools 14個を5個まで減らした実際のリファクタ手順とコード
✦sampling のクライアント対応状況という落とし穴と、未対応クライアントでも壊れないフォールバック設計
Stripe による安全な決済 · いつでもキャンセル可能
✦

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

⬡ API & SDK2026-09-17
画像は20枚ずつ送っています — 21枚目から全画像に別の上限がかかると知った日
素材フォルダの62枚を一度に送ったら invalid_request_error で落ちました。容量ではなく枚数が原因です。視覚トークンを28pxのパッチで数え直し、枚数・寸法・ペイロードの3つの上限を同時に見てバッチを組むまでの記録です。
⬡ API & SDK2026-09-06
訳文は自然なのに、実行時に落ちる翻訳がありました
多言語アプリの文言を Claude API で訳したとき、意味は正しいのに書式指定子だけが壊れることがあります。重大度で分けた受け入れ検査と、壊れた行だけを訳し直す修復ループを実装と実測でまとめました。
⬡ API & SDK2026-05-20
Claude Agent SDK で複数 MCP サーバを束ねるときのツール名衝突 — 名前空間と動的調停の設計
GitHub MCP と Linear MCP の create_issue が同居したとき、Sonnet 4.6 はどちらを呼ぶか分かりません。複数 MCP を束ねる Claude Agent SDK アプリで踏むツール名衝突の構造、TypeScript で書ける Reconciler の実装、そして本記事に最初に載せた schemaHash が5種類のスキーマ変更を全て見逃していた実測と訂正までをまとめます。