news 2026/9/11 10:33:36

如何把 agno AgentOS 暴露成带 PAT 鉴权与工具范围控制的 MCP 服务器?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
如何把 agno AgentOS 暴露成带 PAT 鉴权与工具范围控制的 MCP 服务器?

如何把 agno AgentOS 暴露成带 PAT 鉴权与工具范围控制的 MCP 服务器?

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

当你已经用 AgentOS 跑通了一个或多个 agent,想把它们通过/mcp端点开放给 MCP 客户端(Cursor、FastMCP 程序化客户端、Claude Desktop 等),但又不能把裸的mcp=True直接挂到网络上时,需要完成三件事:让客户端用 PAT(Personal Access Token,前缀agno_pat_的服务账号令牌)而不是根密钥访问/mcp;用标签把暴露的工具面从默认的 8 个收敛到需要的集合;在工具或模型执行前拒绝不在白名单内的调用方。仓库中的 secure_mcp.py 就是这条完整路径的可运行实现,本文按它的真实代码与配套文档组织步骤。

适用前提:Python 环境已通过 demo 环境安装(MCP 额外依赖包含在内),并已配置模型 API 密钥。

准备条件

在仓库根目录执行(来自 14_mcp/README.md 的 Prerequisites 一节):

./scripts/demo_setup.sh export OPENAI_API_KEY=...

secure_mcp.py还需要一个 OS 级根安全密钥,用于通过 REST 铸造 PAT:

export OS_SECURITY_KEY=$(openssl rand -base64 32)

这个根密钥的作用边界要分清:它能调用POST /service-accounts铸造服务账号,但它本身会被 MCP 的authorize门拒掉(返回 401),这正是该例子里刻意演示的匿名路径。

服务端配置:密钥、标签范围与 authorize 回调

secure_mcp.py的服务端核心配置如下(省略了导入语句与客户端函数):

BASE_URL = os.getenv("AGENTOS_URL", "http://localhost:7777").rstrip("/") OS_SECURITY_KEY = os.environ["OS_SECURITY_KEY"] SERVICE_ACCOUNT_PREFIX = "secure-mcp-client-" ALLOWED_HOSTS = [ host.strip() for host in os.getenv("MCP_ALLOWED_HOSTS", "").split(",") if host.strip() ] db = SqliteDb( id="secure-mcp-db", db_file="tmp/secure_mcp.db", ) secure_agent = Agent( id="secure-assistant", name="Secure Assistant", model=OpenAIResponses(id="gpt-5.5"), db=db, instructions="Answer authenticated callers concisely.", ) def authorize_service_account(user_id: str | None) -> bool: """Allow only service accounts minted for this MCP integration.""" return bool(user_id and user_id.startswith(f"sa:{SERVICE_ACCOUNT_PREFIX}")) agent_os = AgentOS( id="secure-mcp-os", description="PAT-authenticated and explicitly scoped AgentOS MCP server.", db=db, agents=[secure_agent], settings=AgnoAPISettings(os_security_key=OS_SECURITY_KEY), mcp=MCPConfig( include_tags={"core", "session"}, exclude_tags={"session"}, result_mode="full", authorize=authorize_service_account, allowed_hosts=ALLOWED_HOSTS, ), ) app = agent_os.get_app()

各配置项的作用,均来自 MCPConfig 定义 与 README:

  • AgnoAPISettings(os_security_key=...):设置根密钥。客户端用它调用POST /service-accounts铸造 PAT,PAT 明文agno_pat_...只返回一次,AgentOS 只存哈希。
  • include_tags={"core", "session"}后接exclude_tags={"session"}:先纳入coresession两个标签组,再减去session,最终暴露 6 个 core 工具:get_agentos_configrun_agentrun_teamrun_workflowcontinue_runcancel_run。裸mcp=True会暴露 8 个默认工具(core 6 个加get_sessionsget_session_runs两个 session 工具),这里显式把 session 只读工具挡在 MCP 面之外。
  • authorize=:每次调用前的门。它收到已验证的调用方 principal(服务账号解析为sa:<account-name>),返回True放行、False以 401 拒绝,发生在任何工具或模型运行之前。authorize_service_account只放行自己铸造的sa:secure-mcp-client-*前缀账号。
  • allowed_hosts=ALLOWED_HOSTS:注意默认值是空列表[],而非None。只要allowed_hosts被设置(哪怕是空列表),AgentOS 就会启用 Host 与 Origin 校验(内置的 localhost 放行保留),其他来源返回 400;部署或隧道场景可设置MCP_ALLOWED_HOSTS=agentos.example.com。若保持None,则不做任何主机校验。
  • result_mode="full":run 工具的structuredContent返回 run 的完整to_dict(),供程序化客户端消费;默认"trimmed"只返回回答内容加run_idsession_idstatus等字段。
  • db=用同步的 OS 级SqliteDb:服务账号存在AgentOS(db=...)上,而不是挂在某个 agent 的库上,所以这里必须给 AgentOS 配库。

关于authorize回调看到sa:...而不是None:该文件未启用AgentOS(authorization=True, ...),服务账号校验器会把 PAT principal 填进request.state.user_idauthorize因此收到sa:...;只有匿名路径(例如直接拿根密钥访问)才会以None到达该门——文档说明启动时库会提示 “authorizeis set whileAgentOS(authorization=False)”,对本例不适用,属预期行为。

运行服务器与验证客户端

在两个终端分别运行(服务器在 1 号终端,验证客户端在 2 号终端,均使用 demo 环境的 Python):

.venvs/demo/bin/python cookbook/05_agent_os/14_mcp/secure_mcp.py
.venvs/demo/bin/python cookbook/05_agent_os/14_mcp/secure_mcp.py --client

客户端流程(run_authenticated_client())内置了四条硬断言,任何一条不满足就抛RuntimeError终止,这就是本场景的验证方式:

  1. OS_SECURITY_KEY作为 Bearer 直接 POST/mcp,断言状态码为401(根密钥不能代替 PAT 过 MCP 门);
  2. 携带Host: untrusted.example.com再发一次,断言400(Host 白名单拒绝了不可信主机);
  3. 用根密钥POST /service-accountsjson={"name": account_name})铸造账号,断言返回令牌以agno_pat_开头;
  4. 用该 PAT 作为 Bearer 连上http://localhost:7777/mcplist_tools()的集合必须恰好等于6 个 core 工具(出现get_sessions/get_session_runs即报错),再调用run_agentagent_id="secure-assistant"),断言structuredContentstatus == "COMPLETED"messages非空(result_mode="full"生效的证据)。

客户端成功后打印的结果形如(文档测试记录中的示例输出,run_id等值随每次运行变化):

Authenticated principal: sa:secure-mcp-client-b0ebb699ad Token display prefix: agno_pat_... Root key at /mcp: 401 Untrusted Host at /mcp: 400 Scoped MCP tools: ['cancel_run', 'continue_run', 'get_agentos_config', 'run_agent', 'run_team', 'run_workflow'] Full run result: 586b12d0-5bc3-47d6-b373-ede496e9d129 -> COMPLETED

看到这个输出即说明:PAT 鉴权生效、主机校验生效、工具面被收敛到 core 六个、full 结果格式返回完整消息列表。

可选分支:从 stdio-only 客户端接入

如果你的客户端(如 Claude Desktop)只支持 stdio 方式,README 给出的做法是把 PAT 存在 JSON 文件之外(环境变量),用mcp-remote桥接远端 streamable-HTTP 服务。下面 JSON 中的${AUTH_HEADER}是该文档原有写法,由同段env中的AUTH_HEADER提供,agno_pat_replace_me替换为你铸造的真实 PAT:

{ "mcpServers": { "agentos": { "command": "npx", "args": [ "-y", "mcp-remote", "https://agentos.example.com/mcp", "--header", "Authorization:${AUTH_HEADER}" ], "env": { "AUTH_HEADER": "Bearer agno_pat_replace_me" } } } }

支持原生远程 MCP 的客户端可直接发送Authorization: Bearer agno_pat_...头。此分支用于验证接入方式,不改变服务端的鉴权与范围配置。

限制与注意

  • PAT 明文只出现一次(创建响应),默认有效期 90 天;成功验证默认缓存 30 秒,撤销在处理该请求的 worker 上立即生效,其他 worker 在缓存过期后收敛(见 07_security/README.md 的 Service Accounts 一节)。
  • 服务账号的默认 scope 为agents:runteams:runworkflows:runsessions:readconfig:read;铸造更特权 scope 需要allow_privileged_scopes=true
  • continue_run/cancel_run随组件暴露而附带;当 core 标签被纳入时,这对工具天然在 6 个工具之中,只对已发布组件的 run 生效。
  • 若 MCP 面上要发布Toolkit,而其中数据是按用户隔离的,README 要求先配置AgentOS(authorization=True, ...),否则 RunContext 中的调用方身份为None,所有客户端共享同一身份。
  • 声明了requires_confirmationrequires_user_inputexternal_execution的工具会在 MCP 面被拒发(构建期失败而不是静默放行),因为 MCP 请求会绕过这些审批路径。

铸造、scope 与撤销的完整 REST 操作参见 service_accounts.py;同目录其余文件(如 agents_as_tools.py、stateless.py)分别覆盖把 agent 直接作为具名工具发布与无状态传输,属于不同的暴露策略,可按需另行阅读。

【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 10:32:21

Linux last命令完全指南:从登录查看到安全审计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:30:07

AI Agent进入业务系统:WorkBuddy开放生态的落地实践与关键缺口

最近很多团队都在讨论 WorkBuddy 开放生态这件事&#xff0c;朋友圈里也刷到不少同行分享的接入案例。我自己帮几家公司的技术团队梳理过这类项目&#xff0c;一个很直观的感受是&#xff1a;WorkBuddy 这类 AI Agent 工作台把插件、Skill、自定义指令这些能力开放出来之后&…

作者头像 李华
网站建设 2026/9/11 10:29:28

鸿蒙自定义组件RcSwitch重构:颜色语义化与状态机设计实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/11 10:28:10

Android车载串口开发实战:UART/RS232/RS485全栈解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华