Skip to content

Commit 3abe011

Browse files
docs(skills): the automation skill's OAuth recipe names a stdio MCP server's credential route as transport.credentialEnv (#30437)
Fixes #29625 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:`skills/objectstack-automation/references/integration-recipes.md` 配方 6 的括注 —— stdio MCP 服务器的凭据不再说「走 `transport.env`」,改为「静态 `auth: { type: 'bearer', credentialRef }`,放进 `transport.credentialEnv` 命名的变量,不在 `transport.env` 里写字面量」;开头段删去两句在 SKILL.md 与配方 4 已有家的复述,用来付这份文件的 token 天花板(10376 → 10363 字节,2591/2595 tokens,行数 183 → 183)。 - **为什么改**:PR #29621 落地后,`mcp` provider 把解析后的静态凭据放进子进程环境变量(`packages/connectors/connector-mcp/src/mcp-provider.ts` 的 `credentialEnvOf`);旧文字教的正是 `connector-credential-literal` 告警所反对的「凭据字面量写进元数据」路线,而这份文字经 `npx skills add` 原样装进客户项目。 - **风险与回滚**:纯教学文本,无代码、无 schema、无 eval 变化;回滚即 revert 本 commit。唯一风险在措辞:读者若把「静态 `auth`」读成 oauth2 也能上 stdio —— 括注开头仍写明 grant 类 `http` only,与 provider 的拒绝一致。 - **先看**:① `integration-recipes.md` 第 155–159 行(配方 6 新括注,风险 hunk);② 同文件第 9–10 行(删句);③ `.changeset/29625-stdio-credential-env.md`。 - **两分钟验证**:`node scripts/check-skills-token-ratchet.mjs` 预期绿;`grep -n "transport.env" skills/objectstack-automation/references/integration-recipes.md` 预期只命中一处,即新括注里的「never a literal in `transport.env`」。 - **需要你做的**:`skills/**` 是 Tier H —— `GOVERNED_APPROVERS` 账号 APPROVE 一次,席位随后落地。 ## What changes Recipe 6 of the automation skill's integration recipes said: "`mcp` (`http` only; a `stdio` server's credentials ride `transport.env`)". That parenthetical teaches the literal-in-metadata route. It now says what the `mcp` provider does on `main`: > `mcp` (`http` only; a `stdio` server takes a static `auth: { type: 'bearer', credentialRef }` in the variable `transport.credentialEnv` names, never a literal in `transport.env`) Read off the code, not the card (`packages/connectors/connector-mcp/src/mcp-provider.ts`, `credentialEnvOf` and `normalizeTransport`): - A stdio transport's `credentialEnv` names the environment variable the child is started with; the value is the resolved static credential — a `bearer` token, an `api-key` key, a `basic` password. The same spelling as `content/docs/automation/connectors.mdx` § *A stdio server's credential* and the `connector-credential-literal` advisory's hint. - "`http` only" stays true for the recipe's grant-bearing kinds: a grant-bearing `auth` on a stdio transport fails the boot, naming this static route. - A literal in `transport.env` is passed to the child as written, stored in metadata, and reported by the advisory — hence "never a literal". Paid inside the file's shrink-only token ceiling (2595) by deleting one sentence of the opening paragraph that restated two facts with surviving homes: "A spec is not a secret and ships in the package" (SKILL.md § *Integrations are declared, not coded*: "a packaged spec"; recipe 1's `spec: './src/connectors/stripe.trimmed.json'`) and "an `api` flow's hook secret goes to the deployment and to the sender" (SKILL.md's `secretRef` row: "an env var the deployment fills"; recipe 4: "The sender signs each post under `CARRIER_HOOK_SECRET`"). No re-wrap bought a line: `skills/**` is 6 added / 6 removed, net 0 lines, net −13 bytes. Also checked, unchanged: recipe 1 never said the old route; recipe 2's `// stdio needs the host's allowlist` comment still holds; neither `SKILL.md`, `evals/integrations-declared.json` nor `evals/flows-triggers-approvals.json` mentions `stdio`, `transport.env` or `credentialEnv`, so no eval asserted the old route and none changes. Changeset: `.changeset/29625-stdio-credential-env.md`, `'@objectstack/skills': patch`, carrying the two declaration lines. ## Tests Prose only; no test file changes. The gates that read this surface are under Verification. ## Acceptance notes - `packages/spec` declares no Zod schema for `providerConfig.transport.credentialEnv`; the key is judged at boot by the `mcp` provider and by the `connector-credential-literal` advisory, which is where `connectors.mdx` points too. Observed only, consistent with the transport being provider-owned; noted, not filed. - `skills/**` line budget: net 0 (budget ≤ +3). Token ceiling: 2591 of 2595 after the edit. ## Verification Head `ef7770a8`; `git status --porcelain` empty when the union ran, so every reading below is of this head's tree. `node scripts/pm/dispatch-gates.mjs --tier --repo objectstack-ai/objectstack skills/objectstack-automation/references/integration-recipes.md` → `Model tier — MANDATORY: claude-fable-5-1`, clause ① (`skills/**`, no one-line exit). This run is that tier. `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no paths; the tool took the change set from the merge-base) derived 43 families. Each ran in the foreground with its exit captured before any pipe, then `--ran` reconciled: <details><summary>`--ran` verdict: 43 derived, 38 run, 5 NOT-MEASURED, 0 UNRUN</summary> ``` Run reconciliation — 43 derived, 38 run, 5 NOT-MEASURED, 0 UNRUN. NOT-MEASURED · DERIVED (5) — your record carries exit 3 for these, the number a gate refusing its own prerequisite exits with. - pnpm --filter @objectstack/lint run check:doc-formula-expressions [recorded exit 3 on line 19 — PREREQUISITE NOT MET] - pnpm check:dts-closure [recorded exit 3 on line 29 — PREREQUISITE NOT MET] - pnpm check:dual-build-cjs-loads [recorded exit 3 on line 30 — PREREQUISITE NOT MET] - pnpm check:lean-entry-closure [recorded exit 3 on line 32 — PREREQUISITE NOT MET] - pnpm check:sourcemap-no-sources-content [recorded exit 3 on line 42 — PREREQUISITE NOT MET] ✓ dispatch-gates --ran: 43 derived famil(ies) accounted for — 38 run, 5 NOT-MEASURED (5 DERIVED from a recorded exit 3). ``` The five read a built `dist/` across the whole workspace ("Not one of the 131 workspace package(s) under . has a `dist/`"); this diff touches no package, so no build closure is owed locally and CI's `Lint & Repo Gates` carries them. Every other family exited 0. </details> <details><summary>The three gates the dispatch named, verdict lines</summary> ``` $ node scripts/check-skills-token-ratchet.mjs # exit 0, re-run on ef7770a ✓ check-skills-token-ratchet: 57 authored bundle file(s) within their ceilings; 10 generator-owned file(s) and 29 eval fixture(s) measured, not ratcheted. $ pnpm --filter @objectstack/spec run check:skill-refs # exit 0 ✅ 9 generated files in sync with packages/spec $ pnpm check:doc-authoring # exit 0 ✓ doc authoring guard: 71 published skill files clean — no internal issue-id references. ``` </details> Dogfood battery: n/a — no `!` in the title and no `(narrowing)` arm. --- _Generated by [Claude Code](https://claude.ai/code/session_012MmDsR4AZoJZyzKQcBEESh)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent b6caa14 commit 3abe011

2 files changed

Lines changed: 16 additions & 6 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the automation skill's OAuth recipe names a stdio MCP server's credential route as `transport.credentialEnv`
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
Recipe 6 of `objectstack-automation/references/integration-recipes.md` said a `stdio` server's credentials ride `transport.env` — the literal-in-metadata route the `connector-credential-literal` advisory warns against. It now says what the `mcp` provider does: the grant-bearing kinds stay `http` only; a `stdio` server takes a static `auth: { type: 'bearer', credentialRef }`, handed to the child process in the variable `transport.credentialEnv` names, never a literal in `transport.env`. Paid inside the file's token ceiling by dropping the opening paragraph's restatement of two facts that keep their homes: the packaged spec (SKILL.md § Integrations are declared, not coded) and where an `api` hook's secret goes (SKILL.md's `secretRef` row and recipe 4). No eval asserted the old route; none changes.

‎skills/objectstack-automation/references/integration-recipes.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,8 @@ the fields the answer lands in, a flow dispatching `connector_action`) and the
66
one thing the **customer's administrator** supplies: the value behind
77
`auth.credentialRef` — an environment variable of that name (wins), or a
88
value set in the running app at `PUT /api/v1/automation/connectors/credential-refs/:ref`;
9-
an unset ref leaves its instance degraded until set. A spec is not a
10-
secret and ships in the package; an `api` flow's hook secret goes to the
11-
deployment and to the sender. Keep `automation` in `requires` whenever a
12-
connector is declared. Full guide: https://objectstack.ai/docs/automation/connectors.
9+
an unset ref leaves its instance degraded until set. Keep `automation` in
10+
`requires` whenever a connector is declared. Full guide: https://objectstack.ai/docs/automation/connectors.
1311

1412
## Holds on every recipe
1513

@@ -155,8 +153,10 @@ export const LegacyErp = defineConnector({
155153
## 6 · OAuth or vendor-app token — the server runs the grant
156154

157155
Recipe 1's shape with a grant-bearing `auth`, served on `rest`, `openapi` and
158-
`mcp` (`http` only; a `stdio` server's credentials ride `transport.env`): the
159-
token is fetched per dispatch; no metadata key holds it.
156+
`mcp` (`http` only; a `stdio` server takes a static `auth: { type: 'bearer',
157+
credentialRef }` in the variable `transport.credentialEnv` names, never a
158+
literal in `transport.env`): the token is fetched per dispatch; no metadata
159+
key holds it.
160160

161161
```ts
162162
// PKCE always on; the ref is the client secret, omitted for a public client.

0 commit comments

Comments
 (0)