Production MCP Server
A production-ready, modular MCP server with 40+ tools across 8 categories, featuring dynamic auto-loading and sandboxed security for file system, database, and network operations.
README
Production MCP Server
🇻🇳 Hướng dẫn tiếng Việt: HUONG_DAN.md · Thêm tool mới: THEM_TOOL.md · Chạy nhiều client (HTTP/SSE): CHAY_NHIEU_CLIENT.md
A production-ready, modular Model Context Protocol (MCP) server built with
FastMCP. It ships 40+ tools across 8 categories and is designed to scale
to 300+ tools without modifying any existing code — you just drop a new file
into tools/<category>/ and it registers itself on startup.
Highlights
- Clean architecture & SOLID — tools stay thin; logic lives in
services/; cross-cutting concerns live incore/. - Dynamic auto-loading — the loader scans
tools/**and calls each module'sregister(mcp). No manual imports, ever. - Standard tool template — every tool has a description, input validation, error handling, logging, type hints and a docstring.
- Uniform responses — every tool returns
{ success, data, error }. - Security by default — filesystem sandbox, SSRF guard on HTTP, secret redaction, write-guarded SQL.
- Async everywhere —
httpx,aiofiles, and threads for blocking libs (sqlite3,openpyxl). - Typed config via
.env(pydantic-settings) and structured logging. - Local AI — AI tools call your local model over an OpenAI-compatible API (vLLM), with an offline heuristic fallback so they work with zero config.
Project structure
mcp-server/
├── main.py # Entrypoint: configures logging, runs the transport
├── server.py # Builds the FastMCP instance + runs the loader
├── config.py # Typed settings from .env (pydantic-settings)
├── requirements.txt
├── pytest.ini
├── .env.example
│
├── core/ # Reusable infrastructure
│ ├── logger.py # Logging (stderr-safe for stdio), JSON option
│ ├── exceptions.py # Domain exception hierarchy
│ ├── decorators.py # @tool_handler: logging + timing + error mapping
│ ├── response.py # make_response() / make_error() envelopes
│ ├── utils.py # validate_path/validate_url/safe_read/safe_write
│ └── loader.py # Dynamic tool discovery & registration
│
├── services/ # Stateful / IO logic (reused by tools)
│ ├── http_service.py # Shared httpx client + SSRF policy
│ ├── database_service.py# Pluggable DB drivers (SQLite now; PG/MySQL later)
│ ├── ai_service.py # OpenAI-compatible (vLLM) chat client
│ └── text_heuristics.py # Offline fallback for AI tools
│
├── models/ # Pydantic models (typed tool outputs)
│ └── common.py
│
├── tools/ # One folder per category, one file per tool
│ ├── system/ # get_system_info, get_os, get_hostname, get_cpu,
│ │ # get_memory, get_disk, get_env, current_time
│ ├── filesystem/ # read/write/append/delete/move/copy/rename files,
│ │ # create/delete/list directory, search, file_info
│ ├── api/ # http_get, http_post, http_put, http_delete
│ ├── network/ # dns_lookup (extend freely)
│ ├── database/ # execute_query, list_tables, describe_table
│ ├── utilities/ # base64, uuid, md5, sha256, json_format, yaml<->json
│ ├── ai/ # count_tokens, summarize_text, extract_keywords
│ └── office/ # csv_reader/writer, excel_reader/writer
│
├── tests/ # pytest suite (loader, core, ~2 tools/category)
└── examples/ # Example client + Claude/VSCode/Cursor/Windsurf configs
Quick start
# 1. Create and activate a virtual environment (Python 3.12+; 3.11 also works)
python -m venv .venv
.\.venv\Scripts\activate # Windows PowerShell
# source .venv/bin/activate # macOS / Linux
# 2. Install dependencies
pip install -r requirements.txt
# 3. Configure
copy .env.example .env # Windows (cp on macOS/Linux)
# edit .env — at minimum set MCP_ALLOWED_ROOT to your working folder
# 4. Run the server (stdio transport by default)
python main.py
# 5. (optional) Run the tests
pytest
To run over HTTP instead of stdio, set MCP_TRANSPORT=http (and MCP_HOST /
MCP_PORT) in .env, then python main.py.
Trying it out with the example client
The in-memory client needs no running server — it imports the server object directly:
python examples/mcp_client.py # in-memory (fastest)
python examples/mcp_client.py --stdio # spawns `python main.py` over stdio
Connecting from an IDE / desktop client
Ready-to-edit configs live in examples/. Replace the absolute paths with your
own (point command at your venv's Python and args at main.py).
| Client | File to edit | Example |
|---|---|---|
| Claude Desktop | %APPDATA%\Claude\claude_desktop_config.json (Win) / ~/Library/Application Support/Claude/… (mac) |
examples/claude_desktop_config.json |
| VSCode | .vscode/mcp.json |
examples/vscode_mcp.json |
| Cursor | .cursor/mcp.json or ~/.cursor/mcp.json |
examples/cursor_mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
examples/windsurf_mcp.json |
After editing, restart the client. The server's tools appear in the client's tool list.
Configuration reference (.env)
All variables are prefixed MCP_. See .env.example for the full list. Key ones:
| Variable | Default | Purpose |
|---|---|---|
MCP_TRANSPORT |
stdio |
stdio | http | sse |
MCP_LOG_LEVEL |
INFO |
Logging verbosity |
MCP_ALLOWED_ROOT |
current working dir | Filesystem sandbox root — tools cannot escape it |
MCP_HTTP_ALLOW_PRIVATE_HOSTS |
false |
Keep false to block SSRF to private/loopback IPs |
MCP_DATABASE_URL |
sqlite:///./data/app.db |
DB connection (SQLite today) |
MCP_DB_ALLOW_WRITE |
true |
Allow write/DDL SQL statements |
MCP_OPENAI_BASE_URL |
(unset) | vLLM OpenAI-compatible URL, e.g. http://localhost:8001/v1 |
MCP_OPENAI_MODEL |
(unset) | Model name served by vLLM |
Tool categories (40+ tools)
Every tool returns the same envelope:
{ "success": true, "data": { ... }, "error": null }
...and on failure:
{ "success": false, "data": null, "error": { "code": "validation_error", "message": "...", "details": {} } }
- System —
get_system_info,get_os,get_hostname,get_cpu,get_memory,get_disk,get_env,current_time - File System (sandboxed) —
read_file,write_file,append_file,delete_file,move_file,copy_file,rename_file,create_directory,delete_directory,list_directory,search_files,file_info - API (SSRF-guarded, httpx) —
http_get,http_post,http_put,http_delete - Network —
dns_lookup - Database (SQLite; PG/MySQL-ready) —
execute_query,list_tables,describe_table - Utilities —
base64_encode,base64_decode,uuid,md5,sha256,json_format,yaml_to_json,json_to_yaml - AI (local vLLM or heuristic fallback) —
count_tokens,summarize_text,extract_keywords - Office —
csv_reader,csv_writer,excel_reader,excel_writer
Using the local AI (vLLM) tools
The AI tools call a local model through an OpenAI-compatible API. Start vLLM:
python -m vllm.entrypoints.openai.api_server \
--model Qwen/Qwen2.5-7B-Instruct \
--port 8001
Then set in .env:
MCP_OPENAI_BASE_URL=http://localhost:8001/v1
MCP_OPENAI_API_KEY=EMPTY
MCP_OPENAI_MODEL=Qwen/Qwen2.5-7B-Instruct
- With these set,
summarize_textandextract_keywordsuse the LLM ("backend": "llm"). - Without them, they fall back to a fast local heuristic (
"backend": "heuristic") — so the tools work out of the box with no model running. count_tokensalways runs locally viatiktoken.
How the dynamic loader works
On startup, core/loader.py walks the tools package
recursively, imports every non-private module, and calls its register(mcp)
function. A broken tool is logged and skipped — it never takes down the server.
That single convention is what makes the project scale to 300+ tools with zero
edits to existing files.
Add a new tool in under a minute
See ADD_A_TOOL.md for the copy-paste template. In short:
- Create
tools/<category>/my_tool.py. - Paste the template, rename the function, write your logic.
- Restart the server — done. It auto-registers.
# tools/utilities/reverse_text.py
from __future__ import annotations
from typing import TYPE_CHECKING
from core.decorators import tool_handler
if TYPE_CHECKING:
from fastmcp import FastMCP
def register(mcp: "FastMCP") -> None:
@mcp.tool()
@tool_handler
async def reverse_text(text: str) -> dict:
"""Reverse a string."""
return {"reversed": text[::-1]}
No imports to update, no registry to edit. That's the whole workflow.
Extending the database to PostgreSQL / MySQL
The database layer is built for this. In
services/database_service.py:
- Implement a class satisfying the
DatabaseDriverprotocol (execute,list_tables,describe_table). - Register it in the
_DRIVERSdict by URL scheme. - Point
MCP_DATABASE_URLat the new backend.
The three database tools do not change — they depend on the abstract driver.
Docker
The image defaults to the HTTP transport (stdio can't work in a container)
and exposes port 8000.
# Compose (recommended) — build, run, persist data in a volume
docker compose up --build
# Or plain Docker
docker build -t production-mcp-server:latest .
docker run -d --name mcp-server -p 8000:8000 -v mcp-data:/data \
production-mcp-server:latest
The server is then reachable at http://localhost:8000/mcp. It runs as a
non-root user, and SQLite + the filesystem-tool workspace live under /data
(mounted as a volume). To wire in a local vLLM model, uncomment the vllm
service in docker-compose.yml and set the MCP_OPENAI_* vars.
Testing
pytest # runs the full suite
pytest -v # verbose
The suite covers the dynamic loader, core helpers (response envelope, path/URL
validation, the @tool_handler decorator), and 1–2 representative tools per
category exercised end-to-end.
Security notes
- Filesystem tools resolve every path and reject anything outside
MCP_ALLOWED_ROOT(blocks../traversal). - HTTP tools reject non-http(s) schemes and, by default, any host resolving to a private/loopback/link-local address (SSRF guard). Toggle with
MCP_HTTP_ALLOW_PRIVATE_HOSTS. get_envredacts values whose names look secret (*key*,*token*,*password*, …).- SQL write/DDL statements are blocked unless
MCP_DB_ALLOW_WRITE=true; use parameterised queries (?). - Logs go to stderr so they never corrupt the stdio JSON-RPC stream.
License
MIT (or your organisation's preferred license).
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。