fastmcp-server
Production-ready FastMCP server image that dynamically loads tools, resources, prompts, and knowledge bases from inline, S3, or Git sources.
README
fastmcp-server
Production-ready FastMCP server image with dynamic loading of tools, resources, prompts, and knowledge bases from multiple sources.
Run your own MCP server anywhere — Docker, Compose, Swarm, Kubernetes, or any container runtime. Define tools in Python, mount them, and your server is live.
Quick Start
# Create a tool
mkdir -p tools
cat > tools/hello.py << 'EOF'
def greet(name: str) -> str:
"""Greet someone by name."""
return f"Hello, {name}!"
EOF
# Run the server
docker run -d \
-p 8000:8000 \
-v $(pwd)/tools:/app/inline/tools \
docker.io/helmforge/fastmcp-server:0.4.0
Your MCP server is now available at http://localhost:8000/mcp.
The built-in Web UI is at http://localhost:8000/ui.
How It Works
The server loads MCP components (tools, resources, prompts, knowledge) from a workspace directory. On startup, it syncs files from one or more sources into the workspace, registers everything with FastMCP, and exposes the server over HTTP.
Sources (Inline, S3, Git)
│
▼
/app/workspace/
├── tools/*.py → registered as MCP tools
├── resources/*.py → registered as MCP resources
├── prompts/*.py → registered as MCP prompts
└── knowledge/* → served as knowledge:// resources
│
▼
FastMCP Server (:8000/mcp)
├── /ui → Web dashboard
├── /healthz → Liveness probe
├── /readyz → Readiness probe
├── /startupz → Startup probe
├── /debug/info → Server diagnostics
├── /metrics → Prometheus metrics (opt-in)
└── /api/* → JSON API for UI
Sources
The image supports three data sources with merge precedence (highest first):
| Source | Best For | Limit |
|---|---|---|
Inline (volume mount at /app/inline/) |
Quick setup, small configs | Host filesystem |
| S3 (AWS S3, MinIO, Cloudflare R2) | Teams, CI/CD pipelines, large knowledge bases | Unlimited |
| Git (any HTTPS repo) | Version-controlled tools, collaboration | Repo size |
All sources can be combined. Inline always wins on conflicts.
Running with Docker Compose
services:
mcp-server:
image: docker.io/helmforge/fastmcp-server:0.4.0
ports:
- "8000:8000"
volumes:
- ./tools:/app/inline/tools
- ./resources:/app/inline/resources
- ./prompts:/app/inline/prompts
- ./knowledge:/app/inline/knowledge
environment:
MCP_SERVER_NAME: my-mcp-server
MCP_AUTH_TYPE: bearer
MCP_AUTH_TOKEN: ${MCP_AUTH_TOKEN}
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:8000/healthz"]
interval: 30s
timeout: 5s
retries: 3
Running with S3 (MinIO example)
services:
mcp-server:
image: docker.io/helmforge/fastmcp-server:0.4.0
ports:
- "8000:8000"
environment:
SOURCE_S3_ENABLED: "true"
SOURCE_S3_ENDPOINT: http://minio:9000
SOURCE_S3_BUCKET: mcp-tools
SOURCE_S3_ACCESS_KEY: minioadmin
SOURCE_S3_SECRET_KEY: minioadmin
minio:
image: docker.io/minio/minio:RELEASE.2025-04-03T14-56-28Z
command: server /data --console-address ":9001"
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
Running with Git Source
docker run -d \
-p 8000:8000 \
-e SOURCE_GIT_ENABLED=true \
-e SOURCE_GIT_REPOSITORY=https://github.com/myorg/mcp-tools.git \
-e SOURCE_GIT_BRANCH=main \
docker.io/helmforge/fastmcp-server:0.4.0
For private repos, set SOURCE_GIT_TOKEN with a personal access token.
Writing Tools
Create a .py file in tools/. By default, every public function defined in that module becomes an MCP tool:
def get_weather(city: str) -> str:
"""Get current weather for a city."""
import httpx
return httpx.get(f"https://wttr.in/{city}?format=3").text
def roll_dice(sides: int = 6) -> int:
"""Roll a die with the given number of sides."""
import random
return random.randint(1, sides)
Utility modules are supported too. Files named like *_helpers.py are skipped by default. For any other utility module, keep helpers private (_helper) or disable auto-registration explicitly:
__mcp_auto_register__ = False
def evidence_true(data, *keys):
return True
If you want exact control over exports, declare a TOOLS allowlist:
TOOLS = ["deploy"]
def deploy(service: str, version: str) -> str:
"""Deploy a service."""
return f"Deployed {service}@{version}"
def helper() -> str:
return "not registered"
Tool Metadata
Add optional module-level variables to control tool registration:
__tags__ = {"devops", "production"} # Categorization tags
__timeout__ = 30.0 # Execution timeout (seconds)
__annotations_mcp__ = { # MCP behavior hints
"destructiveHint": True,
"title": "Deploy Service"
}
def deploy(service: str, version: str) -> str:
"""Deploy a service to production."""
return f"Deployed {service}@{version}"
Async Tools
Both async def and def functions work. Async is preferred for I/O-bound operations:
async def fetch_url(url: str) -> str:
"""Fetch content from a URL."""
import httpx
async with httpx.AsyncClient() as client:
response = await client.get(url)
return response.text
Structured Output
Return ToolResult for full control over response format:
from fastmcp.tools.tool import ToolResult
def analyze(data: str) -> ToolResult:
"""Analyze data with structured output."""
return ToolResult(
content="Analysis complete",
structured_content={"word_count": len(data.split())},
meta={"version": "1.0"}
)
Writing Resources
Create a .py file in resources/ with a RESOURCE_URI constant and a handler function:
import json
RESOURCE_URI = "config://app"
def get_config() -> str:
"""Application configuration."""
return json.dumps({"version": "1.0", "env": "production"}, indent=2)
Important: Resource handlers must return
str,bytes, orlist[ResourceContent]. Returning adictdirectly will cause a serialization error.
Resource Templates
Use {param} placeholders in URIs for parameterized resources:
import json
RESOURCE_URI = "users://{user_id}/profile"
def get_profile(user_id: str) -> str:
"""Get user profile by ID."""
return json.dumps({"user_id": user_id, "name": f"User {user_id}"}, indent=2)
Multiple Resources per File
Use a RESOURCES dict to register multiple resources from one file:
import json
RESOURCES = {
"status://health": "get_health",
"status://version": "get_version",
}
def get_health() -> str:
return json.dumps({"status": "ok"}, indent=2)
def get_version() -> str:
return "1.0.0"
Writing Prompts
Create a .py file in prompts/. Every public function becomes an MCP prompt:
def summarize(text: str) -> str:
"""Summarize the provided text."""
return f"Please provide a concise summary of:\n\n{text}"
Knowledge Base
Any file placed in knowledge/ is served as a knowledge:// resource. Supports text, markdown, JSON, YAML, or any UTF-8 file.
knowledge/
├── product-overview.md
├── api-reference.json
└── troubleshooting/
├── common-errors.md
└── faq.md
These become accessible as knowledge://product-overview.md, knowledge://troubleshooting/common-errors.md, etc.
Authentication
| Type | Variables | Use Case |
|---|---|---|
| None | MCP_AUTH_TYPE=none |
Development, internal networks |
| Bearer | MCP_AUTH_TYPE=bearer + MCP_AUTH_TOKEN |
API keys, service accounts |
| JWT | MCP_AUTH_TYPE=jwt + MCP_AUTH_JWT_* |
OAuth/OIDC, enterprise SSO |
Web UI
The embedded dashboard at /ui provides:
- Dashboard — Server name, version, uptime, component counts, source status
- Tools Explorer — All registered tools with descriptions, parameters, tags, timeout
- Resources Explorer — Resources and templates with URIs, MIME types
- Prompts Explorer — All prompts with descriptions
Auto-refreshes every 15 seconds. Disable with MCP_UI_ENABLED=false.
Observability
Health Endpoints
| Endpoint | Purpose | When 200 |
|---|---|---|
GET /healthz |
Liveness | Always (process is running) |
GET /readyz |
Readiness | Sources synced + components loaded |
GET /startupz |
Startup | Full initialization complete |
Diagnostics
GET /debug/info returns full server diagnostics: version, FastMCP version, uptime, component details, source status, auth type, and configuration.
Prometheus Metrics
Enable with MCP_METRICS_ENABLED=true. Exposes at /metrics:
mcp_tools_total— Number of registered toolsmcp_resources_total— Number of registered resourcesmcp_prompts_total— Number of registered promptsmcp_knowledge_total— Number of knowledge filesmcp_tool_calls_total{tool}— Tool invocation countermcp_tool_duration_seconds{tool}— Tool execution duration histogrammcp_tool_errors_total{tool}— Tool error countermcp_sources_sync_total{source,status}— Source sync operationsmcp_auth_requests_total{result}— Auth attempt counter
Structured Logging
Set LOG_FORMAT=json for JSON-structured logs compatible with Loki, ELK, CloudWatch, and Datadog:
{
"timestamp": "2026-04-05T10:30:00+00:00",
"level": "INFO",
"logger": "fastmcp-server.builder",
"message": "Registered tool: greet"
}
Environment Variables
Server
| Variable | Default | Description |
|---|---|---|
MCP_SERVER_NAME |
fastmcp-server |
Server display name |
MCP_HOST |
0.0.0.0 |
Listen address |
MCP_PORT |
8000 |
Listen port |
MCP_PATH |
/mcp |
HTTP endpoint path |
MCP_WORKSPACE |
/app/workspace |
Workspace directory |
LOG_LEVEL |
INFO |
Logging level |
LOG_FORMAT |
text |
Log format: text or json |
MCP_MASK_ERROR_DETAILS |
false |
Hide internal error details from clients |
MCP_ON_DUPLICATE_TOOLS |
warn |
Duplicate handling: warn, error, replace, ignore |
MCP_STRICT_LOADING |
false |
Fail on boot if any tool/resource has errors |
MCP_UI_ENABLED |
true |
Enable built-in Web UI at /ui |
MCP_METRICS_ENABLED |
false |
Enable Prometheus metrics at /metrics |
EXTRA_PIP_PACKAGES |
Comma-separated pip packages to install at startup |
Authentication
| Variable | Default | Description |
|---|---|---|
MCP_AUTH_TYPE |
none |
bearer, jwt, or none |
MCP_AUTH_TOKEN |
Bearer token value | |
MCP_AUTH_JWT_ISSUER |
JWT issuer | |
MCP_AUTH_JWT_AUDIENCE |
JWT audience | |
MCP_AUTH_JWT_JWKS_URI |
JWKS endpoint URL |
S3 Source
| Variable | Default | Description |
|---|---|---|
SOURCE_S3_ENABLED |
false |
Enable S3 sync |
SOURCE_S3_ENDPOINT |
S3-compatible endpoint URL | |
SOURCE_S3_BUCKET |
Bucket name | |
SOURCE_S3_REGION |
us-east-1 |
AWS region |
SOURCE_S3_PREFIX |
Key prefix filter | |
SOURCE_S3_ACCESS_KEY |
Access key ID | |
SOURCE_S3_SECRET_KEY |
Secret access key |
Git Source
| Variable | Default | Description |
|---|---|---|
SOURCE_GIT_ENABLED |
false |
Enable Git sync |
SOURCE_GIT_REPOSITORY |
Repository HTTPS URL | |
SOURCE_GIT_BRANCH |
main |
Branch to clone |
SOURCE_GIT_PATH |
Subdirectory within the repo | |
SOURCE_GIT_TOKEN |
Auth token for private repos |
Init Container Pattern
For Kubernetes, separate source syncing from server startup using sync_only.py:
python /app/sync_only.py
This script runs sync_sources() and exits. Use it as a Kubernetes init container to pre-populate the workspace volume before the main server starts.
Deployment Options
| Method | Docs |
|---|---|
| Docker / Docker Compose / Swarm | This README |
| Kubernetes (Helm) | helmforge/charts — fastmcp-server |
Connecting MCP Clients
Once your FastMCP server is running and accessible, connect AI assistants to it as an MCP server.
Claude Code
Add the server to your Claude Code settings (~/.claude/settings.json or project .claude/settings.json):
{
"mcpServers": {
"my-mcp-server": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp"
}
}
}
With bearer authentication:
{
"mcpServers": {
"my-mcp-server": {
"type": "streamable-http",
"url": "https://mcp.example.com/mcp",
"headers": {
"Authorization": "Bearer <your-token>"
}
}
}
}
Codex (VS Code Extension)
Add to your Codex configuration file (~/.codex/config.toml):
[mcp_servers.my-mcp-server]
enabled = true
url = "https://mcp.example.com/mcp"
[mcp_servers.my-mcp-server.http_headers]
Authorization = "Bearer <your-token>"
Without authentication (development):
[mcp_servers.my-mcp-server]
enabled = true
url = "https://mcp.example.com/mcp"
Local Development
When running locally with Docker, use http://localhost:8000/mcp as the URL and omit authentication headers if MCP_AUTH_TYPE=none.
License
Apache-2.0 — see 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 模型以安全和受控的方式获取实时的网络信息。