fix: respect requiresOAuth config in user MCP connections

UserConnectionManager hardcoded useOAuth: true for all user connections,
causing non-OAuth servers (e.g., private MCP with OIDC token headers) to
incorrectly trigger OAuth flows on 401 responses.

- Derive useOAuth from config.requiresOAuth/config.oauthMetadata, matching
  the logic already used in MCPManager.discoverServerTools()
- Widen MCPConnectionFactory.create() to accept UserConnectionContext for
  non-OAuth user connections
- Register a fallback oauthRequired handler on non-OAuth connections that
  immediately emits oauthFailed, preventing a 120s timeout hang when the
  server returns 401 for expired tokens
This commit is contained in:
Danny Avila 2026-04-01 19:07:27 -04:00
parent 7b368916d5
commit 0922db9db2
2 changed files with 38 additions and 23 deletions

View file

@ -45,7 +45,7 @@ export class MCPConnectionFactory {
/** Creates a new MCP connection with optional OAuth support */
static async create(
basic: t.BasicConnectionOptions,
oauth?: t.OAuthConnectionOptions,
oauth?: t.OAuthConnectionOptions | t.UserConnectionContext,
): Promise<MCPConnection> {
const factory = new this(basic, oauth);
return factory.createConnection();
@ -232,6 +232,17 @@ export class MCPConnectionFactory {
let cleanupOAuthHandlers: (() => void) | null = null;
if (this.useOAuth) {
cleanupOAuthHandlers = this.handleOAuthEvents(connection);
} else {
const nonOAuthHandler = () => {
logger.info(
`${this.logPrefix} Server does not use OAuth — treating 401/403 as auth failure, not OAuth`,
);
connection.emit('oauthFailed', new Error('Server does not use OAuth'));
};
connection.once('oauthRequired', nonOAuthHandler);
cleanupOAuthHandlers = () => {
connection.removeListener('oauthRequired', nonOAuthHandler);
};
}
try {

View file

@ -161,28 +161,32 @@ export abstract class UserConnectionManager {
try {
const registry = MCPServersRegistry.getInstance();
connection = await MCPConnectionFactory.create(
{
serverConfig: config,
serverName: serverName,
dbSourced: isUserSourced(config),
useSSRFProtection: registry.shouldEnableSSRFProtection(),
allowedDomains: registry.getAllowedDomains(),
},
{
useOAuth: true,
user: user,
customUserVars: customUserVars,
flowManager: flowManager,
tokenMethods: tokenMethods,
signal: signal,
oauthStart: oauthStart,
oauthEnd: oauthEnd,
returnOnOAuth: returnOnOAuth,
requestBody: requestBody,
connectionTimeout: connectionTimeout,
},
);
const basic: t.BasicConnectionOptions = {
serverConfig: config,
serverName: serverName,
dbSourced: isUserSourced(config),
useSSRFProtection: registry.shouldEnableSSRFProtection(),
allowedDomains: registry.getAllowedDomains(),
};
const useOAuth = Boolean(config.requiresOAuth || config.oauthMetadata);
const oauthOptions: t.OAuthConnectionOptions | t.UserConnectionContext = useOAuth
? {
useOAuth: true as const,
user,
customUserVars,
flowManager: flowManager!,
tokenMethods,
signal,
oauthStart,
oauthEnd,
returnOnOAuth,
requestBody,
connectionTimeout,
}
: { user, customUserVars, requestBody, connectionTimeout };
connection = await MCPConnectionFactory.create(basic, oauthOptions);
if (!(await connection?.isConnected())) {
throw new Error('Failed to establish connection after initialization attempt.');