Skip to content

Commit 7f641f9

Browse files
committed
feat: mcp server
1 parent 029a389 commit 7f641f9

File tree

8 files changed

+88
-43
lines changed

8 files changed

+88
-43
lines changed

backend/apps/api.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,21 @@
11
from fastapi import APIRouter
22

3-
from apps.system.api import login, user, aimodel
4-
from apps.settings.api import terminology
5-
from apps.datasource.api import datasource
63
from apps.chat.api import chat
74
from apps.dashboard.api import dashboard_api
5+
from apps.datasource.api import datasource
6+
from apps.settings.api import terminology
7+
from apps.system.api import login, user, aimodel
8+
from apps.mcp import mcp
89

910
api_router = APIRouter()
1011
api_router.include_router(login.router)
1112
api_router.include_router(user.router)
1213
api_router.include_router(aimodel.router)
1314
api_router.include_router(terminology.router)
1415
api_router.include_router(datasource.router)
16+
# api_router.include_router(row_permission.router)
17+
# api_router.include_router(column_permission.router)
1518
api_router.include_router(chat.router)
1619
api_router.include_router(dashboard_api.router)
20+
api_router.include_router(mcp.router)
21+

backend/apps/chat/api/chat.py

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -57,34 +57,6 @@ async def delete(session: SessionDep, chart_id: int):
5757
)
5858

5959

60-
@router.post("/mcp_start", operation_id="mcp_start")
61-
async def mcp_start(session: SessionDep, chat: ChatMcp):
62-
user = await get_current_user(session, chat.token)
63-
return create_chat(session, user, CreateChat(), False)
64-
65-
66-
@router.post("/mcp_question", operation_id="mcp_question")
67-
async def mcp_question(session: SessionDep, chat: ChatMcp):
68-
user = await get_current_user(session, chat.token)
69-
# return await stream_sql(session, user, chat)
70-
return {"content": """这是一段写死的测试内容:
71-
72-
步骤1: 确定需要查询的字段。
73-
我们需要统计上海的订单总数,因此需要从"城市"字段中筛选出值为"上海"的记录,并使用COUNT函数计算这些记录的数量。
74-
75-
步骤2: 确定筛选条件。
76-
问题要求统计上海的订单总数,所以我们需要在SQL语句中添加WHERE "城市" = '上海'来筛选出符合条件的记录。
77-
78-
步骤3: 避免关键字冲突。
79-
因为这个Excel/CSV数据库是 PostgreSQL 类型,所以在schema、表名、字段名和别名外层加双引号。
80-
81-
最终答案:
82-
```json
83-
{"success":true,"sql":"SELECT COUNT(*) AS \"TotalOrders\" FROM \"public\".\"Sheet1_c27345b66e\" WHERE \"城市\" = '上海';"}
84-
```
85-
<img src="https://sqlbot.fit2cloud.cn/images/111.png">"""}
86-
87-
8860
@router.post("/start")
8961
async def start_chat(session: SessionDep, current_user: CurrentUser, create_chat_obj: CreateChat):
9062
try:
@@ -96,7 +68,7 @@ async def start_chat(session: SessionDep, current_user: CurrentUser, create_chat
9668
)
9769

9870

99-
@router.post("/question", operation_id="question")
71+
@router.post("/question")
10072
async def stream_sql(session: SessionDep, current_user: CurrentUser, request_question: ChatQuestion):
10173
"""Stream SQL analysis results
10274

backend/apps/datasource/api/datasource.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
path = "/opt/sqlbot/data/excel"
1919

2020

21-
@router.get("/list", operation_id="get_datasource_list")
21+
@router.get("/list")
2222
async def datasource_list(session: SessionDep):
2323
return get_datasource_list(session=session)
2424

backend/apps/mcp/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Author: Junjun
2+
# Date: 2025/7/1

backend/apps/mcp/mcp.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Author: Junjun
2+
# Date: 2025/7/1
3+
4+
from typing import Annotated
5+
from fastapi import APIRouter, Depends, HTTPException
6+
from fastapi.security import OAuth2PasswordRequestForm
7+
from common.core.deps import SessionDep, get_current_user
8+
from apps.system.crud.user import authenticate
9+
from common.core.security import create_access_token
10+
from datetime import timedelta
11+
from common.core.config import settings
12+
from common.core.schemas import Token
13+
from apps.datasource.crud.datasource import get_datasource_list
14+
from apps.system.models.system_model import AiModelDetail
15+
from apps.chat.models.chat_model import ChatMcp, CreateChat
16+
from apps.chat.api.chat import create_chat, stream_sql
17+
18+
router = APIRouter(tags=["mcp"], prefix="/mcp")
19+
20+
21+
@router.post("/access_token", operation_id="access_token")
22+
def local_login(
23+
session: SessionDep,
24+
form_data: Annotated[OAuth2PasswordRequestForm, Depends()]
25+
) -> Token:
26+
user = authenticate(session=session, account=form_data.username, password=form_data.password)
27+
if not user:
28+
raise HTTPException(status_code=400, detail="Incorrect account or password")
29+
access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
30+
user_dict = user.to_dict()
31+
return Token(access_token=create_access_token(
32+
user_dict, expires_delta=access_token_expires
33+
))
34+
35+
36+
@router.get("/ds_list", operation_id="get_datasource_list")
37+
async def datasource_list(session: SessionDep):
38+
return get_datasource_list(session=session)
39+
40+
41+
@router.get("/model_list", operation_id="get_model_list")
42+
async def get_model_list(session: SessionDep):
43+
return session.query(AiModelDetail).all()
44+
45+
46+
@router.post("/mcp_start", operation_id="mcp_start")
47+
async def mcp_start(session: SessionDep, chat: ChatMcp):
48+
user = await get_current_user(session, chat.token)
49+
return create_chat(session, user, CreateChat(), False)
50+
51+
52+
@router.post("/mcp_question", operation_id="mcp_question")
53+
async def mcp_question(session: SessionDep, chat: ChatMcp):
54+
user = await get_current_user(session, chat.token)
55+
# return await stream_sql(session, user, chat)
56+
return {"content": """这是一段写死的测试内容:
57+
58+
步骤1: 确定需要查询的字段。
59+
我们需要统计上海的订单总数,因此需要从"城市"字段中筛选出值为"上海"的记录,并使用COUNT函数计算这些记录的数量。
60+
61+
步骤2: 确定筛选条件。
62+
问题要求统计上海的订单总数,所以我们需要在SQL语句中添加WHERE "城市" = '上海'来筛选出符合条件的记录。
63+
64+
步骤3: 避免关键字冲突。
65+
因为这个Excel/CSV数据库是 PostgreSQL 类型,所以在schema、表名、字段名和别名外层加双引号。
66+
67+
最终答案:
68+
```json
69+
{"success":true,"sql":"SELECT COUNT(*) AS \"TotalOrders\" FROM \"public\".\"Sheet1_c27345b66e\" WHERE \"城市\" = '上海';"}
70+
```
71+
<img src="https://sqlbot.fit2cloud.cn/images/111.png">"""}

backend/apps/system/api/aimodel.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,3 @@ async def status(session: SessionDep, dto: model_status):
8888
session.add(term)
8989
session.commit()
9090
return {"message": f"AiModel with IDs {ids} updated successfully."}
91-
92-
93-
@router.get("/list", operation_id="get_model_list")
94-
async def get_model_list(session: SessionDep):
95-
return session.query(AiModelDetail).all()

backend/common/utils/whitelist.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
"*.otf",
2424
"/mcp",
2525
"/mcp/message",
26-
"/datasource/list",
27-
"/system/aimodel/list",
28-
"/chat/mcp_question",
29-
"/chat/mcp_start",
26+
"/mcp/ds_list",
27+
"/mcp/model_list",
28+
"/mcp/mcp_question",
29+
"/mcp/mcp_start",
3030
"/system/license",
3131
"/system/config/key",
3232
"/images/*"

backend/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def custom_generate_unique_id(route: APIRoute) -> str:
5151
description="SQLBot MCP Server",
5252
describe_all_responses=True,
5353
describe_full_response_schema=True,
54-
include_operations=["get_datasource_list", "get_model_list", "mcp_question", "mcp_start"]
54+
include_operations=["get_datasource_list", "get_model_list", "mcp_question", "mcp_start", "access_token"]
5555
)
5656

5757
mcp.mount(mcp_app)

0 commit comments

Comments
 (0)