3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Swift Analyzer MCP Serverを実装してみた

Posted at

はじめに

Swift-Selena」(Swift Analyzer MCP Server)を公開しました。MCPの部分は、公式のSwift SDKを利用しています。

Serena があるのになぜ?」と思われるかもしれません。
Serena は優れたツールですが、Swift-SelenaはSwiftで実装することで以下を実現しました。自分がMCPを学習したい、実装したい、というモチベーションもありました。

ゼロ設定:Swiftがあれば動く(Python不要、LSP設定不要)
高速動作:ネイティブビルドによる高速化
完全な Swift 対応:SwiftSyntax による正確な構文解析
プライバシー重視:完全ローカル実行(外部送信ゼロ) ※ Serenaも同じですが

Swift-Selenaの名称は Serena リスペクトですが、実装は Swiftオリジナルの実装です。
生成 AI による Swift 開発効率化を、よりシンプルに、より高速に実現します。

この記事で分かること

この記事では、Swift SDKを使ったMCP Serverの実装方法を、Swift-Selenaの実装例を交えて解説します。

対象読者:

  • Swift でローカル MCP Server を実装したい方
  • MCP の仕組みを Swift のコードで理解したい方
  • Swift-Selena のような解析ツールを自作したい方

MCP サーバーとは

MCP (Model Context Protocol) は、LLM と外部システムを繋ぐ標準プロトコルです。
USB-C に例えられることが多いです。

  • USB-C:様々な機器を接続する標準規格
  • MCP:様々なシステムを LLM に接続する標準規格

公式ドキュメント
https://modelcontextprotocol.io/docs/getting-started/intro

Swift-Selena vs Serena

項目 Swift-Selena Serena
言語 Swift Python
対応言語 Swift専用 多言語(Python, TypeScript, PHP, Go, Java等)
解析エンジン SwiftSyntax LSP (Language Server Protocol)
セットアップ swift build のみ Python環境 + LSP設定
実行速度 ネイティブ(高速) インタープリター
リポジトリ BlueEventHorizon/Swift-Selena oraios/serena

Swift-Selenaの利点

  • 既存の開発環境だけで動作
  • SwiftSyntaxによる分析
  • ネイティブコンパイルによる高速実行

セットアップがゼロ

# Serena の場合
brew install uv
uv venv
source .venv/bin/activate
uv pip install serena-mcp
# LSP設定ファイルの作成...

# Swift-Selena の場合
swift build --configuration release
# 完了(Swiftは既にインストール済み)

解析精度

SwiftSyntax は Swift コンパイラと同じパーサーを使用しているため:

  • SwiftUI の Property Wrapper(@State, @Binding など)を完全理解
  • Swift 6 の最新構文に即座に対応
  • Macro 展開も正確に解析

実装

ここからは、Swift SDK を使った MCP Server の実装方法を、
Swift-Selena のコードを例に見ていきます。

MCP Sereverの機能

MCP Sereverは、MCPを通じてLLMにコンテキストを提供します。
これらのコンテキストには下記の表のように、プロンプト、リソース、ツールの形式があります。

Primitive Control Description Example
Prompts User-controlled あらかじめ定義されたテンプレートや指示で、LLMに指示をする形式 スラッシュコマンド、メニューオプション
Resources Application-controlled データやコンテンツを返す形式 ファイルの内容、Git履歴
Tools Model-controlled アクションを実行したり、情報を返す関数 分析情報取得、ファイル書き込み

初期化

Swift SDKでは、Serverの初期化時にcapabilitiesに対してこのMCP Serverが対応する機能を定義します。

SDK:Server
public final class Server: Sendable {
    public init(
        name: String,
        version: String,
        capabilities: Capabilities
    )

    public func start(transport: Transport) async throws
    public func withMethodHandler<Method: RequestMethod>(
        _ type: Method.Type,
        handler: @escaping @Sendable (Method.Params) async throws -> Method.Result
    ) async
}

下記のようにSwift-Selenaでは、ツールしか対応していません。
これはSwift-Selenaが、機能分析機能が中心ということがあり、他のPrimitiveに対応する意義が薄いからです。

また、ここでは具体的なツールのリストは含まれていません。
単純に「このサーバーはどのPrimitiveに対応しているか」の宣言を行います。

Swift-Selena - Server初期化

let server = Server(
    name: AppConstants.name,
    version: AppConstants.version,
    capabilities: .init(
        tools: .init()
    )
)
  • tools: .init() → 「ツール機能あり」
  • tools: nil → 「ツール機能なし」

機能リストを登録

「具体的にどんなツールがあるか」のリストを登録します。
ツールの登録は、withMethodHandler関数を使って行います。
ここでは、機能実装ではなくツールの説明になるので、下記のようなある意味定型のものになります。

Swift-Selena - ツール登録

await server.withMethodHandler(ListTools.self) { _ in
    ListTools.Result(tools: [
        Tool(
            name: ToolNames.initializeProject,
            description: "Initialize a Swift project for analysis. Must be called first.",
            inputSchema: .object([
                "type": .string("object"),
                "properties": .object([
                    ParameterKeys.projectPath: .object([
                        "type": .string("string"),
                        "description": .string("Absolute path to Swift project root")
                    ])
                ]),
                "required": .array([.string(ParameterKeys.projectPath)])
            ])
        ),
        Tool(
            name: ToolNames.findFiles,
            description: "Find Swift files in the project by pattern (glob-like search)",
            inputSchema: .object([
                "type": .string("object"),
                "properties": .object([
                    ParameterKeys.pattern: .object([
                        "type": .string("string"),
                        "description": .string("File name pattern (e.g., '*Controller.swift', 'User*')")
                    ])
                ]),
                "required": .array([.string(ParameterKeys.pattern)])
            ])
        )
        // 他15個のツール定義...
        // 完全なリストは GitHub を参照
    ])
}

機能(実行内容)を登録

「ツールが実際に何をするか」という、ツールの実行内容は下記のように登録します。

Swift-Selena - ツール実行
  await server.withMethodHandler(CallTool.self) { params in
      switch params.name {
      case ToolNames.findFiles:
          // パラメータ取得(JSONValueからString抽出)
          guard let args = params.arguments,
                let patternValue = args[ParameterKeys.pattern],
                case .string(let pattern) = patternValue else {
              throw MCPError.invalidParams("Missing pattern parameter")
          }

          // ビジネスロジック実行
          let files = try FileSearcher.findFiles(in: projectPath, pattern: pattern)

          // 結果を返す
          return CallTool.Result(
              content: [.text("Found \(files.count) files:\n" + files.joined(separator: "\n"))]
          )

      case ToolNames.listSymbols:
          guard let args = params.arguments,
                let filePathValue = args[ParameterKeys.filePath],
                case .string(let filePath) = filePathValue else {
              throw MCPError.invalidParams("Missing file_path parameter")
          }

          let symbols = try SwiftSyntaxAnalyzer.listSymbols(filePath: filePath)
          let symbolsText = symbols.map { "[\($0.kind)] \($0.name) (line \($0.line))" }
              .joined(separator: "\n")

          return CallTool.Result(content: [.text(symbolsText)])

      default:
          throw MCPError.invalidParams("Unknown tool: \(params.name)")
      }
  }

フロー

Clientからのリクエストを起点とした、全体のフローは下記のようになります。

起動

最後にServerを実行します

Swift-Selena - Server起動

// Stdio transport起動
let transport = StdioTransport(logger: logger)
try await server.start(transport: transport)

Swift-Selenaは、Local Server実行ですので、StdioTransportを選択しています。

SDKのアーキテクチャ

ここまでの実装で MCP Server の基本は理解できましたが、
Swift SDK の内部構造にも触れておきます。

SDKの全体構造を見てみましょう。
今までの説明は MCP SDK Layer の上位2層(Server と Handler)に関連していますが、
MCPの中枢である JSON-RPC Layer(プロトコルレイヤー)は、
SDKの中に(使う側からすると)隠蔽されています。

レイヤー構造

リクエスト処理フロー

先ほど、Swift-SelenaのServer起動の実装で触れた StdioTransportTransport Layer です。
Transport LayerJSON-RPC Layer と同じく、実装や設定を変更する必要はなく、ほぼ隠蔽されていますが、Transportのインスタンスは選択できます。

Transportの比較

下記に既に用意されているTransport実装を記載します。

特徴 StdioTransport HTTPTransport InMemoryTransport
通信方式 stdin/stdout HTTP/HTTPS メモリ
通信特性 プロセス間通信 ネットワーク通信 プロセス内通信
I/O あり(ブロッキング) あり(非同期) なし
パフォーマンス 高速 ネットワーク依存 最速
セットアップ シンプル 複雑(エンドポイント設定) 最もシンプル
デバッグ 中(ログで確認) 難(ネットワークツール必要) 簡単(メモリ内で完結)
適用例 CLIツール、デスクトップアプリ統合 マイクロサービス、API連携 ユニットテスト、統合テスト
認証方式 プロセス権限 HTTPヘッダー 不要
SDK実装状態 安定版 実験的(API変更の可能性) 安定版

※2025年10月13日現在

まとめ

Swift SDK を使えば、短時間で MCP Server を実装できます。

重要なポイント:

  1. capabilities で機能を宣言:Server 初期化時に対応機能を定義
  2. ListTools で一覧を返す:利用可能なツールのカタログ
  3. CallTool で実行:実際のビジネスロジックを実装

ローカル MCP Server の利点:

  • ✅ ソースコードが外部に送信されない
  • ✅ サーバーインフラ不要(AWS/Azure 等)
  • ✅ Swift ネイティブで高速実行

参考リンク

Swift-Selena を試してみたい方は

リポジトリをクローン
  git clone https://github.com/BlueEventHorizon/Swift-Selena.git
  cd Swift-Selena
リリースビルド
  swift build -c release -Xswiftc -Osize
登録スクリプトを実行
  # Claude Desktop に登録する場合
  chmod +x register-mcp-to-claude-desktop.sh
  ./register-mcp-to-claude-desktop.sh

  # Claude Code に登録する場合(特定プロジェクトで使用)
  chmod +x register-selena-to-claude-code.sh
  ./register-selena-to-claude-code.sh /path/to/your/swift/project

完了!Claude Desktop/Code を再起動すれば使えます

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?