AI 访问(MCP)
你可以让 AI 助手——Claude Desktop、Claude Code、Cursor、VS Code——通过 **Model Context Protocol(MCP)**服务器,代表用户访问 DeviceChain 租户。LLM 客户端连接、发现工具并调用它们,回答设备群问题:“3 号楼有哪些设备在过去一小时没有上报?”、“汇总冷库资产今天的告警”、“恒温器 T-114 的最新温度是多少?”
服务器基于一个原则:AI 代理绝不能做超出授权者权限的事。 它不是权限过宽的网关,而是现有 GraphQL API 上轻量、经过选择的只读层,携带已登录用户自己的租户作用域令牌。
**目前可用(只读):**可选 mcp 服务,提供十一种经过选择的读取工具,通过 user-management 上完整的 OAuth 2.1 授权服务器保护。**计划中:**需要提升作用域且必须人工确认的写入工具(发送命令、确认/解除告警),以及动态客户端注册(RFC 7591)。本仓库是判断当前哪些功能能够构建的权威依据。
授权服务器支持带 PKCE 的授权码流程、RFC 8414 元数据、刷新令牌轮换和 RFC 8707 受众绑定。在提供动态客户端注册之前,由管理员注册客户端。
助手可以做什么
服务器提供十一种读取工具。每种都是对控制台所用同一 GraphQL API 的查询,使用调用方令牌运行。因此,工具只返回该用户在该租户中有权看到的内容,不会更多。
设备
list_devices:列出设备,支持筛选。get_device:获取单台设备详情。get_device_capabilities:获取设备能够测量的内容,以及它接受的已发布命令和各命令的参数模式。
实时状态与遥测
get_device_state:设备当前最新已知状态,包括该状态是传输协议明确上报还是根据静默推断。这一区别影响“不活跃”的含义。明确上报表示已知设备断开;推断只表示近期没有收到数据,健康但上报间隔较长的设备也可能如此。get_latest_measurements:各测量的最新值。query_measurements:时间范围内的原始时序读数。aggregate_measurements:时间范围内按桶聚合的结果,如最小值、最大值和平均值。
位置
query_locations:设备在可选时间窗口内上报的位置,支持分页并有数量限制。结果按最新优先排列,因此第一条是最新已知位置;不指定时间窗口并只请求一条,就能回答“现在在哪里”。每个位置包含纬度和经度。如果接收器上报了,也包含海拔、精度、速度和航向。缺失字段表示未上报,即未知,而不是零。读取位置需要其他工具不需要的两项条件:客户端还必须获得独立location作用域,想同时使用两者时请求read-only location;用户还必须拥有位置权限,该权限有意不包含在只读查看者基础权限中。因此,其他读取工具都可用的调用方,也可能被此工具拒绝。
告警
list_alarms:列出告警,支持按状态和实体筛选。get_alarm:获取单条告警详情。
命令
list_commands:列出向设备发出的命令及其状态。
没有通用的“运行这个 GraphQL 查询”工具。敏感读取——凭据、审计记录、通知收件人、配置密钥——有意不包含在工具集中。
安全模型
MCP 正逐渐成为赋予 AI 助手实际能力的标准方式。风险在于,草率实现可能给代理一把权限强大、范围宽泛的密钥。DeviceChain 服务器的设计防止这种情况发生。
- 携带用户令牌,绝不用服务令牌。 MCP 服务器不持有任何特权平台凭据。每次工具调用都将调用方经过验证的租户作用域 JWT 转发给底层 GraphQL 服务,使代理只能访问用户能够访问的内容。为 AI 提供独立服务身份会使它能够按任何人的请求跨租户操作,而这是该设计明确拒绝的情况。
- 授权时固定租户,不通过参数传入。 授权确定令牌可以在哪个租户操作,令牌携带该租户。没有工具接受代理可以修改的“tenant”参数。
- 令牌绑定受众。 为 MCP 服务器签发的访问令牌将该服务器指定为预期受众(RFC 8707),在其他地方会被拒绝。为一个资源签发的令牌不能拿去访问另一个资源。
- 只读且工具经过选择。 每种工具都是查询,没有写入路径、通用查询出口,也不暴露敏感对象。
- 每次调用都认证并重新检查。 每个请求都通过
user-management公钥验证 bearer 令牌,并强制只读作用域。底层 GraphQL 服务随后独立重新执行与控制台相同的租户和角色检查。
连接助手后,它可以读取租户中的设备、状态、测量和告警。它不能访问其他租户、改变任何内容,或运行任意查询。
客户端如何连接
MCP 服务器是 OAuth 2.1 资源服务器,user-management 是其授权服务器。客户端连接使用标准 OAuth 流程,而不是自定义密钥交换:
- 客户端从受保护资源元数据(RFC 9728)读取服务器要求,再从授权服务器的元数据(RFC 8414)发现授权信息。每个文档都位于 well-known 路径:将 well-known 片段插在主机与标识符路径之间。实例位于
iot.example.com时,路径分别是/.well-known/oauth-protected-resource/api/mcp和/.well-known/oauth-authorization-server/api/user-management。 - 客户端引导用户完成带 PKCE 的授权码流程(
/oauth/authorize)。用户登录、选择要授权的租户并同意。全部由服务器渲染,不使用共享密钥。 - 客户端在
/oauth/token将授权码换为租户作用域访问令牌,并按需刷新。刷新令牌只可使用一次并会轮换。重置用户密码、禁用或删除用户会终止授权:下一次刷新被拒绝,客户端必须重新授权。 - 客户端使用该令牌调用 MCP 工具,每次调用都按用户自身权限运行。
管理员通过管理 API 注册客户端,客户端不自行注册。因此,运维人员控制哪些应用可以请求访问,以及允许哪些重定向 URI。
客户端应该指向哪里
你只配置一个 URL:实例公共主机加上 /api/mcp。
https://<your-instance-host>/api/mcp
这一个字符串具有三种用途,因此只需要它:
- 客户端 POST MCP 请求的端点;
- 请求令牌时作为
resource参数发送、并作为令牌受众的资源标识符; - 推导其他信息的发现起点。
无需手动输入其他内容。端点返回 401 时,客户端从响应 WWW-Authenticate 标头读取元数据位置并访问。文档告诉它授权服务器在哪里,然后客户端向授权服务器请求自己的元数据。发现过程就是这三个请求,可以先手动验证:
# 1. The endpoint answers 401 and names its metadata document.
curl -i -X POST https://<your-instance-host>/api/mcp
# 2. That document names the authorization server.
curl https://<your-instance-host>/.well-known/oauth-protected-resource/api/mcp
# 3. The authorization server describes where to log in and get a token.
curl https://<your-instance-host>/.well-known/oauth-authorization-server/api/user-management
well-known 片段位于主机和其余路径之间,而不是之后。起初看起来可能不直观,但标准就是这样规定带路径标识符的位置,因此客户端也会这样构造。对于第二个文档,更直观的 https://<host>/api/mcp/.well-known/oauth-protected-resource 也提供同样内容,以兼容采用这种路径构造方式的客户端。
三个请求都无需认证:发现过程有意公开,不返回密钥。请求 3 只有在授权服务器启用后才响应;参见下文,了解为什么这需要独立步骤。
只运行一个副本
MCP 服务器将各客户端协议会话保存在创建会话的 Pod 内存中。会话不在 Pod 间共享,也没有会话亲和性。运行第二个副本时,每个客户端约一半的请求会到达从未见过该会话的 Pod,并被拒绝。错误间歇发生,消息不会提到扩容,因此看起来像客户端缺陷。
安装 mcp 区域时,多个副本会直接被拒绝,而不是留待运行时出现间歇失败。
限制与边界
有些限制是设计决策,有些是当前实现的边界。下文分组说明。
有意设计,且未计划改变:
- 没有写入。 计划通过 MCP 发送命令或确认告警,但必须提升作用域并且显式人工确认。助手绝不会静默驱动设备。
- 没有跨租户访问。 令牌限定于用户授权时选择的一个租户。租户绝不是工具参数,因此代理无法通过更改参数访问其他租户。
- 没有任意查询。 只能访问选定工具集,没有
run_graphql。 - 任何执行路径都不使用服务凭据。 这比“不使用服务令牌”更强:MCP 服务器的任何代码路径都不获取自身凭据,代理没有可借用的权限。所有下游读取都使用调用方令牌,没有读取权限的代理会收到与用户相同的拒绝。(其 Pod 与其他服务一样挂载实例配置,这是部署配套,不是服务器使用的内容。)
- 不返回命令载荷。
list_commands提供名称、状态和时间信息。发送给设备的内容不会进入代理上下文。
最先会遇到的实现限制:
- 结果默认每页 25 条,最多 100 条;多设备查询每次最多 50 个令牌。代理检查大型设备群时需要分页。
- 服务器从单个下游响应最多读取 8 MiB。这限制从平台 API 获取的内容,不是向代理公布的限制。
- 会话空闲 30 分钟后失效。
仅启用服务还不足以使用。 有两个独立开关。只开启第一个,通常会得到能够响应却无法实际访问的服务器:
- 默认部署不包含
mcp功能区域,需要运维人员显式启用。 user-management上的授权服务器在配置 issuer URL 前保持关闭。在此之前,mcp可以启动并提供元数据,但没有客户端能获得令牌。这是有意的独立开关:设置 issuer 会改变实例签发的所有令牌上的一个声明,而不只是 MCP 使用的令牌。
相关内容
- 多租户:MCP 令牌依赖的租户隔离如何执行。
- 架构:
mcp服务的位置,以及凭据的密钥处理模型。 - GraphQL API:MCP 工具所代理的 API。