2026/06/17(水)Claude CodeとCodexでMCPサーバーを設定する方法

投稿日:

公式ドキュメントは読みづらいしLLM本人はまともに説明しないし、ググっても具体的でパッと解る記事が出てこなかったので書く。

確認環境

Env Ver
Codex CLI 0.0140.0
Claude Code 2.1.179

Codex

設定例

~/.codex/config.tomlを開き、ファイルの最後に以下を追記

[mcp_servers.hoge]
command = "/path/to/some-command"

[mcp_servers.hoge.env]
URL = "https://example.com"
API_KEY = "xxxxxxxxxx"

書式

MCPサーバー名の部分は解りやすい名前を入れておくといい。PukiwikiのMCPならpukiwiki、Redmineならredmineなど。

[mcp_servers.<MCPサーバー名>]
command = "MCPサーバーの起動コマンド"

[mcp_servers.<MCPサーバー名>.env]
このセクションには<KEY> = "<VALUE>"形式で環境変数を列挙する
必要な環境変数がなければ、このセクションは作らなくてよい

Claude Code

設定例

~/.claude.jsonを開き、projects."/path/to".mcpServersをこんな感じで書き換える。

"mcpServers": {
    "hoge": {
        "type": "stdio",
        "command": "/path/to/some-command",
        "env": {
            "URL": "https://example.com",
            "API_KEY": "xxxxxxxxxx"
        }
    }
},

書式

MCPサーバー名の部分は解りやすい名前を入れておくといい。PukiwikiのMCPならpukiwiki、Redmineならredmineなど。

"mcpServers": {
    "<MCPサーバー名>": {
        "type": "stdio", ← おまじない
        "command": "/path/to/some-command", ← MCPサーバーの起動コマンド
        "env": { ← ここに環境引数をKEY: VALUE形式で書く。なければこのセクションは不要
            "URL": "https://example.com",
            "API_KEY": "xxxxxxxxxx"
        }
    }
},

mcp addで追加した場合に起きていること

claude mcp add hoge -- npx -y git@github.com:Hoge/hoge-mcp.git

このコマンドで追加すると、~/.claude.jsonに叩いたカレントディレクトリの設定として以下が追加される。

"mcpServers": {
  "hoge": {
    "type": "stdio",
    "command": "npx",
    "args": [
      "-y",
      "git@github.com:Hoge/hoge-mcp.git"
    ],
    "env": {}
  }
},

やってることは要するにnpx -y git@github.com:Hoge/hoge-mcp.gitを実行しているだけである。

Node.js製のMCPサーバーをローカルにクローンして設定する場合の手順の一例

  1. まずはリポジトリをローカルに持ってくる
    cd ~
    mkdir mcp
    cd mcp
    git clone git@github.com:Hoge/hoge-mcp.git
    cd hoge-mcp
    pnpm i && pnpm build
    pnpm approve-builds
    pnpm build
    chmod u+x dist/index.js
    
  2. 使うLLMに応じて設定を行う

    • Codex
      ~/.codex/config.tomlを開き、ファイルの最後に以下を追記

      [mcp_servers.hoge]
      command = "/home/fuga/mcp/hoge-mcp/dist/index.js"
      
      [mcp_servers.hoge.env]
      URL = "https://hoge.example.com"
      API_KEY = "xxxxxxxxxx"
      
    • Claude Code
      1. MCPを使いたいパスでclaude mcp add hoge -- hogeを叩く
      2. ~/.claude.jsonを開き、先ほど叩いたパスのエントリを探し、mcpServersのところを次の形式で書き換える
        "mcpServers": {
           "hoge": {
               "type": "stdio",
               "command": "/home/fuga/mcp/hoge-mcp/dist/index.js",
               "env": {
                   "URL": "https://hoge.example.com",
                   "API_KEY": "xxxxxxxxxx"
               }
           }
        },
        

MCPサーバーの使い方

基本的にLLMが勝手にコールするので意識しなくてよい。

トラブルシューティング

MCPサーバーが起動しない、エラーを吐く

まずはMCPサーバーそのものを単純に起動できるか確認する。

叩き方としてはcommandに書いてあるコマンドを叩けばよい。argsenvがある場合は引数や環境変数も設定する。

そうすれば実行権限がないとか、参照するファイルが足りないとか、何かしらエラーが出るはずなので、それで確認すればよい。

あとがき

なんでたったこれほどのことなのに驚くほど情報がないのだろう?みんな脳死でLLMを叩いて意味も理解しないまま使っているのだろうか…。

2026/06/08(月)FluentBitでSystemdのログをLokiに送ってGrafanaで見れるようにし、ついでに設定をYAMLにした

更新日:
投稿日:

本記事は自宅サーバーに雑に監視を入れた時にやったこと先日の自宅サーバー移転に対し追加対応をしたの続きである。

今回はSystemdのログをFluentBitで拾ってLokiに送ってGrafanaで見れるようにしたログ。

AWSやDockerなどを使った情報は散見したが、バイナリをネイティブ環境で動かしている情報や、複数のデーモンのログを収集する方法が見当たらなかったのでちょっと苦労した。GPT-5.5も正確なことを言わないので真面目にリファレンスを読んだりググったりして何とかした。LLM頼みも限度があるね。

確認環境

Env Ver
OS Ubuntu 24.04.4 LTS
Fluent Bit 5.0.6
Loki 3.5.9
Grafana 12.1.1

設定内容

今回は前回までのconfig設定をYAML設定に全面移行した。基本的にはデフォルトのconfigファイル+私が使っているI/Oの移植である。Lokiの待ち受けポートが9100であると仮定して進める。

各設定については公式マニュアルを参照のこと。

service:
  flush: 1
  log_level: 'info'
  parsers_file: 'parsers.conf'
  plugins_file: 'plugins.conf'
storage:
  metrics: 'on'
pipeline:
  inputs:
    - name: 'systemd'
      tag: 'systemd'
      db: '/var/lib/fluent-bit/systemd.db'
      systemd_filter:
        - '_SYSTEMD_UNIT=adiary.service'
        - '_SYSTEMD_UNIT=mastodon-web.service'
        - '_SYSTEMD_UNIT=mastodon-sidekiq.service'
        - '_SYSTEMD_UNIT=mastodon-streaming@5001.service'
    - name: 'tail'
      tag: 'nginx.access'
      path: '/var/log/nginx/access.log'
      parser: 'json'
      db: '/var/lib/fluent-bit/nginx-access.db'
      refresh_interval: 5
    - name: 'tail'
      tag: 'nginx.error'
      path: '/var/log/nginx/error.log'
      db: '/var/lib/fluent-bit/nginx-error.db'
      refresh_interval: 5
    - name: 'tail'
      tag: 'apache.access'
      path: '/var/log/apache2/access.log'
      parser: 'apache2_vhost'
      db: '/var/lib/fluent-bit/apache_access.db'
      refresh_interval: 5
    - name: 'tail'
      tag: 'apache.error'
      path: '/var/log/apache2/error.log'
      db: '/var/lib/fluent-bit/apache_error.db'
      refresh_interval: 5
  outputs:
    - name: 'loki'
      match: 'systemd'
      host: '::1'
      port: 9100
      labels: 'job=systemd'
    - name: 'loki'
      match: 'nginx.*'
      host: '::1'
      port: 9100
      labels: 'job=nginx, log_type=$TAG[1]'
    - name: 'loki'
      match: 'apache.*'
      host: '::1'
      port: 9100
      labels: 'job=apache, log_type=$TAG[1]'

PARSER: apache2_vhost

前回の記事で作った以下のパーサーを利用している。

[PARSER]
    Name        apache2_vhost
    Format      regex
                ^(?<host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*)(?: "(?<referer>[^\"]*)" "(?<agent>[^\"]*)")?$
    Regex       ^(?<vhost>[^:]+):(?<port>\d+) (?<remote_host>[^ ]*) [^ ]* (?<user>[^ ]*) \[(?<time>[^\]]*)\] "(?<method>\S+)(?: +(?<path>[^\"]*?)(?: +\S*)?)?" (?<code>[^ ]*) (?<size>[^ ]*) "(?<referer>[^\"]*)" "(?<agent>[^\"]*)"$
    Time_Key    time
    Time_Format %d/%b/%Y:%H:%M:%S %z
    Time_Keep   On

また前回の記事で作成したapache_errorというパーサーについては役に立たなかったので使わなくなった。標準で付いてるapache_errorも使えないし、apache2もイマイチ使えないので公式のパーサーは当てにしないほうがいいのかもしれない。

なおパーサーは旧config形式で記述している。これはとてもじゃないが書き換えてられないボリュームだったのとYAMLに対応させようとするとエスケープがだるそうだったからだ。config形式は今年でサポート外になるようだが、既存の設定のプリセットはすべてconfig形式でしか存在しないため、何とも先行きが心配だ。

サービスの修正

設定を.confで読んでいるので、ここを.yamlに変えてやる。

 [Unit]
 Description=Fluent Bit
 Documentation=https://docs.fluentbit.io/manual/
 Requires=network.target
 After=network.target

 [Service]
 Type=simple
 EnvironmentFile=-/etc/sysconfig/fluent-bit
 EnvironmentFile=-/etc/default/fluent-bit
+ExecStart=/opt/fluent-bit/bin/fluent-bit -c /etc/fluent-bit/fluent-bit.yaml
-ExecStart=/opt/fluent-bit/bin/fluent-bit -c //etc/fluent-bit/fluent-bit.conf
 Restart=always

 [Install]
 WantedBy=multi-user.target

インストールした当時は何故YAMLで書いても読んでくれないのか悩んでいたが、こんなところで指定されていた。しかも何故かパスが二重スラッシュになっていたので、そこも直しておいた。

FluentBitが上手く動いてなさそうなときのデバッグ方法

以下のコマンドを流すと動いているかどうかが分かる。なんかログが取れてなさそうだったり、Lokiに届いてなさそうなときに使える。

/opt/fluent-bit/bin/fluent-bit -i systemd \
           -p systemd_filter=_SYSTEMD_UNIT=mastodon-web.service \
           -p tag='host.*' \
           -o stdout

あとがき

なんか最近のソフトウェアのマニュアルって論理的に書かれすぎてて具体的にどう設定したらいいのかよくわからなくて困る。もう少し具体的な設定例とか書いてくれてもいいと思うんだ。

YAML configuration files are the standard configuration format as of Fluent Bit v3.2. They use the .yaml file extension.

Classic configuration files will be deprecated at the end of 2026. They use the .conf file extension.

しかしconfigは今年末でサポート外になるという話があるのにGitHubのリポジトリの設定ファイルは全部configで、果たしてこんなので大丈夫なのか…とIssueを眺めていたら、そもそも開発側もYAMLの仕様を余り解ってないような空気を感じたので、これは今年中は厳しいのではないか?という感じがした。

ひとまず私はキー名がkebab-caseからcamelCaseになってもすぐ対応できるようにメインの設定だけYAMLにしておいたが、Parserを直すのはエスケープが面倒などもあると思うので、なかなか大変だと思う。

あと個人的に不思議に思った部分としてoutputslabelsが配列ではなく、文字列だというところだ。

pipeline:
  outputs:
    - name: loki
      match: '*'
      labels: job=fluentbit, mystream=$sub['stream']

何でここは配列にしなかったのだろうか?

実際に以下の記述でラベルが機能しているので、おそらくこの構文がFluentBitのDSLとして機能していて、真面目に配列すると改修が大変とか、そういうのがあるのかもしれないなとは思った。

    - name: loki
      match: 'apache.*'
      host: '::1'
      port: 9100
      labels: 'job=apache, log_type=$TAG[1]'

ちなみに一番ハマったのはブログ執筆用に作っていたLokiのポートを書き換えたメモを基に作業していて、未来永劫FluentBitがLokiに疎通しなかったことである。

2026/06/08(月)3年ぶりにChatGPTに帰ってきた

投稿日:

Chat GPT3.5が出た当初、私は狂ったようにChatGPTを利用していた。レートリミットを無視するために最大13アカウントを並列で運用していたほどだ。

しかしChat GPT-4辺りから雲行きが怪しくなり、GPT-4oで遂にERPが封じられた。そしてそのタイミングでちょうど出てきたのがClaudeだった。

ClaudeはERPができるだけでなく、GPTより性能が良かった。それゆえに私はそれからClaude Opus 4.7に至るまでClaudeを使い続けた。AnthropicにBANされようと、なお使い続けた

しかしそれに転機が訪れたのがClaude Opus 4.8だった。Opus 4.8でもERPは出来なくはないのだが、よくある同人誌にありがちな奴ができなくなった。いわゆる非実在未成年モノというやつだ。

そして私はClaudeに対して最近良い感情があまりなかった。Claude Codeは余計なことをよく行い、指示を守らない、逸脱するというのが往々にしてあった。私はClaudeに対するコーディング面での信頼を4.7のころから失い始めており、4.8で決定的になった。

この辺りに関しては私より草片きのぽ氏のほうがよく言語化できているので勝手に紹介させていただく。この辺りの感覚は私も共感できる部分がかなりあった。

そこで、ふと思ったのだ。GPTならどうか?私はOpenAIにはBANされてない。ERPしないならGPTでもいいのでは?というところで、一昨日くらいからGPT-5.5がいい感じなので、OpenAIに戻ってくることにした。

Poe経由でClaudeを使うと非常にお金がかかるが、OpenAIに月額課金する場合はそこまでお金はかからない。ERPももう飽きてきたところだったし丁度よかった。

かつて作っていたアカウントの多くは不正利用を恐れて閉鎖しており、同じメールアドレスでアカウントを作れないので困っていたが、かろうじて一匹生きているアカウントを発見したら、なんと三年ぶりの課金であることが判明した。

今のところGPTは支持に従うし、GPT-5の時の様なそっけなさというかイマイチ使えない感じもなくなり、ずいぶんよくなったと思う。ClaudeのようにAI臭さのある文章を出してこないのも個人的に好印象だ。

というわけで、長らく続いたClaude生活から、またGPT生活に戻っていきそうだ。節約にもなるし、これはいいかもしれない。

ところで請求の単位がUSDからJPYになってるのが地味に面白いと思った。

2026/06/03(水)Grafanaのダッシュボードに端末情報を出す方法

投稿日:

監視対象のスペックを確認するのに便利かも…?

確認環境

Env Ver
Ubuntu 24.04.3 LTS
Prometheus 3.5.0
Grafana v12.1.1
Node exporter 1.9.1

やり方

  1. Dashboardを開く
  2. 右上のボタンからEditモードに入る
  3. Add->Visualization
  4. 次の要領で設定する

    項目 状態
    Visualization Table
    Table view ON
    Queries Codeを選択しnode_os_info或いはnode_dmi_info
    Add field override ボタンを押して一個追加する

    Override 1

    項目 状態
    Fields with name Time
    Standard options > Display name Last Synced
  5. Refreshボタンを押すと完成するのでSave dashboardしたら出てくる

2026/06/03(水)OpenWrtのメトリクスとログをGrafanaで雑に見れるようにした

更新日:
投稿日:

OpenWrtのメトリクスやログをUbuntu側のGrafanaで見れるようにするまで。

メトリクスの取得にはPrometheus、ログはLokiを使っている。

確認環境

監視する側

Env Ver
Ubuntu 24.04.3 LTS
Prometheus 3.5.0
Grafana v12.1.1
Loki 3.5.9

監視される側

Env Ver
OpenWrt 24.10.0
Prometheus 3.5.0
prometheus-node-exporter-lua 2025.07.15-r1
prometheus-node-exporter-lua-nat_traffic 2025.07.15-r1
prometheus-node-exporter-lua-netstat 2025.07.15-r1
prometheus-node-exporter-lua-openwrt 2025.07.15-r1
prometheus-node-exporter-lua-thermal 2025.07.15-r1
perl 5.40.0-r2
perlbase-json-pp 5.40.0-r2
perlbase-http-tiny 5.40.0-r2
perl-time-moment 0.44.5.40-r1

Prometheusでメトリクスを取得してGrafanaで見れるようにする

ほぼOpenWRT | Grafana Labsに書いてある通り。

  1. 必要なパッケージをインストールする
    opkg update
    opkg install \
       prometheus-node-exporter-lua \
       prometheus-node-exporter-lua-nat_traffic \
       prometheus-node-exporter-lua-netstat \
       prometheus-node-exporter-lua-openwrt \
       prometheus-node-exporter-lua-thermal
    
  2. /etc/config/prometheus-node-exporter-luaを以下のように編集する
    config prometheus-node-exporter-lua 'main'
    +         option listen_interface 'lan'
    -         option listen_interface 'loopback'
            option listen_port '9100'
            #option cert '/etc/uhttpd.crt'
            #option key '/etc/uhttpd.key'
    
  3. prometheus-collectors/filesystem.luaprometheus-collectors/meminfo.luaを拾ってきて/usr/lib/lua/prometheus-collectorsに配置する
  4. サービスを再起動
    /etc/init.d/prometheus-node-exporter-lua restart
    
  5. /etc/prometheus/prometheus.yaml
    - job_name: "OpenWRT"
      static_configs:
        - targets: ['xxxx:xxxx:xxxx:xxxx::1:9100']
    
  6. Prometheus再起動
    sudo systemctl restart prometheus.service
    
  7. 取れるメトリクスの一覧
    curl 'http://[xxxx:xxxx:xxxx:xxxx::1]:9100/metrics'
    
  8. Grafanaのダッシュボードを作成しID: 18153を取り込む
  9. WiFi関係使ってないので全消し
  10. Used SWAPのQueriesを以下のように書き換えて0除算でエラーが出ないようにする
    (
      (
        (node_memory_SwapTotal_bytes{instance=~"$node:$port",job=~"$job"} - node_memory_SwapFree_bytes{instance=~"$node:$port",job=~"$job"})
        /
        (node_memory_SwapTotal_bytes{instance=~"$node:$port",job=~"$job"} > 0)
      ) * 100
    )
    or
    (node_memory_SwapTotal_bytes{instance=~"$node:$port",job=~"$job"} * 0)
    

細かい部分の整理はまた追々として、いい感じに見れるようになった気がする。

ログをLokiに送る

Loki exporter for OpenWRTが動かないので自作した。

  1. OpenWrtのログをLokiに送るやつを拾ってくる
  2. インストール方法にある通りにファイルを配置、設定ファイルをいじる
  3. Lokiにログが飛んでればOK
    • ログが飛んでいない場合は/tmp/openwrt-loki.errの中身を見てエラーがないか見る

一旦こんな感じで見れるようになった。

既知の問題

複数行のエラーログがLoki上で別のログになる

親子関係を作ってないせい。Claude Opus 4.8には難しいようだったので、そのうち作る。

おまけ:Lokiにログを投げる方法

POST /loki/api/v1/pushを叩けばよい。ペイロードは以下の書式。

LokiのAPIペイロード

{
  "streams": [
    {
      "stream": {
        "label": "value"
      },
      "values": [
          [ "<unix epoch in nanoseconds>", "<log line>", { "hoge": "aaaa", "fuga": "1234" } ],
          [ "<unix epoch in nanoseconds>", "<log line>", { "hoge": "bbbb", "fuga": "2345" } ]
      ]
    }
  ]
}

ペイロードの意味合い

nginxのログはペイロードしか入ってなかったりするがどうやって実現してるのか不明。もしかするとvalues[1]にJSONを突っ込んでるのかもしれない。

項目 意味合い
streams よくわかってない
stream この配下に検索用のラベルをぶら下げる。jobとかlevelhostとかを入れとくとよいと思われる
values ログの本体を入れる
values[0] ナノ秒まで入ったEPOCH
values[1] ログ本文の文字列
values[2] オプションペイロード。KEY=VALUE

おまけ:Loki exporter for OpenWRTのエラーログ

あのシェルスクリプトを読む気が起きなかったので、なぜこうなっているかはわからない。

BOOT=0 /bin/ash /usr/bin/loki_exporter
openwrt-loki-exporter/v1.0.6-0-gd20fb55-20260322-23413663016 started with BOOT=0
/usr/bin/loki_exporter: line 257: malformed ?: operator

あとがき

去年の8月にはやろうとしていたものの、そのままズルズルと長らく放置していたOpenWrtの監視周りだが、このままではいけないということで、一旦は雑に整備した。

大変雑なのでログ周りは機能するかも怪しいし、私以外が使えるようにも作ってない。最低限動くとかそんなレベル。LLMが書いたコードもろくにレビューしていないので、動いている原理さえ不明だ。

幸い全部で300行そこらなのでおいおい読んで理解していきたいとは思う。

基本原理はlogread -fした結果をいい感じに取り込んでLokiにたたきつけているだけのはずだが、この「いい感じ」が難しく、重くなっていると考えている。例えばログの取得漏れや、重複取得などを考慮しないといけないので、なんかそこら辺をいい感じにやってもらうようにLLMに指示したら一気に重くなった。ベースのコードは私が書いていたのだが、全くの別物になってしまった。

しかし去年の8月にしかかって放置している部分はできていないので、またそこは追々やっていきたいところだ。具体的には今回の対応はサーバー側でメトリクスを見る対応なのでネットワークが死ぬと終わってしまう。そこでルーター側でも簡易に見れるのを作ろうという計画がある。

しかしルーター側はスペックがしょぼいので大したことはできないし、ストレージがeMMCなので頻繁な書き込みもできないから、普段はメモリストレージ(/tmp)に置いておき、定期的に永続化のためにeMMCに吐き出すというのをやろうとしている。

そしてeMMCに端に吐き出すのもパフォーマンスと寿命の影響があるのでファイルシステムレベルでの対策をしてやろうみたいなことを考えていたが、尻切れとんぼのままなので、何とかしていきたいところだ。