mirror of
https://github.com/Sun-ZhenXing/mcp-template-python.git
synced 2026-02-04 02:03:32 +00:00
feat: add docker support
This commit is contained in:
29
.dockerignore
Normal file
29
.dockerignore
Normal file
@@ -0,0 +1,29 @@
|
||||
# Environment files
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
build/
|
||||
dist/
|
||||
wheels/
|
||||
*.egg-info
|
||||
|
||||
# Virtual environments
|
||||
.venv
|
||||
|
||||
# Docker files
|
||||
Dockerfile
|
||||
docker-compose.*
|
||||
|
||||
# Git files
|
||||
.git/
|
||||
.gitignore
|
||||
|
||||
# IDE files
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,3 +1,8 @@
|
||||
# Environment files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Python-generated files
|
||||
__pycache__/
|
||||
*.py[oc]
|
||||
|
||||
29
Dockerfile
Normal file
29
Dockerfile
Normal file
@@ -0,0 +1,29 @@
|
||||
ARG PYPI_MIRROR_URL=https://pypi.org/simple
|
||||
|
||||
FROM python:3.12-bookworm AS deps
|
||||
ARG PYPI_MIRROR_URL
|
||||
WORKDIR /app
|
||||
|
||||
COPY pyproject.toml uv.lock .python-version ./
|
||||
|
||||
ENV UV_DEFAULT_INDEX=${PYPI_MIRROR_URL}
|
||||
|
||||
# Install dependencies
|
||||
RUN pip -V && \
|
||||
pip config set global.index-url ${PYPI_MIRROR_URL} && \
|
||||
pip install uv
|
||||
RUN uv sync --no-dev --no-install-project
|
||||
|
||||
FROM python:3.12-slim-bookworm
|
||||
ARG PYPI_MIRROR_URL
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip -V && \
|
||||
pip config set global.index-url ${PYPI_MIRROR_URL} && \
|
||||
pip install --no-cache-dir uv
|
||||
|
||||
COPY --from=deps /app/.venv/ ./.venv/
|
||||
COPY . ./
|
||||
|
||||
EXPOSE 3001
|
||||
CMD [ "uv", "run", "prod", "--host", "0.0.0.0" ]
|
||||
46
README.md
46
README.md
@@ -1,6 +1,52 @@
|
||||
# MCP FastAPI 应用模板
|
||||
|
||||
本项目提供了 FastAPI 集成的 MCP 应用模板。
|
||||
|
||||
- [x] 支持多 MCP 挂载
|
||||
- [x] 支持命令行调用 Stdio 模式
|
||||
- [x] 支持 SSE / Streamable HTTP 兼容
|
||||
- [x] 支持打包分发
|
||||
|
||||
## 开始
|
||||
|
||||
安装依赖:
|
||||
|
||||
```bash
|
||||
uv sync
|
||||
```
|
||||
|
||||
开发:
|
||||
|
||||
```bash
|
||||
uv run dev
|
||||
```
|
||||
|
||||
可通过 <http://127.0.0.1:3001/math/mcp> 访问示例 MCP 接口(Streamable HTTP),或 <http://127.0.0.1:3001/math/compatible/sse> 访问 SSE 接口。
|
||||
|
||||
## 部署
|
||||
|
||||
生产:
|
||||
|
||||
```bash
|
||||
uv run prod
|
||||
```
|
||||
|
||||
构建 Python Wheel 包:
|
||||
|
||||
```bash
|
||||
uv build
|
||||
```
|
||||
|
||||
## Docker 部署
|
||||
|
||||
运行:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
仅构建:
|
||||
|
||||
```bash
|
||||
docker compose build
|
||||
```
|
||||
|
||||
19
docker-compose.yml
Normal file
19
docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
x-default: &default
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- &localtime /etc/localtime:/etc/localtime:ro
|
||||
- &timezone /etc/timezone:/etc/timezone:ro
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: 1m
|
||||
|
||||
services:
|
||||
app:
|
||||
<<: *default
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
image: ${DOCKER_REGISTRY:-docker.io}/local/mcp-template-python:${BUILD_VERSION:-latest}
|
||||
ports:
|
||||
- "3001:3001"
|
||||
@@ -9,12 +9,14 @@ authors = [
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"fastapi[standard]>=0.115.12",
|
||||
"mcp>=1.9.3",
|
||||
"mcp>=1.9.4",
|
||||
"uvicorn[standard]>=0.34.3",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
mcp-template-python = "mcp_template_python.__main__:main"
|
||||
dev = "mcp_template_python.__main__:dev"
|
||||
prod = "mcp_template_python.__main__:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["hatchling"]
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
__version__ = "0.1.0"
|
||||
__module_name__ = "mcp_template_python"
|
||||
|
||||
@@ -1,43 +1,72 @@
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
from .__about__ import __module_name__, __version__
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Run a MarkItDown MCP server")
|
||||
parser = argparse.ArgumentParser(description="MCP Server")
|
||||
|
||||
parser.add_argument(
|
||||
"--http",
|
||||
"--stdio",
|
||||
action="store_true",
|
||||
help="Run the server with Streamable HTTP and SSE transport rather than STDIO (default: False)",
|
||||
help="Run the server with STDIO (default: False)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--host", default=None, help="Host to bind to (default: 127.0.0.1)"
|
||||
"--host",
|
||||
default="127.0.0.1",
|
||||
help="Host to bind to (default: 127.0.0.1)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--port", type=int, default=None, help="Port to listen on (default: 3001)"
|
||||
"--port",
|
||||
type=int,
|
||||
default=3001,
|
||||
help="Port to listen on (default: 3001)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dev",
|
||||
default=False,
|
||||
action="store_true",
|
||||
help="Run the server in development mode (default: False)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--version",
|
||||
action="version",
|
||||
version=f"%(prog)s {__version__}",
|
||||
help="Show the version of the MCP server",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.http and (args.host or args.port):
|
||||
parser.error(
|
||||
"Host and port arguments are only valid when using streamable HTTP or SSE transport (see: --http)."
|
||||
)
|
||||
sys.exit(1)
|
||||
if args.dev:
|
||||
dev(args.host, args.port)
|
||||
sys.exit(0)
|
||||
|
||||
if args.http:
|
||||
if args.stdio:
|
||||
from .app.math import mcp
|
||||
|
||||
mcp.run()
|
||||
else:
|
||||
import uvicorn
|
||||
|
||||
from .server import app
|
||||
|
||||
uvicorn.run(
|
||||
app,
|
||||
host=args.host or "127.0.0.1",
|
||||
port=args.port or 3001,
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
)
|
||||
else:
|
||||
from .app.math import mcp
|
||||
|
||||
mcp.run()
|
||||
|
||||
def dev(host: str = "127.0.0.1", port: int = 3001):
|
||||
"""Run the MCP server in development mode."""
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(
|
||||
f"{__module_name__}.server:app",
|
||||
host=host,
|
||||
port=port,
|
||||
reload=True,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -35,7 +35,3 @@ async def div_nums(a: float, b: float) -> float:
|
||||
Divides the first number by the second.
|
||||
"""
|
||||
return truediv(a, b)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
mcp.run()
|
||||
|
||||
20
uv.lock
generated
20
uv.lock
generated
@@ -27,11 +27,11 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2025.4.26"
|
||||
version = "2025.6.15"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/e8/9e/c05b3920a3b7d20d3d3310465f50348e5b3694f4f88c6daf736eef3024c4/certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", size = 160705, upload_time = "2025-04-26T02:12:29.51Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/73/f7/f14b46d4bcd21092d7d3ccef689615220d8a08fb25e564b65d20738e672e/certifi-2025.6.15.tar.gz", hash = "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b", size = 158753, upload_time = "2025-06-15T02:45:51.329Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/7e/3db2bd1b1f9e95f7cddca6d6e75e2f2bd9f51b1246e546d88addca0106bd/certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3", size = 159618, upload_time = "2025-04-26T02:12:27.662Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/ae/320161bd181fc06471eed047ecce67b693fd7515b16d495d8932db763426/certifi-2025.6.15-py3-none-any.whl", hash = "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", size = 157650, upload_time = "2025-06-15T02:45:49.977Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -261,7 +261,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "mcp"
|
||||
version = "1.9.3"
|
||||
version = "1.9.4"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
@@ -274,9 +274,9 @@ dependencies = [
|
||||
{ name = "starlette" },
|
||||
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f2/df/8fefc0c6c7a5c66914763e3ff3893f9a03435628f6625d5e3b0dc45d73db/mcp-1.9.3.tar.gz", hash = "sha256:587ba38448e81885e5d1b84055cfcc0ca56d35cd0c58f50941cab01109405388", size = 333045, upload_time = "2025-06-05T15:48:25.681Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/f2/dc2450e566eeccf92d89a00c3e813234ad58e2ba1e31d11467a09ac4f3b9/mcp-1.9.4.tar.gz", hash = "sha256:cfb0bcd1a9535b42edaef89947b9e18a8feb49362e1cc059d6e7fc636f2cb09f", size = 333294, upload_time = "2025-06-12T08:20:30.158Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/45/823ad05504bea55cb0feb7470387f151252127ad5c72f8882e8fe6cf5c0e/mcp-1.9.3-py3-none-any.whl", hash = "sha256:69b0136d1ac9927402ed4cf221d4b8ff875e7132b0b06edd446448766f34f9b9", size = 131063, upload_time = "2025-06-05T15:48:24.171Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/97/fc/80e655c955137393c443842ffcc4feccab5b12fa7cb8de9ced90f90e6998/mcp-1.9.4-py3-none-any.whl", hash = "sha256:7fcf36b62936adb8e63f89346bccca1268eeca9bf6dfb562ee10b1dfbda9dac0", size = 130232, upload_time = "2025-06-12T08:20:28.551Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -292,7 +292,7 @@ dependencies = [
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.12" },
|
||||
{ name = "mcp", specifier = ">=1.9.3" },
|
||||
{ name = "mcp", specifier = ">=1.9.4" },
|
||||
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.34.3" },
|
||||
]
|
||||
|
||||
@@ -307,7 +307,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "pydantic"
|
||||
version = "2.11.5"
|
||||
version = "2.11.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "annotated-types" },
|
||||
@@ -315,9 +315,9 @@ dependencies = [
|
||||
{ name = "typing-extensions" },
|
||||
{ name = "typing-inspection" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f0/86/8ce9040065e8f924d642c58e4a344e33163a07f6b57f836d0d734e0ad3fb/pydantic-2.11.5.tar.gz", hash = "sha256:7f853db3d0ce78ce8bbb148c401c2cdd6431b3473c0cdff2755c7690952a7b7a", size = 787102, upload_time = "2025-05-22T21:18:08.761Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/00/dd/4325abf92c39ba8623b5af936ddb36ffcfe0beae70405d456ab1fb2f5b8c/pydantic-2.11.7.tar.gz", hash = "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", size = 788350, upload_time = "2025-06-14T08:33:17.137Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/69/831ed22b38ff9b4b64b66569f0e5b7b97cf3638346eb95a2147fdb49ad5f/pydantic-2.11.5-py3-none-any.whl", hash = "sha256:f9c26ba06f9747749ca1e5c94d6a85cb84254577553c8785576fd38fa64dc0f7", size = 444229, upload_time = "2025-05-22T21:18:06.329Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/c0/ec2b1c8712ca690e5d61979dee872603e92b8a32f94cc1b72d53beab008a/pydantic-2.11.7-py3-none-any.whl", hash = "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b", size = 444782, upload_time = "2025-06-14T08:33:14.905Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user