MCPサーバ - KiCAD
概要
KiCAD MCPサーバは、電子回路設計ソフトウェアであるKiCADをAIアシスタントから操作可能にするためのModel Context Protocol (MCP) サーバである。
このMCPを使用することにより、Claude等のLLMがKiCADプロジェクトの管理、回路図の操作、PCBレイアウトの編集等を実行することができる。
KiCAD MCPサーバは、以下に示すような機能を提供する。
- KiCADプロジェクトファイルの読み込みと解析
- 回路図の情報取得
- PCB基板レイアウトの情報取得
- コンポーネントライブラリの管理
- ネットリストの生成と検証
- Design Rule Check (DRC) の実行
- Electrical Rule Check (ERC) の実行
MCPサーバの実装において、PythonまたはNode.jsを使用することができる。
KiCADはPython APIを提供しているため、Pythonによる実装が推奨される。
KiCAD MCPサーバは、Standard I/O (STDIO) トランスポートを使用してローカル環境で動作する。
これにより、Claude DesktopやCursor等のMCPクライアントと統合することができる。
以下の例では、PythonとFastMCPを使用したKiCAD MCPサーバの基本構造を示している。
from fastmcp import FastMCP
import pcbnew
import os
mcp = FastMCP("KiCAD MCP Server")
@mcp.tool()
def get_board_info(kicad_pcb_path: str) -> dict:
"""KiCAD PCBファイルから基板情報を取得する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
info = {
"board_name": os.path.basename(kicad_pcb_path),
"num_tracks": board.GetTracks().GetCount(),
"num_modules": len(list(board.GetFootprints())),
"board_thickness": board.GetDesignSettings().GetBoardThickness(),
}
return info
except Exception as e:
return {"error": str(e)}
@mcp.tool()
def run_drc(kicad_pcb_path: str) -> str:
"""Design Rule Checkを実行する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
# DRCの実装
return "DRC実行完了"
except Exception as e:
return f"エラー: {str(e)}"
if __name__ == "__main__":
mcp.run()
セキュリティでは、KiCADプロジェクトファイルへのアクセス権限の管理、ファイルパスのバリデーション、適切なエラーハンドリング等を行う必要がある。
特に、パストラバーサル攻撃を防ぐため、入力されたファイルパスを適切に検証する必要がある。
パフォーマンスとスケーラビリティでは、大規模なPCBファイルの処理時間の最適化、メモリ使用量の管理が重要である。
また、複数のKiCADプロジェクトを同時に扱う場合は、適切なリソース管理を実装する必要がある。
KiCAD MCPサーバを構築・運用する場合は、以下に示す事柄に注意する。
- KiCAD Python APIのバージョン互換性の確認
- STDIOトランスポートでは標準出力 (stdout) にログを出力しない (JSON-RPC通信が破損する)
- ファイル操作の実行時間を適切に管理して、タイムアウトを設定する
- エラーハンドリングを適切に実装して、詳細なエラーメッセージを返す
- KiCADプロジェクトファイルのバックアップを定期的に実施
- アクセスログの監視
- ツールのバージョン管理とドキュメントの維持
サーバ環境のインストール
まず、システムの更新を行う。
# RHEL sudo dnf update # SUSE sudo zypper update # Debian sudo apt update sudo apt upgrade
次に、Linuxサーバに必要な基本環境をインストールする。
# RHEL sudo dnf install curl wget git gcc-c++ make python3 python3-pip # SUSE sudo zypper install curl wget git gcc-c++ make python3 python3-pip # Debian sudo apt install curl wget git build-essential python3 python3-pip
KiCADのインストール
インストールが完了したら、KiCADのバージョンを確認する。
kicad-cli version
RHEL
RHELでは、EPELリポジトリを有効化してKiCADをインストールする。
sudo dnf install epel-release sudo dnf install kicad kicad-doc kicad-packages3d
SUSE
SUSEでは、公式リポジトリからKiCADをインストールする。
sudo zypper install kicad kicad-doc kicad-packages3d
Debian
Debianでは、公式リポジトリからKiCADをインストールする。
sudo apt install kicad kicad-doc-ja kicad-libraries
KiCADの最新版をインストールする場合は、公式PPAを使用する。
sudo add-apt-repository ppa:kicad/kicad-7.0-releases sudo apt update sudo apt install kicad
Python開発環境の準備
Python仮想環境の作成
プロジェクトディレクトリを作成して、Python仮想環境を設定する。
mkdir -p ~/kicad-mcp-server cd ~/kicad-mcp-server python3 -m venv venv
Pythonの仮想環境をアクティベートする。
# Bash / Zshの場合 source venv/bin/activate # Fishの場合 source venv/bin/activate.fish
必要なPythonライブラリをインストールする。
pip install fastmcp
KiCAD Python APIのインストール
KiCADのPython APIは、KiCADのインストール時に自動的にインストールされる。
Python仮想環境からKiCAD Python APIにアクセスできるように、シンボリックリンクを作成する。
まず、KiCAD Python APIの場所を確認する。
python3 -c "import sys; print([p for p in sys.path if 'kicad' in p.lower()])"
通常、以下に示すディレクトリにインストールされている。
- RHEL / SUSE
- /usr/lib64/python3.x/site-packages/
- Debian
- /usr/lib/python3/dist-packages/pcbnew.py
- Debian
- /usr/lib/python3/dist-packages/pcbnew.py
仮想環境からアクセスできるように設定する。
# RHEL / SUSE ln -s /usr/lib64/python3.x/site-packages/pcbnew.so venv/lib/python3.x/site-packages/ # Debian ln -s /usr/lib/python3/dist-packages/pcbnew.py venv/lib/python3.x/site-packages/
または、~/.profileファイル等に環境変数 PYTHONPATH を設定する方法もある。
# ~/.profileファイル等
export PYTHONPATH=/usr/lib/python3/dist-packages:$PYTHONPATH
KiCAD Python APIが正常にインポートできるか確認する。
python3 -c "import pcbnew; print(pcbnew.Version())"
KiCAD MCPサーバの実装
基本的なサーバ構造
KiCAD MCPサーバの基本実装を行う。
# server.pyファイル
from fastmcp import FastMCP
import pcbnew
import os
from pathlib import Path
mcp = FastMCP("KiCAD MCP Server")
@mcp.tool()
def load_board(kicad_pcb_path: str) -> dict:
"""KiCAD PCBファイルを読み込み、基本情報を取得する"""
try:
if not os.path.exists(kicad_pcb_path):
return {"error": "ファイルが見つかりません"}
if not kicad_pcb_path.endswith('.kicad_pcb'):
return {"error": "KiCAD PCBファイルではありません"}
board = pcbnew.LoadBoard(kicad_pcb_path)
info = {
"board_name": os.path.basename(kicad_pcb_path),
"layer_count": board.GetCopperLayerCount(),
"board_thickness": board.GetDesignSettings().GetBoardThickness() / 1000000.0,
"track_count": board.GetTracks().GetCount(),
"footprint_count": len(list(board.GetFootprints())),
}
return info
except Exception as e:
return {"error": f"PCB読み込みエラー: {str(e)}"}
@mcp.tool()
def get_footprints(kicad_pcb_path: str) -> list:
"""基板上のフットプリント一覧を取得する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
footprints = []
for footprint in board.GetFootprints():
fp_info = {
"reference": footprint.GetReference(),
"value": footprint.GetValue(),
"footprint": footprint.GetFPID().GetLibItemName().GetUniChar(),
"layer": footprint.GetLayerName(),
}
footprints.append(fp_info)
return footprints
except Exception as e:
return [{"error": str(e)}]
@mcp.tool()
def get_net_list(kicad_pcb_path: str) -> list:
"""ネットリストを取得する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
nets = []
for net in board.GetNetInfo().NetsByName():
net_info = {
"net_code": net[1].GetNetCode(),
"net_name": net[1].GetNetname(),
}
nets.append(net_info)
return nets
except Exception as e:
return [{"error": str(e)}]
@mcp.tool()
def run_drc(kicad_pcb_path: str) -> dict:
"""Design Rule Checkを実行する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
# DRC設定の取得
drc_settings = board.GetDesignSettings()
# 簡易的なDRCチェック
track_width = drc_settings.m_TrackMinWidth / 1000000.0
clearance = drc_settings.m_MinClearance / 1000000.0
result = {
"min_track_width": track_width,
"min_clearance": clearance,
"status": "DRCチェック完了",
}
return result
except Exception as e:
return {"error": f"DRCエラー: {str(e)}"}
@mcp.tool()
def get_board_outline(kicad_pcb_path: str) -> dict:
"""基板外形の寸法を取得する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
bbox = board.GetBoardEdgesBoundingBox()
dimensions = {
"width": bbox.GetWidth() / 1000000.0,
"height": bbox.GetHeight() / 1000000.0,
"area": (bbox.GetWidth() * bbox.GetHeight()) / 1000000000000.0,
}
return dimensions
except Exception as e:
return {"error": str(e)}
@mcp.tool()
def export_gerber(kicad_pcb_path: str, output_dir: str) -> str:
"""Gerberファイルを出力する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
plot_controller = pcbnew.PLOT_CONTROLLER(board)
plot_options = plot_controller.GetPlotOptions()
plot_options.SetOutputDirectory(output_dir)
plot_options.SetPlotFrameRef(False)
plot_options.SetLineWidth(pcbnew.FromMM(0.1))
# レイヤーごとにプロット
layers = [
("F.Cu", pcbnew.F_Cu, "Top Copper"),
("B.Cu", pcbnew.B_Cu, "Bottom Copper"),
("F.Mask", pcbnew.F_Mask, "Top Soldermask"),
("B.Mask", pcbnew.B_Mask, "Bottom Soldermask"),
("F.SilkS", pcbnew.F_SilkS, "Top Silkscreen"),
("B.SilkS", pcbnew.B_SilkS, "Bottom Silkscreen"),
("Edge.Cuts", pcbnew.Edge_Cuts, "Board Outline"),
]
for layer_info in layers:
plot_controller.SetLayer(layer_info[1])
plot_controller.OpenPlotfile(layer_info[0], pcbnew.PLOT_FORMAT_GERBER, layer_info[2])
plot_controller.PlotLayer()
plot_controller.ClosePlot()
return f"Gerberファイルを {output_dir} に出力しました"
except Exception as e:
return f"エラー: {str(e)}"
if __name__ == "__main__":
mcp.run()
エラーハンドリング
本番環境では、詳細なエラーハンドリングを定義する。
import logging
from typing import Optional
# ログ設定 (stderrに出力)
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[logging.StreamHandler()]
)
logger = logging.getLogger(__name__)
def safe_load_board(kicad_pcb_path: str) -> Optional[pcbnew.BOARD]:
"""安全にKiCAD PCBファイルを読み込む"""
try:
# パスバリデーション
path = Path(kicad_pcb_path).resolve()
# パストラバーサル攻撃を防ぐ
if '..' in str(path):
logger.error(f"無効なパス: {kicad_pcb_path}")
return None
if not path.exists():
logger.error(f"ファイルが存在しません: {kicad_pcb_path}")
return None
if not str(path).endswith('.kicad_pcb'):
logger.error(f"KiCAD PCBファイルではありません: {kicad_pcb_path}")
return None
board = pcbnew.LoadBoard(str(path))
logger.info(f"PCBファイルを読み込みました: {kicad_pcb_path}")
return board
except Exception as e:
logger.exception(f"PCB読み込みエラー: {str(e)}")
return None
MCP Inspectorによるテスト
MCP Inspectorを使用して、KiCAD MCPサーバのツールをテストする。
まず、MCP Inspectorをインストールする。
pip install mcp-inspector
MCP Inspectorを起動する。
mcp-inspector python server.py
Webブラウザが自動的に開いて、インスペクターのインターフェースが表示される。
テスト用のKiCADプロジェクトファイルを準備して、各ツールの動作を確認する。
- load_board
- PCBファイルの基本情報を取得
- get_footprints
- フットプリント一覧を取得
- get_net_list
- ネットリスト一覧を取得
- run_drc
- DRCチェックの実行
- get_board_outline
- 基板外形の寸法を取得
- export_gerber
- Gerberファイルの出力
クライアント接続設定
Claude Desktopからの接続
Claude Desktopの設定ファイルを編集する。
設定ファイルの場所は、以下の通りである。
- Linuxの場合
- ~/.config/Claude/claude_desktop_config.json
- Windowsの場合
- %APPDATA%\Claude\claude_desktop_config.json
設定ファイルの内容を編集する。
{
"mcpServers": {
"kicad-server": {
"command": "/home/<ユーザ名>/kicad-mcp-server/venv/bin/python",
"args": ["/home/<ユーザ名>/kicad-mcp-server/server.py"],
"env": {
# RHEL / SUSEの場合
"PYTHONPATH": "/usr/lib64/python3.x/site-packages"
# Debianの場合
"PYTHONPATH": "/usr/lib/python3/dist-packages"
}
}
}
}
環境変数 PYTHONPATH を設定することにより、KiCAD Python APIにアクセスできるようにする。
Claude Desktopを再起動して、KiCAD MCPサーバが利用可能であることを確認する。
Systemdサービスファイルの作成
KiCAD MCPサーバをシステムサービスとして常時起動させる場合、systemdユニットファイルを作成する。
sudo vi /etc/systemd/system/kicad-mcp-server.service
# /etc/systemd/system/kicad-mcp-server.serviceファイル
[Unit]
Description=KiCAD MCP Server
After=network.target
[Service]
Type=simple
User=<実行する任意のユーザ名>
WorkingDirectory=/home/<ユーザ名>/kicad-mcp-server
ExecStart=/home/<ユーザ名>/kicad-mcp-server/venv/bin/python server.py
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
# 環境変数の設定
## Debianの場合
Environment="PYTHONPATH=/usr/lib64/python3.x/site-packages"
## Debianの場合
Environment="PYTHONPATH=/usr/lib/python3/dist-packages"
Environment="DISPLAY=:0"
[Install]
WantedBy=multi-user.target
サービスを有効化して起動する。
sudo systemctl daemon-reload sudo systemctl enable kicad-mcp-server sudo systemctl start kicad-mcp-server
サービスの状態を確認する。
sudo systemctl status kicad-mcp-server
ログを確認する。
sudo journalctl -u kicad-mcp-server -f
セキュリティ設定
ファイルアクセスの制限
KiCAD MCPサーバがアクセス可能なディレクトリを制限することが推奨される。
# 許可されたディレクトリのリスト
ALLOWED_DIRECTORIES = [
"/home/<ユーザ名>/kicad-projects",
"/home/<ユーザ名>/shared-projects",
]
def is_path_allowed(file_path: str) -> bool:
"""ファイルパスが許可されたディレクトリ内にあるか確認する"""
try:
resolved_path = Path(file_path).resolve()
for allowed_dir in ALLOWED_DIRECTORIES:
allowed_path = Path(allowed_dir).resolve()
if resolved_path.is_relative_to(allowed_path):
return True
return False
except Exception:
return False
@mcp.tool()
def load_board_secure(kicad_pcb_path: str) -> dict:
"""セキュアなPCBファイル読み込み"""
if not is_path_allowed(kicad_pcb_path):
return {"error": "このファイルへのアクセスは許可されていません"}
# 以降の処理
入力データのバリデーション
ツールのパラメータに対して、適切なバリデーションを行う。
import re
def validate_file_path(path: str) -> bool:
"""ファイルパスの妥当性を検証する"""
# パストラバーサル攻撃を防ぐ
if '..' in path:
return False
# 許可された拡張子のみ
allowed_extensions = ['.kicad_pcb', '.kicad_pro', '.kicad_sch']
if not any(path.endswith(ext) for ext in allowed_extensions):
return False
# 不正な文字を含まない
if not re.match(r'^[a-zA-Z0-9._\-/]+$', path):
return False
return True
バックアップとリストア
KiCADプロジェクトのバックアップ
定期的にKiCADプロジェクトをバックアップする。
#!/usr/bin/env sh
## backup-kicad-projects.shファイル
BACKUP_DIR="/var/backups/kicad-projects"
DATE=$(date +%Y%m%d_%H%M%S)
PROJECT_DIR="/home/username/kicad-projects"
# バックアップディレクトリの作成
mkdir -p $BACKUP_DIR
# プロジェクトディレクトリの圧縮
tar -czf "$BACKUP_DIR/kicad-projects_$DATE.tar.gz" -C $(dirname $PROJECT_DIR) $(basename $PROJECT_DIR)
# 30日以上前のバックアップを削除
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete
echo "バックアップ完了: kicad-projects_$DATE.tar.gz"
バックアップスクリプトを実行可能にする。
chmod u+x backup-kicad-projects.sh
cronジョブとして設定する。
crontab -e
# 毎日午前3時にバックアップを実行 0 3 * * * /path/to/backup-kicad-projects.sh
トラブルシューティング
KiCAD Python APIがインポートできない
- KiCADが正しくインストールされているか確認する。
kicad-cli version
- 環境変数
PYTHONPATHが正しく設定されているか確認する。echo $PYTHONPATH
- Python仮想環境からKiCAD Python APIへのシンボリックリンクが正しいか確認する。
ls -la venv/lib/python3.x/site-packages/pcbnew*
PCBファイルの読み込みに失敗する
- ファイルパスが正しいか確認する。
- ファイルの読み取り権限があるか確認する。
ls -la /path/to/file.kicad_pcb
- KiCADのバージョンとファイルフォーマットの互換性を確認する。
Claude Desktopから接続できない
- 設定ファイルのパスが正しいか確認する。
- Python仮想環境のパスが正しいか確認する。
- 環境変数
PYTHONPATHが設定されているか確認する。 - STDIOトランスポートの場合、標準出力にログを出力していないか確認する。
Gerberファイルの出力に失敗する
- 出力ディレクトリの書き込み権限があるか確認する。
- ディスク容量が十分にあるか確認する。
- KiCADのプロットコントローラの設定を確認する。
パフォーマンス最適化
大規模PCBファイルの処理
大規模なPCBファイルを処理する場合、メモリ使用量と処理時間に注意する必要がある。
import gc
@mcp.tool()
def process_large_board(kicad_pcb_path: str) -> dict:
"""大規模なPCBファイルを効率的に処理する"""
try:
board = pcbnew.LoadBoard(kicad_pcb_path)
# 必要な情報のみを抽出
result = extract_board_info(board)
# ボードオブジェクトを明示的に削除
del board
gc.collect()
return result
except Exception as e:
return {"error": str(e)}
キャッシングの実装
頻繁にアクセスされるデータをキャッシュすることにより、パフォーマンスを向上させる。
from functools import lru_cache
import hashlib
def get_file_hash(file_path: str) -> str:
"""ファイルのハッシュ値を計算する"""
with open(file_path, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
@lru_cache(maxsize=10)
def get_cached_board_info(file_path: str, file_hash: str) -> dict:
"""キャッシュされたボード情報を取得する"""
board = pcbnew.LoadBoard(file_path)
# 情報を抽出
return extract_board_info(board)
@mcp.tool()
def load_board_cached(kicad_pcb_path: str) -> dict:
"""キャッシュを使用してPCBファイルを読み込む"""
try:
file_hash = get_file_hash(kicad_pcb_path)
return get_cached_board_info(kicad_pcb_path, file_hash)
except Exception as e:
return {"error": str(e)}
外部リンク