docusealco/docuseal · error · CanCan::AccessDenied

-32603

-32603

Error message

Forbidden

What it means

The MCP (Model Context Protocol) controller rejected the request and rendered JSON-RPC error code -32603 with message 'Forbidden' over HTTP 403. It fires from the before_action chain: Devise's authenticate_user! (no valid session), verify_mcp_enabled! (feature disabled), or the inline CanCan check can?(:manage, :mcp) failing for the authenticated user. Note that -32603 is the JSON-RPC 'internal error' slot reused here for authorization denials, which can confuse generic MCP clients.

Source

Thrown at app/controllers/mcp/mcp_base_controller.rb:12

# frozen_string_literal: true

module Mcp
  class McpBaseController < ActionController::API
    wrap_parameters false

    before_action :authenticate_user!
    before_action :verify_mcp_enabled!
    check_authorization

    before_action do
      raise CanCan::AccessDenied unless can?(:manage, :mcp)
    end

    rescue_from CanCan::AccessDenied do
      render_error(-32_603, 'Forbidden', status: :forbidden)
    end

    rescue_from ActiveRecord::RecordNotFound do
      render_tool_error('Not found')
    end

    private

    def default_url_options
      Docuseal.default_url_options
    end

    def mcp_body
      request.request_parameters

View on GitHub (pinned to 004a22c1c8)

Solutions

  1. Confirm MCP is enabled for the instance/account (the verify_mcp_enabled! gate).
  2. Authenticate with a user whose role grants manage :mcp - check the CanCan ability definitions for :mcp.
  3. Ensure the MCP client sends credentials on every request, not just the initial handshake.
  4. When building an MCP client, treat code -32603 with message 'Forbidden' as a terminal auth error, not a retryable internal error.
Defensive patterns

Strategy: validation

Validate before calling

# smoke-test access before registering tools
curl -i -b 'session=...' https://app.example.com/mcp
# expect 2xx; 403 with -32603 'Forbidden' means stop and fix auth/entitlements first

Type guard

const isMcpForbidden = (err) => err?.code === -32603 && err?.message === 'Forbidden'

Prevention

When it happens

Trigger: Calling any /mcp route without a valid authenticated session; authenticating as a user whose role lacks the manage :mcp ability; hitting MCP endpoints on an instance where the MCP feature is disabled.

Common situations: Wiring an MCP client (editor assistant, agent) with missing cookies/tokens; self-hosted instances without MCP enabled; roles or plans without MCP entitlements.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of docusealco/docuseal@004a22c1c8 (2026-08-21). Data as JSON: /api/errors/ba00eb0c4d266cc4. Report an issue: GitHub.