Django 接入 MCP 实战:让 AI 安全调用数据库和业务接口

Django 接入 MCP 实战:让 AI 安全调用数据库和业务接口

让 AI 查询订单、创建客户或更新工单,最危险的做法是把数据库连接直接交给模型。模型不理解你完整的租户规则、对象权限和副作用边界,一条看似合理的 SQL 就可能绕开业务校验。

更稳妥的结构是:MCP 只暴露少量业务工具,工具继续调用 Django 已有的 REST 接口,认证、序列化器、权限、审计和事务仍由原系统负责。本文用本地 RuyiDjangoCRM 的真实 MCP 实现做验证,相关工具、鉴权和注册测试 25 项全部通过。

MCP 不是数据库万能遥控器

这条链路有三个原则:

  1. AI 看到的是 crm_searchcrm_getcrm_update 等业务工具,不是任意 SQL。
  2. MCP 工具调用 Django API,而不是复制一套 ORM 权限逻辑。
  3. 每个请求携带当前用户自己的访问令牌,服务端不保存一枚全员共享令牌。

如果 MCP 直接访问数据库,Django REST framework 中已经存在的字段校验、对象权限、操作日志和限流都会失效。短期代码看起来少,长期会出现两套互相漂移的业务规则。

先封装一个按请求身份工作的客户端

python 复制代码
import httpx


class CrmClient:
    def __init__(self, base_url: str, token: str):
        self.base_url = base_url.rstrip("/")
        self.headers = {
            "Authorization": f"Bearer {token}",
            "X-Client": "mcp",
            "Accept": "application/json",
        }

    async def request(self, method, path, **kwargs):
        async with httpx.AsyncClient(timeout=30) as client:
            response = await client.request(
                method,
                f"{self.base_url}{path}",
                headers=self.headers,
                **kwargs,
            )
        response.raise_for_status()
        return {} if response.status_code == 204 else response.json()

关键不是 Bearer 字符串本身,而是客户端必须按请求创建。若把第一个用户的客户端缓存成全局对象,后续用户就可能继承他的身份与组织范围。

工具参数要窄,服务端规则要硬

查询工具可以接受实体、关键词、过滤条件和分页,但必须限制最大返回量:

python 复制代码
MAX_LIMIT = 50

async def crm_search(client, entity, query=None, filters=None, limit=20):
    params = dict(filters or {})
    if query:
        params["search"] = query
    params["limit"] = min(max(int(limit), 1), MAX_LIMIT)
    return await client.request("GET", resolve_path(entity), params=params)

写操作仍由 Django 序列化器校验字段。删除、发送邮件等不可逆或对外操作,还要增加一次明确确认:

python 复制代码
async def crm_delete(client, entity, object_id, confirm=False):
    if not confirm:
        raise ValueError("删除是破坏性操作,请传入 confirm=true。")
    return await client.request("DELETE", resolve_path(entity, object_id))


async def crm_action(client, entity, object_id, action, params=None, confirm=False):
    allowed = ENTITY_ACTIONS.get(entity, ())
    if action not in allowed:
        raise ValueError(f"当前实体不允许操作:{action}")
    if action in {"send", "convert"} and not confirm:
        raise ValueError("该操作会产生真实外部影响,需要再次确认。")
    return await client.request(
        "POST",
        f"{resolve_path(entity, object_id)}{action}/",
        json=params or {},
    )

confirm=true 不是完整授权,只是防止模型误触。最终是否允许执行,仍由 Django 根据当前用户、组织、对象状态和角色做决定。

在 Django ASGI 入口挂载 MCP

HTTP 方式的 MCP 可以和 Django 共用一个 ASGI 服务。入口按 /mcp 分流,并在请求进入 MCP 层前拒绝缺少令牌的连接:

python 复制代码
async def application(scope, receive, send):
    path = scope.get("path", "")
    if scope.get("type") == "http" and path.startswith("/mcp"):
        token = bearer_from_headers(scope.get("headers", []))
        if token is None:
            await send_unauthorized(send)
            return
        await mcp_app(scope, receive, send)
        return
    await django_application(scope, receive, send)

RuyiDjangoCRM 还采用了"可选挂载":未安装 MCP 依赖或显式关闭功能时,ASGI 入口回退到普通 Django 应用,不让一个可选的 AI 能力拖垮整个站点。

工具描述也属于安全边界

工具名和参数说明会直接影响模型如何调用。建议做到:

  • 查询、读取、创建、更新、删除分成不同工具,不提供万能 execute
  • 枚举允许的实体和动作,未知值立即拒绝;
  • 对搜索结果、文本长度和批量数量设置上限;
  • 返回稳定、紧凑的 JSON,不把异常堆栈和内部配置暴露给模型;
  • 在 Django 层记录用户、工具、对象、结果和请求编号,令牌本身绝不入日志。

MCP 官方规范也强调,工具调用应保留人在环确认能力,服务端必须验证输入并实施访问控制。不要因为请求来自 AI,就降低普通 API 的安全标准。

本地验证说明了什么

定向测试覆盖了 Bearer 提取、缺失身份拒绝、工具注册、查询上限、未知动作拒绝、删除确认和对外操作确认,共 25 项全部通过。另一个最小工具实验验证:未授权订单返回拒绝,授权用户只能读取自己可见的订单状态。

结论

真正值得复用的不是某个 MCP SDK 的装饰器写法,而是这条稳定边界:AI 负责选择工具和组织参数;MCP 负责协议与工具目录;Django 继续拥有身份、权限、业务规则、数据库和审计。 这样即使以后更换模型或 MCP 客户端,核心业务边界也不会被推倒重写。

参考资料

相关推荐
Python私教1 小时前
Django 搭建 AI 本地知识库:文档上传、向量检索与智能问答
人工智能·python·django
会周易的程序员1 小时前
业力、死亡与共业:多智能体AI的终极形态不会是乌托邦
人工智能
犀利豆1 小时前
Claude Code Tools 研究系列(三)ExitPlanMode
人工智能·后端
always_TT2 小时前
【Python 日志记录:logging 模块入门】
开发语言·python·php
星栈2 小时前
oh-my-pi工程级AI编码工使用体验
人工智能·后端·agent
建筑工程企业管理系统2 小时前
erp工程项目管理系统落地价值:实现工程多项目成本精细化核算与管控
大数据·数据库·人工智能
万岳科技系统开发2 小时前
校园跑腿外卖搭建助力高校周边商家拓展线上业务
大数据·人工智能·小程序
冬奇Lab2 小时前
开源项目第173期:AI For Beginners — 微软出品的 12 周 24 课 AI 完整课程
人工智能·开源·资讯
陈彬深大2 小时前
《AI 渐进编程》之二十三:自适应调度——什么时候启用多Agent流水线
人工智能