---
title: "ChimrのMCPセットアップを手で書かせないために --mcp-install と --mcp-config を足した"
description: "Chimr を Claude Code や Claude Desktop の MCP サーバーとして使うときの設定を、ユーザーに手で書いてもらう前提から、Chimr 自身が `claude mcp add` コマンドや JSON を吐く前提に切り替えました。`--mcp-install` `--mcp-config` `--mcp` の3つの CLI フラグの実装と、bundle path を `CommandLine.arguments[0]` から取る判断をまとめます。"
category: "AI"
tags: ["Chimr","MCP","Claude Code","Swift","macOS"]
publishedAt: "2026-05-29"
lastmod: "2026-05-29"
---

[Chimr](https://chimr.labee.dev/) は macOS 用のメニューバーアプリで、カレンダー予定のリマインダー、Web 会議リンクの自動オープン、そして [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) サーバー機能を提供しています。クライアント側の設定 JSON を「ユーザーに手で書いてもらう」前提から、「Chimr 自身が吐き出す」前提に切り替えました。`--mcp-install` / `--mcp-config` / `--mcp` の3つの CLI フラグで、それぞれ Claude Code 用のコマンド出力 / 設定 JSON 出力 / MCP サーバー本体起動を担当しています。

```bash
# Claude Code に登録
$(/Applications/Chimr.app/Contents/MacOS/Chimr --mcp-install)
# => claude mcp add chimr --scope user -- '/Applications/Chimr.app/Contents/MacOS/Chimr' --mcp

# Claude Desktop / Cursor / Cline などの mcpServers JSON が要るクライアント向け
/Applications/Chimr.app/Contents/MacOS/Chimr --mcp-config
# => 設定 JSON を標準出力に吐く
```

::card[https://chimr.labee.dev/]

この記事では、MCP セットアップの最初の壁になっていた bundle path 入力、`--mcp-install` / `--mcp-config` の中で `CommandLine.arguments[0]` を呼ぶ理由、そして `--mcp` 本体が `StdioTransport` で動く構成を扱います。

## MCP セットアップで最初に詰まるのは bundle path

Chimr の MCP サーバーは、Claude Code や Claude Desktop、Cursor、Cline といった MCP クライアントから呼び出して使います。クライアント側はおおむね「コマンドのパスと引数」を設定 JSON で持ちます。Chimr の場合は次の形です。

```json
{
  "mcpServers": {
    "chimr": {
      "command": "/Applications/Chimr.app/Contents/MacOS/Chimr",
      "args": ["--mcp"]
    }
  }
}
```

`command` に bundle path をそのまま書きます。問題は、この絶対パスをユーザーが手で書く前提が崩れやすいことです。

- Mac App Store からのインストールなら `/Applications/Chimr.app/Contents/MacOS/Chimr`
- DMG を別の場所へ展開した人もいる
- 開発ビルドなら Xcode の DerivedData 配下
- 自分のホーム配下の `~/Applications/` に置いている人もいる

「アプリ名を `mdfind` してください、その結果のパスをコピペしてください」とドキュメントに書くのは、ドキュメントとしては成立しても、ユーザーから見れば余計な手順です。FAQ で「コピペする `command` の値が分からない」という問い合わせが続いたあと、Chimr 側で `command` を出してもらう方針に切り替えました。

## `CommandLine.arguments[0]` で自分の bundle path を取る

実装は Swift の `main.swift` 冒頭で完結しています。CLI 引数を解釈する前段で、フラグを見て対応する分岐に振り分けます。

```swift
let arguments = CommandLine.arguments

if arguments.contains("--mcp-install") {
  let execPath = CommandLine.arguments[0]
  let scope = arguments.contains("--local") ? "local" : "user"
  print("claude mcp add chimr --scope \(scope) -- '\(execPath)' --mcp")
  exit(0)
}
```

`CommandLine.arguments[0]` には、プロセスを起動したコマンドが渡したパス（C/POSIX の `argv[0]` 相当）が入ります。MCP クライアントは設定 JSON の `command` フィールドに絶対パスを書いて Chimr を起動するため、この経路では `argv[0]` から実行ファイルのフルパスが取れます。Mac App Store からインストールされた Chimr なら `/Applications/Chimr.app/Contents/MacOS/Chimr`、DMG を別の場所に展開していればそのパスが返ります。ユーザーがどこに置いていても、Chimr 自身が「自分の path」を答えます。

`--mcp-install` は標準出力に `claude mcp add chimr ...` を吐きます。シェルでそのまま実行してもらえる形なので、ユーザー側の作業は次の**1行**になります。

```bash
$(/Applications/Chimr.app/Contents/MacOS/Chimr --mcp-install)
```

`--local` を付ければ `--scope local`、付けなければ `--scope user` に切り替わります。Claude Code をユーザー全体で使うか、現在のプロジェクトでだけ使うかをここで選びます。

## JSON が要るクライアント向けには `--mcp-config`

Claude Code には `claude mcp add` という CLI コマンドが用意されていますが、Claude Desktop や Cursor、Cline などの MCP クライアントは設定 JSON を直接編集する形を取ります。こちら向けには `--mcp-config` を用意しました。

```swift
if arguments.contains("--mcp-config") {
  let execPath = CommandLine.arguments[0]
  let escapedPath = execPath
    .replacingOccurrences(of: "\\", with: "\\\\")
    .replacingOccurrences(of: "\"", with: "\\\"")
  let config = """
    {
      "mcpServers": {
        "chimr": {
          "command": "\(escapedPath)",
          "args": ["--mcp"]
        }
      }
    }
    """
  print(config)
  exit(0)
}
```

`CommandLine.arguments[0]` のパス文字列をそのまま JSON に埋め込むと、パスにバックスラッシュやダブルクォートが含まれる場合に壊れます。JSON String のエスケープを最低限通してから出力しています。クライアント側の `mcpServers` セクションへコピペしてもらえれば、そのまま動きます。

## `--mcp` が呼ばれたら `StdioTransport` で MCP サーバーを起動する

3つ目のフラグ `--mcp` が、実際の MCP サーバー本体を起動します。

```swift
if arguments.contains("--mcp") {
  let semaphore = DispatchSemaphore(value: 0)
  Task {
    do {
      try await MCPServer.run()
    } catch {
      fputs("MCP Server error: \(error)\n", stderr)
      exit(1)
    }
    semaphore.signal()
  }
  semaphore.wait()
  exit(0)
}
```

`MCPServer.run()` の中身は [MCP の Swift SDK](https://github.com/modelcontextprotocol/swift-sdk) を使って `Server` を組み立て、`StdioTransport` を渡して起動します。クライアントは Chimr のプロセスを `command + args` で立ち上げ、その標準入出力越しに MCP プロトコルを交わします。`stdio` 経由なので、Chimr の GUI を起動する代わりに「サブプロセスとして CLI 実行」する経路です。

サーバーは `chimr_ping`、`chimr_get_today_events`、`chimr_get_events`、`chimr_get_events_range`、`chimr_join_video_meeting`、`chimr_show_notification` といった tool を公開しています。Claude などの MCP クライアントから「今日の予定を見せて」「次の会議に参加して」「ToDo が終わったら通知して」のように頼めば、対応する tool が呼び出される設計です。

::card[https://apps.apple.com/lt/app/chimr-meeting-reminder/id6746831187?mt=12]

## 「ユーザーに JSON を書かせない」を CLI に倒すと収まりがいい

Chimr の場合、`command` に渡したい絶対パスが Chimr 自身しか正しく答えられない情報でした。手順書を整備する選択肢もありましたが、`CommandLine.arguments[0]` という Swift の標準機能で完結する以上、Chimr 自身に答えさせるほうが手数が少なくて済みます。`--mcp-install` と `--mcp-config` を足してからは、**MCP セットアップに関する問い合わせが目に見えて減りました**。

MCP サーバーを配布するアプリ側で「クライアント向けに setup を吐く CLI フラグを用意しておく」アプローチは、手順書を維持し続ける運用よりも安定します。今後ほかの自前 MCP サーバーを作るときも、同じ設計から始めるつもりです。