mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-08-27 12:13:30 +00:00
🛂 fix: Preserve OpenID Re-Auth Errors Through MCP Tool Error Classification (#15010)
The MCP tool-call catch block classifies errors by message substring, so an OpenIDReauthRequiredError raised during header resolution was rewritten into an MCP OAuth configuration prompt and its class identity discarded. The typed error now passes through ahead of the heuristic, so the actionable re-authentication message reaches the caller intact.
This commit is contained in:
parent
b91691937e
commit
7c71d6dc1a
2 changed files with 54 additions and 0 deletions
|
|
@ -35,6 +35,7 @@ const {
|
|||
hasRuntimeUrlPlaceholders,
|
||||
containsGraphTokenPlaceholder,
|
||||
isOAuthServer,
|
||||
OpenIDReauthRequiredError,
|
||||
} = require('@librechat/api');
|
||||
const {
|
||||
Time,
|
||||
|
|
@ -1125,6 +1126,11 @@ function createToolInstance({
|
|||
error,
|
||||
);
|
||||
|
||||
/** Carries the actionable re-auth message; the substring heuristic below would misreport it as an OAuth configuration problem */
|
||||
if (error instanceof OpenIDReauthRequiredError) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
/** OAuth error, provide a helpful message */
|
||||
const isOAuthError =
|
||||
error.message?.includes('401') ||
|
||||
|
|
|
|||
|
|
@ -1642,6 +1642,54 @@ describe('User parameter passing tests', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('preserves OpenIDReauthRequiredError through the OAuth error classification', async () => {
|
||||
const { OpenIDReauthRequiredError } = require('@librechat/api');
|
||||
const mockUser = { id: 'reauth-user', role: 'USER' };
|
||||
const mockRes = { write: jest.fn(), flush: jest.fn() };
|
||||
const { getRoleByName } = require('~/models');
|
||||
getRoleByName.mockResolvedValue({
|
||||
permissions: {
|
||||
[PermissionTypes.MCP_SERVERS]: {
|
||||
[Permissions.USE]: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const reauthError = new OpenIDReauthRequiredError(
|
||||
'OpenID token is expired or unavailable; re-authentication is required to resolve {{LIBRECHAT_OPENID_ACCESS_TOKEN}}.',
|
||||
);
|
||||
mockGetMCPManager.mockReturnValue({
|
||||
callTool: jest.fn().mockRejectedValue(reauthError),
|
||||
});
|
||||
|
||||
const mcpTool = await createMCPTool({
|
||||
res: mockRes,
|
||||
user: mockUser,
|
||||
config: { requiresOAuth: false },
|
||||
toolKey: `test-tool${D}test-server`,
|
||||
provider: 'openai',
|
||||
userMCPAuthMap: {},
|
||||
availableTools: {
|
||||
[`test-tool${D}test-server`]: {
|
||||
function: {
|
||||
description: 'Cached tool',
|
||||
parameters: { type: 'object', properties: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
mcpTool.invoke(
|
||||
{},
|
||||
{
|
||||
configurable: { user: mockUser },
|
||||
metadata: { provider: 'openai', thread_id: 'thread-1', run_id: 'run-1' },
|
||||
toolCall: {},
|
||||
},
|
||||
),
|
||||
).rejects.toBe(reauthError);
|
||||
});
|
||||
|
||||
it('does not label OBO authentication failures as unconfigured MCP OAuth', async () => {
|
||||
const mockUser = { id: 'obo-user', role: 'USER' };
|
||||
const mockRes = { write: jest.fn(), flush: jest.fn() };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue