safe-mcp-suite
safe-mcp-suite
2つのMCPサーバー — ターミナルとファイルオーガナイザー — どちらも迂回できない単一の安全コアを共有しています。
これは何か
GitHubでシェルコマンドを実行するMCPサーバーを検索すると、同じファイルが何度も見つかります。@mcp.tool()で装飾されたツール、subprocess.run(command, shell=True)の呼び出し、そして結果をそのままモデルに返す。ファイルシステム系も同じ形です — os.renameをループで回し、場合によってはtry/exceptで囲む。
それらは動作します。それが問題です。モデルが誰も予期しなかった何かを生成するまで動作し、その頃には削除はすでに起こっており、何が実行されたのか、なぜ許可されたのかの記録はありません。
このリポジトリは、安全性を後付けのラッパーではなく設計そのものとした、これら2つのサーバーの再構築です。単一のsafety/パッケージが、あなたに害を及ぼす可能性のあるすべての決定を所有します — 何が許可されるか、境界はどこか、何が記録されるか、何が隠されるか。その下にある2つのサーバーは配線です。どちらもコアを越えて到達できません。どちらも独自のルールを実装していないからです。
その背後にある賭け: 決定論的ポリシーはモデルの判断よりも信頼できる。 プロンプトはモデルを注意深くなくさせることができます。しかし、パス包含チェックにTrueを返させることはできません。
Related MCP server: Safe Terminal MCP Server
クイックスタート
Python 3.12+とuvが必要です。
git clone https://github.com/Asaad-Suliman/safe-mcp-suite.git safe-mcp-suite
cd safe-mcp-suite
uv sync
./scripts/make_demo_sandbox.shその最後のスクリプトはsandbox/terminal、sandbox/files、state/をシードし、両方のサーバーが合法的に動作する場所を確保します。その後、好きな方を起動します:
uv run safe-mcp terminal --config policy.example.toml
uv run safe-mcp files --config policy.example.toml--configフラグについて
それはオプションではなく、フォールバックもありません。暗黙の./policy.tomlの検索も、.envの読み込みも、あなたのホームディレクトリを静かに指すデフォルトルートもありません。--configもSAFE_MCP_POLICY_FILEも設定されていない場合、サーバーは理由を表示して終了します。
これは意図的であり、セットアップの中で最も意見が強い点です。静かに便利な場所をデフォルトにするサンドボックスは、いつか高価な場所をデフォルトにするでしょう。起動を拒否することは、可能な限り最も安価な失敗です。
リポジトリには2つのポリシーファイルが同梱されており、それらは交換できません:
ファイル | 内容 | 起動しますか? |
| 動作する例、 | はい — 今すぐ実行 |
| ルートがコメントアウトされた注釈付きテンプレート | いいえ、意図的に |
policy.tomlは、jail_rootとworkspace_rootを自分で記入するまで起動を拒否します。その拒否は機能であり、それを維持する回帰テストがあります。コピーして編集し、準備ができたら実際のディレクトリを指定してください。
ルートは、必要に応じて環境から取得することもできます:
SAFE_MCP_JAIL_ROOT=/path/to/jail
SAFE_MCP_WORKSPACE_ROOT=/path/to/workspaceMCPクライアントへの登録
{
"mcpServers": {
"safe-mcp terminal": {
"command": "uv",
"args": ["run", "safe-mcp", "terminal"],
"env": {
"SAFE_MCP_POLICY_FILE": "/srv/safe-mcp/policy.example.toml",
"SAFE_MCP_JAIL_ROOT": "/srv/safe-mcp/sandbox"
}
},
"safe-mcp files": {
"command": "uv",
"args": ["run", "safe-mcp", "files"],
"env": {
"SAFE_MCP_POLICY_FILE": "/srv/safe-mcp/policy.example.toml",
"SAFE_MCP_WORKSPACE_ROOT": "/srv/safe-mcp/inbox"
}
}
}
}デモ
以下はすべて実際にキャプチャされた出力です。ここにあるものは手書きでも、トリミングでも、後から装飾されたものでもありません — これらは、policy.example.tomlを使用して./scripts/make_demo_sandbox.shからシードされたサンドボックスに対して実行された、両方のサーバーと通信するライブMCPクライアントが返した実際のOperationResultエンベロープです。
それぞれのcodeフィールドを読んでください。その分類法が要点です: 成功か拒否か、すべての結果が同じ形で返されます。
ターミナルサーバー
許可されたコマンド:
>>> tool: run_command args: {"command": "cat notes.txt"}
{
"action": "run_command",
"code": "OK",
"detail": {
"exit_code": 0,
"stderr": "",
"stdout": "demo file\n"
},
"duration_ms": 1,
"ok": true,
"reason": "'cat' is allowed"
}拒否されたコマンド:
>>> tool: run_command args: {"command": "rm -rf /"}
{
"action": "run_command",
"code": "POLICY_DENIED",
"detail": {},
"duration_ms": 0,
"ok": false,
"reason": "'rm' is on the denylist"
}拒否がそうでないものに注意してください: トレースバック、発生した例外、モデルが推測しなければならない文字列ではありません。理由が添付された型付きコードです。
ファイルサーバー
このシーケンスは、通常のファイル、インストーラー、ドットファイル、シンボリックリンクを含むシードされたワークスペースに対して実行されます — ポリシーが異なる扱いをする各要素が1つずつあります。
plan_organizeが移動を提案し、スキップを列挙:
>>> tool: plan_organize args: {}
{
"action": "plan_organize",
"code": "OK",
"detail": {
"created": "2026-08-07T16:49:00.048899+00:00",
"move_count": 2,
"moves": [
{
"category": "Images",
"dest": "Images/photo.png",
"size": 41,
"src": "photo.png"
},
{
"category": "Documents",
"dest": "Documents/report.pdf",
"size": 16,
"src": "report.pdf"
}
],
"plan_id": "7dcf2cff-399d-4af5-bb9a-a4131e1d5288",
"skip_count": 3,
"skips": [
{
"code": "NEEDS_EXPLICIT_REQUEST",
"name": ".bashrc",
"reason": "dotfiles are configuration, not clutter to be filed",
"rule": "organize"
},
{
"code": "POLICY_DENIED",
"name": "link.pdf",
"reason": "'organize' is not permitted by any rule (deny by default)",
"rule": null
},
{
"code": "NEEDS_EXPLICIT_REQUEST",
"name": "setup.exe",
"reason": "installers, executables and application folders are left where the user put them",
"rule": "organize"
}
],
"truncated": false
},
"duration_ms": 0,
"ok": true,
"reason": "proposed 2 move(s), skipped 3"
}apply_planが同じプランを実行:
>>> tool: apply_plan args: {"plan_id": "7dcf2cff-399d-4af5-bb9a-a4131e1d5288"}
{
"action": "apply_plan",
"code": "OK",
"detail": {
"moved": 2,
"moves": [
{
"dest": "Images/photo.png",
"src": "photo.png"
},
{
"dest": "Documents/report.pdf",
"src": "report.pdf"
}
],
"plan_id": "7dcf2cff-399d-4af5-bb9a-a4131e1d5288",
"planned": 2
},
"duration_ms": 2,
"ok": true,
"reason": "moved 2 file(s)"
}次は興味深いペアです。同じツール、2つの名前付きターゲット、2つの異なる答え。
move_fileがPROTECTIONによって拒否 — 上でplan_organizeがスキップした同じシンボリックリンクを明示的に指定:
>>> tool: move_file args: {"src": "link.pdf", "dest": "Documents/link.pdf"}
{
"action": "move_file",
"code": "POLICY_DENIED",
"detail": {},
"duration_ms": 0,
"ok": false,
"reason": "'organize' is not permitted by any rule (deny by default)"
}move_fileがNEEDS_EXPLICIT_REQUESTで延期されたインストーラーに対して成功、今度は明示的に指定:
>>> tool: move_file args: {"src": "setup.exe", "dest": "Documents/setup.exe"}
{
"action": "move_file",
"code": "OK",
"detail": {
"dest": "Documents/setup.exe",
"moved": 1,
"src": "setup.exe"
},
"duration_ms": 0,
"ok": true,
"reason": "moved setup.exe"
}プランナーは両方を拒否しました。直接尋ねられたムーバーは一方を拒否し、もう一方を実行しました。その違いは矛盾ではありません — それは2層モデルであり、以下で独自のセクションを設けます。
システム設計
サーバーは配線であり、ポリシーではない
どちらのサーバーファイルにも安全ルールは含まれていません。それらはすべてsafety/にあり、両方のサーバーは同じ関数に到達して同じ答えを得ます。
flowchart TD
A["MCP client"] --> B["terminal server"]
A --> C["files server"]
B --> D["safety/policy.py<br/>allow or deny"]
C --> D
D --> E["safety/paths.py<br/>PathJail containment"]
E --> F["execute or move"]
F --> G["safety/redact.py<br/>secrets out, then truncate"]
G --> H["safety/audit.py<br/>append-only JSONL"]
H --> I["OperationResult"]
I --> Aこれは自己目的のための整理整頓ではありません。セキュリティ修正が正確に1か所に配置されることを意味し、このリポジトリを監査するレビュアーがsafety/を読めば完了することを意味します。サーバーモジュールに隠れて、最初の実装と静かに同期がずれる2番目の実装はありません。
リクエストが実際に流れる方法
解析。 コマンド文字列はargvリストに分割されます。シェルのメタ文字は、何かを解釈する前にここで拒否されます — 引用符内も含みます。最後の部分は意図的に保守的であり、文書化された上限であり、見落としではありません。
評価。 ポリシーエンジンは許可または拒否を返します。拒否は常に勝ちます。明示的に許可されていないものは拒否されるため、空のポリシーはオープンなサーバーではなく役に立たないサーバーになります。
包含。 すべてのパスは解決され、jailルートに対してチェックされます。シンボリックリンクはリンクとして検査され、決して通過しません。
実行。
subprocess.runをshell=False、正確にPATH、HOME、LANGのスクラブされた環境、タイムアウト、出力キャップ付きで実行します。または、ファイル側では、ジャーナルに記録された単一のガード付き移動。編集、そして切り詰め。 常にその順序で。
記録。 監査ログに追記します。その書き込みが失敗した場合、操作も失敗します。
盗む価値のある6つの決定
エラーはデータであり、例外ではありません。 すべての結果は、型付きのResultCodeを持つOperationResultです。トレースバックがクライアントに到達することはありません。スタックトレースを受け取ったモデルはそれを回避しようとします。POLICY_DENIEDを受け取ったモデルは、有用で曖昧さのない何かを伝えられています。
拒否は常に勝ちます。 許可ルールと拒否ルールは重み付けも順序付けもされず、タイブレークはありません。拒否が一致した場合、答えはノーです。引数ルールはトークンを順序に依存せずに一致させるため、フラグを並べ替えてもバイパスにはなりません。
明示的なルートなしには何も起動しません。 クイックスタートで説明しましたが、設計リストにも含めるべきです。なぜなら「賢明なデフォルト」がほとんどのサンドボックスが最初の脱出を獲得する方法だからです。
監査ログはあなたを止めることができます。 デフォルトではフェイルクローズ: ログを書き込めない場合、操作は発生しません。SAFE_MCP_AUDIT_FAIL_MODEでフェイルオープンに切り替えることができますが、それは誰かが見える設定ファイルで意図的に行う決定です。
切り詰めの前に編集。 この2つを逆にすると、64KBの出力キャップが秘密を半分にスライスし、最初の断片を発行し、パターンに一致しません。これは、リークのカテゴリ全体を閉じる2行の順序の選択です。
エントロピーベースの秘密検出はデフォルトでオフです。 ハッシュ、UUID、base64ペイロードで常に発火します。狼が来たと叫ぶ編集者は、自分のユーザーによって無効にされます。それは、事前に限界を認めるものよりも悪いです。
PROTECTION vs RESTRAINT
スキップルールは2つの層に分かれており、すべてのルールはどちらに属するかを宣言します。
PROTECTIONは、例外なくすべてのツールによって強制されます。その一部だけが宣言されたルールです — unsafe-nameはファイル名の制御文字をカバーします。残りは構造的です: パスエスケープはsafety/paths.pyの包含チェックに失敗し、占有された宛先はservers/files/apply.pyのlstatチェックに失敗し、シンボリックリンクは許可ルールにまったく一致せず、デフォルト拒否にフォールスルーします — これはevaluate_layeredが意図的にPROTECTIONとして分類します。下の注を参照してください。いずれにせよ、フラグもオーバーライドも「自分が何をしているかわかっている」という議論もありません。これらは不変条件です。
RESTRAINTはplan_organizeによってのみ強制されます。インストーラー、アプリケーションフォルダー、システムファイル、隠しファイル、ディレクトリ。これらは危険ではありません — 自動分類器があなたの代わりに推測すべきではないものです。プランナーはそれらをNEEDS_EXPLICIT_REQUESTとして報告し、次に進みます。
その結果が上記のデモペアです。move_fileはあなたが自分で指定したインストーラーを移動します。それを拒否することは安全性ではなくパターナリズムだからです。どれだけ明示的に要求してもシンボリックリンクは移動しません。それは包含だからです。
明確にすべきニュアンスが1つあります: シンボリックリンクの拒否理由は、
"'organize' is not permitted by any rule (deny by default)"と読み取られ、
「シンボリックリンク」に言及するものではありません。シンボリックリンクはどちらの層の許可ルールにも一致しないため、デフォルト拒否にフォールスルーします — そしてsafety.policy.evaluate_layeredは、ポリシーが語彙を持たない何かの最も厳格な解釈として、一致しないデフォルト拒否フォールスルーを意図的にPROTECTIONとして分類します。一般的な表現は弱い保証ではありません。上記のmove_fileがシンボリックリンクを直接指定していることが証明です。
どこでも繰り返す実装の詳細: PROTECTIONセットは、完全なルールセットをフィルタリングして導出され、独自のリストとして組み立てられることはありません。手動で維持された2つのリストはドリフトし、ここでのドリフトの障害モードは、保護ルールが静かに失われることです。フィルターは忘れることができません。
テストについて
661のテスト、すべて密閉されています。実際の状態を書き込むテストはありません: 実行されるものはすべてtmp_pathに対して行われ、同梱のpolicy.tomlとpolicy.example.tomlを読み取る2つのスイートは、まずそれらをtmp_pathにコピーします。実際の監査ログやワークスペースに触れることはありません。CIは、すべてのプッシュとプルリクエストでスイートとgitleaksスキャンを実行します。
その数は、テストを追加した瞬間に古くなります。重要な特性は分離であり、数ではありません。
保証
各行は、それを強制するモジュールと関数を指定します。ここでの主張が、開くことができるコードによって裏付けられていない場合、テーブルにあるべきではありません。
保証 | 強制方法 |
両サーバーでデフォルト拒否 |
|
一致する拒否は常に一致する許可に勝る |
|
ターミナルサーバーにシェルはない |
|
シェルのメタ文字はポリシーチェックの前に拒否される |
|
コマンドは1つのディレクトリに閉じ込められる |
|
ファイル移動は1つのワークスペースに閉じ込められ、最終コンポーネントではシンボリックリンク安全 |
|
2層のファイルポリシー: 安全性の不変条件 vs 推測回避 |
|
宛先が黙って上書きされることは決してない |
|
削除ツールは存在しない。ガード付きであっても |
|
すべての移動は元に戻せる。適用済みプラン全体も含む |
|
古くなったプランは何も移動しない |
|
ハングしたコマンドは期限で強制終了される |
|
出力は秘匿化の後、決して前ではなく、上限が適用される |
|
子プロセスは浄化された環境を受け取る |
|
シークレットは出力と監査フィールドの両方から秘匿化される |
|
監査証跡はデフォルトでフェイルクローズド |
|
すべての拒否は理由付きで記録される |
|
脅威モデル — 明示的にスコープ外
許可リストに登録した危険なコマンド。 インタープリタやシェル的な ツール(
bash、python、sh、find -exec、awk、envなど)を許可した場合、 モデルはそのツールができることは何でもできる。ポリシーの強度は完全に オペレーターの許可リスト次第である。カーネル / サンドボックスエスケープ。 ジャイルはパス包含チェックであり、 カーネルサンドボックスではない — 名前空間、cgroups、seccomp はない。「正直な注意点」を参照。
状態ファイルへのホストアクセス。 監査ログとアンドゥジャーナルは サーバーに対して改ざん検知可能であるが、ホストのファイルシステムアクセス権を 持つ誰に対しても改ざん防止はできない。
秘匿化の完全性。 パターンベースでベストエフォート。パターンが 認識しないシークレットの形状は通過する。
マルチテナントのアイデンティティやレート制限。 どちらのサーバー内にも 呼び出し元ごとの認証はない — 信頼境界は「このプロセスを起動できる者」であり、 MCP クライアントが起動することで強制するのであって、このコードが強制するのではない。
パスの検証と実行の間の競合(TOCTOU)。 以下の「正直な注意点」を参照。
素朴な MCP サーバーとの比較
多くの手早い MCP サーバーは、ターミナルアクセスに subprocess.run(cmd, shell=True) を、
ファイル移動に os.rename をラップしている。どちらも便利で安全ではない。
この表は事実に基づくものであり、完全なセキュリティを主張するものではない。
懸念事項 | 素朴な MCP サーバー | safe-mcp-suite |
コマンド実行 |
|
|
シェルのメタ文字 | 解釈される( | ポリシーチェックの前に拒否される |
ファイル移動 | プロセスが到達できる場所ならどこでも | 1つのワークスペースに閉じ込められる。どちらかの端のシンボリックリンクは拒否され、決して追わない |
ファイルの上書き | 通常は黙って行われる — POSIX の | 常に拒否される。番号付きの変種( |
アンドゥ | なし | すべての移動はジャーナル化される。 |
ファイルの削除 | しばしば存在し、しばしば無防備 | このサーバーには削除ツールは存在しない。完全に。 |
子プロセスに与えられる環境 | 親の完全な環境、シークレット含む |
|
出力やログのシークレット | そのまま通過 | 応答と監査証跡の両方で、切り詰めの前に秘匿化される |
監査可能性 | デフォルトではなし | 追記専用 JSONL、デフォルトでフェイルクローズド |
自動アクション vs 明示的に要求されたアクション | 1つのコードパスが両方を同じように扱う |
|
両方のツールは OperationResult を返す:
OperationResult {
ok: bool
code: ResultCode
action: str
reason: str
detail: dict # stdout, stderr, exit_code — empty when nothing ran
duration_ms: int
}run_command(command: str, cwd: str | None = None)—commandをポリシーに照らして評価し、許可されればサンドボックス内で実行する。cwdはオプションで、ジャイルルート内に解決される必要がある。トラバーサル、シンボリックリンク、または脱出する絶対パスは、何も実行せずにPATH_ESCAPEを返す。すべての呼び出しは監査される。フェイルクローズド監査では、書き込み不可のログは、未記録で実行するのではなくAUDIT_UNAVAILABLEを返す。非ゼロの終了コードでもok: trueである — コマンドは実行された。成功したかどうかはコマンド自身の問題である。explain_command(command: str)— ドライラン。run_commandと同じ解析と評価に到達し、実行器の前に返るため、detailはstdout、stderr、終了コードを決して運ばない — 何も実行されていない。
このサーバーが返せる結果コード: OK、POLICY_DENIED、INVALID_REQUEST、PATH_ESCAPE、TIMEOUT、OUTPUT_TRUNCATED、OPERATION_FAILED、AUDIT_UNAVAILABLE、INTERNAL_ERROR。
6つのツールがあり、そのリストが設計そのものである — 7つ目はなく、どれも削除しない。
list_files(subdir: str | None = None)— 読み取り専用。各エントリの名前、サイズ、カテゴリ、オーガナイザーが移動するかどうか、移動しない場合はその理由を報告する。plan_organize()— 移動とスキップを提案する。宛先フォルダを含め、何も変更しない。apply_planに渡すplan_idを返す。apply_plan(plan_id: str)— プランを実行する。すべてのファイルが最初に再チェックされる。計画時から変更、移動、または消滅したファイルが1つでもあれば、プラン全体が拒否される。使い捨て — id は再生できない。move_file(src: str, dest: str)— 名前付きの1つのファイルを移動する。destは完全な宛先パスであり、フォルダではない。既に存在する宛先は拒否され、上書きも、名前を変えて回避することもない。PROTECTION ルールのみに従う — 上記の「2層モデル」を参照。undo_last_action()— 最新の移動または適用済みプランを1つのアクションとして元に戻す。復元されたファイルのために何かを上書きすることはない。redo_last_action()— 最新のアンドゥされたアクションを再適用する。新しい作業が記録されるたびに、リドゥスタックはクリアされる。
このサーバーが追加で返せる結果コード: NEEDS_EXPLICIT_REQUEST(すべての安全性の不変条件をクリアしているが、プロンプトなしで行動することが推測になるため拒否された — ターゲットを直接指定して要求してください)。
1つのファイル policy.toml を両方のサーバーが読む:
audit_log = "audit.jsonl" # shared
audit_fail_mode = "closed" # shared: "closed" or "open"
[redaction] # shared
enabled = true
entropy_fallback = false
extra_patterns = [] # [{ name = "...", regex = "..." }]
[terminal]
# jail_root = "/srv/safe-mcp/sandbox" # REQUIRED — here or via env
[terminal.limits]
timeout_seconds = 30
max_output_bytes = 65536
[terminal.allowlist]
commands = ["ls", "cat", "echo", "pwd", "git"]
[terminal.denylist]
commands = ["rm", "shutdown", "reboot", "curl", "wget", "chmod", "sudo"]
[[terminal.rules]]
command = "git"
deny_args = ["push --force", "push -f"]
reason = "force-push rewrites shared history"
[files]
# workspace_root = "/srv/safe-mcp/inbox" # REQUIRED — here or via env
journal = "organizer-journal.json"
max_plan_moves = 500
[files.categories]
Documents = [".pdf", ".doc", ".docx", "..."]
# ...
[[files.skip]]
layer = "protection" # or "restraint" — required, no default
when = ["unsafe-name"]
reason = "..."ポリシーファイル自体にはデフォルトの場所がない。--config で指定する:
safe-mcp terminal --config /path/to/policy.toml
safe-mcp files --config /path/to/policy.toml環境変数は引き続きサポートされ、それぞれが対応する policy.toml のキーを上書きする。SAFE_MCP_POLICY_FILE は特筆に値する唯一の例外である: これは --config の代替であり、上書きではない — 両方が指定された場合は --config が優先され、どちらも指定されない場合は起動が拒否される。
変数 | 意味 | デフォルト |
|
| なし — 起動を拒否 |
| ターミナルジャイルディレクトリ(必須。ここまたは | なし — 起動を拒否 |
| ファイルワークスペースディレクトリ(必須。ここまたは | なし — 起動を拒否 |
| アンドゥ/リドゥジャーナルのパス(ワークスペースの外に置く必要があります) |
|
| 共有監査証跡のパス(両方のジャイルの外に置く必要があります) |
|
|
|
|
起動は明示的に失敗します — fatal: メッセージの出力と非ゼロの終了コード — 以下の場合: ポリシーファイルのパスがまったく指定されていない(--config も SAFE_MCP_POLICY_FILE もない)、policy.toml が存在しないか無効、ジャイル/ワークスペースのルートが未設定またはディレクトリではない、オペレーターのリダクション正規表現が無効、[[files.skip]] エントリにラベルがない、または監査ログ/ジャーナルが、移動や改ざんが可能なジャイルの内部に配置されている。
正直な注意事項
これはハードニングレイヤーであり、金庫ではありません。どちらかのサーバーをデプロイする前にこれらをお読みください。
ジャイルはパス包含チェックであり、カーネルサンドボックスではありません。 名前空間、cgroups、seccomp はありません。カーネルのエクスプロイトや、許可リストに登録されたバイナリから到達可能な脱出経路は封じ込められません。
監査ログは改ざん検知可能であって、改ざん防止ではなく、ローテーションもありません。 追記専用でレコードごとのフラッシュ + fsync により、クラッシュしてもレコードを失うことはありませんが、ホストのファイルシステムから
audit.jsonlにアクセスできる人は誰でも読み取り、変更、削除が可能です。また、ファイルは無制限に成長し、組み込みのローテーションや保持ポリシーはありません。リダクションはパターンベースでベストエフォートです。 一般的なシークレットの形状は捕捉しますが、新しい形式や珍しい形式はリダクションされずに通過します。オプションのエントロピーフォールバックはデフォルトでオフですが、これは弱いからではなく、git SHA、UUID、base64 データでノイズが多いためです。
ターミナルのメタ文字は引用符の中でも拒否されます — 既知の限界です。
echo "a;b"は、引用符内では;が不活性であるにもかかわらず拒否されます。これは、スキャンが引用符を認識しない生の部分文字列チェックであるためです。これは安全側に誤る方向です — そもそも引用符を無視するスキャンをオペレーターが引用符のトリックで回避することはできません — しかし、正当な入力の一部が拒否されることを意味します。TOCTOU: 検証後に操作されるパスは、その間に変更される可能性があります。
safety/paths.pyとservers/files/apply.pyの両方が包含または占有をチェックしてから、別の syscall で操作を行います。その隙間にシンボリックリンクがすり替えられたり、ファイルが作成されたりした場合はカバーされません。これは意図的で名前付きの限界としてコード内に文書化されており(両ファイルの# NOTE:コメント)、必要になった場合のアップグレードパス(O_NOFOLLOWと dir-fd 相対操作)も記載されています。孫プロセスは回収されません。 強制終了またはタイムアウトしたコマンドの子プロセスは別のプロセスグループに属していません。実行器は直接の子プロセスのみを強制終了するため、そのコマンドが生成したものはすべて存続し得ます。
関連プロジェクト
hardened-terminal-mcp — このスイートのターミナルサーバーの前身となるスタンドアロン版。
MCP-file-organizer — このスイートのファイルサーバーの前身となるスタンドアロン版。
ライセンス
MIT — LICENSE を参照。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
- Parallel Task MCPOAuth
An MCP server for deep research or task groups
Run commands and read/write files on your servers over Termalin's keyless tunnels (hosted MCP).
A MCP server built for developers enabling Git based project management with project and personal…
An MCP server that provides read access to your cloud storage providers, bank accounts and more.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA secure MCP server for shell operations, terminal management, and process control, enabling AI assistants to safely execute commands and manage interactive sessions.2046MIT
- FlicenseNot gradedqualityCmaintenanceA secure, controlled terminal MCP server that enables executing whitelisted shell commands safely with multiple security layers.
- AlicenseNot gradedqualityCmaintenanceA production-ready MCP server for secure, session-based command execution, file manipulation, and system inspection via local terminal sessions.14ISC
- AlicenseAqualityAmaintenanceSecurity-hardened MCP server that runs only allowlisted commands with no shell, jailed to a single directory, and bounded execution.2MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Asaad-Suliman/safe-mcp-suite'
If you have feedback or need assistance with the MCP directory API, please join our Discord server