Bundled local workspace tools
Build and register the local workspace service:
bazel build //mcp/local_tools:local_tools_server
std_slop mcp add local-tools \
--transport stdio \
--command /absolute/path/to/bazel-bin/mcp/local_tools/local_tools_server
The service needs no extra arguments or saved workspace path. It captures its launch directory as the default workspace, launch, then discovers linked Git worktrees when it lists or resolves workspaces. workspace_list refreshes the catalog on every call; workspace_info returns one workspace root. A workspace ID is launch or worktree:/absolute/path. Omit workspace to use launch.
For a reproducible smoke test of this server, including Git and non-Git directories and Mail Mode, see the local-tools MCP smoke-test guide.
Tool reference
Each top-level tool argument object rejects unknown fields. File paths are relative to the selected workspace; absolute paths, .. traversal, and symlink traversal are rejected. The bundled stdio server also enforces its default 1 MiB input-line limit, including the JSON-RPC envelope. The file service has no dedicated Git MCP tools.
| Tool | Arguments and behavior |
|---|---|
workspace_list | No arguments. Lists the launch workspace and currently linked Git worktrees. |
workspace_info | Optional workspace; defaults to launch. Returns the selected root path. |
read_file | Required path; optional workspace, start_line, end_line (1-based), and line_numbers (default false). Reads at most 4 MiB. |
list_directory | Optional path (default .), workspace, depth (default 1, range 1–8), include_ignored (default false), and ignore (additional patterns). Lists at most 1,000 entries. By default it skips .git, node_modules, bazel-*, dist, build, .cache, .next, and target; include_ignored disables those defaults. |
grep | Required pattern; optional path (default .), workspace, context (default 0, range 0–20), limit (default 200, range 1–1,000), fixed_strings (default false), include_ignored (default false), and ignore (additional patterns). Patterns are limited to 4 KiB; a search scans at most 10,000 files and 32 MiB of file content, and returned output is limited to 1 MiB. limit caps returned matches. RE2 regular-expression matching is the default; set fixed_strings to true for literal matching. |
write_file | Required path and content; optional workspace. Writes atomically; the underlying file operation accepts up to 4 MiB, subject to the server's 1 MiB JSON input-line limit. |
edit_tool | Required path and non-empty edits array; optional workspace. Supports replace, insert_before, insert_after, and delete; at most 256 edits. Each edit uses exact, non-empty find text. Supply non-empty text for replace/insert operations. which selects only (default), first, last, or a zero-based match index. |
execute_bash | Required non-empty command (maximum 64 KiB); optional workspace, workspace-relative cwd (default .), timeout_seconds (default 30, range 1–45), and allow_nonzero_exit (default false). Combined stdout/stderr is limited to 1 MiB. |
For example, this edit replaces the first exact match in a selected worktree:
{
"workspace": "worktree:/work/project-feature",
"path": "src/config.h",
"edits": [{"op": "replace", "find": "old_value", "text": "new_value", "which": "first"}]
}
execute_bash starts in the selected workspace, or its optional workspace-relative cwd, but it is not a sandbox. A command can use absolute paths, change directory, run Git, or access files outside the workspace. Register and use this server only when you trust the process and command source.
In mail mode, every dynamically imported MCP tool requires the host to be on a slop/staging/* branch, including tools that declare a read-only hint. Native read tools and the staging-branch bootstrap tool remain available off staging. This is a branch gate, not sandboxing: a call allowed on staging can still affect another worktree or external system.
Smoke test
This test checks the actual std_slop → local-tools MCP stdio path in one Git directory and one non-Git directory. It uses temporary marker files.
For a model-driven smoke call, configure an OpenRouter API key in ~/.config/slop/config.ini, set openai_base_url = https://openrouter.ai/api/v1, and set openai_oauth = false. Do not put the key on the command line. openrouter/free:medium is a convenient free-route model for basic tool-call tests when OpenRouter makes it available. You can use another configured model that supports tools.
From the repository root, build the server and prepare the test folders. Pick an unused MCP registry name; the example uses local_smoke:
bazel build //mcp/local_tools:local_tools_server
SERVER="$(pwd)/bazel-bin/mcp/local_tools/local_tools_server"
SMOKE_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/slop-mcp-smoke.XXXXXX")"
GIT_ROOT="$SMOKE_ROOT/git-root"
PLAIN_ROOT="$SMOKE_ROOT/plain-root"
mkdir -p "$GIT_ROOT" "$PLAIN_ROOT"
git -C "$GIT_ROOT" init --quiet -b main
git -C "$GIT_ROOT" config user.name "MCP smoke test"
git -C "$GIT_ROOT" config user.email "mcp-smoke@example.invalid"
printf 'GIT_ROOT_SMOKE\n' > "$GIT_ROOT/marker.txt"
printf 'PLAIN_ROOT_SMOKE\n' > "$PLAIN_ROOT/marker.txt"
git -C "$GIT_ROOT" add marker.txt
git -C "$GIT_ROOT" commit --quiet -m "seed MCP smoke root"
std_slop mcp add local_smoke --transport stdio --command "$SERVER"
std_slop mcp list
Run the following command once from each root. Starting std_slop from a root also makes that directory the MCP server's launch workspace. To run both from one shell, cd "$GIT_ROOT", run it, then cd "$PLAIN_ROOT" and run it again. If you are inside tmux, you can first open detached windows with tmux new-window -d -n mcp-smoke-git -c "$GIT_ROOT" and tmux new-window -d -n mcp-smoke-plain -c "$PLAIN_ROOT".
std_slop --model openrouter/free:medium \
--prompt 'Call MCP tool mcp_local_smoke_workspace_info and mcp_local_smoke_read_file with path marker.txt. Return the workspace root and exact file contents.' \
--output json
The Git run should report $GIT_ROOT and GIT_ROOT_SMOKE; the non-Git run should report $PLAIN_ROOT and PLAIN_ROOT_SMOKE. A non-Git directory is supported; its workspace list contains only launch. This command does not pass --no_local_tools, so it tests the normal catalog with native and MCP tools enabled. Add --no_local_tools only when you specifically want to test MCP-only mode.
Mail Mode
Keep the Git root clean and keep the database outside it. From $GIT_ROOT, start std_slop --db "$SMOKE_ROOT/mail.db", enter /mode mail mcp-smoke, and let it create a slop/staging/* branch. Then exit the interactive app and run this prompt from the same root:
std_slop --prompt_db "$SMOKE_ROOT/mail.db" --model openrouter/free:medium \
--prompt 'Call MCP tool mcp_local_smoke_workspace_info and mcp_local_smoke_read_file with path marker.txt. Return the workspace root and exact file contents.' \
--output json
Do not pass --no_local_tools in this run; it checks that MCP tools work in Mail Mode while native tools remain enabled. Dynamically imported MCP calls require the staging branch.
Cleanup
After checking the output, remove the temporary registration and test folders:
std_slop mcp remove local_smoke
rm -r -- "$SMOKE_ROOT"Content is generated from the repository sources listed above. See the source files for the complete reference.
