Skip to content

feat(mcp): oauth MCP servers — headless provider + connect tool (CLEAN-75) - #10

Merged
dmitriyzhuk merged 2 commits into
mainfrom
feat/CLEAN-75-mcp-oauth-connect
Sep 10, 2026
Merged

dmitriyzhuk merged 2 commits into
mainfrom
feat/CLEAN-75-mcp-oauth-connect

Conversation

@dmitriyzhuk

Copy link
Copy Markdown
Contributor

CLEAN-75 — https://dreamvention.atlassian.net/browse/CLEAN-75 (runtime side; api side is CleanSlice/ranch#71)

Runtime half of the in-chat MCP OAuth connect flow. An oauth MCP server carries no static credential — the interactive authorize half runs in Ranch, and the runtime refreshes headlessly.

  • RuntimeMcpOauthProvider — an SDK OAuthClientProvider seeded from the per-agent token secret (mcpOauth:<id>). tokens()/saveTokens() read/persist the bundle; the SDK refreshes the access token on 401 from the stored refresh token with no browser. Rotated tokens are written back so they survive a pod restart. redirectToAuthorization throws (headless) — a dead refresh token means the user reconnects from chat.
  • McpGateway — oauth branch in buildTransport (passes the authProvider to StreamableHTTPClientTransport); when there's no token yet, connect returns a synthetic ${name}__connect tool instead of failing. The agent calls it when the user wants that service; it asks ranch to start the handshake (POST /mcp-servers/:id/oauth/start via x-bridle-api-key) and returns a login URL to hand the user.
  • IMcpServerConfig — + id, + 'oauth' authType; secret store threaded via McpModule from index.ts.

Follow-up (not here)

Same-session live reload on the mcp_connected control event — AgentRuntime takes its tool list as a fixed array at boot, so freshly-connected tools currently appear on the next boot. Connect + refresh work immediately.

Verified: tsc --noEmit clean for all touched files (6 remaining errors are a pre-existing bridleAttachments.spec, untouched here).

🤖 Generated with Claude Code

dmitriyzhuk and others added 2 commits September 10, 2026 17:37
An `oauth` MCP server carries no static credential. The interactive authorize
half runs in Ranch (in-chat Connect); the runtime seeds a headless
OAuthClientProvider from the per-agent token secret (`mcpOauth:<id>`) and the
SDK refreshes the access token on 401 from the stored refresh token — no
browser. Rotated tokens are persisted back to the secret so they survive a
pod restart; if the refresh token dies, redirectToAuthorization throws and the
user must reconnect from the chat.

- IMcpServerConfig: + id, + 'oauth' authType
- RuntimeMcpOauthProvider (tokens/saveTokens/clientInformation from the secret
  store) + oauth branch in McpGateway.buildTransport
- thread a per-agent secret store through McpModule -> McpGateway from index.ts

Verified: tsc clean for all touched files (6 remaining errors are a
pre-existing bridleAttachments.spec, untouched here).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
When an oauth MCP server has no stored token, McpGateway.connect returns a
synthetic `${name}__connect` tool instead of failing. The agent calls it when
the user wants that service; it asks ranch to start the OAuth handshake
(POST /mcp-servers/:id/oauth/start via x-bridle-api-key) and returns a login
URL for the agent to hand the user. A dead refresh token also falls back to
this tool so the user can re-authorize. Once a token lands, the real MCP tools
replace it on the next boot.

Follow-up (not in this change): live same-session reload on the mcp_connected
control event — AgentRuntime takes its tool list as a fixed array at boot, so
surfacing the freshly-connected tools mid-conversation needs dynamic tool
support there.

Verified: tsc clean for touched files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dmitriyzhuk
dmitriyzhuk merged commit 1fee451 into main Sep 10, 2026
maksymhryzodub-prog added a commit that referenced this pull request Oct 26, 2026
…1.0 (CLEAN-158)

Phase 9 of the ECC harness principles (specs/001-ecc-agent-principles,
T085-T090), plus the third security-review fix set (US5/US6).

- CI: a separate `eval` job on main runs `bun run eval`; model-backed sets
  report "pending" without an LLM_API_KEY secret, a missed threshold fails
  the job, and the image build does not depend on it.
- Docs: guard/instinct/session/skill/task rows in the agent slice README,
  loop row in the runtime README; IMPROVEMENT_PLAN marks #4, #6, #10, #15
  as superseded by the CLEAN-150 phases; quickstart-results.md records what
  each scenario is covered by and which steps remain manual.
- Version 0.31.0.
- Security review: workers are bounded by effect, not by a name list —
  without tools.delegate.allowWrite they get read tools (plus per-call
  tools such as file/http whose write calls the loop refuses), so a new or
  MCP write tool is excluded by default; the verifier prompt fences tool
  output and the draft as data and a rewrite that balloons or adds a link
  the draft never had is dropped in favour of the draft marked unverified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
maksymhryzodub-prog added a commit that referenced this pull request Oct 26, 2026
…1.0 (CLEAN-158)

Phase 9 of the ECC harness principles (specs/001-ecc-agent-principles,
T085-T090), plus the third security-review fix set (US5/US6).

- CI: a separate `eval` job on main runs `bun run eval`; model-backed sets
  report "pending" without an LLM_API_KEY secret, a missed threshold fails
  the job, and the image build does not depend on it.
- Docs: guard/instinct/session/skill/task rows in the agent slice README,
  loop row in the runtime README; IMPROVEMENT_PLAN marks #4, #6, #10, #15
  as superseded by the CLEAN-150 phases; quickstart-results.md records what
  each scenario is covered by and which steps remain manual.
- Version 0.31.0.
- Security review: workers are bounded by effect, not by a name list —
  without tools.delegate.allowWrite they get read tools (plus per-call
  tools such as file/http whose write calls the loop refuses), so a new or
  MCP write tool is excluded by default; the verifier prompt fences tool
  output and the draft as data and a rewrite that balloons or adds a link
  the draft never had is dropped in favour of the draft marked unverified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
maksymhryzodub-prog added a commit that referenced this pull request Oct 26, 2026
…1.0 (CLEAN-158) (#31)

* chore(harness): polish — eval job in CI, docs, plan supersession, 0.31.0 (CLEAN-158)

Phase 9 of the ECC harness principles (specs/001-ecc-agent-principles,
T085-T090), plus the third security-review fix set (US5/US6).

- CI: a separate `eval` job on main runs `bun run eval`; model-backed sets
  report "pending" without an LLM_API_KEY secret, a missed threshold fails
  the job, and the image build does not depend on it.
- Docs: guard/instinct/session/skill/task rows in the agent slice README,
  loop row in the runtime README; IMPROVEMENT_PLAN marks #4, #6, #10, #15
  as superseded by the CLEAN-150 phases; quickstart-results.md records what
  each scenario is covered by and which steps remain manual.
- Version 0.31.0.
- Security review: workers are bounded by effect, not by a name list —
  without tools.delegate.allowWrite they get read tools (plus per-call
  tools such as file/http whose write calls the loop refuses), so a new or
  MCP write tool is excluded by default; the verifier prompt fences tool
  output and the draft as data and a rewrite that balloons or adds a link
  the draft never had is dropped in favour of the draft marked unverified.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* fix(security): refuse executable frontmatter in rule, instinct and skill files (CLEAN-158)

gray-matter evaluates `---js` / `---javascript` frontmatter as code. Rule
files are operator-authored, instinct files runtime-written and SKILL.md
files model-written via skill_write — all live in the agent directory, so
none may carry executable frontmatter. A shared safeMatter() refuses any
language tag on the opening fence and disables every non-YAML engine; the
three gateways use it, and a spec proves `---js` is rejected without
running.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(spec): manual acceptance checklist for the harness principles (CLEAN-158)

Seven sections mirroring the user stories, run against a live agent over a
channel. Marks which scenarios also have automated counterparts and which
exist only here (protected-rule override, tool_describe, instinct conflicts,
hard-limit compaction, quiet tool failures, verifier injection, worker
concurrency and history isolation, audit trail, frontmatter refusal).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs(spec): link the manual acceptance checklist from quickstart results (CLEAN-158)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant