Skip to content

fix(organization): widen ORGANIZATION_MEMBER_UPDATE_ROLE outputSchema to accept custom roles#4909

Merged
pedrofrxncx merged 1 commit into
mainfrom
fix/member-update-role-output-schema-w4
Jul 21, 2026
Merged

fix(organization): widen ORGANIZATION_MEMBER_UPDATE_ROLE outputSchema to accept custom roles#4909
pedrofrxncx merged 1 commit into
mainfrom
fix/member-update-role-output-schema-w4

Conversation

@pedrofrxncx

@pedrofrxncx pedrofrxncx commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Bug found while auditing apps/mesh/src/auth/roles.ts and its callers for hardening opportunities.

The bug: ORGANIZATION_MEMBER_UPDATE_ROLE's outputSchema.role was hardcoded to z.union([literal("admin"), literal("member"), literal("owner")]). But organizations can define custom roles (see org-role-detail.tsx / the organizationRole storage table), and canAssignRole already permits an owner/admin assigning any non-owner custom role name — it's not restricted to the three built-ins.

Why it matters: the MCP SDK (@modelcontextprotocol/sdk's McpServer.validateToolOutput) parses a tool's returned structuredContent against its declared outputSchema before returning the result to the caller, throwing an McpError on mismatch. So assigning a member to any custom role (e.g. "editor") — or returning a multi-role array, which Better Auth's organization plugin supports — succeeds in the DB but the tool call itself throws an "Output validation error" back to the caller, even though the write already happened. This is misleading for any agent/client acting on the tool result (retries, error surfacing, etc).

The sibling tool ORGANIZATION_MEMBER_ADD (same role field, same underlying Better Auth data) already uses the correct permissive shape: z.union([z.string(), z.array(z.string())]). This PR just brings ORGANIZATION_MEMBER_UPDATE_ROLE in line with it.

Fix: widen outputSchema.role to z.union([z.string(), z.array(z.string())]), matching ORGANIZATION_MEMBER_ADD.

Regression test: apps/mesh/src/tools/organization/member-update-role.test.ts — parses the tool's outputSchema directly (pure Zod logic, no mocks/DB) against a custom-role string and a multi-role array, both of which fail against the old schema and pass against the fix.

To verify: bun test apps/mesh/src/tools/organization/member-update-role.test.ts

Checks run locally: bun run fmt, bunx tsc --noEmit in apps/mesh (clean), and the targeted test file above. Full CI validates the rest.


Summary by cubic

Widened ORGANIZATION_MEMBER_UPDATE_ROLE output schema to accept custom roles and arrays, preventing tool output validation errors. Aligns with the existing shape used by ORGANIZATION_MEMBER_ADD.

  • Bug Fixes
    • role now accepts string | string[] to cover custom and multi-role returns.
    • Prevents @modelcontextprotocol/sdk output validation errors after successful writes.
    • Added regression test: validates custom role and multi-role array parsing.

Written for commit cddd58f. Summary will update on new commits.

Review in cubic

… to accept custom roles

The role field was hardcoded to z.union([literal("admin"), literal("member"),
literal("owner")]), but organizations can define custom roles (see
org-role-detail.tsx / organizationRole storage), and canAssignRole already
permits assigning them. The MCP SDK validates a tool's structuredContent
against its declared outputSchema before returning the result, so assigning
any custom role (or a multi-role array) succeeded in the DB but the tool call
itself threw an "Output validation error" back to the caller. The sibling
ORGANIZATION_MEMBER_ADD tool already uses the correct, permissive
z.union([z.string(), z.array(z.string())]) shape for the same field.
@pedrofrxncx
pedrofrxncx merged commit 02ca316 into main Jul 21, 2026
14 checks passed
@pedrofrxncx
pedrofrxncx deleted the fix/member-update-role-output-schema-w4 branch July 21, 2026 01:09
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