graves/awful_mcp
If you are the rightful owner of awful_mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
A comprehensive MCP (Model Context Protocol) server that exposes powerful developer tools for AI coding agents.
awful_mcp
A comprehensive MCP (Model Context Protocol) server that exposes powerful developer tools for AI coding agents. Built with Rust using the official rmcp SDK, this server provides 34+ tools organized into 8 categories: Nushell pipelines, process execution, filesystem operations, code search, multi-file replacement, git operations, cargo commands, and AST-based code analysis.
Features
- Nushell Integration: Run Nushell pipelines with structured JSON output
- Code Search: Fast search with ripgrep and fd
- Multi-file Replace: Safe search-and-replace with ruplacer (dry-run by default)
- AST Analysis: Syntax-aware code search and transformation with ast-grep
- Git Operations: Status, diff, add, commit, push
- Cargo Integration: Build, test, fmt, clippy
- Filesystem Tools: Read, write, append, list, glob
- Safe Execution: All commands have timeouts and output limits
Dependencies
Required
- Rust (1.70+) - For building the server
- Nushell (
nu) - For running Nushell commands# macOS brew install nushell # Linux cargo install nu
Optional (for specific tools)
-
ripgrep (
rg) - Forsearch.rgtoolbrew install ripgrep # macOS apt install ripgrep # Debian/Ubuntu -
fd - For
search.fdtoolbrew install fd # macOS apt install fd-find # Debian/Ubuntu -
ruplacer - For
replace.ruplacertoolcargo install ruplacer -
ast-grep - For
ast.greptoolcargo install ast-grep # or npm install -g @ast-grep/cli -
git - For git tools (usually pre-installed)
-
cargo - For cargo tools (installed with Rust)
Installation
# Clone the repository
git clone <repository-url>
cd awful_mcp
# Build release binary
cargo build --release
# Binary will be at: target/release/awful_mcp
Setup
For Claude Desktop
Add to your MCP configuration file (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"awful_mcp": {
"command": "/absolute/path/to/awful_mcp/target/release/awful_mcp"
}
}
}
For Cursor or other MCP clients
Add to your client's MCP configuration:
{
"mcpServers": {
"awful_mcp": {
"command": "/absolute/path/to/awful_mcp/target/release/awful_mcp"
}
}
}
Environment Variables
NU_PATH- Path to thenubinary (defaults tonuin PATH)RUST_LOG- Set logging level (e.g.,RUST_LOG=info)
Usage
Once configured, the MCP server will automatically start when your MCP client connects. The server exposes 34 tools across 8 categories.
Available Tools
Nushell Tools (15 tools)
Run Nushell commands and pipelines with structured data processing.
nu.run
Run any Nushell command and get stdout/stderr.
Arguments:
command(string, required) - Nushell command to executecwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 60000)env(object, optional) - Environment variables
Example:
{
"command": "ls | where size > 1mb | get name"
}
nu.json
Run a Nushell command that outputs JSON and get structured data back.
Arguments:
command(string, required) - Nushell command that outputs JSONallow_stderr(boolean, optional) - Allow stderr output (default: false)cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
Example:
{
"command": "sys | to json"
}
nu.ls
List directory contents with Nushell's rich metadata.
Arguments:
path(string, optional) - Directory to list (default: current)all(boolean, optional) - Include hidden filescwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.open
Open and parse files (JSON, YAML, TOML, CSV, etc.) with Nushell.
Arguments:
path(string, required) - File to openraw(boolean, optional) - Read as raw textcwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.save
Save structured data to file in various formats.
Arguments:
path(string, required) - Output file pathcontent(string, required) - Content to savecwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.str_contains
Check if a string contains a pattern.
Arguments:
input(string, required) - Input stringpattern(string, required) - Pattern to search forinsensitive(boolean, optional) - Case insensitive search
Example:
{
"input": "Hello World",
"pattern": "world",
"insensitive": true
}
nu.str_replace
Replace text in a string.
Arguments:
input(string, required) - Input stringfind(string, required) - Text to findreplace(string, required) - Replacement textall(boolean, optional) - Replace all occurrences (default: false)
Example:
{
"input": "foo bar foo",
"find": "foo",
"replace": "baz",
"all": true
}
nu.ps
Get running processes with Nushell.
Arguments:
cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.sys
Get system information (CPU, memory, host, disks, etc.).
Arguments:
subsystem(string, optional) - Specific subsystem: cpu, mem, host, disks, temp, net, userscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.find_by_extension
Find files by extension with metadata and sorting.
Arguments:
extensions(array, required) - File extensions to search (e.g., ["rs", "toml"])path(string, optional) - Directory to search (default: current)sort_by(string, optional) - Sort by: name, size, modified (default: modified)limit(number, optional) - Limit number of resultscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
Example:
{
"extensions": ["rs", "toml"],
"sort_by": "size",
"limit": 10
}
nu.recent_files
Find recently modified files within a time window.
Arguments:
minutes(number, optional) - Minutes to look back (default: 60)path(string, optional) - Directory to search (default: current)limit(number, optional) - Limit number of resultscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.large_files
Find files larger than a size threshold.
Arguments:
min_size(number, optional) - Minimum size in bytes (default: 1000000 = 1MB)path(string, optional) - Directory to search (default: current)limit(number, optional) - Limit number of results (default: 20)cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.code_stats
Analyze code repository statistics by file type.
Arguments:
path(string, optional) - Directory to analyze (default: current)extensions(array, optional) - File extensions to include (default: common code extensions)cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
Example:
{
"path": "src",
"extensions": ["rs", "toml"]
}
nu.grep_stats
Search for pattern and return aggregated statistics.
Arguments:
pattern(string, required) - Pattern to search forpath(string, optional) - Directory to search (default: current)extensions(array, optional) - File extensions to filterinsensitive(boolean, optional) - Case insensitive searchcwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
nu.dir_tree
Generate directory tree visualization with file counts and sizes.
Arguments:
path(string, optional) - Directory to visualize (default: current)max_depth(number, optional) - Maximum depth (default: 3)all(boolean, optional) - Show hidden filescwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
Process Tool (1 tool)
process.run
Run any binary with arguments (no shell).
Arguments:
program(string, required) - Program to executeargs(array, optional) - Command argumentscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 60000)env(object, optional) - Environment variables
Example:
{
"program": "cargo",
"args": ["test", "--", "--nocapture"],
"timeout_ms": 120000
}
Filesystem Tools (5 tools)
fs.read_text
Read a UTF-8 text file.
Arguments:
path(string, required) - File path to read
Returns: { path, content, size }
fs.write_text
Write (overwrite) a text file atomically.
Arguments:
path(string, required) - File path to writecontent(string, required) - Content to write
fs.append_text
Append text to a file (creates if missing).
Arguments:
path(string, required) - File path to append tocontent(string, required) - Content to append
fs.list
List directory contents with metadata.
Arguments:
path(string, optional) - Directory to list (default: current)include_hidden(boolean, optional) - Include hidden files
Returns: Array of { name, path, kind, size, modified }
fs.glob
Expand Unix-style glob patterns.
Arguments:
pattern(string, required) - Glob pattern (e.g., "**/*.rs")cwd(string, optional) - Working directory
Search Tools (2 tools)
search.rg
Fast code search with ripgrep (returns JSON).
Arguments:
pattern(string, required) - Search patternpath(string, optional) - File or directory to searchextra(array, optional) - Extra ripgrep flagscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 60000)
Example:
{
"pattern": "fn main",
"path": "src",
"extra": ["-i", "--type", "rust"]
}
search.fd
Fast file/directory finder (respects .gitignore).
Arguments:
pattern(string, optional) - Search patternpath(string, optional) - Directory to searchtype_filter(string, optional) - Type: f (file), d (directory), l (symlink), x (executable)extension(string, optional) - File extension (e.g., "rs", "ts")max_depth(number, optional) - Maximum search depthhidden(boolean, optional) - Include hidden filesno_ignore(boolean, optional) - Include ignored filescase_sensitive(boolean, optional) - Case sensitive searchabsolute_path(boolean, optional) - Return absolute pathsextra(array, optional) - Extra fd flagscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
Example:
{
"extension": "rs",
"type_filter": "f",
"max_depth": 3
}
Replace Tool (1 tool)
replace.ruplacer
Safe multi-file search and replace (dry-run by default).
Arguments:
pattern(string, required) - Pattern to search forreplacement(string, required) - Replacement stringpath(string, optional) - Directory or file to searchregex(boolean, optional) - Use regex modefile_type(string, optional) - File type filter (e.g., "*.rs")go(boolean, optional) - Actually perform replacement (default: false = dry-run)ignore_case(boolean, optional) - Case insensitive searchhidden(boolean, optional) - Include hidden filesno_ignore(boolean, optional) - Include ignored filesword(boolean, optional) - Word boundary matchingextra(array, optional) - Extra ruplacer flagscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 120000)
Example (dry-run):
{
"pattern": "old_name",
"replacement": "new_name",
"file_type": "*.rs"
}
Example (actual replace):
{
"pattern": "old_name",
"replacement": "new_name",
"file_type": "*.rs",
"go": true
}
Git Tools (5 tools)
git.status
Run git status --porcelain=v2.
Arguments:
cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 30000)
git.diff
Run git diff with optional arguments.
Arguments:
extra(array, optional) - Extra git diff flags (e.g., ["--cached"])paths(array, optional) - Specific paths to diffcwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 60000)
git.add
Run git add for given paths.
Arguments:
paths(array, required) - Paths to add (at least one required)cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
Example:
{
"paths": ["src/main.rs", "Cargo.toml"]
}
git.commit
Run git commit with message.
Arguments:
message(string, required) - Commit messagesignoff(boolean, optional) - Add --signoff flagallow_empty(boolean, optional) - Allow empty commitcwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
git.push
Run git push to remote.
Arguments:
remote(string, required) - Remote name (e.g., "origin")branch(string, required) - Branch name (e.g., "main")force(boolean, optional) - Use --force-with-leasecwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 120000)
Cargo Tools (4 tools)
cargo.build
Run cargo build.
Arguments:
extra(array, optional) - Extra cargo flags (e.g., ["--release"])cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 300000)
cargo.test
Run cargo test.
Arguments:
extra(array, optional) - Extra cargo flags (e.g., ["--", "--nocapture"])cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 300000)
cargo.fmt
Run cargo fmt.
Arguments:
cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
cargo.clippy
Run cargo clippy.
Arguments:
cwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds
AST Tools (1 tool)
ast.grep
AST-based code search and manipulation.
Arguments:
pattern(string, required) - AST pattern (e.g., "fn $NAME() {}")language(string, optional) - Language: rust, typescript, python, javascript, etc.path(string, optional) - Directory or file to searchregex(boolean, optional) - Use regex moderewrite(string, optional) - Rewrite pattern for transformationsjson(boolean, optional) - JSON output formathidden(boolean, optional) - Include hidden filesno_ignore(boolean, optional) - Include ignored filesfile_type(string, optional) - File type filterinteractive(boolean, optional) - Interactive modeupdate_all(boolean, optional) - Update files in placeextra(array, optional) - Extra ast-grep flagscwd(string, optional) - Working directorytimeout_ms(number, optional) - Timeout in milliseconds (default: 120000)
Example (search):
{
"pattern": "fn $NAME() {}",
"language": "rust",
"json": true
}
Example (transform):
{
"pattern": "fn $NAME() {}",
"rewrite": "async fn $NAME() {}",
"language": "rust",
"update_all": true
}
Safety Features
- Timeouts: All commands have configurable timeouts with sensible defaults
- Output Limits: Stdout/stderr are truncated to prevent memory issues
- Binary Validation:
process.runuseswhichto validate binaries before execution - No Shell Injection: Direct binary execution bypasses shell (use
nu.runfor intentional shell pipelines) - Safe Defaults: Destructive operations like
replace.ruplacerdefault to dry-run mode
Development
# Run tests
cargo test
# Run with logging
RUST_LOG=info cargo run
# Format code
cargo fmt
# Lint
cargo clippy
Architecture
The server follows a modular architecture with separation of concerns:
- Main Server (
src/main.rs) - MCP protocol handling and routing - Tool Modules (
src/tools/) - Individual tool implementationsnushell.rs- Nushell pipeline toolsprocess.rs- Process executionfilesystem.rs- File operationssearch.rs- ripgrep and fdreplace.rs- ruplacer integrationgit.rs- Git operationscargo.rs- Cargo commandsast_grep.rs- AST-based analysis
- Utilities (
src/util.rs) - Shared utilities for process execution
Each tool module follows a testability pattern:
- Business logic functions return
anyhow::Result - MCP wrapper functions handle protocol conversion
- Comprehensive unit tests for business logic
License
MIT
Contributing
Contributions welcome! Please ensure:
- All tests pass (
cargo test) - Code is formatted (
cargo fmt) - Clippy is happy (
cargo clippy) - New tools follow the established patterns