Delphi で Model Context Protocol(MCP)サーバーを構築する

· コンポーネント

Model Context Protocol とは?

Model Context Protocol(MCP)は、AI モデルにツール、データ、プロンプトテンプレートを公開するための Anthropic のオープン標準です。AI のための USB と考えてください: 任意の MCP サーバーは任意の MCP クライアント(Claude Desktop、Cursor、Continue、自前の Delphi アプリ、OpenAI のクライアント)に差し込むことができ、モデルはどのツールが利用可能で、どう呼び出すかを即座に発見します。

MCP 以前は、AI 統合のたびにツール定義、JSON スキーマ、呼び出しディスパッチャーを個々のアプリに配線する必要がありました。MCP はそれを単一プロトコルに集約します: サーバーを 1 度書けば、すべてのモデルが使えます。すでに業務 API の山 — ERP クエリ、CRM ルックアップ、ファイル生成、課金関数 — を持つ Delphi ショップにとって、MCP はそれらの API を AI からアクセス可能にする最も安価な経路です。

このチュートリアルでは、TsgcAI_MCP_Server コンポーネントを使って Delphi で MCP サーバーを構築し、動作する get_weather ツールを公開し、stdio と HTTP 両方のトランスポートで Claude Desktop に接続する手順を解説します。

少しの歴史で MCP を位置付けます。Anthropic は 2024 年末、互換性のないツール呼び出し形式の乱立 — OpenAI の関数呼び出し JSON、Google Gemini のグラウンディングツール、各ベンダー独自のスキーマ — への回答として仕様を公開しました。読者がこれを読む頃には、MCP サポートは Claude Desktop、Cursor、Continue、Zed、ほとんどの最新 AI IDE に出荷されています。OpenAI は 2026 年初頭に互換性を発表しました。つまり: 今日 MCP サーバーを構築することは、顧客が今日も明日も使うかもしれないあらゆるクライアントに対して支払いが続く一度きりの投資です。

MCP の構成要素

MCP サーバーは 3 種類の機能を公開できます:

ツール

型付き入力を持つ呼び出し可能関数。モデルがいつ呼び出すかを決定。例: get_weather(city)create_invoice(customer, lines)
リソース

モデルが取得できる読み取り専用データ。各リソースは URI を持つ。例: file:///docs/handbook.mddb://customers/12345
プロンプト

ユーザー(モデルではなく)が選択する再利用可能なプロンプトテンプレート。例: 「フォーマルなスペイン語に翻訳」または「会議メモを要約」テンプレート。

プロトコルは JSON-RPC 2.0 を 2 つのトランスポートのいずれかで使用します: stdio(サーバーは子プロセス。メッセージは stdin/stdout で流れる)または、サーバーからクライアントへのストリームに Server-Sent Events を使う HTTP。stdio は Claude Desktop のようなデスクトップ統合のデフォルト。HTTP はサーバーが別マシンで動作したり複数クライアントに提供したりするときの正解です。

コンポーネントは両トランスポートを単一 API の背後に隠します。ツールハンドラーを 1 度書き、起動時にトランスポートを選びます。同じコードが Claude Desktop ヘルパー、ネットワーク到達可能な HTTP サービス、または同じプロセス内で両方として動作します。1 つの箱で 30 以上の MCP サーバーを別ポートと 1〜2 個の stdio バイナリで多重化し、すべて同じ Delphi コードベースから提供するショップを見たことがあります。

プロジェクトセットアップ

uses 句に sgcAI_MCP_Server を追加し、フォームに TsgcAI_MCP_Server をドロップ(またはコンソールアプリではコードで生成)し、イベントを結線し、起動します。コンポーネントは JSON-RPC フレーミング、メッセージルーティング、機能アドバタイズメントを処理します。

uses
  sgcAI_MCP_Server, sgcAI_MCP_Classes;

procedure TForm1.FormCreate(Sender: TObject);
begin
  oMCP := TsgcAI_MCP_Server.Create(Self);
  oMCP.ServerInfo.Name    := 'weather-mcp';
  oMCP.ServerInfo.Version := '1.0.0';

  // Wire handlers
  oMCP.OnListTools := DoListTools;
  oMCP.OnCallTool  := DoCallTool;

  // Stdio transport for Claude Desktop integration
  oMCP.Transport := mtStdio;
  oMCP.Active    := True;
end;

ツールの実装: get_weather

ツールには 2 つのハンドラーが必要です。OnListTools はクライアントにどのツールが存在し、どんな引数を受け付けるかを伝えます。OnCallTool が実際に呼び出しを実行します。両ハンドラーはプレーンな Delphi イベント — JSON 構築は手作業なし。

procedure TForm1.DoListTools(Sender: TObject;
  const aTools: TsgcAI_MCP_Tools);
var
  oTool: TsgcAI_MCP_Tool;
begin
  oTool := aTools.Add;
  oTool.Name        := 'get_weather';
  oTool.Description := 'Return the current weather for a city.';
  oTool.InputSchema :=
    '{"type":"object",' +
     '"properties":{' +
       '"city":{"type":"string","description":"City name, e.g. Madrid"},' +
       '"units":{"type":"string","enum":["metric","imperial"],"default":"metric"}' +
     '},' +
     '"required":["city"]}';
end;

procedure TForm1.DoCallTool(Sender: TObject;
  const aRequest : TsgcAI_MCP_ToolCall;
  const aResult  : TsgcAI_MCP_ToolResult);
var
  vCity, vUnits, vReport: string;
begin
  if aRequest.Name = 'get_weather' then
  begin
    vCity  := aRequest.Arguments.S['city'];
    vUnits := aRequest.Arguments.S['units'];
    if vUnits = '' then vUnits := 'metric';

    // Call your own weather backend
    vReport := MyWeatherService.Fetch(vCity, vUnits);
    aResult.AddText(vReport);
  end
  else
    aResult.Error('Unknown tool: ' + aRequest.Name);
end;

これが完全な実装です。コンポーネントはスキーマに対する入力検証、エラー整形、根底にある JSON-RPC エンベロープを処理します。あなたはビジネスロジックに集中します。

良いツールを設計するための原則をいくつか、苦労して学んだもの:

リソースの公開

ツールはアクション用。リソースはモデルが読めるデータ用です。リソースハンドラーは指定された URI のコンテンツを返します。ドキュメントサーバーは docs/ 下のすべての Markdown ファイルをリソースとして公開するかもしれません。

oMCP.OnListResources := DoListResources;
oMCP.OnReadResource  := DoReadResource;

procedure TForm1.DoListResources(Sender: TObject;
  const aResources: TsgcAI_MCP_Resources);
var
  vFiles: TStringDynArray;
  i     : Integer;
  oRes  : TsgcAI_MCP_Resource;
begin
  vFiles := TDirectory.GetFiles('C:\docs', '*.md');
  for i := 0 to High(vFiles) do
  begin
    oRes := aResources.Add;
    oRes.URI         := 'file:///' + StringReplace(vFiles[i], '\', '/', [rfReplaceAll]);
    oRes.Name        := ExtractFileName(vFiles[i]);
    oRes.MimeType    := 'text/markdown';
    oRes.Description := 'Documentation page';
  end;
end;

procedure TForm1.DoReadResource(Sender: TObject;
  const aURI: string; const aResult: TsgcAI_MCP_ResourceResult);
var
  vPath: string;
begin
  vPath := StringReplace(Copy(aURI, 9, MaxInt), '/', '\', [rfReplaceAll]);
  if FileExists(vPath) then
    aResult.AddText(TFile.ReadAllText(vPath))
  else
    aResult.Error('Resource not found');
end;

stdio vs HTTP トランスポート

サーバーがどこで動作し、誰が使うかに基づいてトランスポートを選んでください。

トランスポート 用途 長所 短所
stdio ローカルデスクトップツール(Claude Desktop、Cursor) 設定ゼロ、ポート不要、OS がプロセスライフサイクルを処理 プロセスあたり 1 クライアント、リモートアクセスなし
HTTP / SSE 共有企業サーバー、ウェブクライアント、マルチテナント 多数のクライアント、ネットワーク到達可能、TLS、認証 ホスティング、ポート管理、認証設計が必要

トランスポートの切り替えはワンライナー:

// Stdio (default for desktop)
oMCP.Transport := mtStdio;

// HTTP listener on port 8080
oMCP.Transport       := mtHTTP;
oMCP.HTTPOptions.Host := '0.0.0.0';
oMCP.HTTPOptions.Port := 8080;
oMCP.HTTPOptions.TLS.Enabled  := True;
oMCP.HTTPOptions.TLS.CertFile := 'cert.pem';
oMCP.HTTPOptions.TLS.KeyFile  := 'key.pem';
oMCP.Active := True;

Claude Desktop から接続する

stdio の場合、claude_desktop_config.json を編集してコンパイル済み .exe を指すエントリを追加します。Claude Desktop はオンデマンドでプロセスを起動します。

{
  "mcpServers": {
    "weather": {
      "command": "C:\\Tools\\weather-mcp.exe",
      "args": []
    }
  }
}

Claude Desktop を再起動すると、プロンプト領域に小さなツールアイコンが表示されます。「東京の天気は?」と尋ねれば、Claude があなたの Delphi ツールを呼び出します。

HTTP の場合、任意の MCP 対応クライアントを https://your-host:8080/mcp に向けます。同じ Delphi サーバーは、Claude Desktop インスタンス、Cursor セッション、カスタム Delphi MCP クライアントを同時に喜んで処理します。

プロンプト: 過小評価された 3 番目の機能

ツールとリソースが注目を浴びますが、プロンプトは静かな主力です。MCP プロンプトは再利用可能なテンプレート — 「この会議を要約」「アクションアイテムを抽出」「フォーマルなスペイン語に翻訳」 — で、ユーザーが MCP クライアントの UI メニューから呼び出します。モデルはユーザーが提供する任意のコンテキストに対してそのプロンプトを実行します。

社内ツールではこれは金です。サポートチームは Claude Desktop 内で会社公認のプロンプトの厳選リストを得ます。営業チームは会社のトーンを既に知っている「フォローアップメール下書き」プロンプトを得ます。プロンプトエンジニアリングを学ぶ必要はありません — テンプレートを選ぶだけです。

oMCP.OnListPrompts := DoListPrompts;
oMCP.OnGetPrompt   := DoGetPrompt;

procedure TForm1.DoListPrompts(Sender: TObject;
  const aPrompts: TsgcAI_MCP_Prompts);
var
  oPrompt: TsgcAI_MCP_Prompt;
begin
  oPrompt := aPrompts.Add;
  oPrompt.Name        := 'summarise_meeting';
  oPrompt.Description := 'Turn a meeting transcript into bullet decisions and actions.';
  oPrompt.AddArgument('transcript', 'Plain-text transcript', True);
end;

procedure TForm1.DoGetPrompt(Sender: TObject;
  const aRequest: TsgcAI_MCP_PromptRequest;
  const aResult : TsgcAI_MCP_PromptResult);
begin
  if aRequest.Name = 'summarise_meeting' then
    aResult.AddMessage('user',
      'You are a meeting note taker. Read the transcript and produce: ' +
      '(1) a 3-sentence summary, (2) decisions taken, (3) action items ' +
      'with owners and due dates.' + sLineBreak + sLineBreak +
      aRequest.Arguments.S['transcript']);
end;

本番考慮事項

MCP サーバーを本番に出す前に予算化すべき事項:

デバッグのヒント

MCP は AI クライアントの中で見えずに動作するため、最初に何かおかしくなったときデバッグが難しいです。3 つの習慣が何時間も節約します:

まとめ

MCP は内部 Delphi API を、クライアントごとの配線なしにあらゆるモダンな AI クライアントが使えるものに変えます。TsgcAI_MCP_Server コンポーネントは JSON-RPC フレーミング、トランスポート、機能アドバタイズメントを処理し、あなたを実際のツール本体の記述に専念させます。1 つの読み取り専用ツールから始め、モデルがどう使うかを見て、書き込み、リソース、プロンプトに拡張してください。

社内で MCP をロールアウトして得た最大の教訓: 価値は「Claude が今や私たちの API を使える」ではありません。価値は「会社の誰もが API を学ばずに自然言語で API を使える」ことです。それは誰が何をできるかを変え、そこに実際の生産性向上が生まれます。

対応するクライアントが必要ですか?任意の MCP サーバーを消費する Delphi アプリの構築には TsgcAI_MCP_Client を参照してください。AI コンポーネントが初めての方は、はじめにハブ が 5 分でインストール手順を案内します。