Claude 用ローカル MCP サーバーの構築方法:ファイル操作、コマンド実行、スクリーンショット、アプリ制御

@hrswatigupta
英語1 か月前 · 2026年6月04日
1.0M
72
16
2
224

TL;DR

本ガイドでは、Python を使用して Claude 用のローカル MCP サーバーを作成する方法を解説します。安全な許可リストアーキテクチャを通じて、ローカルファイル、コマンド、デスクトップツールへの安全かつ制御されたアクセスを実現します。

Claude は単なるチャットインターフェースではなくなると、はるかに便利になります。

ローカルの MCP サーバーを使えば、Claude が実際のマシンとやり取りできるようになります。ローカルファイル、承認済みのコマンド、スクリーンショット、アプリの起動などです。重要なのは、無制限のアクセスではなく、制御されたアクセスであることです。

このガイドでは、実用的で範囲を限定し、実際のワークフローで安全に使える、Claude 用のローカル MCP サーバーを構築します。

構築を始める前に、ひとこと

この記事では、「コンピューター制御」という無責任なバージョンは意図的に避けています。

Claude に無制限のシェルアクセス、ファイルシステム全体の権限、またはガードレールなしでマシンを変更する許可を与えることは しません。悪いローカル MCP サーバーを最も早く作る方法は、巨大な run_anything() ツールを公開して、それを革新と呼ぶことです。

より良いパターンは次のとおりです。

  • 許可リストに登録されたディレクトリ
  • 許可リストに登録されたコマンド
  • 安全なデフォルト設定
  • 人間が読めるログ
  • 明示的な応答
  • 読み取り専用ツールとアクション実行ツールの明確な分離

Claude が何でもできてしまうなら、それはデモに過ぎません。

Claude が安全に正しいことを実行できるなら、それは使えるものになります。

このアーキテクチャを学ぶ価値がある理由

ローカル MCP サーバーの価値は、目新しさではなく、摩擦の低減にあります。

ローカルツールレイヤーがない場合、ワークフローは次のようになります。

  1. Claude に何をすべきか尋ねる
  2. 回答をコピーする
  3. 自分でフォルダを開く
  4. 自分でコマンドを実行する
  5. 自分でスクリーンショットを撮る
  6. 結果をチャットに貼り付ける

ローカル MCP サーバーがあれば、このループは劇的に短縮されます。Claude は必要なコンテキストを検査し、範囲が限定されたツールを使用して、実際のマシンの状態に基づいた回答を返すことができます。

これは、以下のような場合に役立ちます。

  • 開発ワークフロー
  • ログの検査
  • コンテンツ運用
  • リサーチパイプライン
  • デスクトップ自動化
  • 反復的な管理タスク

そして、ツールレイヤーはあなたのものなので、モデルの動作をどこで止めるかを正確に選択できます。

AI を学び、キャリアで先を行きたいですか?

私がシェアしていること:

1,000 以上の AI プロンプト

AI ツールと自動化

キャリアと LinkedIn 成長のヒント

厳選されたテクノロジーと生産性向上リソース

こちらから購読:

https://bytebuilders.beehiiv.com/subscribe

フォローして、さらに AI のヒントをゲット

👇

構築する設計

Swati Gupta - inline image

5 つのツールを持つローカルサーバーを構築します。

  1. list_files — 承認されたフォルダ内の内容を確認する
  2. read_file — 安全なテキストベースのファイルを開く
  3. run_command — 承認されたローカルコマンドの小さなセットを実行する
  4. take_screenshot — スクリーンショットを既知の場所に保存する
  5. open_target — アプリ、ファイル、フォルダ、または URL を開く
Swati Gupta - inline image

この範囲は意図的なものです。

ローカルマシンで Claude を有意義に役立てるには十分でありながら、安全でない汎用的な自動化に陥ることはありません。

メンタルモデルは次のようになります。

Claude → MCP クライアント → ローカル MCP サーバー → 限定されたツール → オペレーティングシステム

Claude がオペレーティングシステムと直接通信することは絶対に避けるべきです。MCP サーバーがその中間の制御プレーンとなります。

使用する技術スタック

ローカルビルドには、公式の MCP SDK が成熟しており、FastMCP の抽象化が簡潔で、ファイルシステム、サブプロセス、デスクトップスクリプトの作業に Python が最も簡単な言語であるため、Python が適切な選択肢です 4 2

使用するもの:

  • Python 3.11+
  • mcp[cli] (MCP サーバーランタイム用)
  • mss (クロスプラットフォームのスクリーンショット用)
  • ファイルアクセス、サブプロセス呼び出し、OS 処理用の標準ライブラリモジュール

新しいプロジェクトをセットアップします:

bash
1mkdir local-mcp-server
2cd local-mcp-server
3uv init --python 3.11
4uv add "mcp[cli]>=1.0,<2.0" "mss>=9.0,<10.0"

デコレータベースの FastMCP スタイルにより、プロトコルレイヤーが邪魔にならないため、配線ではなくツールの品質に集中できます 4 5

シンプルなプロジェクト構成で十分です:

text
1mkdir local-mcp-server
2cd local-mcp-server
3uv init --python 3.11
4uv add "mcp[cli]>=1.0,<2.0" "mss>=9.0,<10.0"

v1 に複雑なアーキテクチャは必要ありません。必要なのは明確さです。

実際のサーバー

server.py を作成し、ポリシー駆動型の実装から始めます。

python
1from __future__ import annotations
2
3import json
4import os
5import platform
6import shlex
7import subprocess
8from pathlib import Path
9from typing import Any
10
11import mss
12from mcp.server.fastmcp import FastMCP
13
14app = FastMCP("local-computer-control", json_response=True)
15
16HOME = Path.home()
17PROJECT_ROOT = Path(__file__).parent.resolve()
18CAPTURE_DIR = PROJECT_ROOT / "captures"
19CAPTURE_DIR.mkdir(exist_ok=True)
20
21ALLOWED_ROOTS = [
22 HOME / "Documents",
23 HOME / "Desktop",
24 PROJECT_ROOT,
25]
26
27ALLOWED_COMMANDS = {
28 "pwd",
29 "ls",
30 "git status",
31 "git diff --stat",
32 "python --version",
33 "node --version",
34 "npm --version",
35}
36
37READABLE_EXTENSIONS = {
38 ".txt",
39 ".md",
40 ".json",
41 ".py",
42 ".js",
43 ".ts",
44 ".tsx",
45 ".jsx",
46 ".yaml",
47 ".yml",
48 ".toml",
49 ".csv",
50 ".log",
51}
52
53def _resolve_path(raw_path: str) -> Path:
54 path = Path(raw_path).expanduser().resolve()
55 for root in ALLOWED_ROOTS:
56 root = root.resolve()
57 if path == root or root in path.parents:
58 return path
59 raise ValueError(f"パスが許可されていません: {path}")
60
61def _ensure_safe_command(command: str) -> str:
62 normalized = " ".join(shlex.split(command))
63 if normalized not in ALLOWED_COMMANDS:
64 raise ValueError(
65 "コマンドが許可されていません。本当に必要な場合は、ALLOWED_COMMANDS に明示的に追加してください。"
66 )
67 return normalized
68
69@app.tool()
70def list_files(path: str = "~") -> dict[str, Any]:
71 """承認されたディレクトリ内のファイルとフォルダを一覧表示します。"""
72 target = _resolve_path(path)
73 if not target.is_dir():
74 raise ValueError(f"ディレクトリではありません: {target}")
75
76 items = []
77 for child in sorted(target.iterdir(), key=lambda p: (not p.is_dir(), p.name.lower())):
78 items.append(
79 {
80 "name": child.name,
81 "path": str(child),
82 "type": "directory" if child.is_dir() else "file",
83 }
84 )
85
86 return {
87 "path": str(target),
88 "count": len(items),
89 "items": items,
90 }
91
92@app.tool()
93def read_file(path: str, max_chars: int = 12000) -> dict[str, Any]:
94 """承認された場所から安全なテキストファイルを読み取ります。"""
95 target = _resolve_path(path)
96 if not target.is_file():
97 raise ValueError(f"ファイルではありません: {target}")
98 if target.suffix.lower() not in READABLE_EXTENSIONS:
99 raise ValueError(f"サポートされていないファイルタイプです: {target.suffix}")
100
101 content = target.read_text(encoding="utf-8", errors="replace")
102 truncated = len(content) > max_chars
103 content = content[:max_chars]
104
105 return {
106 "path": str(target),
107 "truncated": truncated,
108 "content": content,
109 }
110
111@app.tool()
112def run_command(command: str, cwd: str | None = None, timeout: int = 15) -> dict[str, Any]:
113 """許可リストに登録されたローカルコマンドを 1 つ実行します。"""
114 safe_command = _ensure_safe_command(command)
115 working_dir = _resolve_path(cwd) if cwd else PROJECT_ROOT
116
117 completed = subprocess.run(
118 safe_command,
119 shell=True,
120 cwd=str(working_dir),
121 capture_output=True,
122 text=True,
123 timeout=timeout,
124 )
125
126 return {
127 "command": safe_command,
128 "cwd": str(working_dir),
129 "returncode": completed.returncode,
130 "stdout": completed.stdout.strip(),
131 "stderr": completed.stderr.strip(),
132 }
133
134@app.tool()
135def take_screenshot(name: str = "latest") -> dict[str, Any]:
136 """スクリーンショットを撮り、ローカルに保存します。"""
137 output_path = CAPTURE_DIR / f"{name}.png"
138
139 with mss.mss() as sct:
140 sct.shot(output=str(output_path))
141
142 return {
143 "saved": True,
144 "path": str(output_path),
145 }
146
147@app.tool()
148def open_target(target: str) -> dict[str, Any]:
149 """ローカル OS を使用して、承認されたファイル、フォルダ、アプリ、または URL を開きます。"""
150 system = platform.system().lower()
151
152 if target.startswith("http://") or target.startswith("https://"):
153 resolved = target
154 else:
155 resolved = str(_resolve_path(target))
156
157 if system == "darwin":
158 subprocess.run(["open", resolved], check=True)
159 elif system == "windows":
160 os.startfile(resolved) # type: ignore[attr-defined]
161 else:
162 subprocess.run(["xdg-open", resolved], check=True)
163
164 return {
165 "opened": True,
166 "target": resolved,
167 }
168
169if __name__ == "__main__":
170 app.run(transport="stdio")

これはコンパクトなサーバーですが、重要なのはその長さではありません。重要なのはインターフェースの形状です。

  • すべてのツールには非常に明確な役割があります
  • すべてのツールは構造化データを返します
  • コマンドの実行は制限されています
  • ファイルアクセスはルート化されています
  • スクリーンショットは既知のフォルダに保存されます

これこそが、ローカル MCP サーバーに求められるものです。

これらのツールがこのように設計されている理由

エージェントツールに関する上級者向けのコンテンツは、「ここにコードがあります」で終わらせるべきではありません。ツールの形状こそが本当の教訓です。

list_files

このツールは、Claude に安全な発見のためのサーフェスを提供します。次のような質問に答えられるようにする必要があります。

  • このプロジェクトフォルダには何がある?
  • Documents にはどのようなメモがある?
  • 検査できるログファイルは既に存在するか?

しかし、ディスク全体を再帰的にクロールするものになってはいけません。

read_file

これは、多くの場合、すべてのローカルツールの中で最も便利です。実際の作業の大部分は、依然としてローカルの Markdown メモ、ログ、CSV、ドキュメント、プロジェクトファイルに隠れています。

max_chars の上限は重要です。大きなファイルはコンテキストとレイテンシの問題を引き起こします。巨大なログファイルの内容全体を返すことは、ほとんど役に立ちません。

run_command

ここで、ほとんどの人がずさんになります。

安全なパターンは、「シェルアクセスを許可して、後は運任せ」ではありません。安全なパターンは、「正確でレビュー可能なコマンドの小さなセットを許可する」ことです。そのため、この例では厳格な許可リストを使用しています。

take_screenshot

スクリーンショットツールは、Claude がデスクトップワークフローに参加できるようにするため、価値があります。最初のバージョンが画像をディスクに保存するだけでも、レポート、UI デバッグ、ドキュメントキャプチャ、構造化されたハンドオフにすでに役立ちます。

open_target

アプリ制御は、GUI 自動化から始める必要はありません。多くのワークフローでは、「正しいフォルダ、ファイル、または URL を開く」だけで十分です。

これは、初日から完全なカーソル自動化が必要だと仮定するよりも、より耐久性のある v1 です。

サーバーを Claude に接続する

ローカル MCP サーバーは通常、stdio を介して実行されます。つまり、Claude がローカルでプロセスを起動し、stdin/stdout を介して直接通信します。ローカルのコンピューター制御サーバーにとって、これは不必要なネットワーク露出を避けることができるため、適切なデフォルトです 4 5

Claude Desktop は、設定を介してローカル MCP サーバーをサポートしており、サーバープロセスを自動的に起動します。実際には、インタプリタとスクリプトに絶対パスを使用することが最も堅牢なセットアップです。これは、ローカル GUI アプリ環境がターミナルよりも厳しいことが多いためです 2

最小限の設定は次のようになります:

json
1{
2 "mcpServers": {
3 "local-computer-control": {
4 "command": "/absolute/path/to/python",
5 "args": [
6 "/absolute/path/to/local-mcp-server/server.py"
7 ]
8 }
9 }
10}

uv を希望する場合も、それで問題ありません:

json
1{
2 "mcpServers": {
3 "local-computer-control": {
4 "command": "/absolute/path/to/uv",
5 "args": [
6 "--directory",
7 "/absolute/path/to/local-mcp-server",
8 "run",
9 "python",
10 "server.py"
11 ]
12 }
13 }
14}

設定を保存して Claude を再起動すると、サーバーツールがローカル MCP ツールリストに表示されるはずです。Claude Desktop のローカル MCP セットアップは、まさにこのモデルを中心に構築されています。ローカルプロセスを起動し、stdio を介して接続し、ツールをモデルに公開します 2 3

Swati Gupta - inline image

テストに実際に役立つプロンプト

サーバーが接続されたら、複雑なオーケストレーションから始めないでください。直接的な、基本的なチェックから始めてください。

次のようなプロンプトを試してみてください:

  • 「Desktop フォルダのファイルを一覧表示して。」
  • ~/Documents/todo.md を読んで、上位 3 つの優先事項を要約して。」
  • 「ローカルプロジェクトフォルダで git status を実行して、何が変更されたか説明して。」
  • workspace-check という名前のスクリーンショットを撮って。」
  • 「プロジェクトの README を開いて。」

これらの単純なフローが一貫して機能すれば、拡張する価値のあるサーバーができています。

機能しない場合、ツールを追加しても、本当の問題が隠れるだけです。

ローカル MCP サーバーが真に価値を発揮する場面

明白なユースケースは開発ですが、それだけが唯一の用途ではありません。

開発者ワークフロー

Claude は以下のことができます:

  • リポジトリフォルダを検査する
  • 設定ファイルを読む
  • git status を実行する
  • バグの状態のスクリーンショットを撮る
  • プロジェクトディレクトリを開く

これにより、多くのコンテキストスイッチがすでに排除されます。

研究ワークフロー

Claude は以下のことができます:

  • 研究フォルダを一覧表示する
  • Markdown メモを開いて要約する
  • 構造化された CSV を読む
  • ツールやダッシュボードからスクリーンショットを保存する
  • ソースファイルやブラウザリンクを開く

コンテンツワークフロー

Claude は以下のことができます:

  • 下書きフォルダをスキャンする
  • 既存の記事のアイデアを読む
  • デザインリファレンスのスクリーンショットを撮る
  • 適切なライティングファイルや URL を開く
  • ビルドアーティファクトや下書きのエクスポートを生成する限定されたコマンドを実行する

運用ワークフロー

Claude は以下のことができます:

  • 承認されたディレクトリからログを検査する
  • 読み取り専用の診断コマンドを実行する
  • 関連するフォルダやダッシュボードリンクを開く
  • 証拠となるスクリーンショットを保存する

これがこのアーキテクチャの本当のポイントです。「コンピューター制御」をスタントとして行うことではなく、ワークフローの圧縮です。

セキュリティレイヤーこそが製品である

これは、あまりにも多くの技術記事が軽視しているセクションです。

ローカル MCP の危険な部分はプロトコルではありません。それは、不適切な権限設計です。

このサーバーをデモ以上のものとして使いたいのであれば、早期に安全モデルを構築してください。

Swati Gupta - inline image

ディレクトリ許可リストを使用する

Claude は、明示的に承認したパスのみを表示できるようにする必要があります。これが、_resolve_path() がファイルツールの中核である理由です。

コマンド許可リストを使用する

最初のバージョンで、任意のシェル実行を公開しないでください。1 行ずつ監査できる正確なコマンドから始めてください。

読み取りツールとアクションツールを分離する

読み取り専用ツールをデフォルトにしてください。アクションツールは意図的に導入する必要があります。

すべてをログに記録する

単純な追記専用の JSON ログでも、デバッグと信頼性が劇的に向上します。

書き込み用の確認レイヤーを追加する

後で write_filemove_file、または delete_file を追加する場合は、それらのツールに 2 回目の確認トークンを要求するか、デフォルトで無効のままにしてください。

ドライランモードを検討する

アクション実行ツールの場合、ドライランモードは過小評価されています。Claude がアクションを実行する前に、何をしようとしているのかを説明できるようになります。

可能な場合は制限されたユーザーで実行する

ローカル自動化を真剣に考えているなら、MCP サーバーに必要以上の OS 権限を与えないでください。

役立つ経験則:

  • レベル 1: 読み取り専用ツール
  • レベル 2: ファイルを開く / アプリを開くなどの低リスクアクション
  • レベル 3: 確認が必要な書き込みアクション
  • レベル 4: 通常は公開すべきではない破壊的なアクション

ほとんどの人はレベル 4 を必要としません。

Swati Gupta - inline image

v1 の後に改善すべき点

堅実な最初のバージョンは、より高度な機能を追加する権利を得ます。

基本的なサーバーが安定したら、次に検討すべきアップグレードは次のとおりです。

  1. 一元化されたポリシーファイル

ルールを config/policy.json に移動して、変更を宣言的にします。

例:

json
1{
2 "allowed_roots": [
3 "~/Documents",
4 "~/Desktop",
5 "./"
6 ],
7 "allowed_commands": [
8 "pwd",
9 "ls",
10 "git status",
11 "git diff --stat",
12 "python --version"
13 ]
14}
  1. 構造化ロギング

ツール呼び出し、タイムスタンプ、引数、結果を logs/server.log または JSONL ファイルに記録します。

  1. より安全なコマンド実行

単一の汎用コマンドツールの代わりに、コマンドを次のようなより限定されたツールに分割します:

  • git_status
  • show_current_directory
  • list_project_files

これにより、Claude がツールを選択しやすくなり、安全性も向上します。

  1. より良いスクリーンショット処理

「スクリーンショットをディスクに保存する」から、次のように進化させることができます:

  • タイムスタンプ付きキャプチャ
  • アクティブウィンドウのキャプチャ
  • 領域キャプチャ
  • ファイル保持ルール
  1. OS 固有の自動化アダプター

macOS では、後で AppleScript や Shortcuts を追加できます。Windows では、PowerShell や UI Automation。Linux では、デスクトップ固有のランチャーやウィンドウツール。

しかし、これらは、基本的なローカルコアが信頼できるようになった後に行うべきです。

ローカル MCP サーバーで人々が犯すよくある間違い

間違いは予測可能です。

間違い 1: 早すぎる段階での過剰な権限

人々は完全なコンピューター制御というアイデアを好みます。しかし、そのデバッグを嫌います。もっと小さく始めてください。

間違い 2: 曖昧なツール名

ツール名が曖昧だと、Claude はそれらをうまく使いこなせません。明確にしてください。

悪い例:

  • system_action
  • computer_control

良い例:

  • list_files
  • read_file
  • run_command
  • take_screenshot
  • open_target

間違い 3: 非構造化された出力

テキストが混在したブロブは、クリーンな JSON オブジェクトよりも Claude が推論するのが難しくなります。

間違い 4: ログがない

ツールが失敗したときに理由がわからなければ、システムは推測作業になります。

間違い 5: モデルを制御レイヤーとして扱う

Claude は推論レイヤーです。サーバーは依然として強制レイヤーでなければなりません。

この区別は譲れません。

このアーキテクチャが単なる自動化よりも優れている点

従来のデスクトップ自動化は、通常、次の 2 つのうちのいずれかです。

  • 壊れやすい GUI スクリプト
  • 人間がいつ実行するかを正確に知っている必要がある孤立したスクリプト

ローカル MCP サーバーはこれを変えます。なぜなら、Claude はユーザーのリクエストと利用可能なコンテキストに基づいて、どのツールを使用するかを決定できるからです。

つまり、単にコマンドを自動化しているわけではありません。モデルが推論できるローカル機能レイヤーを構築しているのです。

これが、MCP が重要だと感じられる理由です。これは単なる別の統合パターンではありません。アプリケーションレイヤーですべての可能なワークフローをハードコーディングすることなく、ツールの使用をモデルに公開する、よりクリーンな方法です。

尊重すべき限界

優れたローカル MCP サーバーであっても、実際の限界があります。

  • デスクトップ自動化は、オペレーティングシステムによって不安定になる可能性があります。
  • スクリーンショットは便利ですが、魔法ではありません。
  • アプリの起動は簡単ですが、信頼性の高い UI 操作はより困難です。
  • 汎用的なシェルアクセスは危険です。
  • ツールの出力が大きすぎると、コンテキストの肥大化が現実の問題になります。
  • 重要なことには、人間による承認が依然として価値があります。

言い換えれば、「モデルがアクションを実行できる」ことと、「モデルが監視なしで行動すべき」ことを混同しないでください。

より価値のあるパターンは、盲目的な自律性ではなく、協調的な制御です。

ワンクリック保存

YouMindでバイラル記事をAI深読み

ソースを保存し、的を絞った質問をし、主張を要約して、バイラル記事を再利用できるノートに変えます。すべてを1つのAIワークスペースで行えます。

YouMindを探索
クリエイターのために

あなたの Markdown をきれいな 𝕏 記事に

自分の長文を投稿するとき、画像・表・コードブロックを 𝕏 向けに整形するのは手間がかかります。YouMind は Markdown 全体を、そのまま投稿できるきれいな 𝕏 記事に変換します。

Markdown → 𝕏 を試す

解読すべきパターンをもっと

最近のバイラル記事

バイラル記事をもっと見る