fastmcp-server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fastmcp-serverWhat tools do you have?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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.0Your MCP server is now available at http://localhost:8000/mcp.
The built-in Web UI is at http://localhost:8000/ui.
Related MCP server: FastMCP Demo Server
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 UISources
The image supports three data sources with merge precedence (highest first):
Source | Best For | Limit |
Inline (volume mount at | 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: 3Running 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: minioadminRunning 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.0For 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 TrueIf 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.textStructured 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.mdThese become accessible as knowledge://product-overview.md, knowledge://troubleshooting/common-errors.md, etc.
Authentication
Type | Variables | Use Case |
None |
| Development, internal networks |
Bearer |
| API keys, service accounts |
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 |
| Liveness | Always (process is running) |
| Readiness | Sources synced + components loaded |
| 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 |
|
| Server display name |
|
| Listen address |
|
| Listen port |
|
| HTTP endpoint path |
|
| Workspace directory |
|
| Logging level |
|
| Log format: |
|
| Hide internal error details from clients |
|
| Duplicate handling: |
|
| Fail on boot if any tool/resource has errors |
|
| Enable built-in Web UI at |
|
| Enable Prometheus metrics at |
| Comma-separated pip packages to install at startup |
Authentication
Variable | Default | Description |
|
|
|
| Bearer token value | |
| JWT issuer | |
| JWT audience | |
| JWKS endpoint URL |
S3 Source
Variable | Default | Description |
|
| Enable S3 sync |
| S3-compatible endpoint URL | |
| Bucket name | |
|
| AWS region |
| Key prefix filter | |
| Access key ID | |
| Secret access key |
Git Source
Variable | Default | Description |
|
| Enable Git sync |
| Repository HTTPS URL | |
|
| Branch to clone |
| Subdirectory within the repo | |
| Auth token for private repos |
Init Container Pattern
For Kubernetes, separate source syncing from server startup using sync_only.py:
python /app/sync_only.pyThis 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) |
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.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/helmforgedev/fastmcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server