About Devout
Devout is an MCP server in the Science category: a pun on Devin, Devout is an MCP server for complete (i.e., dangerous) software engineering project development operations.
Install
git clone https://github.com/erlsci/devoutThis server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.
Ask AI
Ask AI about Devout
Powered by Claude · Grounded in docs
Security checks
- ✓README presentThe repository ships documentation.
5 checks have not been run yet.
README
Devout
A Devin Clone MCP Server
A Model Context Protocol (MCP) server written in Erlang that provides file, directory, and version control operations for software projects.
Features
🛠️ File Operations
- new-dir: Create directories with automatic parent creation
- new-dirs: Create directory structures with multiple children
- move: Safely move/rename files and directories
- write: Create/write files with append mode support
- read: Read file contents with size limits
- list-files: List directory contents with file types and sizes
- show-cwd: Display current working directory
- change-cwd: Navigate to relative directories
🐙 Git Operations
- git-add: Add files to staging area (
git add <files>) - git-log: Show commit history (
git log [options]) - git-diff: Show differences (no args, 1 arg, or 2 args)
- git-pull: Pull with rebase (
git pull <remote> <branch> --rebase) - git-checkout-branch: Create and checkout new branch (
git checkout -b <name>) - git-push: Push to remote (
git push <remote>) - git-commit-all: Commit all changes (
git commit -a -m <message>) - git-commit-files: Commit specific files (
git commit <files> -m <message>) - git-clone: Clone repository (
git clone <url>) - git-status: Show repository status (
git status --porcelain)
🔒 Security Features
- Path Validation: Only relative paths allowed, prevents directory traversal
- Sandboxing: All operations restricted to base directory
- File Size Limits: Configurable maximum file sizes
- Extension Filtering: Optional file type restrictions
- Safe Error Handling: No information disclosure through errors
📚 Resources & Prompts
- devout://status: Real-time service status and configuration
- devout://help: Comprehensive tool documentation
- create_project: Intelligent project structure generation (Erlang, web, API, library)
Architecture
OTP Design
- Application:
devout_app- Standard OTP application callback - Supervisor:
devout_sup- Manages server process lifecycle - Server:
devout_server- Main gen_server coordinating with erlmcp - Operations:
devout_fs- File system operation implementations - Validation:
devout_path_validator- Security and path validation - Git Operations:
devout_git- Git command implementations - Entry Point:
devout_stdio_main- Standalone stdio executable
Security Model
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Claude AI │───▶│ devout_server │───▶│ devout_fs │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ ▼ ▼
┌──────────────────┐ ┌─────────────────┐
│ erlmcp_stdio │ │ path_validator │
│ (MCP Protocol) │ │ (Security) │
└──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐ ┌─────────────────┐
│ devout_git │───▶│ erlexec │
└──────────────────┘ │ (Proc. Mgmt.) │
└─────────────────┘
Prerequisites
- Erlang/OTP 25+
- Rebar3
- Git (for dependencies & Git operations)
Quick Start
1. Build
git clone <repository-url>
cd devout
rebar3 get-deps
rebar3 compile
2. Test
rebar3 eunit
3. Configure Claude Desktop
Edit claude_desktop_config.json:
macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Windows: %APPDATA%\Claude\claude_desktop_config.json
Linux: ~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"devout": {
"command": "/Users/oubiwann/lab/erlsci/devout/bin/start.sh",
"args": []
}
}
}
4. Restart Claude Desktop
Usage Examples
📁 Directory Operations
User: Create a new project structure called "my_app" with src, test, and docs directories
Claude: I'll create that project structure for you.
[Uses new-dirs tool: {"path": "my_app", "children": ["src", "test", "docs"]}]
Directory structure created successfully:
- my_app
- src
- test
- docs
🔍 Directory Exploration
User: What files are in the current directory?
Claude: I'll show you the files in the current directory.
[Uses list-files tool: {}]
Contents of .:
[FILE] .DS_Store (6148 bytes)
[DIR] .git
[DIR] .github
[FILE] .gitignore (164 bytes)
[FILE] LICENSE (11358 bytes)
[FILE] Makefile (4612 bytes)
[FILE] README.md (9895 bytes)
[DIR] _build
[DIR] bin
[DIR] config
[DIR] src
[DIR] test
📝 File Operations
User: Create a configuration file with some JSON content
Claude: I'll create a config file for you.
[Uses write tool: {"path": "my_app/config.json", "content": "{\n \"port\": 3000,\n \"debug\": true\n}"}]
Content written to file successfully: my_app/config.json (32 bytes)
🔍 Navigation & Reading
User: Show me the current directory and then read the config file
Claude: I'll show the current directory and read the config file.
[Uses show-cwd tool: {}]
Current working directory: /Users/developer/projects
[Uses read tool: {"path": "my_app/config.json"}]
Content of my_app/config.json (32 bytes):
{
"port": 3000,
"debug": true
}
🏗️ Project Generation
User: Use the create_project prompt for an Erlang application
Claude: I'll generate an Erlang project structure prompt.
[Uses create_project prompt: {"project_name": "my_erlang_app", "project_type": "erlang"}]
Create a project structure for 'my_erlang_app' (erlang project). Create an Erlang/OTP project structure with directories: src, include, test, priv, rebar.config. Use the devout tools to create the necessary directories and files.
🐙 Git Operations
View Git History
User: Show me the last 5 commits
Claude: I'll show you the recent commit history.
[Uses git-log tool: {"args": "--oneline -5"}]
Git log:
a1b2c3d Add new feature
e4f5g6h Fix bug in parser
i7j8k9l Update documentation
m1n2o3p Initial commit
Create Feature Branch
User: Create a new feature branch called "user-authentication"
Claude: I'll create and checkout a new branch for you.
[Uses git-checkout-branch tool: {"branch": "user-authentication"}]
Git checkout branch successful:
Switched to a new branch 'user-authentication'
Commit Changes
User: Add these files to git and commit them with message "Add user auth"
Claude: I'll stage and commit those files for you.
[Uses git-add tool: {"files": ["src/auth.erl", "test/auth_test.erl"]}]
[Uses git-commit-files tool: {"files": ["src/auth.erl", "test/auth_test.erl"], "message": "Add user auth"}]
Git operations successful!
Sync with Remote
User: Pull latest changes from origin main and then push my branch
Claude: I'll sync with the remote repository for you.
[Uses git-pull tool: {"remote": "origin", "branch": "main"}]
[Uses git-push tool: {"remote": "origin"}]
Git sync completed successfully!
Configuration
Edit config/sys.config to customize behavior:
[
{devout, [
{max_file_size, 10485760}, % 10MB limit
{allowed_extensions, all}, % or [<<".txt">>, <<".md">>]
{enable_recursive_delete, false}, % Safety first
{allowed_operations, [ % Restrict available operations
new_dir, new_dirs, move, write, read, list_files, show_cwd, change_cwd
]}
]},
{erlexec, [
{debug, false}, % Enable for git command debugging
{verbose, false}
]}
].
Git Command Reference
| MCP Tool | Git Command | Description |
|---|---|---|
git-add |
git add <files> |
Stage files for commit |
git-log |
git log [options] |
Show commit history |
git-diff |
git diff [ref1] [ref2] |
Show differences |
git-pull |
git pull <remote> <branch> --rebase |
Pull with rebase |
git-checkout-branch |
git checkout -b <name> |
Create new branch |
git-push |
git push <remote> |
Push to remote |
git-commit-all |
git commit -a -m <message> |
Commit all changes |
git-commit-files |
git commit <files> -m <message> |
Commit specific files |
git-clone |
git clone <url> |
Clone repository |
git-status |
git status --porcelain |
Show repo status |
Git Tool Parameters
git-add
{
"files": ["file1.txt", "file2.txt"] // Array of files
// OR
"file": "single_file.txt" // Single file
}
git-log
{
"args": "--oneline -10" // Optional: git log arguments
}
git-diff
{
// No parameters = working directory vs staged
// OR
"ref": "HEAD~1" // Compare against reference
// OR
"ref1": "HEAD~2", // Compare two references
"ref2": "HEAD~1"
}
git-pull
{
"remote": "origin", // Required: remote name
"branch": "main" // Required: branch name
}
git-push
{
"remote": "origin" // Required: remote name
}
Security Features
🛡️ Path Security
- Relative Paths Only: Absolute paths rejected (
/etc/passwd❌) - Traversal Prevention: Parent directory access blocked (
../../../etc❌) - Base Directory Enforcement: Operations confined to working directory
- Path Normalization: Handles
./,//, and other edge cases
📏 Resource Limits
- File Size Limits: Configurable maximum file sizes (default: 10MB)
- Operation Whitelisting: Restrict available operations per deployment
- Extension Filtering: Optional file type restrictions
- Memory Protection: Streaming for large files
🔒 Error Handling
- Information Hiding: Errors don't leak system details
- Graceful Degradation: Partial failures handled cleanly
- Audit Logging: All operations logged for security review
- Input Sanitization: All inputs validated before processing
Development
Running Tests
# Full test suite
rebar3 eunit
# Specific test modules
rebar3 eunit --module=devout_test
# With coverage
rebar3 cover
Code Quality
# Static analysis
rebar3 dialyzer
# Cross references
rebar3 xref
# Linting
rebar3 lint
Development Mode
# Interactive shell
rebar3 shell
# Start stdio server manually
devout_server:start_stdio().
Adding FS Operations
-
Add to allowed_operations in
devout.app.src -
Implement in devout_fs.erl (if needed):
my_operation(Path) -> case devout_path_validator:validate_path(Path) of {ok, ValidPath} -> % Your operation here ok; {error, Reason} -> {error, Reason} end. -
Register tool in devout_app.erl:
ok = erlmcp_stdio:add_tool( <<"my-operation">>, <<"Description">>, fun devout_server:handle_my_operation/1, SchemaMap ). -
Add handler in devout_server.erl:
handle_my_operation(#{<<"path">> := Path}) -> case devout_fs:my_operation(Path) of ok -> <<"Success message">>; {error, Reason} -> devout_fmt:err(Reason) end.
Adding New Git Operations
-
Add function to devout_git.erl:
my_git_operation(Args) -> execute_git(["my-operation"] ++ Args). -
Add MCP handler:
handle_my_git_operation(#{<<"param">> := Value}) -> case my_git_operation([binary_to_list(Value)]) of {ok, Output} -> <<"Success: ", Output/binary>>; {error, Reason} -> devout_fmt:err(Reason) end. -
Register tool in devout_server.erl:
ok = erlmcp_stdio:add_tool( <<"git-my-operation">>, <<"Description of my git operation">>, fun devout_git:handle_my_git_operation/1, Schema ). -
Add tests in devout_git_test.erl
Monitoring & Debugging
Health Checks
# Check if application is running
erl -eval "io:format('~p~n', [application:which_applications()]), halt()."
# Verify processes
erl -eval "io:format('~p~n', [whereis(devout_server)]), halt()."
Logging
Logs go to stderr to avoid interfering with MCP protocol on stdout:
- Info: Service lifecycle events
- Warning: Security violations, invalid paths
- Error: Operation failures, system errors
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Service not starting | Missing dependencies | rebar3 get-deps && rebar3 compile |
| Path rejected | Absolute/traversal path | Use relative paths only |
| File too large | Exceeds size limit | Check max_file_size config |
| Permission denied | File system permissions | Check directory ownership |
| Tool not found | Version mismatch | Verify erlmcp compatibility |
| Git command timeout | Long-running operation | Increase timeout in erlexec config |
| Git not found | Missing git installation | Install git and ensure it's in PATH |
| Merge conflicts | Conflicting changes | Resolve conflicts manually |
| Repository not found | Not in git repository | Initialize with git init first |
Performance
- Memory Usage: ~10MB base + file buffers
- Throughput: 1000+ operations/second for small files
- Latency: <10ms for typical operations
- Scalability: Single-threaded, suitable for interactive use
Contributing
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all tests pass:
rebar3 eunit - Run static analysis:
rebar3 dialyzer - Submit a pull request
Code Style
- Use OTP principles and gen_server patterns
- Comprehensive error handling with proper types
- Security-first design - validate all inputs
- Document all public functions with edoc
- Follow Erlang naming conventions
Acknowledgments
- Built on the excellent erlmcp library
- Inspired by security practices from the Erlang/OTP ecosystem
- Thanks to the Claude Desktop team for MCP protocol specification
License
Apache License 2.0 - see LICENSE file for details.
External Resources
README copy captured when this server was indexed; it may lag the repository. The original is authoritative.
