Skip to content

Commit 28f2e99

Browse files
docs(skills): the automation mcp recipe teaches the providerConfig.toolsSnapshot pin (#28597)
Fixes #28582 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`skills/objectstack-automation/references/integration-recipes.md` 的 `mcp` 配方(第 2 则,GitHub 远程 MCP)新增一段,教 `providerConfig.toolsSnapshot` 钉住工具定义:钉的是什么、钉名必须在 `include` 里、摘要从哪来(`computeMcpToolsSnapshot`,或占位钉后读降级原因里打印的实际摘要)、钉不匹配时作者看到什么,并链接 connectors 文档的 pin 小节;配套一个 `@objectstack/skills` 的 `patch` changeset。 - 为什么改:PR #28515 落地了 pin 但刻意没动 `skills/**`(Tier H);写应用的 agent 先读 skills,配方里没有 pin 就等于每个 MCP 应用都不设防,服务端悄悄改一个工具的含义不会被发现。 - 风险与回滚:只改一份已发布的技能参考文件与一个 changeset,不改代码、schema、键或行为;回滚即 revert 这一个 commit。教学内容逐句对照 `packages/connectors/connector-mcp/src/mcp-provider.ts` 与 `tool-snapshot.ts` 在本基点的实现写成,没有运行时不兑现的键。 - 先看:① `integration-recipes.md` 第 2 则代码块后的新段(唯一的新增内容);② 同文件为付 token 棘轮而删掉的几处重复(Holds 的 `input` 条、第 4 则的签名格式句、第 6 则的连接细节、WeChat 条的括号)——每处在 SKILL.md、objectstack-platform 或 connectors 文档页另有原文;③ `.changeset/28582-skills-mcp-tools-snapshot.md`。 - 两分钟验证:`node scripts/check-skills-token-ratchet.mjs`,预期看到 `integration-recipes.md is 2487 tokens (ceiling 2488; headroom 1)`,且结尾 `57 authored bundle file(s) within their ceilings`。 - 需要你做的:`skills/**` 是 Tier H,本 PR 等待维护者批准或合入;无其他动作。 ## What changes - `skills/objectstack-automation/references/integration-recipes.md`, recipe 2 (remote MCP server): one paragraph after the code block teaching `providerConfig.toolsSnapshot` — tool name → sha256 of the tool's `tools/list` definition; a pinned name `include` leaves out is refused at boot; digests from `computeMcpToolsSnapshot(tools)` (`@objectstack/connector-mcp`) or, with a 64-hex placeholder pin, from the served digest the degraded reason prints; a pinned tool served differently degrades the instance with no actions (`CONNECTOR_UPSTREAM_CHANGED`), one no longer listed too, with the reason on `GET /api/v1/automation/connectors` naming each tool and the digest it now serves; link to the connectors page's pin section. Every clause is read off `packages/connectors/connector-mcp/src/mcp-provider.ts` (`parseToolsSnapshot`, the include-coverage refusal, `compareToolsSnapshot`, `toolsSnapshotDriftError`, `pinnedToolsHiddenError`) and `tool-snapshot.ts` at the base commit `cd065d36`; no example block is added. - Paid for under the skills token ratchet (the file was at 2488/2488, headroom 0) by deleting restatements in the same file, each with a surviving home: the Holds `input` bullet's `{…}`-token refusal and "`os validate` does not judge the `inputSchema` fit yet" (verbatim in SKILL.md's `connector_action` row); recipe 4's `x-objectstack-signature` format (SKILL.md § Inbound webhook, *Signature*; the recipe now points there); the intro's `plugins:` / `requires` reminder (objectstack-platform SKILL.md, the `requires` table and its "Keep `automation` whenever `plugins:` lists a connector" bullet); recipe 6's "(organization admins by default)", "for the admin to open" and "`GET` reads the status; `DELETE` revokes" (the connectors page); recipe 2's "folded into the request headers" aside and recipe 4's `// no trigger user` (recipe 1's own comment); the WeChat bullet's `alipay-v3` aside (the Holds `auth` list). The file lands at 2487 tokens against its 2488 ceiling; no ceiling moves. - `.changeset/28582-skills-mcp-tools-snapshot.md`: `@objectstack/skills` `patch` (the `skills/**` tree ships as that package), carrying `Clause-②: no` and `Metadata-protocol: none`. ### Skills readings (`skills/**` diff — line and token counts, before → after) | Reading | Before (`cd065d36`) | After (`64882e22`) | |:--|:--|:--| | `integration-recipes.md`, lines | 176 | 178 | | `integration-recipes.md`, bytes / ratchet tokens | 9949 / 2488 (ceiling 2488, headroom 0) | 9946 / 2487 (ceiling 2488, headroom 1) | | Whole package `skills/**`, lines (every tracked file) | 19546 | 19548 | | All `SKILL.md` files, lines (sum of 11) | 4587 | 4587 (untouched) | | Whole shipped bundle, ratchet tokens (`check-skills-token-ratchet` bundle total) | 221928 | 221927 | Net content is a small deletion (−3 bytes, −1 token); the two added lines are wrap, not growth. ## Tests - No package code changes: the dependency closure (①) is empty and no package test or typecheck is owed (②). No tested behaviour changed, so no ablation. - The skills token ratchet measured this edit in both directions on this tree: at an intermediate state it was red — `integration-recipes.md is 2496 tokens; the ratchet ceiling is 2488 (over by 8)`, exit 1 — and after the last cut it is green at 2487/2488, exit 0 (log below). That red→green is the reading that the gate prices this file. - Control-character scan of the two edited files: `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` → no match (exit 1). ## Acceptance notes - Judgement on the card's open question ("too niche for a recipe?"): not too niche. The pin is one key beside `include`, the recipe already shows `include`, and an app author has a no-code path to the digests — pin a 64-hex placeholder, boot, and read the served digest off the degraded reason on `GET /api/v1/automation/connectors` (`toolsSnapshotDriftError` prints `tool 'X' changed (it now hashes to …)`). `computeMcpToolsSnapshot(tools)` is exported for anyone with a tool list in code; there is no CLI command that prints a server's digests today (a finding, not a defect: the docs page says the same and the placeholder route is what the runtime delivers). - Deliberately not taught in the recipe (docs-only detail, same file budget): the canonical-JSON definition of the digest, the retry backoff on which a degraded instance recovers, and the credential-path nuance for a pinned tool the server no longer lists on an instance whose `auth` reached the server (`pinnedToolsHiddenError`, a 403 naming both causes). The recipe says such a tool "degrades it too" without naming the code, which is true on both paths; the linked pin section carries the rest. - Observation, noted and not filed (`carrier: none`): `connectors.mdx`'s failure-mode table row for the pin folds the "no longer listed" case under `CONNECTOR_UPSTREAM_CHANGED`, while the prose above it and `mcp-provider.ts` route that case through the credential-rejected path when the instance's credential reached the server; the prose is correct and the row is a summary, so this is read as a precision note, not a contract violation. - `Skills-docs:` line: not owed (`Clause-②: no`, `Metadata-protocol: none`). ## Verification Final head `64882e22` (`git rev-parse --short HEAD` at the time of the sweep; the working tree had no further edits). Gate list derived with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on this diff (2 paths vs merge base `cd065d36`), every command run with its exit captured before any pipe, then reconciled with `--ran`. - Reconciliation verdict: `✓ dispatch-gates --ran: 44 derived famil(ies) accounted for — 40 run, 4 NOT-MEASURED (4 DERIVED from a recorded exit 3).` — 0 UNRUN. - 40 run, all exit 0, including the skills gates: `check-skills-token-ratchet` (`check-skills-token-ratchet: skills/objectstack-automation/references/integration-recipes.md is 2487 tokens (ceiling 2488; headroom 1).`), `check:skill-identifier-liveness`, `check:skill-frame-sync`, `check:skill-compatibility`, `@objectstack/spec check:skill-refs`, `check:nul-bytes`, `check:doc-authoring`, `check:pm-skill-id-lint`, `check:empty-changeset`, `check:changeset-no-major`, `check:adr-0087-registration`, `check:closing-keyword-parity`. `@objectstack/lint check:doc-formula-expressions` first answered exit 3 (its `@objectstack/formula` / `@objectstack/lint` dist absent in a fresh worktree); after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` under `os-verify-lock.sh` (`VERDICT command-exit 0`) it re-ran green, exit 0. - NOT MEASURED locally, 4 (exit 3, `PREREQUISITE NOT MET` — each reads the `dist/` of the whole 129-package closure, which this diff does not touch): `check:dts-closure`, `check:dual-build-cjs-loads`, `check:lean-entry-closure`, `check:sourcemap-no-sources-content`. CI runs them after its build; read them off this PR's check-runs. - The intermediate ratchet red that proves the gate prices this file: `integration-recipes.md is 2496 tokens; the ratchet ceiling is 2488 (over by 8)` (exit 1), then the final green above (exit 0). - Not run locally, by rule: `pnpm lint`, `test:repo` and the rest of the repo-wide farm (CI's `Lint & Repo Gates`, `Test Core`); no package test or typecheck is owed by a diff that touches no package. - Local-only families (`check:dev-prereqs`, `check:skill-frame-freshness`): the derivation did not name them and the diff touches no four-axis frame file or `packages/spec` build, so they were not run. <details> <summary>Run record (`ran.list`, the input to `--ran`): 44 commands with exit codes</summary> ```text node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0 node scripts/check-adr-0087-registration.mjs --self-test :: exit 0 node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0 node scripts/check-closing-keyword-parity.mjs :: exit 0 node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0 node scripts/check-comment-mask-corpus.mjs :: exit 0 node scripts/check-doc-route-spelling.mjs --advisory :: exit 0 node scripts/check-doc-route-spelling.mjs --self-test :: exit 0 node scripts/check-dts-emitted.mjs --self-test :: exit 0 node scripts/check-empty-changeset.mjs --base origin/main :: exit 0 node scripts/check-empty-changeset.mjs --self-test :: exit 0 node scripts/check-skills-token-ratchet.mjs :: exit 0 node scripts/check-skills-token-ratchet.mjs --self-test :: exit 0 node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0 node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0 node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0 node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0 node scripts/release-pending-publish.mjs --self-test :: exit 0 pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 pnpm --filter @objectstack/spec run check:skill-refs :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:changeset-gate-self-tests :: exit 0 pnpm check:ci-filter-parity :: exit 0 pnpm check:closed-ceremony-text :: exit 0 pnpm check:corpus-claim-drift :: exit 0 pnpm check:cross-package-test-inputs :: exit 0 pnpm check:doc-authoring :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:dts-closure :: exit 3 pnpm check:dual-build-cjs-loads :: exit 3 pnpm check:gitlink-declared :: exit 0 pnpm check:lean-entry-closure :: exit 3 pnpm check:nul-bytes :: exit 0 pnpm check:pm-changeset-deadline-census :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-skill-id-lint :: exit 0 pnpm check:published-files :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:role-word :: exit 0 pnpm check:skill-compatibility :: exit 0 pnpm check:skill-frame-sync :: exit 0 pnpm check:skill-identifier-liveness :: exit 0 pnpm check:sourcemap-no-sources-content :: exit 3 pnpm check:watch-hint-literal :: exit 0 ``` </details> <details> <summary>`check-skills-token-ratchet` final lines on `64882e22`</summary> ```text ✓ check-skills-token-ratchet: skills/objectstack-automation/references/integration-recipes.md is 2487 tokens (ceiling 2488; headroom 1). ✓ 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. ``` </details> --- _Generated by [Claude Code](https://claude.ai/code/session_017xRBcVBKAFk1ntXSjvPt7U)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent a798ae8 commit 28f2e99

3 files changed

Lines changed: 53 additions & 39 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): the automation skill's `mcp` recipe teaches the `providerConfig.toolsSnapshot` pin
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** `providerConfig.toolsSnapshot` (a sha256 pin per MCP tool, checked on every materialization) shipped in `@objectstack/connector-mcp` and on the connectors page, but no published skill named it. An agent authoring an MCP connector app from the skills declared `include` alone, with no protection against a server silently rewriting a tool's description or `inputSchema`.
11+
- **Now.** `skills/objectstack-automation/references/integration-recipes.md`, recipe 2 (remote MCP), gains one paragraph: what `toolsSnapshot` pins and that a pinned name `include` leaves out is refused at boot; where the digests come from (`computeMcpToolsSnapshot(tools)`, or the served digest the degraded reason prints against a placeholder pin); what an author sees when a pin no longer matches (the instance degraded with no actions, `CONNECTOR_UPSTREAM_CHANGED`, the reason on `GET /api/v1/automation/connectors` naming each tool and the digest it now serves), and the link to the connectors page's pin section.
12+
- The file stays under its token ceiling: the paragraph is paid for by deleting restatements in the same file that keep a home elsewhere (the `connector_action` row of `SKILL.md`, its `api`-trigger *Signature* bullet, the objectstack-platform skill's `plugins:`/`requires` rule, and the connectors page). No ceiling moves, no frontmatter, schema, key or behaviour changes.

‎content/docs/automation/connectors.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -866,7 +866,7 @@ retry):
866866
| Instance `name` collides with a plugin-registered connector | **Hard boot error** — there is no silent precedence between the two worlds |
867867
| stdio transport without host opt-in | **Hard boot error** with the `declarativeStdio` fix inline — a security rejection is never retried |
868868
| MCP server unreachable / `tools/list` fails; remote spec URL unreachable or transiently failing (408/429/5xx) | **Not fatal**: the instance registers **degraded** (visible on `GET /api/v1/automation/connectors` with the reason), dispatches fail with that reason, and the platform retries on a 5s→5min backoff — recovery is automatic |
869-
| An `mcp` instance's `toolsSnapshot` does not match what the server serves (a pinned tool's description or `inputSchema` changed, or a pinned tool is no longer listed) | **Not fatal**: the instance registers **degraded** with no actions, its reason naming each tool and the digest it now serves, and the logs say "serves a tool definition that differs from its toolsSnapshot" (`CONNECTOR_UPSTREAM_CHANGED`); it is re-checked on the same backoff and recovers once the server serves the pinned definitions again — or set the pin to the reviewed digest |
869+
| An `mcp` instance's `toolsSnapshot` does not match what the server serves (a pinned tool's description or `inputSchema` changed; or a pinned tool is no longer listed, on an instance whose credential did not reach the server) | **Not fatal**: the instance registers **degraded** with no actions, its reason naming each tool and the digest it now serves, and the logs say "serves a tool definition that differs from its toolsSnapshot" (`CONNECTOR_UPSTREAM_CHANGED`); it is re-checked on the same backoff and recovers once the server serves the pinned definitions again — or set the pin to the reviewed digest. A pinned tool no longer listed on an instance whose credential **did** reach the server (its `auth` over http) takes the credential row above instead: a server may hide tools from a credential, so the reason names both causes (fix the credential and set it again, or update the pin), a stored credential is marked `rejected`, and no retry runs against the same value |
870870
| A retry of a degraded instance reaches the upstream and meets a configuration fault (an `include` naming a tool or operation the upstream does not offer; on an `mcp` instance that sends a static credential, the credential row above applies instead) | **Not fatal**: the instance stays **degraded**, now with the configuration reason, and is **not retried** — dispatches fail saying to edit its declaration; an edit of the entry (or a restart) re-attempts it |
871871
| Wrong spec URL (other 4xx) or unparseable document | Configuration fault — **hard boot error** |
872872
| An `openapi` operation's parameter declares a `style` OpenAPI 3.x does not define for its location (`deepObject` on a path parameter, `form` on a header) | Configuration fault — **hard boot error** naming the operation and the parameter; correct the document or leave the operation out of `include` |

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

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,27 @@ one thing the **customer's administrator** supplies: the value behind
88
value set in the running app at `PUT /api/v1/automation/connectors/credential-refs/:ref`;
99
an unset ref leaves its instance degraded until set. A spec is not a
1010
secret and ships in the package; an `api` flow's hook secret goes to the
11-
deployment and to the sender. Scaffolded apps list the three executors in
12-
`plugins:`; keep `automation` in `requires` whenever a connector is declared. Full guide: https://objectstack.ai/docs/automation/connectors.
11+
deployment and to the sender. Keep `automation` in `requires` whenever a
12+
connector is declared. Full guide: https://objectstack.ai/docs/automation/connectors.
1313

1414
## Holds on every recipe
1515

1616
- Actions come from the upstream (an OpenAPI `operationId`, an MCP tool name,
1717
the `rest` provider's single `request`) — authored on the entry, they are refused.
18-
- `connectorConfig.input` is a value-slot map like `subflow` `input`: a top-level
19-
CEL envelope is evaluated, any other value kept as written. `registerFlow` and
20-
`os validate` refuse a `{…}` token at `connectorConfig.input.<key>`: a value
21-
from the record is a CEL envelope. Before dispatch the result is judged
22-
against the action's `inputSchema`: `required`, `type` (with OpenAPI
23-
`nullable`), `properties`, `items`; `null` on an optional parameter is
24-
absent. A breach fails the step `VALIDATION_ERROR` (`fault`-routable) naming
25-
each path, nothing sent. Text is never converted: cast a string-held id,
26-
`path: { dialect: 'cel', source: '{"petId": int(record.vendor_id)}' }`.
27-
`os validate` does not judge the `inputSchema` fit yet.
18+
- `connectorConfig.input` is a value-slot map (SKILL.md's `connector_action`
19+
row): a top-level CEL envelope is evaluated, any other value kept as written,
20+
so a value from the record is a CEL envelope. The pre-dispatch `inputSchema`
21+
judgement honours OpenAPI `nullable`, reads `null` on an optional parameter
22+
as absent and names each breached path. Text is never converted: cast a
23+
string-held id, `path: { dialect: 'cel', source: '{"petId": int(record.vendor_id)}' }`.
2824
- The answer lands as `<nodeId>.<key>` (`rest` / `openapi`: `{ status, ok, body }`;
2925
`mcp`: `{ ok, content, structuredContent? }`), read from a later node's value slot.
3026
- `auth`, each variant with a `credentialRef`: `bearer`; `api-key` (`headerName`,
31-
default `X-API-Key`, or `paramName` for a query-string key); `basic` (`username`
27+
or `paramName` for a query-string key); `basic` (`username`
3228
stays in metadata); `signed` on `openapi` / `rest` (`scheme`
3329
`aliyun-acs3-hmac-sha256`, `tencentcloud-tc3-hmac-sha256` or `alipay-v3`,
34-
`keyId`) signs each request; `oauth2` / `app-credential`: recipe 6. A secret in a url path has no variant — use a
30+
`keyId`) signs each request — WeChat Pay has no scheme yet: blocked;
31+
`oauth2` / `app-credential`: recipe 6. A secret in a url path has no variant — use a
3532
token-authenticated connector. `authentication: { token }` is refused at `os validate`.
3633
- `defineConnector`: `@objectstack/spec/integration`; `defineFlow`: `@objectstack/spec`; both register in `defineStack`.
3734

@@ -47,14 +44,14 @@ export const StripeConnector = defineConnector({
4744
// crm_account gains `stripe_available` (currency) and `stripe_synced_at` (datetime).
4845
export const StripeBalanceSweep = defineFlow({
4946
name: 'stripe_balance_sweep', label: 'Stripe balance → house account',
50-
type: 'schedule', runAs: 'system', // a scheduled run has no trigger user
47+
type: 'schedule', runAs: 'system',
5148
nodes: [
5249
{ id: 'start', type: 'start', label: 'Nightly 03:00',
5350
config: { schedule: { type: 'cron', expression: '0 3 * * *' } } },
5451
{ id: 'bal', type: 'connector_action', label: 'GET /v1/balance',
5552
connectorConfig: { connectorId: 'stripe', actionId: 'GetBalance', input: {} } },
5653
{ id: 'write', type: 'update_record', label: 'Write balance', config: {
57-
objectName: 'crm_account', filter: { is_house_account: true }, // multi-tenant: pin the organization too
54+
objectName: 'crm_account', filter: { is_house_account: true },
5855
fields: { stripe_available: { dialect: 'cel', source: 'bal.body.available[0].amount / 100.0' },
5956
stripe_synced_at: { dialect: 'cel', source: 'isoDatetime(now())' } } } },
6057
{ id: 'end', type: 'end', label: 'End' },
@@ -66,8 +63,8 @@ export const StripeBalanceSweep = defineFlow({
6663

6764
`requires: ['automation', 'triggers', 'job']`. Convert Swagger 2.0 or YAML and
6865
dereference every `$ref` offline, in-document ones included: the loader skips a
69-
`$ref` parameter, request body or response, and the derived input/output schema
70-
silently loses it. `providerConfig.include` allowlists operation keys as the
66+
`$ref` parameter, request body or response and the derived schema silently
67+
loses it. `providerConfig.include` allowlists operation keys as the
7168
`mcp` provider's does (absent = every operation; a non-array or unknown entry is
7269
fatal).
7370

@@ -77,23 +74,35 @@ fatal).
7774
export const GitHubMcp = defineConnector({
7875
name: 'github_mcp', label: 'GitHub (remote MCP)', type: 'api', provider: 'mcp',
7976
providerConfig: {
80-
transport: { kind: 'http', url: 'https://api.githubcopilot.com/mcp/', // `stdio` spawns a local process:
81-
headers: { 'X-MCP-Readonly': 'true' } }, // denied unless the host allowlists the command
77+
transport: { kind: 'http', url: 'https://api.githubcopilot.com/mcp/', // `stdio` needs the host's allowlist
78+
headers: { 'X-MCP-Readonly': 'true' } },
8279
include: ['search_issues'],
8380
},
84-
auth: { type: 'bearer', credentialRef: 'GITHUB_MCP_TOKEN' }, // folded into the request headers
81+
auth: { type: 'bearer', credentialRef: 'GITHUB_MCP_TOKEN' },
8582
});
8683
// Weekly flow: connector_action { connectorId: 'github_mcp', actionId: 'search_issues',
8784
// input: { query: 'repo:acme/app is:open label:p0' } } → update_record on `eng_kpi`
8885
// fields: { open_p0: { dialect: 'cel', source: 'p0.content' } } (a `json` field).
8986
```
9087

88+
`toolsSnapshot` pins each tool the app depends on: tool name → sha256 of its
89+
`tools/list` definition; a pinned name `include` leaves out is refused at boot.
90+
Digests: `computeMcpToolsSnapshot(tools)` (`@objectstack/connector-mcp`), or a
91+
64-hex placeholder pin, then the served digest from the degraded reason. The
92+
instance degrades with no actions; the `GET /api/v1/automation/connectors` reason
93+
names each tool: a pinned tool served differently is `CONNECTOR_UPSTREAM_CHANGED`
94+
with the digest it now serves (review, re-pin); one no longer listed is
95+
the credential path when the instance's credential reached the server, as this
96+
`bearer` does — the stored credential marked `rejected`: fix the credential and
97+
set it again, or update the pin — and `CONNECTOR_UPSTREAM_CHANGED` otherwise. Rest:
98+
https://objectstack.ai/docs/automation/connectors#pinning-tools-toolssnapshot.
99+
91100
## 3 · Plain REST API — `provider: 'rest'`, the one `request` action
92101

93102
```ts
94103
export const WarehouseApi = defineConnector({
95104
name: 'warehouse_api', label: 'Warehouse API', type: 'api', provider: 'rest',
96-
providerConfig: { baseUrl: 'https://wms.example.com', defaultHeaders: { Accept: 'application/json' } },
105+
providerConfig: { baseUrl: 'https://wms.example.com' },
97106
auth: { type: 'api-key', credentialRef: 'WMS_API_KEY', headerName: 'X-Api-Key' },
98107
});
99108
// connector_action { connectorId: 'warehouse_api', actionId: 'request',
@@ -107,7 +116,7 @@ export const WarehouseApi = defineConnector({
107116
```ts
108117
export const CarrierCallback = defineFlow({
109118
name: 'carrier_callback', label: 'Carrier status callback', type: 'api',
110-
runAs: 'system', // no trigger user
119+
runAs: 'system',
111120
nodes: [
112121
{ id: 'start', type: 'start', label: 'POST …/hooks/carrier_callback/carrier',
113122
config: { hookId: 'carrier', secretRef: 'CARRIER_HOOK_SECRET' } },
@@ -121,10 +130,9 @@ export const CarrierCallback = defineFlow({
121130
```
122131

123132
`requires: ['automation', 'triggers', 'queue']`. The sender signs each post
124-
`x-objectstack-signature: t=<unix>,v1=<hex>` — HMAC-SHA256 of `<t>.<raw body>`
125-
under `CARRIER_HOOK_SECRET`. The door has no unsigned form and verifies no other
126-
scheme, so a vendor whose webhook signs its own header cannot post here today:
127-
poll its status (recipe 1's shape) until vendor schemes verify.
133+
under `CARRIER_HOOK_SECRET` (SKILL.md *Signature* bullet). The door verifies no
134+
other scheme: a vendor whose webhook signs its own header cannot post here
135+
today — poll its status (recipe 1's shape) until vendor schemes verify.
128136

129137
## 5 · Vendor with no machine-readable spec — write the document by hand
130138

@@ -162,15 +170,9 @@ auth: { type: 'app-credential', vendor: 'feishu', appId: 'cli_a1b2', credentialR
162170

163171
`client_credentials` and `app-credential` dispatch once the ref is set.
164172
`authorization_code` is degraded "not connected" until an administrator holding
165-
`manage_connector_connections` (organization admins by default) connects it,
166-
once per organization, from a signed-in session (an agent or API key is refused):
167-
`POST /api/v1/automation/connectors/:name/connection` answers `{ authorizationUrl }`
168-
for the admin to open; the vendor redirects to `GET …/connectors/oauth/callback`.
169-
`GET` reads the status; `DELETE` revokes. Endpoints are `https://`; an
170-
`authorization_code` instance on `mcp` may omit them and `clientId`, discovered at connect.
173+
`manage_connector_connections` connects it, once per organization, from a
174+
signed-in session (an agent or API key is refused):
175+
`POST /api/v1/automation/connectors/:name/connection` answers `{ authorizationUrl }`;
176+
the vendor redirects to `GET …/connectors/oauth/callback`. An `authorization_code`
177+
instance on `mcp` may omit the `https://` endpoints and `clientId`, discovered at connect.
171178
Still never park a short-lived token behind a `bearer` ref.
172-
173-
## Not yet — mark the integration blocked, write no plugin
174-
175-
- **WeChat Pay**: no `signed` scheme yet (Alipay's is `alipay-v3`, which also
176-
verifies responses).

0 commit comments

Comments
 (0)