混合MCP服务器模板

一个用于构建混合MCP(模型上下文协议)服务器的模板,同时提供REST API和MCP服务器功能,适用于AI客户端和人类用户共享业务逻辑。

  • by matheus-szfig
  • HCL
  • 本地部署
  • 更新于
在 GitHub 查看 matheus-szfig/hybrid-mcp-boilerplate

使用说明

混合MCP沸腾板

建筑样板 混合MCP(模型上下文协议)服务器 -一个单一的可部署应用程序,在同一进程上通过SSE公开标准REST API(FastAPI)和MCP服务器。AI客户端连接到MCP端点;人工和集成使用REST API。两者都有相同的商业逻辑。

这是什么

main.py                        # entry point — mounts MCP SSE onto FastAPI
src/
  settings.py                  # typed env-var config with pydantic-settings
  api/
    app.py                     # FastAPI instance + CORS + router registration
    routers/
      words.py                 # example: CRUD REST endpoints
  mcp/
    app.py                     # FastMCP instance
    tools/
      words.py                 # example: same CRUD exposed as MCP tools
  services/
    words.py                   # business logic — shared by REST and MCP
iac/
  terraform/                   # Azure infrastructure (App Service + PostgreSQL)
  scripts/bootstrap.sh         # one-time state storage setup
.github/workflows/
  iac-plan.yml                 # terraform plan on PR
  iac-deploy.yml               # terraform apply on merge

堆栈: Python 3.12、FastAPI、FastMCP、Pydantic v2、uv、Alembic、PostgreSQL
部署: Azure应用服务(基本B1,始终开启)+PostgreSQL灵活服务器
IaC: 具有按订阅环境和GitHub Actions CI/CD的Terraform


设置

先决条件

1.克隆并安装

git clone <your-repo-url>
cd hybrid-mcp
uv sync

2.配置环境

复制示例env并填写您的值:

cp .env.example .env

.env 值:

# App
APP_NAME=hybrid-mcp

# CORS
CORS_ALLOWED_ORIGINS=["http://localhost:3000"]
CORS_ALLOWED_METHODS=["GET","POST","PUT","DELETE","OPTIONS"]
CORS_ALLOWED_HEADERS=["*"]

# Database
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=hybrid_mcp
DATABASE_USER=postgres
DATABASE_PASSWORD=secret

# Sharepoint (optional integration)
SHAREPOINT_CLIENT_ID=
SHAREPOINT_CLIENT_SECRET=
SHAREPOINT_TENANT_ID=
SHAREPOINT_SITE_URL=
SHAREPOINT_FILE_PATH=

3.在本地运行

uv run uvicorn main:app --host 0.0.0.0 --port 8000 --reload

3a。使用Docker运行(推荐给开发人员)

需要 Docker 桌面版 使用Compose v2.22+。

docker compose watch

这将启动API和本地PostgreSQL数据库。文件更改会自动处理:

你改变了什么 会发生什么
任何东西 src/ 或 main.py Compose将文件同步到容器中,uvicorn重新加载
pyproject.toml 或 uv.lock 图像已重建(uv sync 重新运行),容器重新启动
Dockerfile.dev 图像已重建

当地的Postgres运行 localhost:5432 与:

  • 用户: postgres,密码: secret,db: hybrid_mcp

确保你的 .env 有 DATABASE_HOST=db (Docker服务名称)在通过Compose运行时。

端点 描述
http://localhost:8000/docs REST API文档(Swagger UI)
http://localhost:8000/api/words/ 示例单词CRUD
http://localhost:8000/mcp/sse MCP SSE流

4.测试MCP连接

使用官方MCP检查员:

npx @modelcontextprotocol/inspector http://localhost:8000/mcp/sse

它打开一个浏览器UI,您可以在其中浏览和调用所有注册的MCP工具。


进行更改

添加新功能(REST+MCP)

每个功能都遵循相同的三文件模式:

1.创建服务-- src/services/<feature>.py

纯业务逻辑,无HTTP或MCP问题:

def list_items() -> list[str]:
    ...

def add_item(item: str) -> list[str]:
    ...

2.创建REST路由器-- src/api/routers/<feature>.py

from fastapi import APIRouter
from src.services.<feature> import list_items, add_item

router = APIRouter(prefix="/<feature>", tags=["<feature>"])

@router.get("/", response_model=list[str])
def get_items():
    return list_items()

@router.post("/", response_model=list[str], status_code=201)
def create_item(body: ItemBody):
    return add_item(body.item)

在中注册 src/api/app.py:

from src.api.routers.<feature> import router as feature_router

api_router.include_router(feature_router)

3.创建MCP工具-- src/mcp/tools/<feature>.py

from mcp.server.fastmcp import FastMCP
from src.services.<feature> import list_items, add_item

def register_feature_tools(server: FastMCP) -> None:

    @server.tool()
    def feature_list() -> list[str]:
        """List all items."""
        return list_items()

    @server.tool()
    def feature_add(item: str) -> list[str]:
        """Add an item."""
        return add_item(item)

在中注册 src/mcp/app.py:

from src.mcp.tools.<feature> import register_feature_tools

register_feature_tools(mcp)

添加环境变量

向添加新设置 src/settings.py。每个嵌套类都是独立的 BaseSettings 它独立读取自己的前缀env变量:

class _MyIntegration(BaseSettings):
    model_config = _config("MY_INTEGRATION_")

    api_key: str
    base_url: str = "https://api.example.com"

然后将其添加到 _Settings:

class _Settings(BaseSettings):
    ...
    my_integration: _MyIntegration = _MyIntegration()

通过以下方式随时随地访问 from src.settings import settings → settings.my_integration.api_key.

数据库迁移

迁移使用 蒸馏器.从项目根开始:

# Create a new migration
uv run alembic revision --autogenerate -m "add my table"

# Apply migrations
uv run alembic upgrade head

# Rollback one step
uv run alembic downgrade -1

部署

步骤0--选择您的提供商

选择一个提供者并将其设置在一个地方。 打开 .github/workflows/iac-deploy.yml 并在此行设置默认值:

# .github/workflows/iac-deploy.yml
TF_PROVIDER: ${{ inputs.provider || 'azure' }}  # ← change to: azure | aws | gcp

这控制了每个自动分支触发部署(PR合并到 dev 或 main).对于一次性手动部署到其他提供商,您始终可以通过以下方式覆盖它 workflow_dispatch 而不改变这条线。

所有基础设施均位于 iac/terraform/<provider>/ --只需填写所选提供商的文件。

提供者 计算 数据库 ~成本/环境/月
蔚蓝 应用服务基本B1(始终打开) PostgreSQL灵活服务器B1ms 28美元
亚马逊网络服务 Elastic Beanstalk t3.micro RDS PostgreSQL t4g.micro 20美元
谷歌云平台 云运行(最小实例数=1) 云SQL db-f1-micro 22美元

仅适用于GCP: Cloud Run需要容器映像。在部署之前构建并推送到工件注册表(见下文)。 仅限AWS: 弹性豆茎需要 Procfile 在repo根目录: web: uvicorn main:app --host 0.0.0.0 --port 5000

先决条件

首次设置

1.填写您的值

编辑所选提供商和环境的后端配置和环境文件:

iac/terraform/<provider>/backends/dev.conf   # state storage config
iac/terraform/<provider>/envs/dev.tfvars     # subscription/project ID + app values

2.Bootstrap状态存储

每个提供者+环境运行一次以创建远程状态存储桶/存储帐户:

./iac/scripts/bootstrap.sh azure dev
./iac/scripts/bootstrap.sh aws   dev
./iac/scripts/bootstrap.sh gcp   dev

3.部署

# Azure
terraform -chdir=iac/terraform/azure init -backend-config=backends/dev.conf
terraform -chdir=iac/terraform/azure apply -var-file=envs/dev.tfvars -var="db_password=<password>"

# AWS
terraform -chdir=iac/terraform/aws init -backend-config=backends/dev.conf
terraform -chdir=iac/terraform/aws apply -var-file=envs/dev.tfvars -var="db_password=<password>"

# GCP (build and push image first — see below)
terraform -chdir=iac/terraform/gcp init -backend-config=backends/dev.conf
terraform -chdir=iac/terraform/gcp apply -var-file=envs/dev.tfvars -var="db_password=<password>"

GCP——构建和推送容器映像

跑 terraform apply 首先创建工件注册表存储库,然后:

# Build
docker build -t <region>-docker.pkg.dev/<project>/<app-name>-dev/app:latest .

# Push
gcloud auth configure-docker <region>-docker.pkg.dev
docker push <region>-docker.pkg.dev/<project>/<app-name>-dev/app:latest

然后更新 container_image 在 iac/terraform/gcp/envs/dev.tfvars 并重新申请。

你还需要一个 Dockerfile 在repo根目录下。例子:

FROM python:3.12-slim
WORKDIR /app
COPY . .
RUN pip install uv && uv sync --no-dev
CMD ["uv", "run", "uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

GitHub操作CI/CD

工作流文件位于 iac/github-actions/ 并且是 默认情况下处于非活动状态 因此,它们不会在样板回购本身上运行。要在项目中激活它们,请执行以下操作:

mkdir -p .github/workflows
cp iac/github-actions/iac-plan.yml   .github/workflows/
cp iac/github-actions/iac-deploy.yml .github/workflows/

一旦 .github/workflows/,GitHub将自动接收它们。

事件 行动
公关定位 dev terraform plan 反对dev,结果作为公关评论发布
PR合并为 dev terraform apply 到dev
公关定位 main terraform plan 反对prod
PR合并为 main terraform apply 刺激
手册 workflow_dispatch 选择提供商+环境

提供程序默认为 azure 由树枝引发的跑步。要部署到其他提供程序,请使用 workflow_dispatch 并选择提供者。

所需的GitHub机密(根据GitHub环境设置 dev / prod)

机密 由使用
DB_PASSWORD 所有供应商
AZURE_CLIENT_ID 蔚蓝
AZURE_TENANT_ID 蔚蓝
AZURE_SUBSCRIPTION_ID 蔚蓝
AWS_ROLE_ARN aws
AWS_REGION aws
GCP_WORKLOAD_IDENTITY_PROVIDER GCP
GCP_SERVICE_ACCOUNT GCP

仅为您使用的提供商设置机密。所有供应商使用 OIDC/工作负载标识 --GitHub中没有存储长期有效的凭据。