Skip to content

Server SDK support for scope challenges on tool calls #1151

@pcarleton

Description

@pcarleton

Summary

Server SDK authors currently have no ergonomic way to trigger OAuth scope challenges during tool execution. While the MCP spec (SEP-835) supports returning 403 responses with WWW-Authenticate headers to request additional scopes, server SDKs don't provide facilities to easily declare required scopes per tool or dynamically trigger scope challenges during tool execution.

Use Case

Consider a server like GitHub's MCP server that wraps an API with granular permissions:

  1. A user connects with a valid OAuth token but minimal/no scopes
  2. They call a tool that may or may not need additional scopes depending on the specific resource (e.g., accessing a public vs private repo)
  3. The server makes a request to its backend service, which responds indicating more scopes are needed
  4. The server wants to pass this back as a scope challenge rather than a failed tool call

Currently, server authors must implement this entirely outside SDK land:

  • Parse raw JSON-RPC
  • Match tool names
  • Check required scopes against active scopes
  • Manually construct and return the 403 response with WWW-Authenticate header

Proposed Features

1. Declare required scopes when registering a tool (minimum viable)

server.registerTool(
  'get_private_repo',
  {
    description: 'Get private repository details',
    inputSchema: { ... },
    requiredScopes: ['repo:read']  // New field
  },
  async (args) => { ... }
);

The SDK would automatically check scopes before tool execution and return a 403 scope challenge if the token lacks required scopes.

2. Dynamically trigger scope challenge during tool execution (ideal)

server.registerTool(
  'get_repo_issue', 
  {
    description: 'Get repository issue',
    inputSchema: { ... }
  },
  async (args, context) => {
    const response = await fetchFromBackend(args);
    
    if (response.needsAdditionalScopes) {
      // Trigger scope challenge mid-execution
      throw new ScopeChallengeError(['repo:read']);
      // or: context.requestScopes(['repo:read']);
    }
    
    return { content: [...] };
  }
);

This would allow servers to defer scope decisions to downstream services and pass through their authorization requirements.

Additional Considerations

  • Scope hierarchy: Some OAuth systems have hierarchical scopes (e.g., repo includes repo:read). SDKs may need to support scope resolution/comparison utilities.
  • Access to current scopes: Tool handlers need a way to inspect what scopes the current token has (perhaps via context parameter).

Related

Context

Discussion from Discord with @SamMorrowDrums (GitHub MCP server author) about server-side SDK support for scope challenges.
https://discord.com/channels/1358869848138059966/1441013745496227870

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions