Skip to content

Publish 403s for org namespace despite Owner role + public membership (io.github.<org>/*) #1649

Description

@JohnCari

Summary

mcp-publisher publish returns 403 for an organization namespace (io.github.librocat/librocat) even though both documented prerequisites in docs/modelcontextprotocol-io/authentication.mdx are met: I am the sole Owner of the librocat GitHub org (not just a member), and org membership visibility is Public (confirmed independently via the unauthenticated GitHub API: GET /orgs/librocat/public_members/JohnCari → 204).

Error

Error: publish failed: server returned status 403: {"title":"Forbidden","status":403,"detail":"You do not have permission to publish this server. You have permission to publish: io.github.JohnCari/*. Attempting to publish: io.github.librocat/librocat. If you're trying to publish to a GitHub organization, you may need to make your organization membership public in your GitHub settings: ..."}

What I checked/tried (5 attempts across 2 browser sessions)

  1. mcp-publisher init → generated server.json with "name": "io.github.librocat/librocat"; mcp-publisher validate server.json passes.
  2. mcp-publisher login github (interactive device flow) → succeeds, confirms identity as JohnCari.
  3. mcp-publisher publish → 403 as above. Repeated with a fresh login each time (5 times total), including after:
    • Flipping org membership from Private to Public in GitHub org settings (github.com/orgs/librocat/people), verified via the public API as above.
    • A several-minute wait for possible propagation delay.
    • A full browser restart, to rule out stale cookies/session cache client-side.
  4. Ruled out the org's classic OAuth App access-restriction policy (Third-party access → OAuth app policy) — that governs classic OAuth Apps, and the registry's login uses a GitHub App (MCP Registry Login (Prod), client id Iv23liUydBbI7Z2Q9bOZ), not a classic OAuth App.
  5. Noted that MCP Registry Login (Prod) is not installed on the librocat org (only personally authorized via the device flow) — github.com/organizations/librocat/settings/installations shows only an unrelated app (Vercel) installed. authentication.mdx doesn't document org installation as a requirement for the interactive login path (only for CI-style Personal Access Tokens needing read:org / fine-grained Members: Read-only), so I didn't want to grant broader org access speculatively without knowing it's actually needed — flagging this as a possible missing requirement/doc gap if it turns out to be the real cause.

Expected

Given I'm the org's sole Owner with public membership, mcp-publisher publish should succeed for io.github.librocat/*, per the documented rules in authentication.mdx.

Environment

  • mcp-publisher version: 1.8.1 (commit f52dc852, built 2026-08-06)
  • Auth method: GitHub (interactive device flow)
  • Target namespace: io.github.librocat/librocat
  • Personal namespace that does work: io.github.JohnCari/*

Happy to provide more detail (org id, timestamps of attempts, etc.) if useful.

Activity

  1. 4hmetuyar commented on Sep 28, 2026

    @4hmetuyar

    Same issue, adding a data point since I hit the exact same 403 today (mcp-publisher 1.8.1).

    Setup: sole admin of a GitHub org (GuardBee), org membership flipped from private to public, confirmed independently via the unauthenticated API:

    $ gh api orgs/GuardBee/public_members/4hmetuyar
    (200/204 — empty body, confirms public)
    

    Error (identical shape to OP's):

    Error: publish failed: server returned status 403: {"title":"Forbidden","status":403,"detail":"You do not have permission to publish this server. You have permission to publish: io.github.4hmetuyar/*. Attempting to publish: io.github.GuardBee/*. ..."}
    

    Retried with a fresh mcp-publisher login github (fresh device-flow auth, confirmed "Successfully authenticated!") several hours after making membership public — same 403, no change.

    I also checked the hypothesis from the OP's point 5 (that the login app isn't installed on the org, only personally authorized): confirmed the same here. gh api orgs/GuardBee/installations shows no MCP Registry Login app installed (only an unrelated app). I tried to find an install page for it to test that theory directly, but the app doesn't appear to be listed on the public GitHub Marketplace/App search, so I couldn't self-serve an install to rule this in or out. If org installation actually is a requirement for the token to carry org-publish permission, that's not currently documented in authentication.mdx as far as I can tell, and there's no accessible way for an org owner to add the installation.

    Environment: mcp-publisher 1.8.1, GitHub device-flow auth, target namespace io.github.GuardBee/*.

  2. loyderhy2 commented on Oct 1, 2026

    @loyderhy2

    Additional data point from trekmail (GitHub account loyderhy2, active organization Owner).

    I am using official mcp-publisher 1.8.1 (commit f52dc852) and the GitHub App MCP Registry Login (Prod) (client ID Iv23liUydBbI7Z2Q9bOZ). A successful device login, while my membership was private, returned Registry publish permission only for io.github.loyderhy2/*, not io.github.trekmail/*. I stopped before publishing.

    I have now made my membership public: unauthenticated GET /orgs/trekmail/public_members/loyderhy2 returned 204 on 2026-10-01. No post-change login has occurred, so I am not claiming the problem persists after that change. The app is personally authorized and shows zero organization installations.

    What is the supported procedure to obtain the organization namespace? If installation is required for this official app, could you provide its supported installation URL and exact minimum permissions?

  3. jcolvin1056 commented on Oct 11, 2026

    @jcolvin1056

    Root Cause Identified + Working Workaround

    I hit the same 403 and traced it to a specific root cause with a confirmed workaround.

    Root Cause: Device-flow token lacks read:org scope

    The mcp-publisher login github device flow uses the GitHub App MCP Registry Login (Prod) (client ID Iv23liUydBbI7Z2Q9bOZ). This app does not request read:org scope in the device-flow authorization. The resulting GitHub token therefore has no ability to query org memberships — even public ones are invisible to it.

    When the registry backend exchanges this token (via POST /v0/auth/github-at), it calls GitHub's GET /user/memberships/orgs?state=active (or equivalent) to discover org namespaces. Because the token lacks read:org, GitHub returns only the personal account — no orgs. The registry then mints a JWT with only the personal namespace:

    {
      "permissions": [
        {"action": "publish", "resource": "io.github.jcolvin1056/*"}
      ]
    }

    The org namespace (io.github.aegisgatesecurity/*) is silently omitted.

    Evidence

    1. Decoded JWT from mcp-publisher login github — only personal namespace present (see above).
    2. gh auth token has read:org — confirmed via gh api -i /user → X-OAuth-Scopes: ..., read:org, ...
    3. Same token exchanged manually — produces JWT with both namespaces:
    {
      "permissions": [
        {"action": "publish", "resource": "io.github.jcolvin1056/*"},
        {"action": "publish", "resource": "io.github.aegisgatesecurity/*"}
      ]
    }
    1. All GitHub-side prerequisites confirmed met: org membership public (GET /orgs/aegisgatesecurity/public_members/jcolvin1056 → 204), org role is admin (GET /user/memberships/orgs/aegisgatesecurity → role: admin).

    Workaround (confirmed working)

    Exchange a token that does have read:org scope directly with the registry auth endpoint, bypassing mcp-publisher login:

    # Use any token with read:org scope (gh CLI token works)
    GH_TOKEN=$(gh auth token)
    
    # Exchange for a registry JWT
    RESPONSE=$(curl -s -X POST https://registry.modelcontextprotocol.io/v0/auth/github-at \
      -H "Content-Type: application/json" \
      -d "{\"github_token\": \"$GH_TOKEN\"}")
    
    # Extract and save the registry token
    REGISTRY_TOKEN=$(echo "$RESPONSE" | jq -r .registry_token)
    mkdir -p ~/.config/mcp-publisher
    echo "{\"method\":\"github\",\"registry\":\"https://registry.modelcontextprotocol.io\",\"token\":\"${REGISTRY_TOKEN}\"}" > ~/.config/mcp-publisher/token.json
    
    # Publish
    mcp-publisher publish

    Result:

    ✓ Successfully published
    ✓ Server io.github.aegisgatesecurity/aegisgate-mcp version 1.5.0
    

    Why mcp-publisher logout && login doesn't fix it

    The device flow always requests the same scopes from the same GitHub App. No amount of re-login changes the scopes — the token will always lack read:org. The only way to get org permissions into the registry JWT is to provide a token that already has read:org.

    Suggested Fix

    The MCP Registry Login (Prod) GitHub App should request read:org scope during the device flow authorization. This would allow the registry backend to see public org memberships and mint JWTs with org namespaces. Alternatively, the registry backend could use the unauthenticated GET /orgs/{org}/public_members/{user} endpoint (returns 204/404, no auth needed) to verify public org membership without requiring read:org on the token.

    This explains all the reported cases in this issue and the related ones (#1527, #1537, #1468, #1551) — the device flow token simply cannot see orgs.

    Environment: mcp-publisher 1.8.1, org aegisgatesecurity, user jcolvin1056, admin role, public membership confirmed.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions