Skip to content

Commit 5e99bd9

Browse files
docs(adr): ADR-0097 "Upstream availability" states the mcp credential-rejection carve-out (fatal configuration fault, never retried) (#26985)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9X1enG32HTqd5EwZdc8niqU)_ Fixes #26809 Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:ADR-0097「Upstream availability」一节追加一条带日期的修订注(append-only):MCP 服务器**拒收凭据**(connect / handshake / `tools/list` 收到 HTTP 401 / 403)是**配置错误**——启动即失败、reload 时跳过、不重试;原句不改。文件头 `**Amended by**` 行同步补一条目。 - **为什么改**:PR #26798(#26709 第 3(b) 项)已让 `mcp` provider 这样分类,但 ADR 原句仍说「所有 connect / `tools/list` 失败都归为 unavailable(降级重试)」。读 ADR 的 agent / 运维会得出「凭据错了会自动重试」的错误结论,而运行时实际直接拒绝启动。 - **风险与回滚**:仅 `docs/adr/0097-declarative-connector-instances.md` 一个文件、+20/-1 行,不发布、无 changeset。回滚即 revert 本 PR。 - **先看**:`docs/adr/0097-declarative-connector-instances.md` 的修订注(「Upstream availability」节 `mcp` 段落之后),再看第 8 行 `**Amended by**` 新条目。 - **两分钟验证**:`pnpm check:adr-symbol-anchors && pnpm check:adr-index`,预期两者绿;`git grep -n "classifies connect" -- docs/adr/0097*` 仍恰好 1 处(原句未动)。 - **需要你做的**:批准本 PR(`docs/adr/**` 受管面 Tier H,席位不 ready / 不 arm / 不入队)。 ## What changes One file, `docs/adr/0097-declarative-connector-instances.md`: - **A dated, append-only amendment note** under "Upstream availability: degrade, don't die", directly after the paragraph that says the `mcp` provider classifies connect / `tools/list` failures as unavailable. The note states the one exception past transport validation: a credential the MCP server rejects (HTTP 401 / 403 on connect, handshake or `tools/list`) is a configuration fault, thrown plain — fatal at boot, a skipped entry on reload, never `CONNECTOR_UPSTREAM_UNAVAILABLE`, never retried — on the fatal side of the section's own split beside an unresolvable `credentialRef` and the `openapi` provider's non-retryable `4xx` on its spec fetch. It says why (retrying cannot make a wrong, missing or under-scoped credential valid; a degraded instance retried with backoff reads as "server down") and how the rejection is told apart (the MCP SDK's own `StreamableHTTPError` 401 / 403 or `UnauthorizedError` in the `cause` chain; a JSON-RPC error merely carrying code 401 stays operational). The original sentence is **not rewritten** (AGENTS.md Prime Directive 13). - **A second entry on the header's existing `**Amended by**` line** pointing at that note, so a reader of the header learns where the record was amended, as it already does for ADR-0143. Every claim in the note is read off `origin/main`, not invented: the classification and its reason from `packages/connectors/connector-mcp/src/mcp-provider.ts#credentialRejectionStatus` and `#createMcpProviderFactory` (the catch branch and its docblock), the "skipped on reload" half from this ADR's own "Runtime reconcile" section, and the real-server pin from `packages/connectors/connector-mcp/src/mcp-http-discovery-faults.test.ts` (`credential rejection — real Streamable-HTTP server`: 401 and 403 throw plain and are sent once; a declarative instance answered 401 fails `kernel.bootstrap()` with no degraded reason and no retry). Anchors are `path#symbol` as the ADR symbol-anchor gate requires (no `file:line`). The note cites #26709 item 3(b) and PR #26798 as the card asks. ## Tests No code changes, no tests added or edited. No forced test edits. ## Acceptance notes - The card body asks for the note to cite "#26709 item 3(b) and the PR"; the note's heading carries both, matching this file's existing `Amendment note (date, source)` convention and ADR-0092 / ADR-0099's non-ADR amendment notes. - The card delta asked for "the Status-line `Amended` entry if the file's convention has one". This file's convention is the `**Amended by**:` header line (ADR-0005 carries several comma-separated entries there); the `**Status**:` line itself is untouched, so `check:adr-index` still reads the record as `Accepted` (the index reads the status keyword earliest in that line, and `Amended` is in its vocabulary — adding it to the Status line could have re-keyed the record). - `skills/**` is not touched: no skill page teaches the `openapi` or `mcp` provider (PR #26798's body measured 0 hits for the provider spellings under `skills/`), so no skill text owes a mirror of this note. - Not published (`docs/adr/**` is on the changeset fast lane): no changeset; `skip-changeset` is declared on the `pr_create` stroke. ## Verification Head `f15b7824` (worktree `objectstack-issue-26809`, base `origin/main` 7bb8084, merge base 7bb8084). <details><summary>Derived gates — node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (21 commands) and --ran reconciliation</summary> ``` 21 command(s) derived (13 by path, 8 whole-tree); exit codes captured per command before any pipe (runner: bash -c CMD < /dev/null; EXIT recorded): 20 exit 0 on the first pass; 1 exit 3 (PREREQUISITE NOT MET, nothing measured): pnpm --filter @objectstack/lint run check:doc-formula-expressions — needs @objectstack/formula + @objectstack/lint dist fix applied: os-verify-lock: VERDICT command-exit 0 · held the lock 91s (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint, 4 tasks) rerun: pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 check:adr-symbol-anchors ✅ 2724 anchors across 145 records resolve — 592 symbol (566 declaration, 26 literal), 2085 file-level, 47 cross-repo, 6 exempt, 31 continuation. 0 line anchors survive check:adr-index adr-index --report: OK — docs/adr/README.md is current, every record parsed, self-test 46 assertions green check:adr-anchors OK (91 anchored file(s), every governing ADR still referenced; 139 decision number(s) …) check:adr-links, check:doc-authoring, check:nul-bytes, check:pm-governed-merges, check:pm-prior-rulings, check:closed-ceremony-text, check:gitlink-declared, check:ci-filter-parity, check:cross-package-test-inputs, check:driver-memory-census, check:refd-timer-probe, check:watch-hint-literal, check-closing-keyword-parity (+ --self-test), check-comment-mask-corpus, plan-test-core-shards --self-test: exit 0 --ran (node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt): ✓ dispatch-gates --ran: 21 derived famil(ies) accounted for — 21 run, 0 NOT-MEASURED (a DERIVED zero — all 21 recorded an exit code and none of them is 3). ``` </details> <details><summary>Tier (dispatch-gates --tier)</summary> ``` Model tier — MANDATORY: claude-fable-5-1 (derived from the file surface, not recalled). - docs/adr/0097-declarative-connector-instances.md ⇢ 'docs/adr/**' … no one-line exemption Changed lines — 21 (+20 / -1; generated files INCLUDED) vs the human-merge threshold 3000: under. ``` </details> Not built, not tested locally: the diff touches no package (local verification scope ① / ② are empty); `pnpm lint` and every whole-tree family are CI's — see this PR's check-runs. ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. (no code change) - [x] 2. Every name I added uses the casing for its kind. (none added) - [x] 3. No formatter ran; `pnpm lint` is CI's (no lintable file changed). - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. Nothing publishes: no changeset, `skip-changeset`; both declaration lines are above. - [x] 6. `packages/spec` touched: n/a. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English (the 维护者速读 block is the required Chinese business view). Card thread: `session_01ADHkmSPesP5MpCBMrkkQw5`, dispatched by `domain:skills#1` (`session_01ADHkmSrRBdu9MdC6fc33Uu`). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01ADHkmSPesP5MpCBMrkkQw5 --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmSPesP5MpCBMrkkQw5)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent eae9a58 commit 5e99bd9

1 file changed

Lines changed: 20 additions & 1 deletion

File tree

‎docs/adr/0097-declarative-connector-instances.md‎

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
**Builds on**: [ADR-0015](./0015-external-datasource-federation.md) (open mechanism / enterprise lifecycle split), [ADR-0018](./0018-unified-node-action-registry.md) (`connector_action` baseline dispatch + `engine.registerConnector()`), [ADR-0022](./0022-connectors-vs-messaging-channels.md) (Connector = transport/integration mechanism), [ADR-0023](./0023-openapi-to-connector-generator.md) (OpenAPI → Connector generator), [ADR-0024](./0024-mcp-connectors.md) (MCP servers as connectors)
66
**Tracking**: framework#2977 (supersedes the interim descriptor-only contract from framework#2612)
77
**Consumers**: `@objectstack/spec` (`stack.zod.ts`, `integration/connector.zod.ts`), `@objectstack/service-automation` (boot audit → provider binding), `@objectstack/connector-openapi`, `@objectstack/connector-mcp`, `@objectstack/connector-rest`, the Studio flow palette, AI authoring (stack generation)
8-
**Amended by**: [ADR-0143](./0143-outbound-connector-auth-is-open-tier.md) (2026-10-27) — OAuth2, the vendor app-credential exchange and request signing join `ConnectorInstanceAuthSchema` in the open tier, and those kinds resolve per dispatch; see the notes under §3 and under "Open / enterprise line".
8+
**Amended by**: [ADR-0143](./0143-outbound-connector-auth-is-open-tier.md) (2026-10-27) — OAuth2, the vendor app-credential exchange and request signing join `ConnectorInstanceAuthSchema` in the open tier, and those kinds resolve per dispatch; see the notes under §3 and under "Open / enterprise line". PR #26798 (2026-10-27, #26709 item 3(b)) — a credential the MCP server rejects (HTTP `401` / `403`) is a fatal configuration fault, not upstream-unavailable; see the note under "Upstream availability".
99

1010
---
1111

@@ -190,6 +190,25 @@ dispatch was rejected: MCP actions ARE the server's `tools/list`, so a
190190
connector materialized without connecting would register a zero-action def —
191191
exactly the plausible-but-dead shape this ADR exists to kill.
192192

193+
> **Amendment note (2026-10-27, #26709 item 3(b) — PR #26798).** The sentence
194+
> above has one exception past transport validation: a credential the MCP
195+
> server **rejects** — an HTTP `401` / `403` on connect, handshake or
196+
> `tools/list` — is a **configuration fault**, thrown plain: fatal at boot, a
197+
> skipped entry on reload, never `CONNECTOR_UPSTREAM_UNAVAILABLE` and never
198+
> retried. It sits on the fatal side of this section's own split, beside an
199+
> unresolvable `credentialRef` and the `openapi` provider's non-retryable
200+
> `4xx` (a `401` included) on its spec fetch: retrying cannot make a wrong,
201+
> missing or under-scoped credential valid, and a degraded instance retried
202+
> with backoff reads as "server down", hiding the misconfiguration the author
203+
> has to fix. The rejection is told apart by the MCP SDK's own HTTP-transport
204+
> errors only — a `StreamableHTTPError` with status 401 / 403, or an
205+
> `UnauthorizedError`, in the error's `cause` chain
206+
> (`packages/connectors/connector-mcp/src/mcp-provider.ts#credentialRejectionStatus`, `#createMcpProviderFactory`);
207+
> a JSON-RPC error that merely carries a numeric code 401 stays operational, as
208+
> does every other connect / `tools/list` failure. Pinned against a real
209+
> Streamable-HTTP server in
210+
> `packages/connectors/connector-mcp/src/mcp-http-discovery-faults.test.ts`.
211+
193212
The `openapi` provider applies the same split to its **remote-URL spec fetch**:
194213
an unreachable endpoint (network error) or a transient HTTP status
195214
(`408` / `429` / `5xx`, mirroring the `retryableStatusCodes` convention) is

0 commit comments

Comments
 (0)