Skip to main content
Glama
zyay
by zyay
README.md
# πŸ€– mcp-agent-tools

A custom **MCP (Model Context Protocol) server** that gives AI agents real-world tools: file access, **read-only MySQL queries**, web summarization, calculations, and system info. Built to understand how agentic tool calling works end-to-end β€” server side *and* client side.

## Architecture

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    MCP Client (AI Agent)                 β”‚
β”‚         Claude Code / Qwen Code / Claude Desktop        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                        β”‚ stdio (JSON-RPC)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    MCP Server (this)                     β”‚
β”‚                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚read_file β”‚ β”‚query_    β”‚ β”‚summarize_β”‚ β”‚calculator β”‚ β”‚
β”‚  β”‚list_dir  β”‚ β”‚mysql     β”‚ β”‚url       β”‚ β”‚get_datetimeβ”‚ β”‚
β”‚  β”‚          β”‚ β”‚(SELECT   β”‚ β”‚          β”‚ β”‚sysinfo    β”‚ β”‚
β”‚  β”‚          β”‚ β”‚ only)    β”‚ β”‚          β”‚ β”‚word_count β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## Tools (8)

| Tool | What it does | Safety |
|---|---|---|
| `read_file` | Read a text file (first 5000 chars) | read-only, path validation |
| `list_dir` | List directory contents with type indicators | read-only |
| `query_mysql` | Query MySQL database | **SELECT-only guardrail** |
| `summarize_url` | Fetch & summarize a webpage | no writes, timeout protected |
| `calculator` | Evaluate math expressions safely | AST-based (no eval) |
| `get_datetime` | Current date/time (UTC + local) | read-only |
| `sysinfo` | System information (OS, Python, arch) | read-only |
| `word_count` | Count words, chars, lines, sentences | read-only |

## Quick start

```bash
# 1. Clone and set up
git clone https://github.com/zyay/mcp-agent-tools.git
cd mcp-agent-tools
python -m venv venv && venv\Scripts\activate    # Windows
# source venv/bin/activate                      # macOS/Linux

# 2. Install dependencies
pip install -r requirements.txt

# 3. Set up MySQL (optional β€” for query_mysql tool)
mysql -u root -p < setup.sql

# 4. Test the server
python client_test.py
```

## Connecting an MCP client

### Option 1: Qwen Code

```bash
qwen mcp add agent-tools -- python /full/path/to/server.py
```

Or add to `.qwen/settings.json`:
```json
{
  "mcpServers": {
    "agent-tools": {
      "command": "python",
      "args": ["/full/path/to/mcp-agent-tools/server.py"]
    }
  }
}
```

### Option 2: Claude Code

```bash
claude mcp add agent-tools -- python /full/path/to/server.py
```

### Option 3: Claude Desktop

Edit `claude_desktop_config.json`:
```json
{
  "mcpServers": {
    "agent-tools": {
      "command": "python",
      "args": ["/full/path/to/mcp-agent-tools/server.py"]
    }
  }
}
```

Location:
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`

### Option 4: Any MCP client

The server uses **stdio transport** (JSON-RPC over stdin/stdout). Any MCP-compatible client can connect.

## Usage examples

Once connected, the AI agent can use these tools naturally:

### File operations
```
User: "What's in the config file at ./settings.json?"
Agent: [calls read_file("./settings.json")]
```

### Database queries
```
User: "Show me all clients with projects over $1000"
Agent: [calls query_mysql("SELECT * FROM clients WHERE price > 1000")]
```

### Web research
```
User: "What does the Python docs say about async generators?"
Agent: [calls summarize_url("https://docs.python.org/3/reference/expressions.html")]
```

### Calculations
```
User: "If I have 150 items at $12.99 each with 15% discount, what's the total?"
Agent: [calls calculator("150 * 12.99 * 0.85")]
```

### System debugging
```
User: "What Python version am I running and what OS?"
Agent: [calls sysinfo()]
```

## MySQL setup

The `query_mysql` tool connects to a demo database. Set up:

```bash
# Load demo data
mysql -u root -p < setup.sql

# Verify
mysql -u root -p -e "SELECT * FROM demo.clients;"
```

### Environment variables

| Variable | Default | Description |
|---|---|---|
| `MYSQL_HOST` | `localhost` | MySQL server host |
| `MYSQL_USER` | `root` | MySQL username |
| `MYSQL_PASS` | *(empty)* | MySQL password |
| `MYSQL_DB` | `demo` | Database name |

Example with custom credentials:
```bash
set MYSQL_HOST=localhost
set MYSQL_USER=myuser
set MYSQL_PASS=mypassword
python server.py
```

## Design decisions

| Decision | Why |
|---|---|
| **SELECT-only guardrail** | An agent with DROP TABLE access is a bug waiting to happen. Least-privilege by default. |
| **AST calculator** | No `eval()` β€” the calculator uses Python AST parsing to safely evaluate math expressions only. |
| **stdio transport** | Simplest, works with any MCP client. No HTTP server needed. |
| **Docstrings = tool descriptions** | The `@mcp.tool()` decorator uses the function's docstring as the tool description the LLM reads. Good docstrings = better tool selection. |
| **Error messages, not exceptions** | Tools return error strings instead of raising β€” the agent can read the error and adapt. |
| **No state between calls** | Each tool call is independent. No shared state = no race conditions. |

## Testing

```bash
# Run the test client β€” lists all tools and calls each one
python client_test.py

# Expected output:
# πŸ”§ Tools (8): ['read_file', 'list_dir', 'query_mysql', 'summarize_url', ...]
# βœ… query_mysql: [{'id': 1, 'name': 'Firma A', ...}, ...]
# βœ… calculator: 100.0
# βœ… get_datetime: UTC: 2026-08-11 ...
# ...
```

## Adding your own tools

```python
@mcp.tool()
def my_tool(param: str) -> str:
    """Describe what this tool does β€” the LLM reads this description.

    Be specific about:
    - What it does
    - What parameters it takes
    - What it returns
    - Any safety considerations
    """
    # Your implementation
    return f"Result for {param}"
```

Then restart the server. The new tool appears automatically.

## Production upgrade path

1. **Human-in-the-loop** β€” add confirmation prompts for destructive operations
2. **Auth / permissions** β€” per-tool access control, API keys
3. **HTTP transport** β€” deploy as a remote MCP server (streamable-http)
4. **Rate limiting** β€” prevent abuse of web fetching / database queries
5. **Logging** β€” structured logs for debugging and auditing
6. **More databases** β€” PostgreSQL, SQLite, MongoDB adapters

## What I learned

- MCP protocol: JSON-RPC over stdio, tool schema from docstrings
- Why guardrails matter: agents are powerful but need boundaries
- AST-based evaluation: safe math without `eval()` security risks
- Tool description quality directly affects agent behavior
- The MCP ecosystem is growing fast β€” Claude, Qwen Code, Cursor all support it

## Security

| Check | Status |
|---|---|
| No `eval()` anywhere | βœ… AST-based calculator only |
| SELECT-only MySQL guardrail | βœ… All non-SELECT queries rejected |
| Path traversal protection | βœ… Sandbox with allowed_paths + blocked_paths |
| Timeout on network calls | βœ… All HTTP calls have timeouts |
| Rate limiting | βœ… Configurable per-tool rate limits |
| Human-in-the-loop writes | βœ… 2-step prepare β†’ confirm flow |
| Tool-call logging | βœ… Every call logged to JSONL |
| Config-driven tool enable/disable | βœ… Toggle tools in config.yaml |

## License

MIT

Maintenance

ActivitySlowing
ResponsivenessNo issues