Skip to main content
Glama
zyay
by zyay

πŸ€– 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

# 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

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

Or add to .qwen/settings.json:

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

Option 2: Claude Code

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

Option 3: Claude Desktop

Edit claude_desktop_config.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:

# 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:

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

# 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

@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