Compare commits

...

2 Commits

Author SHA1 Message Date
3fd4a375ed 更新 README 2026-03-31 22:21:17 +08:00
88694fcdee 修复 Docker 构建问题 2026-03-31 22:17:33 +08:00
6 changed files with 352 additions and 4 deletions

64
.dockerignore Normal file
View File

@@ -0,0 +1,64 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Virtual environments
venv/
.venv/
ENV/
env/
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# Testing
.pytest_cache/
.coverage
htmlcov/
tox/
# Git
.git/
.gitignore
# Documentation
docs/
*.md
!README.md
# Docker
Dockerfile*
docker-compose*
.docker/
# Local data (应该在运行时通过卷挂载)
data/
*.html
*.env
.env*
# Logs
*.log
logs/

149
DEPLOY.md Normal file
View File

@@ -0,0 +1,149 @@
# NJUPT MCP Server - Docker 部署指南
## 🚀 快速开始
### 方式一:使用 Docker Compose推荐
```bash
# 1. 构建并启动
docker-compose up -d
# 2. 查看日志
docker-compose logs -f
# 3. 停止服务
docker-compose down
```
### 方式二:使用 Docker 命令
```bash
# 1. 构建镜像
docker build -t njupt-mcp:latest .
# 2. 运行容器
docker run -d \
--name njupt-mcp \
-p 8000:8000 \
-e COURSE_SCHEDULE=/app/data/course_schedule.html \
-v $(pwd)/src/njupt_mcp/resources/course_schedule/B240423-22.html:/app/data/course_schedule.html:ro \
--restart unless-stopped \
njupt-mcp:latest
# 3. 查看日志
docker logs -f njupt-mcp
# 4. 停止并删除
docker stop njupt-mcp
docker rm njupt-mcp
```
## ⚙️ 配置说明
### 环境变量
| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `COURSE_SCHEDULE` | 课表 HTML 文件路径(容器内) | `/app/data/course_schedule.html` |
### 传输模式
容器默认使用 **SSE 模式** 运行,可通过修改 `docker-compose.yml` 或启动命令切换:
```yaml
# stdio 模式(主要用于调试)
command: ["--transport", "stdio"]
# streamable-http 模式
command: ["--transport", "streamable-http", "--host", "0.0.0.0", "--port", "8000"]
```
## 📁 数据挂载
课表文件通过 Docker Volume 挂载到容器内,这样无需重新构建镜像即可更新课表:
```yaml
volumes:
- /主机/路径/到/课表.html:/app/data/course_schedule.html:ro
```
`:ro` 表示只读挂载,增加安全性。
## 🔒 安全建议
1. **使用非 root 用户**Dockerfile 已配置使用 `appuser` (UID 1000) 运行
2. **只读挂载**:课表文件以只读方式挂载
3. **资源限制**docker-compose.yml 中配置了 CPU 和内存限制
4. **健康检查**:自动检测服务状态,失败时重启
## 🌐 访问方式
启动后MCP 服务器在以下地址可用:
- **SSE 端点**: `http://<服务器IP>:8000/sse`
- **消息端点**: `http://<服务器IP>:8000/messages`
在 MCP Inspector 中配置服务器 URL
```
http://your-server-ip:8000/sse
```
## 🐛 常见问题
### 1. 课表文件找不到
确保挂载路径正确:
```bash
# 检查容器内文件是否存在
docker exec njupt-mcp ls -la /app/data/
```
### 2. 端口冲突
修改 `docker-compose.yml` 中的端口映射:
```yaml
ports:
- "8080:8000" # 主机8080映射到容器8000
```
### 3. 权限问题
如果课表文件权限不足:
```bash
chmod 644 /path/to/course_schedule.html
```
### 4. 查看详细日志
```bash
# 调试模式启动
docker run -e DEBUG=1 njupt-mcp:latest
# 或修改 docker-compose.yml
environment:
- DEBUG=1
```
## 🔄 更新部署
更新代码后重新构建:
```bash
# 拉取最新代码后
docker-compose down
docker-compose build --no-cache
docker-compose up -d
```
## 📊 监控
查看容器状态:
```bash
docker ps
docker stats njupt-mcp
```
查看资源使用:
```bash
docker system df
```

71
Dockerfile Normal file
View File

@@ -0,0 +1,71 @@
# ===========================================
# NJUPT MCP Server - Dockerfile
# ===========================================
# ---- 阶段 1: 构建依赖 ----
FROM python:3.12-slim AS builder
# 安装构建工具
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# 使用 uv 加速依赖安装(可选但推荐)
RUN pip install --no-cache-dir uv
# 设置工作目录
WORKDIR /build
# 创建虚拟环境
RUN uv venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 复制项目文件
COPY pyproject.toml .
COPY README.md .
COPY src/ ./src/
# 安装依赖和包(非 editable 模式)
RUN uv pip install .
# ---- 阶段 2: 运行镜像 ----
FROM python:3.12-slim AS runtime
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
PYTHON_ENV=production
# 创建非 root 用户(安全最佳实践)
RUN groupadd --gid 1000 appgroup && \
useradd --uid 1000 --gid appgroup --shell /bin/bash --create-home appuser
# 从 builder 复制虚拟环境
COPY --from=builder /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
# 设置工作目录
WORKDIR /app
# 包已经在 builder 阶段安装到虚拟环境,无需重新安装
# 但如果需要,可以复制源码供参考(可选)
COPY src/ ./src/
# 创建数据目录并设置权限
RUN mkdir -p /app/data && chown -R appuser:appgroup /app
# 切换到非 root 用户
USER appuser
# 暴露端口SSE 模式默认 8000
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/sse')" 2>/dev/null || exit 1
# 默认启动命令SSE 模式)
ENTRYPOINT ["njupt-mcp"]
CMD ["--transport", "sse", "--host", "0.0.0.0", "--port", "8000"]

View File

@@ -6,15 +6,17 @@
为 LLM 提供南京邮电大学NJUPT相关功能的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器。
本项目几乎完全由 Kimi 生成,感谢 Kimi 🙏
## ✨ 功能特性
### 🔧 Tools工具
| 工具名 | 描述 | 示例 |
|--------|------|------|
| `search_course` | 搜索课程信息 | `search_course("数据结构")` |
| `get_course_schedule` | 获取学生课表 | `get_course_schedule("B21010101", "2024-2025-1")` |
| `search_library_book` | 搜索图书馆藏书 | `search_library_book("Python", "title")` |
| `get_course_schedule_json` | 获取整学期全部课表数据 | `get_course_schedule_json()` |
| `get_week_course_schedule_json` | 获取指定教学周的全部课表 | `get_week_course_schedule_json(week=5)` |
| `get_week_day_course_schedule_json` | 获取指定周和星期的课表 | `get_week_day_course_schedule_json(week=5, day=1)` |
### 📚 Resources资源

54
docker-compose.yml Normal file
View File

@@ -0,0 +1,54 @@
version: "3.8"
services:
njupt-mcp:
build:
context: .
dockerfile: Dockerfile
image: njupt-mcp:latest
container_name: njupt-mcp-server
# 端口映射(主机端口:容器端口)
ports:
- "8000:8000"
# 环境变量
environment:
# 课表 HTML 文件路径(容器内路径)
- COURSE_SCHEDULE=/app/data/course_schedule.html
# 可选:调试模式
# - DEBUG=1
# 数据卷挂载(将主机上的课表文件映射到容器)
volumes:
# 挂载课表文件(请确保路径正确)
- ./src/njupt_mcp/resources/course_schedule/B240423-22.html:/app/data/course_schedule.html:ro
# 可选:挂载日志目录
- ./logs:/app/logs
# 重启策略
restart: unless-stopped
# 资源限制
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
# 健康检查
healthcheck:
test: ["CMD", "python", "-c", "import urllib.request; urllib.request.urlopen('http://localhost:8000/sse')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 5s
# 可选:使用已有网络
# networks:
# default:
# external:
# name: my-network

View File

@@ -14,6 +14,7 @@ from dotenv import load_dotenv
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from mcp.server.fastmcp import FastMCP
from mcp.server.transport_security import TransportSecuritySettings
from mcp.types import ToolAnnotations
from njupt_mcp.resources.types import course_dict_serializer
@@ -61,7 +62,14 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[dict]:
# 1. 创建标准的 FastMCP 实例
mcp = FastMCP("njupt-mcp", lifespan=app_lifespan)
# 禁用 DNS rebinding protectionDocker 环境中 Host header 验证会失败)
mcp = FastMCP(
"njupt-mcp",
lifespan=app_lifespan,
transport_security=TransportSecuritySettings(
enable_dns_rebinding_protection=False,
),
)
# 2. 创建 FastAPI 实例并配置 CORS
app = FastAPI()