Home / Docs / workspacemcp
On this page

workspacemcp

πŸ“ 20 tools

workspacemcp β€” Secure, auditable workspace management for AI agents

Python 3.11+ License AGPL-3.0 Tests

v1.0 β€” Secure workspace access for AI agents. Claude Code-style edit tools (edit_file/multi_edit/apply_patch), cat -n reads, ripgrep search, glob, plus DLP secret redaction, human approval gates, auto-checkpointing, and full audit logging. Expose as an MCP server with 20 tools.


Part of the MCP AI suite: ragmcp (knowledge) Β· memorymcp (memory) Β· planningmcp (reasoning) Β· workspacemcp (interaction)

Philosophy: β€œThe Agent is a Guest, not the Master.” β€” Least privilege, sandboxed paths, read-only by default, human approval for dangerous operations.


What is workspacemcp?

workspacemcp gives AI agents controlled access to the filesystem β€” reading, writing, searching, and versioning files β€” while enforcing strict security at every step:

  • Path sandboxing confines all operations to a workspace root; symlink escapes are blocked
  • DLP content filtering scans every read and write for 15 secret/PII patterns, redacting before the LLM sees content
  • Approval gates require human sign-off for writes to critical files (production configs, .env, Dockerfiles, keys)
  • Auto-checkpointing snapshots files before every mutation for instant rollback
  • Ambiguity guard refuses edit patches that match multiple locations β€” no silent corruption
  • Full audit trail logs every action (read, write, edit, delete, search, checkpoint, approval, DLP event)
  • Event bus streams 12 event types for real-time monitoring and integration
  • MCP server exposes 20 tools natively to Claude Desktop, Cursor, or any MCP client

Security Flow

Every request passes through the full security chain before execution:

Request
Sandboxpath-escape prevention
DLPsecret redaction (15 patterns)
Gatehuman approval for critical files
Checkpointauto-save before mutation
Executeatomic write
Auditimmutable log entry
Eventreal-time notification

Quick Start

3-line usage

from workspacemcp import WorkspaceFactory

workspace = WorkspaceFactory.default()
entry = await workspace.read_file("src/main.py")

Agent usage

from workspacemcp import WorkspaceFactory

workspace = WorkspaceFactory.create(
    root_path="/home/user/project",
    file_store="local",
    read_only=False,
    allowed_write_patterns=["src/**", "tests/**"],
    auto_checkpoint=True,
)

# Read a file (secrets are automatically redacted)
entry = await workspace.read_file("config.yaml")
print(entry.content)  # API keys replaced with [REDACTED:generic_api_key]

# Edit with ambiguity guard (refuses if old_text matches >1 location)
await workspace.edit_file(
    "src/main.py",
    old_text='print("hello")',
    new_text='print("world")',
    description="Update greeting",
)

# Search across the workspace
results = await workspace.search_workspace(r"TODO|FIXME", glob="*.py")
for r in results:
    print(f"{r.path}:{r.line}: {r.content}")

# Create a named checkpoint for rollback
cp = await workspace.create_checkpoint(label="before-refactor")

# Get workspace statistics
stats = await workspace.get_workspace_stats()
print(f"{stats.total_files} files, {stats.total_size_bytes} bytes")

MCP server

from workspacemcp import WorkspaceFactory
from workspacemcp.mcp_server import WorkspaceMCPServer

workspace = WorkspaceFactory.create(root_path=".", file_store="local")
WorkspaceMCPServer(workspace).run()

Or from the command line:

workspacemcp serve

Claude Desktop claude_desktop_config.json:

{
  "mcpServers": {
    "workspacemcp": {
      "command": "workspacemcp",
      "args": ["serve", "--root", "/path/to/project"]
    }
  }
}

Features

  • πŸ”’ Path sandboxing β€” chroot-like confinement, symlink escape prevention via os.path.realpath()
  • πŸ›‘οΈ DLP content filter β€” 15 secret/PII patterns detected and redacted (AWS keys, GitHub PATs, JWTs, passwords, credit cards, SSNs, and more)
  • βœ‹ Approval gates β€” human-in-the-loop for writes to critical files, with async approve/deny and configurable timeout
  • πŸ“Έ Auto-checkpointing β€” automatic file snapshot before every write, edit, and delete
  • ✏️ Ambiguity guard β€” edit patches that match multiple locations are refused, preventing silent corruption
  • πŸ“‚ File operations β€” read (cat -n numbered), write (atomic), edit (search & replace, replace_all), multi-edit (atomic all-or-nothing), apply-patch (unified git diff), move, delete, list
  • 🧭 Claude Code-style edits β€” old_string/new_string, rich errors with match line numbers, and a freshness guard that refuses edits to a file changed since it was last read
  • πŸ” Fast search & glob β€” ripgrep-backed content search (Python fallback), case-sensitive option, and recursive ** glob sorted newest-first
  • 🌳 Semantic tree β€” project structure with file descriptions, languages, and token estimates
  • πŸ“Š Workspace stats β€” file counts, size, language distribution, largest files, checkpoint and audit counts
  • πŸ“ˆ Mermaid diagrams β€” export workspace structure as Mermaid for documentation
  • πŸ•°οΈ Checkpoint versioning β€” create, list, and restore full workspace snapshots
  • πŸ“ Immutable audit log β€” every action logged with timestamp, agent ID, success/failure, and detail
  • πŸ“‘ Event bus β€” 12 event types with subscribe/emit/async stream
  • πŸ”Œ MCP server β€” 20 tools, stdio transport, compatible with any MCP client
  • 🏭 WorkspaceFactory β€” default() / create() / from_env() / from_yaml()
  • πŸ“‹ CLI β€” 20 commands for workspace management without writing code
  • 🏒 Multi-namespace β€” namespace-based isolation for multi-agent setups
  • 🏘️ Multi-tenant isolation β€” tenant_isolation=True gives each namespace its own root subdirectory (root_path/<namespace>/), with list_tenants() enumeration
  • πŸ”— Suite integration β€” works with ragmcp, memorymcp, and planningmcp

Installation

# Minimal β€” local file store + on-disk SQLite, no external services
pip install mcpaisuite-workspacemcp

# With ragmcp integration (semantic search over workspace)
pip install "mcpaisuite-workspacemcp[ragmcp]"

# Full stack (ragmcp + memorymcp + planningmcp + webhooks)
pip install "mcpaisuite-workspacemcp[all]"

Requirements: Python 3.11+


Security

Path Sandboxing

All file operations are confined to the workspace root. The PathSandbox resolves every path through os.path.realpath() to prevent symlink escape attacks. Traversal attempts like ../../etc/passwd are caught and rejected as SandboxViolation.

sandbox.resolve("../../etc/passwd")
# => SandboxViolation: Path escapes workspace sandbox: ../../etc/passwd

Configuration controls:

SettingDefaultDescription
root_path"."Workspace root β€” all paths are confined here
read_onlyTrueDeny all writes unless explicitly allowed
allowed_write_patterns[]Glob patterns for writable paths (e.g. ["src/**", "tests/**"])
restricted_paths[".env", "*.key", "*.pem", "secrets/"]Paths blocked even for reads
approval_required_patterns["production.*", ".env*", "*.key", "Dockerfile", "docker-compose.*"]Paths requiring human approval for writes

DLP Patterns

The ContentFilter scans every file read and write for 15 secret and PII patterns:

PatternExample Match
aws_access_keyAKIA1234567890ABCDEF
aws_secret_keyaws_secret_access_key = ...
github_patghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
github_oauthgho_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
generic_api_keyapi_key = "sk-...", secret_key: ...
generic_passwordpassword = "hunter2"
private_key-----BEGIN RSA PRIVATE KEY-----
jwt_tokeneyJhbGciOiJIUzI1NiIs...
slack_tokenxoxb-...
stripe_keysk_live_...
emailuser@example.com
phone+1 (555) 123-4567
credit_card4111 1111 1111 1111
ssn123-45-6789
connection_stringpostgres://user:pass@host/db

On read: secrets are replaced with [REDACTED:pattern_name] before the agent sees the content. On write: secrets are detected and logged to the audit trail.

Approval Gates

Writes to files matching approval_required_patterns are held until a human approves or denies via approve_operation(). The gate uses asyncio.Future to block the requesting coroutine, with a configurable timeout (default: 1 hour).

# Agent tries to write production config β†’ blocked, waiting for approval
await workspace.write_file("production.yaml", new_config)

# Human approves from CLI or another tool
await workspace.approve_operation(request_id, approved=True)

Writes, edits and deletes that touch a path matching approval_required_patterns require approval when an approval gate is configured (deletes are not gated unconditionally β€” only when the path matches a sensitive pattern).

Multi-tenant isolation

By default, all namespaces share the same workspace root. When tenant_isolation=True, each namespace is confined to its own subdirectory under the root (root_path/<namespace>/), so tenants cannot read or write each other’s files. The isolation is transparent to callers β€” file operations take the same relative paths regardless.

The PathSandbox.tenant_root(namespace) method resolves (and lazily creates) the per-tenant directory:

  • When isolation is off, or the namespace is empty or "default", tenant_root() returns the shared workspace root unchanged.
  • When isolation is on and a non-default namespace is given, it returns root_path/<namespace>/, creating the directory if needed.

All path resolution then happens relative to that tenant root, and the sandbox still blocks traversal outside it β€” so a tenant cannot escape its subdirectory via ../.

Enable it via the factory:

# Per-tenant root directories
workspace = WorkspaceFactory.create(
    root_path="/srv/workspaces",
    tenant_isolation=True,
    read_only=False,
)

# File ops carry a namespace; each tenant lands in its own subdirectory
await workspace.write_file("notes.txt", "...", namespace="acme")    # β†’ /srv/workspaces/acme/notes.txt
await workspace.read_file("notes.txt", namespace="globex")          # β†’ /srv/workspaces/globex/notes.txt

Or from environment variables:

export WORKSPACEMCP_TENANT_ISOLATION=true
export WORKSPACEMCP_ROOT=/srv/workspaces
workspace = WorkspaceFactory.from_env()

List the tenant namespaces that currently have workspace directories with PathSandbox.list_tenants():

sandbox = PathSandbox(workspace.config)
sandbox.list_tenants()   # e.g. ["acme", "globex"] β€” sorted; ["default"] when isolation is off or no tenants exist

list_tenants() returns ["default"] when tenant isolation is disabled. When enabled, it lists the immediate subdirectories of the root (excluding dot-directories), sorted β€” falling back to ["default"] if none exist yet.


MCP Tools

workspacemcp exposes 20 tools via the MCP protocol (stdio transport):

ToolDescription
list_filesList files in a directory with .gitignore respect
read_fileRead a file with cat -n line numbers, pagination, and DLP filtering (secrets redacted)
write_fileWrite a file with sandbox check, auto-backup, and approval gate
edit_fileReplace old_string with new_string (unique match, or replace_all); rich errors with match line numbers
multi_editApply a sequence of edits to one file atomically (all-or-nothing)
apply_patchApply a unified diff (git patch) across one or more files
globFind files matching a glob pattern (supports **), sorted newest-first
delete_fileDelete a file (requires approval, creates checkpoint first)
search_workspaceFast content search (ripgrep when available, regex fallback), case-sensitive option, context lines
create_checkpointSnapshot workspace state (all files) for rollback
restore_checkpointRollback workspace to a previous checkpoint
list_checkpointsList available workspace checkpoints
get_file_infoGet file metadata (size, modified, checksum, token estimate)
get_semantic_treeGet project structure with descriptions and token estimates
get_workspace_statsGet workspace statistics (files, size, languages)
workspace_configView workspace configuration (sandbox, DLP, approval settings)
audit_logQuery audit log entries
approve_operationApprove or deny a pending write operation
workspace_diagramExport workspace structure as Mermaid diagram
move_fileMove or rename a file in the workspace

CLI

workspacemcp provides 20 CLI commands (including multi-edit, apply-patch, and glob):

# Start MCP server (stdio transport)
workspacemcp serve
workspacemcp serve --root /path/to/project --writable
workspacemcp serve --config workspace.yaml

# Initialize workspace configuration file
workspacemcp init --root . --namespace my_project

# List files
workspacemcp ls
workspacemcp ls src/ -r -p "*.py"

# Read a file
workspacemcp read src/main.py

# Write content to a file
workspacemcp write src/hello.py "print('hello')"

# Edit a file (search and replace)
workspacemcp edit src/main.py "old text" "new text"

# Delete a file
workspacemcp delete src/old_file.py

# Get file metadata
workspacemcp info src/main.py

# Search workspace content (regex)
workspacemcp search "TODO|FIXME" -g "*.py"

# Manage checkpoints
workspacemcp checkpoint create -l "before-refactor"
workspacemcp checkpoint list
workspacemcp checkpoint restore --id abc12345

# View audit log
workspacemcp audit
workspacemcp audit -a write_file -n 20

# Show workspace statistics
workspacemcp stats

# Show workspace configuration
workspacemcp config

# Show semantic project tree
workspacemcp tree --max-depth 3

# Approve or deny a pending operation
workspacemcp approve <request_id>
workspacemcp approve <request_id> --deny

# Export workspace as Mermaid diagram
workspacemcp diagram

# Move or rename a file
workspacemcp move src/old.py src/new.py

Factory

WorkspaceFactory provides four entry points:

WorkspaceFactory.default(root_path=".")

Zero-config: an in-memory file store with on-disk SQLite checkpoint and audit logs (workspacemcp.db, workspacemcp_audit.db). No external services required.

WorkspaceFactory.create(...)

Full configuration:

workspace = WorkspaceFactory.create(
    root_path=".",                              # Workspace root directory
    file_store="local",                         # "local" | "memory"
    checkpoint_store="memory",                  # "memory" | "sqlite"
    audit_logger="memory",                      # "memory" | "sqlite"
    content_filter=True,                        # Enable/disable DLP scanning
    read_only=True,                             # Read-only mode (default)
    allowed_write_patterns=["src/**"],          # Glob patterns for writable paths
    restricted_paths=[".env", "*.key"],         # Paths blocked even for reads
    approval_required_patterns=["production.*"],# Paths requiring human approval
    max_file_size_mb=10,                        # Max file size limit
    auto_checkpoint=True,                       # Auto-snapshot before mutations
    tenant_isolation=False,                     # Per-tenant root dirs (see Multi-tenant isolation)
    namespace="default",                        # Namespace for multi-agent isolation
    sqlite_path="workspacemcp.db",              # SQLite path for persistent stores
)

WorkspaceFactory.from_env()

Reads configuration from environment variables:

VariableDefaultDescription
WORKSPACEMCP_ROOT"."Workspace root path
WORKSPACEMCP_FILE_STORE"local"File store backend
WORKSPACEMCP_CHECKPOINT_STORE"memory"Checkpoint store backend
WORKSPACEMCP_AUDIT"memory"Audit logger backend
WORKSPACEMCP_READONLY"true"Read-only mode
WORKSPACEMCP_TENANT_ISOLATION"false"Per-tenant root directories (multi-tenant isolation)
WORKSPACEMCP_NAMESPACE"default"Namespace
WORKSPACEMCP_SQLITE_PATH"workspacemcp.db"SQLite database path

WorkspaceFactory.from_yaml(path)

Reads configuration from a YAML file and passes all keys to create().


Versioning: Checkpoints + Diff Engine

Checkpoints

Checkpoints snapshot the entire workspace state (all file checksums and contents) for rollback:

# Create a named checkpoint
cp = await workspace.create_checkpoint(label="before-refactor")

# List checkpoints
checkpoints = await workspace.list_checkpoints()

# Restore to a previous state
await workspace.restore_checkpoint(cp.id)

Auto-checkpointing creates a mini-checkpoint of each individual file before every write, edit, or delete β€” so even without explicit checkpoints, you can always recover the previous version.

Diff Engine with Ambiguity Guard

The DiffEngine applies search & replace patches with strict safety:

  • Missing match β€” raises ValueError if the search text is not found
  • Ambiguous match β€” raises AmbiguousPatchError if the search text appears more than once
  • Single match β€” applies the replacement exactly once

This prevents the common failure mode where an LLM’s edit silently corrupts a file by replacing the wrong occurrence. The agent must provide enough surrounding context to make the match unique.

# This will raise AmbiguousPatchError if "import os" appears more than once
await workspace.edit_file("main.py", old_text="import os", new_text="import os\nimport sys")

The engine also provides compute_diff() for unified diff output and preview_patch() for dry-run previews.


Analysis: Semantic Tree, Search, Stats

Semantic Tree

Build a hierarchical view of the project structure with file metadata:

tree = await workspace.get_semantic_tree(max_depth=5)
# SemanticNode with: path, type (file/directory), description, children, size, language, estimated_tokens

Export as Mermaid diagram:

mermaid = await workspace.workspace_diagram()

Regex-powered content search with glob filtering:

results = await workspace.search_workspace(
    pattern=r"TODO|FIXME",
    directory="src/",
    glob="*.py",
    max_results=50,
)
for r in results:
    print(f"{r.path}:{r.line}: {r.content}")
    # Each result includes context_before and context_after lines

Stats

stats = await workspace.get_workspace_stats()
# WorkspaceStatsModel:
#   total_files, total_dirs, total_size_bytes,
#   language_distribution ({"python": 42, "yaml": 5, ...}),
#   largest_files, last_modified,
#   checkpoint_count, audit_entry_count

Events

workspacemcp emits 12 event types through an async event bus:

Event TypeTriggered When
file.readA file is read
file.writtenA file is written
file.editedA file is edited via search & replace
file.deletedA file is deleted
checkpoint.createdA checkpoint is created (manual or auto)
checkpoint.restoredA checkpoint is restored
approval.requestedA write to a critical file is pending approval
approval.grantedAn approval request is granted
approval.deniedAn approval request is denied
dlp.alertDLP detects secrets in content
sandbox.violationA path escape attempt is detected
config.changedWorkspace configuration is modified

Subscribing to events

from workspacemcp.events import workspace_event_bus, WorkspaceEventType

# Subscribe to events for a specific namespace
queue = workspace_event_bus.subscribe(namespace="default")

# Or stream all events asynchronously
async for event in workspace_event_bus.stream():
    print(f"{event.type}: {event.file_path} β€” {event.message}")

Configuration (YAML)

root_path: /home/user/project
file_store: local
checkpoint_store: sqlite
audit_logger: sqlite
sqlite_path: workspacemcp.db

read_only: false
allowed_write_patterns:
  - "src/**"
  - "tests/**"
  - "docs/**"

restricted_paths:
  - ".env"
  - "*.key"
  - "*.pem"
  - "secrets/"

approval_required_patterns:
  - "production.*"
  - ".env*"
  - "*.key"
  - "Dockerfile"
  - "docker-compose.*"

max_file_size_mb: 10
auto_checkpoint: true
namespace: my_project
content_filter: true

Load it with:

workspace = WorkspaceFactory.from_yaml("workspace.yaml")

Integration with ragmcp + memorymcp + planningmcp

workspacemcp is the interaction layer of the MCP AI suite. It works alongside:

  • ragmcp (knowledge) β€” semantic search and retrieval-augmented generation. workspacemcp can use ragmcp to index and search workspace content by meaning.
  • memorymcp (memory) β€” persistent cognitive memory for AI agents. Store facts learned while working in the workspace; retrieve relevant context across sessions.
  • planningmcp (reasoning) β€” structured planning and task decomposition. Plans can include workspace operations as steps, with workspacemcp providing the secure execution layer.

Install integration extras:

pip install "mcpaisuite-workspacemcp[ragmcp]"       # Semantic workspace search
pip install "mcpaisuite-workspacemcp[memorymcp]"     # Cross-session workspace memory
pip install "mcpaisuite-workspacemcp[planningmcp]"   # Plan-driven workspace operations
pip install "mcpaisuite-workspacemcp[all]"           # Everything

Together, the four servers give an AI agent a complete cognitive stack: knowledge retrieval (ragmcp), persistent memory (memorymcp), structured reasoning (planningmcp), and secure workspace interaction (workspacemcp).


Development / Contributing

git clone https://github.com/gashel01/workspacemcp
cd workspacemcp
pip install -e ".[dev]"

# Run tests
pytest tests/ -v                          # 151 tests

# With coverage
pytest tests/ --cov=workspacemcp --cov-report=html

Project structure:

workspacemcp/
  core/             β€” Base classes and Pydantic models (FileEntry, Checkpoint, AuditEntry, etc.)
  filesystem/       β€” File store backends and path sandbox
    sandbox.py         β€” PathSandbox (chroot-like confinement)
    local_store.py     β€” Local filesystem backend
    memory_store.py    β€” In-memory file store (testing)
  security/         β€” Security layer
    dlp.py             β€” ContentFilter (15 secret/PII patterns)
    gates.py           β€” ApprovalGate (human-in-the-loop)
    audit.py           β€” InMemoryAuditLogger, SQLiteAuditLogger
  versioning/       β€” Checkpoint and diff engine
    checkpoint.py      β€” InMemoryCheckpointStore, SQLiteCheckpointStore
    diff.py            β€” DiffEngine with ambiguity guard
  analysis/         β€” Workspace analysis tools
    tree.py            β€” SemanticTreeBuilder + Mermaid export
    search.py          β€” Content search engine
    stats.py           β€” WorkspaceStatsBuilder
  integration/      β€” Integration with ragmcp, memorymcp, planningmcp
  events.py         β€” Event bus (12 event types, subscribe/emit/stream)
  mcp_server.py     β€” MCP server (20 tools, stdio transport)
  factory.py        β€” WorkspaceFactory (default/create/from_env/from_yaml)
  pipeline.py       β€” WorkspacePipeline (central orchestrator)
  cli.py            β€” CLI (20 commands)

Contributions are welcome. Please open an issue before submitting a large PR.


License

AGPL-3.0 β€” see LICENSE.

For commercial licensing (closed-source usage), contact the author.