news 2026/9/10 1:38:03

使用 Terraform 管理 Onyx MCP 服务器:`onyx_mcp_server` 资源配置与最佳实践

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
使用 Terraform 管理 Onyx MCP 服务器:`onyx_mcp_server` 资源配置与最佳实践

使用 Terraform 管理 Onyx MCP 服务器:onyx_mcp_server资源配置与最佳实践

【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer

Onyx 支持接入 MCP(Model Context Protocol)服务器,让外部工具通过标准协议挂载到 Agent 上。本文以 onyx_mcp_server 资源文档 为核心,系统讲解如何在terraform-provider-onyx中创建、认证、授权与导入 MCP 服务器,并深入mcp_server_resource.gowrite_only.go等源码,剖析其配置校验、凭证生命周期与 API 调用链。读完本文,你将能够用纯声明式配置管理 Onyx 的 MCP 服务器接入,包括共享令牌、按用户密钥、Craft 可用性与访问控制,并规避 Terraform 与浏览器式登录、敏感信息状态存储等关键陷阱。

一、资源定位与适用边界

onyx_mcp_server描述的是"Onyx 连接到的 MCP 服务器",其价值在于把该服务器的工具挂载到 Agent 上。在 Onyx 的整个 MCP 体系里,此资源只负责"服务器本身"的注册与授权,不负责服务器暴露的工具清单——工具由 Onyx 主动调用服务器后自行学习(discover),与工具选择(tool selection)以及 Craft 审批策略(approval policies)相关的配置,都只对 Onyx 已经发现过的工具生效。

文档明确划定了本资源可管理的能力边界:

  • 支持无需交互式登录的认证方式NONE(无凭证)与API_TOKEN(令牌)。
  • 拒绝 OAuth 服务器:文档声明 "An OAuth server is refused while the plan is built",因为 OAuth 流程需要浏览器往返(browser round-trip),这是 Terraform 无法执行的。从客户端源码 mcp_server.go 可看到完整的认证类型枚举,除NONEAPI_TOKEN外还有OAUTHPT_OAUTH,后两者均被拒于 plan 阶段(详见下文"配置校验"一节)。

因此,对于需要 OAuth 交互式登录的服务器,应先在 Onyx 管理后台手工添加,再用 Terraform 管理部署的其余部分——这是官方文档给出的明确指引。

二、完整示例:三种典型用法

原文档提供了三个覆盖不同认证与授权形态的完整配置示例,应作为实战起点(三个示例可直接合并到同一.tf文件中):

# 一个无需任何凭证的公共 MCP 服务器。 resource "onyx_mcp_server" "docs" { name = "Docs" description = "Public documentation search" server_url = "https://mcp.example.com/mcp" } # 一个使用共享 API 令牌的服务器。Onyx 返回令牌时会被掩码处理,因此 # 配置文件是令牌的唯一记录:轮换令牌时请改这里,不要在 UI 中改。 resource "onyx_mcp_server" "weather" { name = "Weather" server_url = "https://weather.example.com/mcp" auth_type = "API_TOKEN" auth_performer = "ADMIN" api_token = var.weather_api_token # 仅允许 Craft agent 访问该服务器。 available_in_craft = true is_public = false } # 每个用户各自提供密钥的服务器。模板声明用户需要填写的字段, # admin_credentials 是应用该配置的管理员自己的值。 resource "onyx_mcp_server" "tickets" { name = "Tickets" server_url = "https://tickets.example.com/mcp" auth_type = "API_TOKEN" auth_performer = "PER_USER" auth_template_headers = { "X-Api-Key" = "{api_key}" } admin_credentials = { api_key = var.tickets_admin_api_key } }

三个示例分别对应三类部署形态:

形态auth_typeauth_performer凭证字段
公共无凭证NONE(默认)ADMIN(默认)无需设置
共享令牌API_TOKENADMINapi_token(或api_token_wo
按用户密钥API_TOKENPER_USERauth_template_headers+admin_credentials(或_wo变体)

三、Schema 全解:必填、可选与只读属性

原文档给出的 Schema 定义已相当完整,下表在保留全部字段的基础上补充了默认值与底层含义(字段默认值均来自 mcp_server_resource.go 的 Schema 定义):

Required(必填)

参数类型说明
nameString显示名称。Onyx 不要求唯一,两个服务器可以同名
server_urlStringOnyx 调用该服务器的 URL。无论 SSRF 保护级别如何,Onyx 都会拒绝 loopback 与 link-local 地址,因此部署在 Onyx 宿主本机的服务器无法通过主机名被访问

Optional(可选)

参数类型默认值说明
descriptionString""自由文本描述
transportStringSTREAMABLE_HTTPSTREAMABLE_HTTP或已弃用的SSE
auth_typeStringNONENONEAPI_TOKEN
auth_performerStringADMIN凭证提供方:ADMIN表示单一共享令牌,PER_USER表示每个用户各自提供令牌
api_tokenString(Sensitive)共享 API 令牌,用于API_TOKEN+ADMIN组合。Onyx 返回时掩码,Terraform 永不读回:配置值是唯一记录,导入的服务器没有该值。优先使用api_token_wo,二者不能同时设置
api_token_woString(Sensitive,Write-only)仅存于配置中的共享令牌。每次 apply 都会发送,状态中不存储任何内容。与api_token_wo_version配合轮换。需要 Terraform 1.11 或更高版本
api_token_wo_versionNumberapi_token_wo的轮换计数器。Terraform 不存储 write-only 值,无法感知密钥变化;提升该数字使下次 apply 发送当前值。不要用密钥本身派生它——与密钥不同,该数字保留在 state 中
auth_template_headersMap of String(Sensitive)用于PER_USER的请求头模板。值中的{placeholder}声明每个用户需填写的字段。共享令牌场景下由 Onyx 自行写入该模板;若请求未声明,Onyx 会保留已有值——因此从按用户切换到共享令牌后,原按用户头仍会残留,需重建服务器才能清零
admin_credentialsMap of String(Sensitive)auth_template_headers占位符的值,PER_USER下必填、其他形态下被拒绝(共享令牌走api_token)。Onyx 按"应用该配置的身份"而非"服务器"存储它们,返回时掩码。优先使用admin_credentials_wo,二者不能同时设置
admin_credentials_woMap of String(Sensitive,Write-only)仅存于配置的模板字段值。Terraform 每次 apply 发送、不存储任何内容。与admin_credentials_wo_version配合轮换。需要 Terraform 1.11+
admin_credentials_wo_versionNumberadmin_credentials_wo的轮换计数器,语义同api_token_wo_version
is_publicBooleantrue是否所有用户都可用。为false时仅usersgroups指定的对象可用
groupsSet of Number服务器非公开时允许使用的用户组 id。Onyx 拒绝内置的Admin,遇到该场景应改用公开服务器。该列表由配置"拥有":从配置中移除会清空服务器上的组(包括管理后台添加的)
usersSet of String服务器非公开时允许使用的用户 id(UUID)。同样由配置"拥有",移除即清空
available_in_craftBooleanfalseCraft agent 是否可以使用该服务器。该字段由 Onyx 存放在独立端点,因此设置它需要额外一次 API 调用

Read-Only(只读)

参数类型说明
idString服务器 id,由 Onyx 分配
ownerString配置该服务器的身份。对 Terraform 运行而言是 API key 的合成地址,而非真实邮箱
statusString连接状态,由 Onyx 自行流转:CREATEDAWAITING_AUTHFETCHING_TOOLSCONNECTEDDISCONNECTED
tool_countNumberOnyx 在该服务器上已发现的工具数量
last_refreshed_atStringOnyx 最近一次列出该服务器工具的时间

注意:Write-only 参数(*_wo)依赖 Terraform 1.11 及以后版本才支持的 Write-only Arguments 特性,使用前请确认 CLI 版本满足要求。

四、配置校验:Apply 之前的本地交叉检查

ValidateConfig(mcp_server_resource.go)在 plan 构建阶段即执行全部本地校验,无需已配置的客户端,其检查顺序与组合逻辑值得关注:

  1. 先校验auth_performer,再校验auth_type:因为后续检查依赖 performer 是否已知,且对 Onyx 不认识的 performer,无论auth_type解析为何值都是错误的。performer 必须是ADMINPER_USER,否则直接报Unknown authentication performer
  2. 拒绝 OAuth:当auth_typeOAUTHPT_OAUTH时直接报错,提示需要在 Onyx 管理后台添加服务器。这正是前文"OAuth 被拒绝于 plan 阶段"的源码级实现。
  3. auth_type合法值:仅允许NONEAPI_TOKEN,其余值报Unknown authentication type
  4. 认证矩阵交叉检查(核心逻辑,按 performer 分支):
    • auth_type = NONE:任何凭证类字段(api_token/api_token_woadmin_credentials/admin_credentials_woauth_template_headers)一旦被设置即报Credentials set on a server that takes none
    • ADMIN共享令牌:必须设置api_tokenapi_token_wo(否则报Missing api_token);不允许设置auth_template_headers(Onyx 会自行写入共享令牌的模板)与admin_credentials(共享令牌本身就是凭证);
    • PER_USER按用户:必须设置auth_template_headers(声明用户填写字段的模板)与admin_credentials/admin_credentials_wo(应用管理员自己的字段值);禁止设置api_token/api_token_wo(那是共享令牌专用)。

源码中eitherAttributeIsSet(write_only.go)把普通敏感字段与其 write-only 孪生字段折叠为一次"是否存在"判断:任一侧有值即视为已设置,仅当两侧都未知时才返回 unknown——这保证了api_tokenapi_token_wo互斥但等效。配套的ConflictsWith校验器(stringvalidator.ConflictsWithmapvalidator.ConflictsWith)则确保成对字段不能同时出现。

五、凭证生命周期:掩码、write-only 与轮换

本资源在凭证处理上有三个设计要点,直接决定了你的使用方式:

1. Onyx 返回的凭证永远被掩码。客户端模型注释明确指出:管理员的 API 令牌在回读时是一串 bullet 字符(mcp_server.go),因此没有任何响应字段适合回写进 upsert。资源在刷新时刻意跳过api_tokenadmin_credentials(applyRemoteMCPServer),以免把一屏掩码写进 state 覆盖真实配置值。

2. Write-only 孪生字段让密钥彻底离开 state。Terraform 会把 write-only 值从 plan 与 state 中剥离,密钥只存在于配置文件(write_only.go)。由于 Onyx 的 API 在更新时会整体替换字段,resolveWriteOnly保证每次 apply 都能拿到配置中的值发送,不会因更新而清空已存密钥。该机制由markWriteOnlySource/writeOnlySourceMarked通过 private state 标记记录来源,确保刷新时不会把密钥误写回 state。

3. 轮换通过版本计数器触发。Terraform 无法 diff 一个它从不存储的值,所以单独修改_wo字段不会产生任何 plan。writeOnlyVersionAttribute(write_only.go)为此提供了配套的*_wo_version计数器:提升数字才会产生 diff,从而驱动下一次 apply 发送当前密钥;同时用AlsoRequires校验器强制该计数器必须伴随对应_wo字段使用。文档特别警告:不要用密钥本身派生版本号——版本号留在 state 中,密钥不在。

六、访问控制:公开、用户、组与 Craft 可用性

  • is_public = true(默认):所有用户可用;false时仅usersgroups所列对象可用。二者均可同时配置,形成白名单。
  • groups使用用户组数字 id,且Onyx 拒绝内置Admin,遇到"全员可用"需求请直接设is_public = true
  • 这两个集合遵循"配置即权威"原则:从配置中删除某个用户/组,apply 时会同步清空服务器上的对应项——包括在管理后台手工添加的。实现上,writeFromModel(mcp_server_resource.go)在配置缺省时发送空列表而非省略字段,因为 Onyx 把"缺省"解读为"保持原样",而配置语义是"没有访问列表";若不显式发送空列表,从配置中移除的列表会残留在服务器上,并在下次 read 时与已删除它们的 plan 产生永久 diff。
  • available_in_craft走独立端点:upsert 请求体(MCPServerWrite)不携带该字段,只有 PATCH 端点(/admin/mcp/server/{id}MCPServerPatch)接受它,因此完整定义一台服务器需要两次调用(mcp_server.go)。资源在创建/更新后会调用applyCraftAvailability补齐该字段,并容忍"服务器已建好但 PATCH 失败"的中间态——先记录 id 再报错,避免留下孤儿服务器。

七、工具发现与状态流转

资源本身不含工具清单。Onyx 通过调用服务器来学习其工具:tool_count反映已发现工具数量,last_refreshed_at记录最近一次工具列表刷新时间,status则由 Onyx 独立流转CREATED → AWAITING_AUTH → FETCHING_TOOLS → CONNECTED(或DISCONNECTED)。这与 Onyx 后端 MCP 服务器生命周期管理一致——连接建立、工具拉取、鉴权等待均由服务端异步完成,Terraform 只负责注册与配置。正因如此,文档强调:工具选择与 Craft 审批策略只对 Onyx已经见过的工具生效,配置中引用未发现工具会被拒绝。

八、导入既有服务器

资源支持terraform import,按数字 id 导入(与后端 APIGET /admin/mcp/servers/{id}的寻址方式一致):

#!/bin/sh # 按数字服务器 id 导入。凭证返回时为掩码状态,因此导入的服务器 # 不携带任何凭证:请在下次 apply 前把 api_token 或 admin_credentials # 补回配置文件中。 terraform import onyx_mcp_server.weather 3

导入后需要特别留意凭证状态:掩码机制意味着导入的服务器没有凭证记录,若不补回api_token/admin_credentials,后续 apply 可能因缺少凭证而失败或被 Onyx 拒绝(Onyx 会直接拒绝掩码值)。

九、底层 API 调用链

从 mcp_server.go 可以完整还原资源的 REST 调用链(均为/admin管理端点):

操作HTTP 方法与路径说明
创建 / 更新POST /admin/mcp/servers/createUpsertMCPServer,同一请求体通过existing_server_id区分新建与更新,返回摘要(仅 server id),完整记录需再读一次
读取GET /admin/mcp/servers/{id}GetMCPServer,404 表示不存在
PATCHPATCH /admin/mcp/server/{id}PatchMCPServer,仅补available_in_craft
删除DELETE /admin/mcp/server/{id}DeleteMCPServer,真实删除,重复删除返回 404

资源生命周期(Create/Read/Update/Delete/ImportState,见 mcp_server_resource.go)严格对应上述端点;Read遇到 404 会从 state 中移除资源,Delete同样容忍 404(幂等删除)。

十、测试验证:行为即规格

仓库中的验收测试直接印证了上述行为:

  • mcp_server_resource_test.go 验证了:无凭证服务器的默认值(auth_type = NONEauth_performer = ADMINtransport = STREAMABLE_HTTPis_public = true)、available_in_craft在 create 时通过后续 PATCH 生效、未设置的集合(groups/users/auth_template_headers)保持未设置以避免永久 diff,以及重命名、清空描述、翻转标志后的更新行为。
  • 同一文件的TestAccMCPServerResourceAPIToken验证了共享令牌的完整生命周期:创建 → 不轮换的 apply 产生空 plan → 轮换后新值生效,并断言共享令牌场景下 Onyx 自行写入的模板头为Authorization: Bearer {api_key}
  • 测试还确认了server_url只需通过结构性校验即可创建(数据库写入 + URL 结构检查,不会真正连接服务器),但必须为外部地址——这与文档中"Onyx 拒绝 loopback 地址"的约束一致。

小结

onyx_mcp_serverterraform-provider-onyx中把外部 MCP 工具接入 Onyx Agent 体系的关键资源。使用时要始终牢记三条主线:认证矩阵NONE/API_TOKEN×ADMIN/PER_USER)决定了凭证字段的合法组合,OAuth 必须走管理后台;凭证只活在配置里(掩码回读 + write-only 孪生字段 + 版本计数器轮换),state 中永远没有明文密钥;配置即权威users/groups列表删除即清空,缺省列表会被显式置空以保持一致)。掌握这些规则后,你就能把 MCP 服务器接入纳入完全声明式的 IaC 工作流。

【免费下载链接】danswerOpen Source AI Platform - AI Chat with advanced features that works with every LLM项目地址: https://gitcode.com/GitHub_Trending/da/danswer

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

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

蓝牙芯片选型避坑指南:链路预算、基带延迟与固件裁剪

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

作者头像 李华
网站建设 2026/9/10 1:34:36

VESA DSC C Model实战:从标准文档到编码解码链路跑通

简介:VESA DSC(Display Stream Compression)压缩标准是针对高分辨率、高刷新率显示场景降低传输带宽压力的核心技术,以视觉无损方式提升显示链路传输效率。资源包汇集了从 v1.1 至 v1.2b 的多个版本规范 PDF,并附带可阅…

作者头像 李华
网站建设 2026/9/10 1:34:04

Java企业产供销系统项目实战:从需求分析到系统部署

1. 项目概述与需求拆解先聊点实在的。最近几年,几乎每隔一段时间就能看到有人问“Java企业产供销系统怎么做”“毕设想做个ERP方向的项目有没有思路”,这类问题在技术社区里反复出现。我本人也带过不少新人和实习生,说实话,企业生…

作者头像 李华