Summary
Droid's HTTP MCP OAuth flow relies on RFC 7591 Dynamic Client Registration (DCR). Some hosted MCP servers only accept pre-registered public OAuth clients and refuse DCR, which makes those servers impossible to connect from Droid regardless of interactive vs exec mode. Confirmed on Slack (mcp.slack.com/mcp); likely affects other vendors with tightly scoped OAuth apps.
Environment
- Droid CLI 0.104.0
- macOS (darwin 25.4.0)
- Terminal: iTerm.app and VS Code, same result
Reproduction
-
droid mcp add slack https://mcp.slack.com/mcp --type http
-
Start interactive droid. Observe slack in "Authentication required".
-
/mcp -> slack -> Authenticate.
-
Browser never opens. TUI loops:
Authenticating with slack...
Authentication cancelled for slack.
Authentication flow did not start for slack.
Log evidence (~/.factory/logs/droid-log-single.log)
INFO: [McpService] Server requires OAuth, initiating authentication
name: slack, url: https://mcp.slack.com/mcp
INFO: OAuth callback server started { port: 54623 }
WARN: [McpHub] Error in MCP server
name: slack
cause: Error: Incompatible auth server: does not support dynamic client registration
at MAD (../../node_modules/@modelcontextprotocol/sdk/dist/esm/client/auth.js:805:23)
Root cause
mcp.slack.com/mcp implements OAuth 2.0 Protected Resource Metadata (RFC 9728) but its authorization server does not implement RFC 7591 DCR. @modelcontextprotocol/sdk throws Incompatible auth server: does not support dynamic client registration. Droid has no fallback to pre-registered client credentials, so the flow aborts before the browser is opened and the TUI surfaces it as a cancellation.
Proposed behaviour
Allow configuring a pre-registered OAuth client per HTTP server in mcp.json, e.g.:
{
"mcpServers": {
"slack": {
"type": "http",
"url": "https://mcp.slack.com/mcp",
"oauth": {
"clientId": "1601185624273.8899143856786",
"callbackPort": 3118
}
}
}
}
oauth.clientId populated -> pass as clientInformation to the MCP SDK auth provider so the DCR branch is skipped.
oauth.callbackPort populated -> bind the OAuth callback listener to that port rather than scanning for a free one, so the server's pre-registered redirect URI (often http://localhost:<fixed_port>/) still matches.
This mirrors how Claude Code configures Slack today. 1601185624273.8899143856786 is the public slackapi/slack-mcp-plugin app id with redirect URI registered to http://localhost:3118/.
Affected servers
Any hosted MCP whose OAuth server lacks DCR. Confirmed: Slack (mcp.slack.com/mcp). Likely others with tightly scoped OAuth apps.
Workarounds
None inside Droid. Users either run a local stdio MCP (for Slack, korotovsky/slack-mcp-server with xoxc/xoxd tokens) or drop that server entirely.
Summary
Droid's HTTP MCP OAuth flow relies on RFC 7591 Dynamic Client Registration (DCR). Some hosted MCP servers only accept pre-registered public OAuth clients and refuse DCR, which makes those servers impossible to connect from Droid regardless of interactive vs exec mode. Confirmed on Slack (
mcp.slack.com/mcp); likely affects other vendors with tightly scoped OAuth apps.Environment
Reproduction
droid mcp add slack https://mcp.slack.com/mcp --type httpStart interactive
droid. Observeslackin "Authentication required"./mcp-> slack -> Authenticate.Browser never opens. TUI loops:
Log evidence (
~/.factory/logs/droid-log-single.log)Root cause
mcp.slack.com/mcpimplements OAuth 2.0 Protected Resource Metadata (RFC 9728) but its authorization server does not implement RFC 7591 DCR.@modelcontextprotocol/sdkthrowsIncompatible auth server: does not support dynamic client registration. Droid has no fallback to pre-registered client credentials, so the flow aborts before the browser is opened and the TUI surfaces it as a cancellation.Proposed behaviour
Allow configuring a pre-registered OAuth client per HTTP server in
mcp.json, e.g.:{ "mcpServers": { "slack": { "type": "http", "url": "https://mcp.slack.com/mcp", "oauth": { "clientId": "1601185624273.8899143856786", "callbackPort": 3118 } } } }oauth.clientIdpopulated -> pass asclientInformationto the MCP SDK auth provider so the DCR branch is skipped.oauth.callbackPortpopulated -> bind the OAuth callback listener to that port rather than scanning for a free one, so the server's pre-registered redirect URI (oftenhttp://localhost:<fixed_port>/) still matches.This mirrors how Claude Code configures Slack today.
1601185624273.8899143856786is the publicslackapi/slack-mcp-pluginapp id with redirect URI registered tohttp://localhost:3118/.Affected servers
Any hosted MCP whose OAuth server lacks DCR. Confirmed: Slack (
mcp.slack.com/mcp). Likely others with tightly scoped OAuth apps.Workarounds
None inside Droid. Users either run a local stdio MCP (for Slack,
korotovsky/slack-mcp-serverwithxoxc/xoxdtokens) or drop that server entirely.