fix(organization): widen ORGANIZATION_MEMBER_UPDATE_ROLE outputSchema to accept custom roles#4909
Merged
Merged
Conversation
… 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug found while auditing
apps/mesh/src/auth/roles.tsand its callers for hardening opportunities.The bug:
ORGANIZATION_MEMBER_UPDATE_ROLE'soutputSchema.rolewas hardcoded toz.union([literal("admin"), literal("member"), literal("owner")]). But organizations can define custom roles (seeorg-role-detail.tsx/ theorganizationRolestorage table), andcanAssignRolealready 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'sMcpServer.validateToolOutput) parses a tool's returnedstructuredContentagainst its declaredoutputSchemabefore returning the result to the caller, throwing anMcpErroron 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(samerolefield, same underlying Better Auth data) already uses the correct permissive shape:z.union([z.string(), z.array(z.string())]). This PR just bringsORGANIZATION_MEMBER_UPDATE_ROLEin line with it.Fix: widen
outputSchema.roletoz.union([z.string(), z.array(z.string())]), matchingORGANIZATION_MEMBER_ADD.Regression test:
apps/mesh/src/tools/organization/member-update-role.test.ts— parses the tool'soutputSchemadirectly (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.tsChecks run locally:
bun run fmt,bunx tsc --noEmitinapps/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.
rolenow acceptsstring | string[]to cover custom and multi-role returns.@modelcontextprotocol/sdkoutput validation errors after successful writes.Written for commit cddd58f. Summary will update on new commits.