Content
# LocalOps MCP Server
`localops_mcp_server` is a small Rust-based MCP server that communicates over `stdin`/`stdout` using JSON-RPC style messages. It exposes a set of local machine utilities for file access, shell command execution, system stats, macOS notification reading, simple file editing helpers, text diffing, Java rule lookup, and port cleanup.
The server is designed to be launched as a long-running process by an MCP-compatible client. It reads one JSON request per line from standard input and writes one JSON response per line to standard output.
## What This Project Does
At runtime, the binary:
1. Starts an MCP-style request loop on `stdin`.
2. Accepts methods such as `initialize`, `tools/list`, and `tools/call`.
3. Dispatches `tools/call` requests to locally implemented Rust functions.
4. Returns tool results as JSON text content.
The current toolset includes:
| Tool | Purpose |
| --- | --- |
| `localops.system.stats` | Returns current CPU and memory usage using `sysinfo`. |
| `localops.files.find_by_extension` | Recursively finds files by extension under a path. |
| `localops.files.read` | Reads a local file, with a 1 MB size cap. |
| `localops.files.write` | Writes or overwrites a file with supplied content. |
| `localops.shell.run` | Runs a `zsh -c` shell command and returns exit code, stdout, and stderr. |
| `localops.shell.stream` | Runs a command and streams stdout to stderr in real time. |
| `localops.notifications.read` | Reads recent macOS notifications and formats them as readable text. |
| `localops.notifications.triage` | Reads recent macOS notifications and returns structured JSON text for downstream triage. |
| `localops.files.insert_after_pattern` | Inserts a line after the first line matching an anchor pattern. |
| `localops.files.insert_after_line` | Inserts a line after a specific 1-based line number. |
| `localops.files.delete_lines_containing` | Removes lines containing a matching string. |
| `localops.files.delete_line` | Deletes a specific 1-based line number. |
| `localops.text.diff_words` | Produces a word-level diff between two long strings. |
| `localops.repo.find_java_rule_source` | Searches a repo for a Java file matching a rule name. |
| `localops.net.clear_port` | Kills a process bound to a port using `lsof` and `kill -9`. |
## Project Structure
```text
.
├── Cargo.toml
└── src
├── main.rs
├── mcp_types.rs
└── tools.rs
```
- `src/main.rs`: MCP request loop and tool dispatch.
- `src/mcp_types.rs`: request and response-related data types.
- `src/tools.rs`: implementations for the server’s local tools.
## Requirements
### General
- Rust toolchain with `cargo`
- `zsh` available on the host machine
### macOS-specific features
Some tools are macOS-specific:
- `read_notifications`
- `get_notifications_for_triage`
These tools read Apple’s local notifications database at:
```text
~/Library/Group Containers/group.com.apple.usernoted/db2/db
```
They may require Full Disk Access depending on your macOS privacy settings.
### External command dependencies
Some tools assume the following commands exist on the host:
- `zsh`
- `lsof`
- `kill`
## Installation
Clone the repo and build it:
```bash
cargo build
```
Or run it directly without a separate build step:
```bash
cargo run
```
## How To Run
This project is not an HTTP server. It is a line-oriented MCP process intended to be started by another tool or client.
### Start the server
```bash
cargo run
```
After startup, the process waits for JSON messages on standard input.
### Build a release binary
```bash
cargo build --release
```
The binary will be available at:
```text
target/release/localops_mcp_server
```
You can then run it directly:
```bash
./target/release/localops_mcp_server
```
## Example MCP Session
Send one JSON object per line.
### Initialize
Input:
```json
{"jsonrpc":"2.0","id":1,"method":"initialize"}
```
Expected response:
```json
{"jsonrpc":"2.0","id":1,"result":{"capabilities":{},"protocolVersion":"2024-11-05","serverInfo":{"name":"localops_mcp_server","version":"1.0.0"}}}
```
### List tools
Input:
```json
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
```
The server returns the available tool definitions and input schemas.
### Call a tool
Input:
```json
{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"localops.system.stats","arguments":{}}}
```
Example response shape:
```json
{"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"{\"cpu_usage\":5.4,\"memory_used_gb\":12,\"memory_total_gb\":36}"}]}}
```
## Tool Usage Examples
### Find Rust files
```json
{"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"localops.files.find_by_extension","arguments":{"path":"./src","extension":"rs"}}}
```
### Read a file
```json
{"jsonrpc":"2.0","id":11,"method":"tools/call","params":{"name":"localops.files.read","arguments":{"path":"Cargo.toml"}}}
```
### Run a shell command
```json
{"jsonrpc":"2.0","id":12,"method":"tools/call","params":{"name":"localops.shell.run","arguments":{"command":"ls -la"}}}
```
### Insert a line after a matching anchor
```json
{"jsonrpc":"2.0","id":13,"method":"tools/call","params":{"name":"localops.files.insert_after_pattern","arguments":{"path":"app.yml","anchor":"monitors:","content":" - name: sample"}}}
```
### Delete a line by pattern
```json
{"jsonrpc":"2.0","id":14,"method":"tools/call","params":{"name":"localops.files.delete_lines_containing","arguments":{"path":"app.log","pattern":"[DEBUG_MCP]"}}}
```
### Clear a port
```json
{"jsonrpc":"2.0","id":15,"method":"tools/call","params":{"name":"localops.net.clear_port","arguments":{"port":8080}}}
```
## Development Notes
### Dependencies
The project currently uses:
- `tokio` for the async runtime
- `serde` and `serde_json` for JSON serialization
- `sysinfo` for CPU and memory metrics
- `rusqlite` with bundled SQLite for notification DB access
- `plist` for decoding Apple notification payloads
- `similar` for word-level diff generation
### Current behavior worth knowing
- `initialize` returns protocol version `2026-04-27`.
- `notifications/initialized` is accepted and only logs to stderr.
- Unknown tool names return a JSON-RPC `Tool not found` error.
- Unknown methods return a JSON-RPC `Method not found` error when an `id` is present.
- `read_file` enforces a 1 MB size limit.
- `run_command` uses a simple keyword blacklist, not a full sandbox.
- `run_command_stream` streams stdout to stderr and returns only final completion status in the MCP response.
## Security, Privacy, and Safety Warnings (Read This)
This server exposes **powerful local-machine capabilities** to an MCP client. Treat it like granting a program local admin-style reach into your workstation.
### Command execution tools: `run_command`, `run_command_stream`
**Impact if misused:**
- Data loss (deleting files, changing permissions)
- Credential theft (reading SSH keys, cloud credentials)
- Network exfiltration (uploading local files)
- Persistence (installing launch agents, altering shell config)
**Where the risk is enforced:**
- `src/main.rs` dispatches to `tools::validate_command_strict(...)` (strict checks).
- These checks are **not a sandbox**. Assume a determined attacker can still find ways around simple filters.
**What you should do:**
- Prefer an allowlist model (only allow known-safe commands like `ls`, `cat`, `cargo build`).
- Run the server under a low-privilege user.
- Do not run it on machines that have production secrets unless you fully trust the client.
### File tools: `localops.files.*`
**Impact if misused:**
- Reading secrets: `~/.ssh/*`, `~/.aws/*`, `.env`, keychains, config files
- Modifying system or app state: `~/Library/*`, `/etc/*`, launch agents, shell rc files
- Corrupting repositories or configs by deleting/inserting lines
**Where the risk is enforced:**
- `src/tools.rs` path validation rejects traversal/metacharacters, symlinks, and **common sensitive paths**.
- `write_file` is capped in size and refuses common executable/script extensions.
**What you should do:**
- Restrict operations to a single safe workspace directory (recommended: add an allowlisted root).
- Avoid granting Full Disk Access unless you explicitly want notification tooling.
### macOS notifications / Apple private DB reads: `localops.notifications.*`
These tools read Apple’s local notifications SQLite database:
```text
~/Library/Group Containers/group.com.apple.usernoted/db2/db
```
**Impact:**
- Notifications can contain sensitive content (2FA codes, password reset links, internal incident details, customer data).
- If your MCP client forwards tool outputs to an LLM or remote service, you may leak private data.
**What you should do:**
- Treat notification tools as **high sensitivity**.
- Disable these tools in production / shared environments.
- Avoid enabling Full Disk Access unless absolutely necessary.
## Limitations and Caveats
- The server assumes a trusted local environment. Several tools can modify files or run shell commands.
- Safety checks exist, but **they are not a security boundary**.
- Notification-reading features are specific to macOS and Apple’s internal notification DB layout.
- The `get_system_stats` tool name is generic; the implementation uses `sysinfo` APIs and is not tied to any specific hardware.
- Tests exist under `tests/` (run `cargo test`).
## Troubleshooting
### Build warnings
The project currently compiles, but emits a few warnings for unused imports, unused fields, and unnecessary `mut` bindings. These warnings do not prevent the binary from running.
### Notification tools fail
Check:
- macOS is the host OS
- the notifications database exists
- the process has permission to read it
### Shell tools fail
Check:
- `zsh` is installed
- the invoked command exists on the machine
- the process has permission to access the requested files or paths
## Suggested Next Improvements
- Add automated tests for MCP request handling and tool functions.
- Add stronger command execution safeguards or an allowlist model.
- Add a sample MCP client configuration for Claude Desktop or another host.
- Add structured logging and clearer error messages.
- Clean up compile warnings.
Connection Info
You Might Also Like
ai-native-pm-os
The exhaustive guide to mastering Claude for Product Managers. Build your...
Train-in-Silence
The first Task-Aware MCP server and automated VRAM calculator for LLM...
stacklit
108,000 lines of code. 4,000 tokens of index. One command makes any repo...
AppClaw
AI-powered mobile automation agent — describe what you want in plain...
pdf-mcp
Production-ready MCP server for PDF processing with intelligent caching....
kotadb
Local-only code intelligence API for AI developer workflows (Bun +...