Skip to main content
Glama
helmforgedev

fastmcp-server

by helmforgedev

fastmcp-server

SafeSkill 50/100 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.

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 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, or list[ResourceContent]. Returning a dict directly 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 tools

  • mcp_resources_total — Number of registered resources

  • mcp_prompts_total — Number of registered prompts

  • mcp_knowledge_total — Number of knowledge files

  • mcp_tool_calls_total{tool} — Tool invocation counter

  • mcp_tool_duration_seconds{tool} — Tool execution duration histogram

  • mcp_tool_errors_total{tool} — Tool error counter

  • mcp_sources_sync_total{source,status} — Source sync operations

  • mcp_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.

F
license - not found
-
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
0dRelease cycle
26Releases (12mo)
Commit activity

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

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