Compare commits
2 Commits
d30e4db319
...
3fd4a375ed
| Author | SHA1 | Date | |
|---|---|---|---|
| 3fd4a375ed | |||
| 88694fcdee |
64
.dockerignore
Normal file
64
.dockerignore
Normal 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
149
DEPLOY.md
Normal 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
71
Dockerfile
Normal 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"]
|
||||||
@@ -6,15 +6,17 @@
|
|||||||
|
|
||||||
为 LLM 提供南京邮电大学(NJUPT)相关功能的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器。
|
为 LLM 提供南京邮电大学(NJUPT)相关功能的 [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 服务器。
|
||||||
|
|
||||||
|
本项目几乎完全由 Kimi 生成,感谢 Kimi 🙏
|
||||||
|
|
||||||
## ✨ 功能特性
|
## ✨ 功能特性
|
||||||
|
|
||||||
### 🔧 Tools(工具)
|
### 🔧 Tools(工具)
|
||||||
|
|
||||||
| 工具名 | 描述 | 示例 |
|
| 工具名 | 描述 | 示例 |
|
||||||
|--------|------|------|
|
|--------|------|------|
|
||||||
| `search_course` | 搜索课程信息 | `search_course("数据结构")` |
|
| `get_course_schedule_json` | 获取整学期全部课表数据 | `get_course_schedule_json()` |
|
||||||
| `get_course_schedule` | 获取学生课表 | `get_course_schedule("B21010101", "2024-2025-1")` |
|
| `get_week_course_schedule_json` | 获取指定教学周的全部课表 | `get_week_course_schedule_json(week=5)` |
|
||||||
| `search_library_book` | 搜索图书馆藏书 | `search_library_book("Python", "title")` |
|
| `get_week_day_course_schedule_json` | 获取指定周和星期的课表 | `get_week_day_course_schedule_json(week=5, day=1)` |
|
||||||
|
|
||||||
### 📚 Resources(资源)
|
### 📚 Resources(资源)
|
||||||
|
|
||||||
|
|||||||
54
docker-compose.yml
Normal file
54
docker-compose.yml
Normal 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
|
||||||
@@ -14,6 +14,7 @@ from dotenv import load_dotenv
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
from mcp.server.fastmcp import FastMCP
|
from mcp.server.fastmcp import FastMCP
|
||||||
|
from mcp.server.transport_security import TransportSecuritySettings
|
||||||
from mcp.types import ToolAnnotations
|
from mcp.types import ToolAnnotations
|
||||||
|
|
||||||
from njupt_mcp.resources.types import course_dict_serializer
|
from njupt_mcp.resources.types import course_dict_serializer
|
||||||
@@ -61,7 +62,14 @@ async def app_lifespan(server: FastMCP) -> AsyncIterator[dict]:
|
|||||||
|
|
||||||
|
|
||||||
# 1. 创建标准的 FastMCP 实例
|
# 1. 创建标准的 FastMCP 实例
|
||||||
mcp = FastMCP("njupt-mcp", lifespan=app_lifespan)
|
# 禁用 DNS rebinding protection(Docker 环境中 Host header 验证会失败)
|
||||||
|
mcp = FastMCP(
|
||||||
|
"njupt-mcp",
|
||||||
|
lifespan=app_lifespan,
|
||||||
|
transport_security=TransportSecuritySettings(
|
||||||
|
enable_dns_rebinding_protection=False,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
# 2. 创建 FastAPI 实例并配置 CORS
|
# 2. 创建 FastAPI 实例并配置 CORS
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|||||||
Reference in New Issue
Block a user