Zenn
🎯

DartでMCPサーバーを作る

2025/03/24に公開

DartでMCPサーバー、実装したいですよね?というわけでSDKを作ってみました。

https://pub.dev/packages/straw_mcp

多くの言語でMCP SDKが実装されていますが、Dartにはまだありませんでした。試しにClaudeに書かせてみたらあっという間に動いてしまったので、せっかくなのでライブラリにして公開することにしました。なにぶんほぼすべてのコードを突貫で生成したので自分でも把握しきれていないところがありますがご了承ください。

こうもあまりに簡単だと、別に自分がライブラリを作らなくてもいいのでは?と思うのですが、まあ他にパッケージがありませんし。 いずれ公式あるいは熱心な誰かがもっと堅牢なMCP Dart SDKをきちんと作ってくれることを期待しています。でもAIで誰でも作れるのなら自分のパッケージを自分向けにメンテし続けるほうが気楽なような気もします。

(パッケージ登録にあたって再度検索したら、 mcp というそのものなパッケージが登録されていました。ただ中身が空っぽでしたが、名前だけ確保しておいたのかな?)

セットアップ

大半のパッケージと同様、プロジェクトを作ったらこれを実行するだけです:

dart pub add straw_mcp

サンプルコード

以下のコードはstdio経由で起動するシンプルなMCPサーバーのサンプルです。
リポジトリの example/stdio/stdio_server.dart に含まれています。

// example/stdio/stdio_server.dart
import 'dart:io';
import 'package:straw_mcp/straw_mcp.dart';

void main() async {
  // MCPサーバーの定義
  final handler =
      ProtocolHandler('MCP Example Server', '0.1.0', [
          withToolCapabilities(listChanged: true),
          withResourceCapabilities(subscribe: true, listChanged: true),
          withPromptCapabilities(listChanged: true),
          withLogging(),
          withInstructions(
            'This is a simple MCP server example that communicates via stdio.',
          ),
        ])
        // ツールを追加
        ..addTool(
          newTool('echo', [
            withDescription('Echoes back a message.'),
            withString('message', [
              required(),
              description('Message to echo back'),
            ]),
          ]),
          (request) async {
            final message = request.params['arguments']['message'] as String;
            return newToolResultText('Echo: $message');
          },
        )
        ..addTool(
          newTool('add', [
            withDescription('Adds two numbers together.'),
            withNumber('a', [required(), description('First number')]),
            withNumber('b', [required(), description('Second number')]),
          ]),
          (request) async {
            final a = (request.params['arguments']['a'] as num).toDouble();
            final b = (request.params['arguments']['b'] as num).toDouble();
            return newToolResultText('Result: ${a + b}');
          },
        );

  // stdio でサーバーを起動
  await serveStdio(
    handler,
    options: StreamServerOptions.stdio(logger: Logger('StreamServer')),
  );
}

APIは mcp-go をベースに諸々の変更を加えています。

あとは普通にコンパイルします:

dart compile exe stdio_server.dart -o stdio_server

MCPサーバーを動かすために他のツールをインストールする必要はありません。Dartはシングルバイナリを生成できるので、MCPクライアントが要求する位置にコピーするか、バイナリのパスを指定するだけです。

Claude Desktopに組み込む場合は以下のように設定を追記します:

{
  "mcpServers": {
    "dart-mcp-sample": {
      "command": "絶対パス/stdio_server",
      "args": []
    }
  }
}

設定を変更してからClaude Desktopを起動または再起動します。MCP関連のエラーメッセージが表示されず、ツールリストボタンをタップすると以下のスクリーンショットのようにツールが追加されていれば成功です。

利用可能なMCPツールに追加されている

使用例:

ツールaddのプロンプトの例。「ツールaddを使って1+1を計算して」

MCPサーバー作成にDartを使うメリット

  • シングルバイナリを生成できる: セットアップが簡単です。実行可能なバイナリをコピーすればおしまい。
  • Dartのエコシステムを利用できる: とは言え、Flutter関連以外のパッケージは他の言語と比べて充実しているとは言い難く…あまりメリットとも言えない気がしますね。
  • 普段使いの言語で実装できる: これが結構快適です。AIにコードを書かせるのであれば言語を問いませんが、まだまだ人間が介入する必要があるので、手慣れた言語だと頭を切り替えずに済みます。 バックエンドからMCPサーバーまで、Dartでフルスタックな開発ができますね!

よかったらバッジをいただけると嬉しいです。コーヒー代とモチベの足しになります。

Discussion

ログインするとコメントできます