Skip to content

Follow up Zoo Code upstream updates: MCP oauth now validated per transport (clientId required) #3629

Closed

Description

@dyoshikawa

Summary

Zoo Code's MCP schema now validates oauth for each transport (Zoo-Code-Org/Zoo-Code#2129, merged 2026-10-24 after v3.92.0, so it ships in the next release). rulesync's Roo/Zoo MCP adapter copies the canonical Claude-style oauth object into .roo/mcp.json unchanged. After the next Zoo Code release, an oauth block on a stdio or sse server, or one without clientId, makes the whole project .roo/mcp.json fail validation. Zoo then shows an error and loads none of the project MCP servers. Claude's callbackPort is also silently dropped.

Recent Releases

  • v3.88.0 (2026-10-10): adds the alwaysDenyUnapprovedCommands auto-deny option. It lives in extension state, not in a zoo-code.* VS Code setting, so rulesync cannot write it and there is no gap.
  • v3.90.0 (2026-10-17) and v3.92.0 (2026-10-24): providers, tool-loop detection, skills discovered through symlinked directories, MCP marketplace entries. No config-surface change.
  • main after v3.92.0: #2129 (8383de48) adds static OAuth client settings for Streamable HTTP MCP servers (#2096).

Gaps

  • mcp: oauth is now validated for each transport. In McpHub.ts after Claude Code permissions: support permissions.defaultMode and permissions.additionalDirectories (and note sandbox.*) #2129:
    • stdio and sse servers declare oauth: z.undefined(), so any oauth value is rejected.
    • streamable-http servers accept oauth: { clientId, scope?, redirectUri?, resource? }. clientId is required and cannot be empty. redirectUri must be an HTTP loopback URL with an explicit port and a /callback path. resource is "auto", false or a URL. Unknown keys are stripped.
    • Whole-file failure: McpSettingsSchema.safeParse runs on the whole project file. On failure, Zoo falls back to the raw config only for global settings, not for project ones. So one bad server disables every server in .roo/mcp.json.
  • rulesync side (project; the roo and zoocode targets share RooMcp in src/features/mcp/roo-mcp.ts): oauth passes through verbatim. A dry run (generate --targets zoocode,roo --features mcp) shows two problems:
    • An http server with oauth: { "clientId": "abc", "callbackPort": 8080 } becomes "type": "streamable-http" with the same oauth object. Zoo drops callbackPort, so the fixed callback port the user set is lost.
    • An sse server with oauth: { "clientId": "abc" } is written as-is. The next Zoo Code release will reject it, and with it the whole project MCP config.
  • Canonical oauth without clientId: the canonical oauth follows Claude Code, where clientId is optional (for example, only callbackPort or authServerMetadataUrl is set). Such a block also fails Zoo's clientId check.

Proposed Follow-up

  • In the Roo/Zoo MCP conversion:
    • Drop oauth from stdio and sse servers, with a warning.
    • For streamable-http, map Claude's callbackPort to redirectUri: "http://127.0.0.1:<port>/callback" and pass clientId and scope through. Drop authServerMetadataUrl (and any other key Zoo does not know) with a warning. Drop the whole block, with a warning, when clientId is missing.
    • Optionally let a tool-scoped zoocode.mcpServers block carry Zoo-only resource ("auto" / false / URL).
  • Apply the same rules to the roo target, since Zoo Code reads the same .roo/mcp.json. At minimum, the warning should say why the key was dropped.
  • On import, map redirectUri back to callbackPort when it is a loopback /callback URL.

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    consideringenhancementNew feature or requestmaintainer-scrapRough notes for AI implementation. Not for human eyes.

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions