MCP ToolHub
Provides read-only Git repository inspection with status and diff tools, and supports administrator-approved Git shell commands for broader Git operations.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MCP ToolHubShow me the git status and current diff in the workspace."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MCP ToolHub
MCP ToolHub is a local, stdio-only Model Context Protocol server that exposes bounded workspace filesystem operations, read-only Git inspection, structured command execution, and an audit trail. Mutating filesystem operations and all agent-selected external shell commands use the existing out-of-band human approval model.
ToolHub does not expose an HTTP, SSE, or other network listener.
Features
Workspace-confined file reading, directory listing, writes, and patches
Read-only Git status and diff operations
Structured shell commands with deny-by-default risk classification
OS-backed process-tree containment for external executions
Atomic, expiring, single-use approval requests
Versioned, resumable Contract V1 lifecycle results
Separate trusted administrator CLI; no MCP self-approval tool
Bounded, redacted JSON Lines audit events
Windows and POSIX support
Related MCP server: enterprise-agent-lab
Requirements
Python 3.12 or newer (CI currently validates 3.12 and 3.13)
An MCP client that supports stdio servers
gitfor Git tools and approval-gated Git shell requests
Installation
Install from a source checkout with uv:
uv tool install .Or build and install the wheel:
uv build
uv tool install dist/mcp_toolhub-0.1.0-py3-none-any.whlInstallation provides two executables:
mcp-toolhub— the stdio MCP servermcp-toolhub-admin— the trusted human approval CLI
Runtime configuration
Workspace root
TOOLHUB_WORKSPACE_ROOT is required for mcp-toolhub serve and for the admin
CLI. It must contain an absolute path to an existing directory. ToolHub
canonicalizes the path once and freezes it for the lifetime of the process.
ToolHub intentionally does not default to the current directory, source checkout, or installation directory.
Trusted state root
TOOLHUB_STATE_ROOT optionally selects the directory containing
workspace-binding.json, approvals.json, and audit.jsonl. It must be
absolute when provided. The directory is permanently bound on first valid use
to exactly one canonical workspace; reusing it for another workspace fails
closed.
When TOOLHUB_STATE_ROOT is unset, ToolHub uses the platform-appropriate
per-user state directory from platformdirs as a base. Each canonical
workspace receives an independent namespace below workspaces/, named with a
deterministic SHA-256 identifier derived from the platform-normalized canonical
workspace path. The identifier avoids placing the workspace path in directory
names, but it is namespace separation rather than an authentication secret.
Moving or renaming a workspace normally creates a new default namespace.
The state directory is created as needed, canonicalized, and frozen with the workspace configuration. Startup fails if the state root is inside the workspace. The server and administrator CLI must run as the same user and with the same workspace and state configuration so they share this state.
POSIX example
export TOOLHUB_WORKSPACE_ROOT=/home/alice/projects/example
export TOOLHUB_STATE_ROOT=/home/alice/.local/state/mcp-toolhub
mcp-toolhub serveWindows PowerShell example
$env:TOOLHUB_WORKSPACE_ROOT = "D:\work\example"
$env:TOOLHUB_STATE_ROOT = "$env:LOCALAPPDATA\mcp-toolhub"
mcp-toolhub serveThe server writes no banner or human log text to stdout. Stdout is reserved exclusively for MCP protocol messages. Expected configuration errors are reported concisely on stderr and exit nonzero.
Commands
mcp-toolhub --version
mcp-toolhub serve
python -m mcp_toolhub serve
mcp-toolhub-admin --help
mcp-toolhub-admin list
mcp-toolhub-admin approve REQUEST_ID
mcp-toolhub-admin reject REQUEST_ID
mcp-toolhub-admin prune approvals --older-than-days N [--apply]
mcp-toolhub-admin prune audit --keep-last N [--apply]The admin command is human-facing and may write ordinary output to stdout. It is not an MCP transport process.
MCP client configuration
The exact outer configuration key varies by client. A typical POSIX stdio entry is:
{
"mcpServers": {
"toolhub": {
"command": "mcp-toolhub",
"args": ["serve"],
"env": {
"TOOLHUB_WORKSPACE_ROOT": "/home/alice/projects/example",
"TOOLHUB_STATE_ROOT": "/home/alice/.local/state/mcp-toolhub"
}
}
}
}Windows paths require JSON escaping:
{
"mcpServers": {
"toolhub": {
"command": "mcp-toolhub",
"args": ["serve"],
"env": {
"TOOLHUB_WORKSPACE_ROOT": "D:\\work\\example",
"TOOLHUB_STATE_ROOT": "C:\\Users\\alice\\AppData\\Local\\mcp-toolhub"
}
}
}
}Use an absolute executable path if the MCP client does not inherit the shell's
PATH.
Tool inventory
The production server exposes exactly these 14 MCP tools:
toolhub.pingtoolhub.audit_recenttoolhub.capabilitiestoolhub.request_statusfilesystem.list_directoryfilesystem.read_filefilesystem.write_filefilesystem.write_file_approvedfilesystem.apply_patchfilesystem.apply_patch_approvedgit.statusgit.diffshell.runshell.run_approved
There is no MCP administration, approve, or reject tool.
Execution Contract V1
ToolHub's agent-facing execution contract is version 1.0. This contract
version is independent of the package/server version: compatible package
releases may keep Contract V1, while a deliberate incompatible contract change
requires a new contract version.
toolhub.capabilities is a deterministic, read-only discovery tool. It returns
contract_version, package_version, transport (stdio), the public human
approval model, approval-gated initial/resume tool mappings, and bounded public
limits. shell_output_retained_chars and git_output_retained_chars are the
number of original output characters retained before a truncation marker is
appended; they are not maximum lengths for the returned strings. Capabilities
does not expose approval payloads, trusted state paths, executable paths, or
secrets.
Approval-gated operation results use MCP structuredContent as the primary
machine-readable API and include:
outcome: one ofAPPROVAL_REQUIRED,APPROVAL_PENDING,APPROVAL_APPROVED,APPROVAL_REJECTED,APPROVAL_EXPIRED,APPROVAL_CONSUMED,SUCCEEDED,COMMAND_FAILED,TIMED_OUT,CONFLICT,REFUSED, orFAILED.trace_id: one lifecycle correlation ID preserved across submission, approval state, approved execution, and audit events.approval: when applicable, an approval handle containingrequest_id,status,expires_at, and the server-derivedresume_tool.error: when applicable, a bounded object containing a stablecode, a human-readablemessage, and aretryableboolean.
Expected domain outcomes—approval states, unknown requests, policy refusals,
conflicts, nonzero commands, and timeouts—are structured results. Input-schema
validation errors and unexpected internal failures remain MCP/tool errors.
Clients must make decisions from outcome, approval, and error, never by
parsing the compatibility message or text representation.
toolhub.request_status accepts only request_id. It is strictly read-only:
it never approves, rejects, consumes, extends expiry, or persists lazy expiry.
For a current-workspace request it returns the effective approval state,
original trace_id, and safe approval handle without the protected payload.
An unknown ID and an ID belonging to another workspace receive the same
REFUSED / REQUEST_NOT_FOUND shape, with no existence metadata.
Generic client state machine
Call
toolhub.capabilitiesand select a supported Contract V1 operation.Call the operation and inspect its structured
outcome.On
APPROVAL_REQUIRED, retain the returned approval handle.Poll
toolhub.request_statusat an appropriate cadence while it reportsAPPROVAL_PENDING.On
APPROVAL_APPROVED, invokeapproval.resume_toolwith only the storedrequest_id.Inspect the resumed structured outcome; do not retry a consumed approval.
CLIENTS MUST NEVER AUTOMATE HUMAN APPROVAL. Approval and rejection remain out-of-band actions performed through the trusted local administrator CLI. Coding-agent integrations, including Repo Doctor-style clients, should use only the generic MCP discovery, status, and resume flow; ToolHub embeds no client-specific code.
Human approval workflow
An MCP mutation or external shell request returns
APPROVAL_REQUIREDand an approval handle.The administrator runs
mcp-toolhub-admin listwith the same workspace and state environment as the server.For approval, the administrator runs
mcp-toolhub-admin approve REQUEST_ID.The CLI displays the protected request and requires the operator to type
APPROVEexactly.The MCP caller observes
APPROVAL_APPROVEDthroughtoolhub.request_status, then invokes the handle'sresume_toolwith the request ID. Successful consumption is atomic and single-use.
For shell requests, the approval display includes the original program, canonical resolved executable, SHA-256, byte size, cwd, and separately JSON-escaped argument values. It does not represent arguments as an ambiguous shell command string.
Trusted state maintenance
Trusted-state pruning is explicit, human-operated maintenance through
mcp-toolhub-admin; it never runs at server startup or from an environment
toggle. Both commands are dry runs unless --apply is supplied:
mcp-toolhub-admin prune approvals --older-than-days 30
mcp-toolhub-admin prune approvals --older-than-days 30 --apply
mcp-toolhub-admin prune audit --keep-last 10000
mcp-toolhub-admin prune audit --keep-last 10000 --applyApproval pruning removes only terminal REJECTED, EXPIRED, or CONSUMED
records whose terminal timestamp is at or before the cutoff. A PENDING or
APPROVED record that is already past expires_at is effectively expired and
uses that expiry time for eligibility; still-valid pending and approved
requests are never pruned. Apply mode re-reads and recomputes eligibility while
holding the approval-store lock.
Approval store capacity
approvals.json has fixed ceilings of 10,000 records and 16 MiB for its final
serialized UTF-8 representation. ToolHub checks both limits atomically under
the approval-store lock when adding a request. Values exactly at either limit
are allowed; a new request that would exceed a limit fails closed without
changing the store.
ToolHub never automatically deletes approval records or evicts valid active
requests to make room. When the store is full, an administrator can inspect and
prune old terminal records with mcp-toolhub-admin prune approvals --older-than-days N; pruning remains explicit and is a dry run unless
--apply is supplied. Existing approval state transitions and explicit pruning
remain available when a previously created or restored store is already over a
current ceiling. These ceilings bound ToolHub's approval store; they are not a
general disk quota or operating-system resource sandbox.
Audit pruning retains the newest N complete events in their original order;
--keep-last 0 --apply explicitly empties an existing valid log. Appends and
compaction share a cross-process lock, and malformed audit content causes a
safe refusal without replacement. Maintenance uses only the already-bound
workspace/state namespace, exposes no protected payloads, and adds no MCP tool
or Contract V1 surface.
Security model and limitations
Structured shell commands
shell.run uses a deny-by-default command policy. LOW is limited to exact
ToolHub intrinsics, currently the running Python version query. LOW never
searches PATH and never creates an external subprocess. Generic Git, shell
interpreters, Windows batch scripts, the py launcher, and unknown programs
are never LOW.
Every external shell command is MEDIUM or HIGH and requires an out-of-band administrator approval. The approval captures immutable program, argument, cwd, timeout, workspace, and primary-executable snapshots. An approved shell request is atomically consumed before snapshot validation; any later failure permanently consumes it, so a retry requires a new approval.
Immediately before subprocess launch, ToolHub validates the primary
executable's canonical path, size, and SHA-256. Execution uses that absolute
path with shell=False. This is a validated primary executable identity
immediately before launch, not a cryptographic guarantee of the exact bytes
ultimately mapped by the operating system.
Sanitized child-process environment
External commands do not inherit the ToolHub server process environment. ToolHub builds a minimal sanitized environment when the approval request is created, stores its versioned snapshot in protected approval state, and executes with exactly that snapshot after approval. The snapshot is validated and bound by a deterministic digest; legacy or malformed shell approvals that lack it cannot execute and require a new request.
On POSIX, the current baseline inherits no environment variables. On Windows,
only validated absolute SystemRoot, WINDIR, TEMP, and TMP values are
preserved when present. PATH, shell profiles, home/configuration locations,
credentials, and interpreter, module, loader, shell, and Git injection
variables are excluded. Users should not expect arbitrary profile or PATH
behavior in approved children. Primary executable selection remains absolute,
fingerprinted, and approval-bound; the sanitized environment is not used to
select it.
Process-tree containment
Every approved external command executes inside a dedicated OS-backed process-tree lifetime boundary. Fixed read-only Git subprocesses use the same boundary. On POSIX, ToolHub creates a new session/process group for each execution. On Windows, it creates a per-execution Job Object with kill-on-job-close enabled, starts the primary process suspended, assigns it to the Job Object, and resumes it only after assignment succeeds.
On timeout, ToolHub terminates the contained execution tree rather than only the primary process, captures available output with bounded cleanup waits, and reaps the launched child. Containment resources are released deterministically on success, command failure, timeout, start/setup failure, and internal cleanup paths; remaining contained descendants are terminated when an execution is cleaned up.
Process-tree containment complements rather than replaces human approval, primary-executable identity binding, workspace binding, and the sanitized environment policy. It is a process-lifetime boundary, not a full OS sandbox or container: it does not add filesystem or network sandboxing, CPU or memory resource isolation, kernel-level protection, or safe execution of arbitrary hostile code.
Bounded output capture
Approved shell executions and fixed read-only Git subprocesses capture stdout
and stderr through dedicated per-stream drain threads that start together with
the child process and read continuously until the pipes reach EOF. Each stream
retains at most 256 KiB (MAX_CAPTURE_BYTES_PER_STREAM) in a fixed-capacity
buffer; everything beyond that cap is still drained from the pipe but
discarded, with exact total/retained/dropped byte counters recorded as bounded
audit metadata.
Because the pipes are always drained, a child or contained descendant that
writes more than the OS pipe capacity cannot block the capture path, and
ToolHub memory for retained output is O(1) in the volume produced. Output
truncation is a reporting fact, not a failure: a command that exits 0 with
discarded output remains SUCCEEDED, a non-zero exit remains
COMMAND_FAILED, and timeouts remain TIMED_OUT. When the capture cap
discards output, the affected stdout/stderr value carries a deterministic
[ToolHub discarded N output bytes] marker.
Timeout and process-tree containment semantics are unchanged: on timeout
ToolHub terminates the contained tree, keeps draining until EOF or a bounded
deadline, closes its pipe handles as a final unblock if needed, and returns
TIMED_OUT with the retained output prefix.
Bounded capture protects ToolHub's own retained output memory (at most a few hundred KiB of retained buffers per subprocess plus small transient read chunks). It is not a CPU, memory, network, or filesystem sandbox for the child process itself.
ToolHub guarantees:
LOW never creates an external subprocess.
Every external shell execution requires MEDIUM or HIGH approval.
The agent cannot replace approved program, arguments, cwd, or timeout.
Approvals are atomic, expiring, and single-use.
Workspace and primary-executable snapshots are required and fail closed.
The primary executable's canonical identity and hash are revalidated immediately before launch.
Filesystem paths remain within the frozen workspace boundary.
Mutation paths reject symlink traversal and enforce
expected_hashconcurrency checks where applicable.
ToolHub does not guarantee:
Exact byte identity against a concurrent local filesystem adversary during the narrow final check-to-exec race.
Identity of DLLs, interpreters, helpers, plugins, configuration files, code-selected dependencies, or descendant processes.
That approved executable bytes are benign, signed, or from a reputable publisher.
The administrator approval display exposes the protected canonical path, hash, size, cwd, and exact JSON-escaped argument boundaries. Agent-readable audit events omit external executable directories, retaining basename, hash, size, scope, and request-ID correlation.
Audit behavior
Audit events are appended to audit.jsonl under the trusted state root. They
contain bounded metadata, redact recognizable secret arguments, and store only
stdout/stderr character counts and bounded capture byte counters rather than
raw process output. Audit write failures are non-fatal to tool execution.
Contract compatibility fixture
tests/fixtures/contract_v1.json locks Contract V1's tool names, annotations,
approval mappings, stable enums, public input/output fields, required fields,
and normalized schema digests. Descriptions and other cosmetic SDK noise are
excluded.
To update it deliberately, first decide whether the change is compatible. An
incompatible change requires a new contract version and fixture. For a
compatible intentional schema change, update the models, regenerate the
normalized surface using tests/test_contract.py::_contract_surface, review
the readable field lists and changed digests, then update the fixture in the
same reviewed change. Never accept a fixture diff solely to make the test pass.
Development
Install all locked runtime and development dependencies:
uv sync --all-groupsRun the required checks:
uv run ruff check .
uv run ruff format --check .
uv run python -m compileall -q src/mcp_toolhub
uv run pytest -q
uv build
git diff --checkTo apply formatting deliberately:
uv run ruff format .Artifact smoke test
After uv build, run the cross-platform smoke driver with a virtual
environment outside the checkout.
POSIX:
uv run python scripts/artifact_smoke.py --dist-dir dist --venv /tmp/mcp-toolhub-wheel-env --repository .Windows PowerShell:
uv run python scripts/artifact_smoke.py --dist-dir dist --venv "$env:TEMP\mcp-toolhub-wheel-env" --repository .The driver inspects wheel contents, installs only the wheel into the isolated
environment, verifies console/version behavior, and runs initialize,
list_tools, structured capability/status calls, complete write/patch/shell
approval lifecycles, rejection, replay, cross-workspace isolation, invalid
configuration, and server/admin shared-state tests from outside the repository.
Troubleshooting
TOOLHUB_WORKSPACE_ROOT is required: add an absolute existing workspace path to the MCP client's environment.Workspace is not a directory: create the directory or correct the path.
State root must be outside the workspace: move
TOOLHUB_STATE_ROOTto a trusted directory the MCP filesystem tools cannot address.State namespace belongs to a different workspace: select a different explicit
TOOLHUB_STATE_ROOT; bindings are never silently reassigned.Client reports invalid stdio JSON: verify wrappers and startup scripts do not print banners or logs to stdout.
Admin cannot see a request: confirm server and admin run as the same user with identical workspace and state-root settings.
Executable changed after approval: request a new approval; consumed or invalidated approvals are never replayed.
Production ToolHub intentionally exposes no HTTP, SSE, public network, authentication-server, container-orchestration, or cloud-hosting surface.
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 Servers
- FlicenseNot gradedqualityBmaintenanceEnables AI coding agents to evaluate actions against team-defined policies, record decisions, and obtain human approvals for potentially risky operations.1651
- FlicenseNot gradedqualityCmaintenanceEnables controlled AI-agent access to enterprise-shaped tools with a deny-by-default gated write path, human approval, dry-run execution, and append-only audit logging.
- FlicenseNot gradedqualityCmaintenanceEnables AI coding agents to run Kubernetes inspection and Terraform plan/apply operations inside ephemeral gVisor-sandboxed jobs with short-lived, narrowly-scoped credentials, while routing destructive changes through a human approval gate.
- AlicenseNot gradedqualityCmaintenanceEnables controlled delegation of tasks to local coding-agent CLIs and the Manus API, with strict sandboxing, approval tracking, and remote-egress safeguards.11MIT
Related MCP Connectors
Runtime permission, approval, and audit layer for AI agent tool execution.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Preflight, approve, and prove consequential agent actions with signed evidence and x402 tools.
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/zhihaochen67/mcp-toolhub'
If you have feedback or need assistance with the MCP directory API, please join our Discord server