マギのクロゼミ
研究室 #Claude#MCP 公開 2026.04.27

MCP サーバーの自作手順。最小30行で動かす天気サーバー入門

既製の MCP サーバーが見つからないとき、自作するという選択肢があります。意外なことに、最小構成のサーバーは **30〜50 行程度** で書けます。本記事では「天気を答えるサーバー」を例に、TypeScript SDK のセットアップから、Claude Code に繋いで動かすまでを通しで歩いてみます。

はじめに

MCP シリーズの最終章、自作編 です。前2本で、

を見てきました。今回は、自分で MCP サーバーを書いて Claude Code に繋ぐ ところまで進みます。

「サーバーを自作」と聞くと身構える方が多いと思います。私も最初は同じ感覚でした。実際にやってみると、最小構成は驚くほど短い——tools を1個だけ持つサーバーなら 30〜50 行のコードで動きます。本記事では、「都市名を渡すと天気を答えるサーバー」を例に、いちから組み立てます。

本記事の対象は、

  • TypeScript(または JavaScript)の 基本的な書き方は知っている
  • ターミナルで npm コマンドが使える
  • Node.js が手元の PC に入っている

くらいの方を想定しています。ガッツリ系の解説は避けて、「動くものを最短で1つ作る」 ことに集中します。

自作の前に:何を作りたいかを決める

最初に、「自分が MCP サーバーを書きたい理由」をはっきりさせておきます。これが曖昧だと、サンプルを作って終わりになりがちです。

よくある自作の動機としては、

動機
既製サーバーがない外部 API を使いたい自社の社内システム、独自 SaaS
既製サーバーが大きすぎて、最小限だけ欲しいGitHub サーバーから Issue 作成だけ抜き出す
既製サーバーの認証方式が合わない自社の SSO 経由でアクセスしたい
自分の作業フローを Claude に任せたい「LP の見出しを CSV に出力して」を1コマンド化

本記事では入門用に、外部 API を叩くサーバー の代表例として「天気を答えるサーバー」を作ります。Tool は1個(get_weather)だけのシンプルな構成です。

使う SDK を決める

MCP には、サーバーを楽に書くための 公式 SDK が用意されています。記事公開時点で主要な選択肢は、

SDK言語おすすめ度
@modelcontextprotocol/sdkTypeScript / JavaScript◎ Web 制作経験者にいちばん馴染む
mcpPython○ Python に慣れていれば

本記事では TypeScript SDK を使います。Web 制作経験のある方なら、JavaScript の延長で書けるので入りやすいはずです。

最小構成プロジェクトを作る

ターミナルで作業していきます。プロジェクトフォルダを作って、必要なものをセットアップします。

Step 1. フォルダを作る

mkdir ~/mcp-weather
cd ~/mcp-weather
npm init -y

npm init -y で、デフォルト設定の package.json がいきなり作られます。

Step 2. SDK と TypeScript を入れる

npm install @modelcontextprotocol/sdk
npm install -D typescript tsx @types/node
パッケージ役割
@modelcontextprotocol/sdkMCP サーバーの本体機能
typescriptTypeScript コンパイラ
tsxTypeScript を直接実行できるツール
@types/nodeNode.js の型定義

Step 3. tsconfig.json を作る

最小の TypeScript 設定。

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "strict": true,
    "outDir": "dist"
  },
  "include": ["src/**/*"]
}

src/ フォルダの下に TypeScript ファイルを置く、という前提です。

Step 4. package.json"type": "module" を足す

ESM(モジュール形式)で動かしたいので、package.json に1行加えます。

{
  "name": "mcp-weather",
  "version": "1.0.0",
  "type": "module",
  ...
}

これでセットアップ完了。次から実コードに入ります。

サーバー本体を書く

src/index.ts を新規作成して、以下を書きます。長く見えますが、半分はコメントです。

// MCP の SDK から、サーバー本体と「コマンドラインで起動する型」を取り込む
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';

// サーバーを作る
const server = new Server(
  { name: 'mcp-weather', version: '1.0.0' },
  { capabilities: { tools: {} } }
);

// 「どんな Tool が使えるか」を Claude に伝える
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: 'get_weather',
      description: '都市名を受け取って、その都市の現在の天気を返す',
      inputSchema: {
        type: 'object',
        properties: {
          city: {
            type: 'string',
            description: '都市名(例:「東京」「大阪」)',
          },
        },
        required: ['city'],
      },
    },
  ],
}));

// Claude が「Tool を呼んでくれ」と言ってきたときの実処理
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  if (request.params.name === 'get_weather') {
    const city = request.params.arguments?.city as string;
    // ※本来はここで天気 API を叩く。今回はダミーの返答。
    return {
      content: [
        {
          type: 'text',
          text: `${city} の天気は晴れ、気温は 22°C です(ダミー応答)。`,
        },
      ],
    };
  }
  throw new Error(`Unknown tool: ${request.params.name}`);
});

// サーバーを「標準入出力経由」で起動する
const transport = new StdioServerTransport();
await server.connect(transport);

50 行ちょっとです。本当に書いている処理は、

  1. Server を作る
  2. 「使える Tool の一覧」 を返すハンドラを登録
  3. 「Tool を呼んだときの実処理」 を登録
  4. 標準入出力で起動

の4ステップだけです。

コードの読みどころ

  • nameversionServer のコンストラクタで自分のサーバーの名前を決めます
  • ListToolsRequestSchema のハンドラ:Claude が「君は何ができるの?」と聞いてきたときの返答。ここの descriptioninputSchema が、Claude にとっての判断材料になります
  • CallToolRequestSchema のハンドラ:Claude が実際に Tool を呼んだときの本処理。今回はダミー応答ですが、実用では fetch で外部 API を叩いて結果を返します
  • StdioServerTransport:「標準入出力でやり取りする」型。これが ローカル STDIO 系サーバー の正体です

Claude Code に繋ぐ

サーバーを書いたら、Claude Code から呼べるように登録します。ターミナルで、

claude mcp add weather tsx -- ~/mcp-weather/src/index.ts
部分意味
weather自分でつけるニックネーム
tsxTypeScript を直接実行できるコマンド
~/mcp-weather/src/index.ts自分のサーバーファイル

実行すると、

✓ Added MCP server 'weather'
  Tools: get_weather

——となれば成功です。

動作確認

Claude Code を起動して、

> 東京の天気を教えて

と頼んでみてください。Claude が mcp__weather__get_weather を呼んで、

東京の天気は晴れ、気温は 22°C です(ダミー応答)。

を返してきたら、自作 MCP サーバーが動いています。

さっき書いた50行のファイル が、いまの返答の正体です」と気づくと、ちょっと感動的な瞬間があります。

スキーマ定義のコツ

get_weather を Claude が 正しく呼んでくれるかどうか は、inputSchemadescription の質に大きく左右されます。Skills のときと同じ、「どういう場面で呼ぶべきか」を具体的に書く のが大事です。

部分良くない例良い例
description天気を返す都市名を受け取って、その都市の現在の天気を返す。日本国内の主要都市が対象
properties.city.description文字列都市名(例:「東京」「大阪」)。日本語と英語のどちらでも可

「Claude が 何の場面で どう呼ぶべきか が伝わるか」を判断軸に、自分の言葉で書き直してみてください。

入力が複雑になるとき

入力が単純な文字列1個ではなく、複数のフィールドが必要なときは、properties を増やします。

inputSchema: {
  type: 'object',
  properties: {
    city: { type: 'string', description: '都市名' },
    days: {
      type: 'integer',
      description: '何日先までの天気を返すか(1〜7)',
      minimum: 1,
      maximum: 7,
    },
    units: {
      type: 'string',
      enum: ['celsius', 'fahrenheit'],
      description: '気温の単位',
    },
  },
  required: ['city'],
},

enum で選択肢を絞る、minimum/maximum で範囲を絞る、といった指定もできます。ここを丁寧に書くと、Claude の呼び出しの精度が目に見えて上がります

エラーハンドリング

実用的なサーバーには、エラーハンドリングを足す必要があります。最小限の方針は、

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  try {
    if (request.params.name === 'get_weather') {
      const city = request.params.arguments?.city as string;

      // 入力チェック
      if (!city || city.trim() === '') {
        return {
          content: [{ type: 'text', text: 'エラー:city は必須です' }],
          isError: true,
        };
      }

      // 本処理
      const result = await fetchWeather(city);
      return {
        content: [{ type: 'text', text: result }],
      };
    }
    throw new Error(`Unknown tool: ${request.params.name}`);
  } catch (e) {
    // 例外は `isError: true` で返す
    return {
      content: [{ type: 'text', text: `エラー:${(e as Error).message}` }],
      isError: true,
    };
  }
});

ポイントは、

  • 入力検証を先頭でcity が空文字なら早期リターン)
  • try / catch で例外を吸い上げる(外部 API の障害で落ちないように)
  • isError: true を付けて返す(Claude にエラーだと伝える)

の3点。これだけで、サーバーが落ちずに「困ったときに困った理由を返す」状態にできます。

デバッグの方法

サーバーが思ったように動かないとき、よく使う確認手段は3つです。

1. サーバー本体をターミナルで直接起動して見る

tsx ~/mcp-weather/src/index.ts

エラーが起きていれば、ここでスタックトレースが見えます。「サーバーが起動すらしていない」ケースは、ここでだいたい原因がわかります。

2. Claude Code の /mcp で接続状態を見る

/mcp

weatherConnected になっているか、Tool が見えているか。エラーがあれば /mcp の表示に出ることがあります。

3. ログ出力を仕込む

サーバーのコードに console.error を仕込むと、Claude Code の MCP ログに出力されます。console.log(標準出力)は MCP の通信に使われるので 使ってはいけない 点だけ注意してください。

console.error(`[debug] get_weather called with city=${city}`);

このログは、Claude Code のログフォルダ(~/.claude/logs/ 配下)で確認できます。

公開・配布の選択肢

自作サーバーが手元で動くようになったら、公開する という選択肢があります。

公開先向いている場面
GitHub の自分のリポジトリ個人プロジェクトとして配布
npm レジストリnpx でインストールできる」状態にしたい
マーケットプレイス(プラグイン経由)プラグインの中身として MCP サーバーを同梱して配布
社内 Git のみ社外には出さず、社内で配布

最初のうちは GitHub に置くだけ で十分です。npm 公開やマーケットプレイス配布は、サーバーが安定して使える状態になり、他の人にも配ろうと思った段階で検討すれば足ります。

自作の心構え

最後にひとつだけ、自作 MCP サーバーへの心構えを。

「使う側」と「作る側」では、求められる責任が変わります。自分のサーバーが間違った返答をしたら、Claude はそれを信じて動きます。 認証情報を扱うサーバーなら、その情報の保管・破棄まで自分の責任です。

ただ、最初の1個目は 「動かしてみる」 が圧倒的に大事です。実用に耐えるサーバーは、最初の1個を動かして、3個目くらいで形が見えてきます。本記事のダミー応答からスタートして、徐々に 「自分の作業に直結する Tool」 に育てていく順序がおすすめです。

まとめ

MCP サーバー自作の要点を整理します。

  1. 最小構成は 30〜50 行:プログラミング経験があれば1日で書ける
  2. TypeScript SDK が Web 制作経験者には親しみやすい:JavaScript の延長で書ける
  3. 必要なのは Server + 2つのハンドラ:Tool 一覧 + Tool 呼び出し
  4. Claude Code への接続は claude mcp add <name> tsx -- <パス>
  5. descriptioninputSchema の質が呼び出し精度を左右する
  6. エラーハンドリングは isError: true で返す:try / catch で例外を吸う
  7. デバッグは「直接起動」「/mcp」「console.error ログ」の3点
  8. console.log は MCP の通信を壊すので禁止
  9. 公開は GitHub に置くだけから:npm・マーケットプレイスは段階的に
  10. 作る側の責任が増える:返答が間違えば Claude も間違える

これで MCP シリーズの3本(とは/使う/自作する)を読み終わりました。MCP は概念が広く、最初は身構えがちですが、実際にやってみると素朴な仕組みの組み合わせ であることが体感できたと思います。

次の記事「Claude Code の Hooks とは」では、Step 3 の残るテーマ Hooks(フック) に進みます。Hooks は「特定のイベントで自動的にシェルコマンドを走らせるしくみ」で、Skills や MCP とはまた違った角度で Claude Code の挙動を拡張できる機能です。続けて読むと、「Claude を賢くする」 の Skills、「外の世界に繋ぐ」 の MCP、「タイミングで動かす」 の Hooks という3つの拡張軸が出揃います。

WordPressを実際に動かしてきたサーバー:ロリポップ

Claude Code でWordPressサイトを組み立てるとき、最初に置く先として無理のないレンタルサーバー。月数百円から始められ、WordPressの自動インストールにも対応しています。設定で詰まりがちな初期段階の時間をかなり減らせます。

🌱
ロリポップ!レンタルサーバー
GMOペパボ運営 / 月額¥220〜
ロリポップを見る →
Roadmap 学習マップでこの記事の位置を見る
Step 1 →