mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
📦 chore: Bump MCP SDK: Fix Types and MCP OAuth due to Update (#10811)
* chore: Bump @modelcontextprotocol/sdk to version 1.24.3 * refactor: Update resource handling in MCP parsers and types - Simplified resource text checks in `parseAsString` and `formatToolContent` functions to ensure proper existence checks. - Removed unnecessary resource name and description handling to streamline output. - Updated type definitions in `index.ts` to align with the new structure from `@modelcontextprotocol/sdk`, enhancing type safety and clarity. - Added `logo_uri` and `tos_uri` properties to `MCPOAuthHandler` for improved OAuth metadata support. * refactor: Update custom endpoint configurations and type definitions - Removed unused type imports and streamlined the custom parameters handling in `loadCustomEndpointsConfig`. - Adjusted the `TCustomEndpointsConfig` type to utilize `TConfig` instead of `TEndpoint`, enhancing type accuracy. - Made the endpoint schema optional in the configuration to improve flexibility. * fix: Implement token cleanup and error handling for invalid OAuth tokens - Added `cleanupInvalidTokens` method to remove invalid OAuth tokens from storage when detected. - Introduced `isInvalidTokenError` method to identify errors indicating revoked or expired tokens. - Integrated token cleanup logic into the connection attempt process to ensure fresh OAuth flow on invalid token detection. * feat: Add revoke OAuth functionality in Server Initialization - Introduced a new button to revoke OAuth for servers, enhancing user control over OAuth permissions. - Updated the `useMCPServerManager` hook to include a standalone `revokeOAuthForServer` function for managing OAuth revocation. - Adjusted the UI to conditionally render the revoke button based on server requirements. * fix: error handling for authentication in MCPConnection - Updated the error handling logic in MCPConnection to better identify various authentication error indicators, including 401 status, invalid tokens, and unauthorized messages. - Removed the deprecated cleanupInvalidTokens method and integrated its logic into the connection attempt process for improved clarity and efficiency. - Adjusted the MCPConnectionFactory to streamline the connection attempt process and handle OAuth errors more effectively. * refactor: Update button rendering in ServerInitializationSection - Removed the existing button for server initialization and replaced it with a new button implementation, maintaining the same functionality. - Ensured consistent rendering of the button within the component's layout. * chore: update resource type usage in parsers.test.ts
This commit is contained in:
parent
e6288c379c
commit
394bb6242b
14 changed files with 155 additions and 90 deletions
|
|
@ -47,7 +47,7 @@
|
|||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
"@modelcontextprotocol/sdk": "^1.21.0",
|
||||
"@modelcontextprotocol/sdk": "^1.24.3",
|
||||
"@node-saml/passport-saml": "^5.1.0",
|
||||
"@smithy/node-http-handler": "^4.4.5",
|
||||
"axios": "^1.12.1",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
import React from 'react';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
import { RefreshCw, Trash2 } from 'lucide-react';
|
||||
import { Button, Spinner } from '@librechat/client';
|
||||
import { useLocalize, useMCPServerManager, useMCPConnectionStatus } from '~/hooks';
|
||||
import { useGetStartupConfig } from '~/data-provider';
|
||||
|
||||
interface ServerInitializationSectionProps {
|
||||
sidePanel?: boolean;
|
||||
|
|
@ -22,12 +21,13 @@ export default function ServerInitializationSection({
|
|||
const localize = useLocalize();
|
||||
|
||||
const {
|
||||
getOAuthUrl,
|
||||
isCancellable,
|
||||
isInitializing,
|
||||
cancelOAuthFlow,
|
||||
initializeServer,
|
||||
availableMCPServers,
|
||||
cancelOAuthFlow,
|
||||
isInitializing,
|
||||
isCancellable,
|
||||
getOAuthUrl,
|
||||
revokeOAuthForServer,
|
||||
} = useMCPServerManager({ conversationId });
|
||||
|
||||
const { connectionStatus } = useMCPConnectionStatus({
|
||||
|
|
@ -73,7 +73,6 @@ export default function ServerInitializationSection({
|
|||
|
||||
// Unified button rendering
|
||||
const isReinit = shouldShowReinit;
|
||||
const outerClass = isReinit ? 'flex justify-start' : 'flex justify-end';
|
||||
const buttonVariant = isReinit ? undefined : 'default';
|
||||
|
||||
let buttonText = '';
|
||||
|
|
@ -94,13 +93,24 @@ export default function ServerInitializationSection({
|
|||
);
|
||||
|
||||
return (
|
||||
<div className={outerClass}>
|
||||
<div className="flex items-center gap-2">
|
||||
{requiresOAuth && revokeOAuthForServer && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="destructive"
|
||||
onClick={() => revokeOAuthForServer(serverName)}
|
||||
aria-label={localize('com_ui_revoke')}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
{localize('com_ui_revoke')}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant={buttonVariant}
|
||||
onClick={() => initializeServer(serverName, false)}
|
||||
disabled={isServerInitializing}
|
||||
size={sidePanel ? 'sm' : 'default'}
|
||||
className="w-full"
|
||||
className="flex-1"
|
||||
>
|
||||
{icon}
|
||||
{buttonText}
|
||||
|
|
|
|||
|
|
@ -517,6 +517,19 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
|
|||
[selectedToolForConfig, updateUserPluginsMutation, mcpValues, setMCPValues],
|
||||
);
|
||||
|
||||
/** Standalone revoke function for OAuth servers - doesn't require selectedToolForConfig */
|
||||
const revokeOAuthForServer = useCallback(
|
||||
(serverName: string) => {
|
||||
const payload: TUpdateUserPlugins = {
|
||||
pluginKey: `${Constants.mcp_prefix}${serverName}`,
|
||||
action: 'uninstall',
|
||||
auth: {},
|
||||
};
|
||||
updateUserPluginsMutation.mutate(payload);
|
||||
},
|
||||
[updateUserPluginsMutation],
|
||||
);
|
||||
|
||||
const handleSave = useCallback(
|
||||
(authData: Record<string, string>) => {
|
||||
if (selectedToolForConfig) {
|
||||
|
|
@ -678,6 +691,7 @@ export function useMCPServerManager({ conversationId }: { conversationId?: strin
|
|||
setSelectedToolForConfig,
|
||||
handleSave,
|
||||
handleRevoke,
|
||||
revokeOAuthForServer,
|
||||
getServerStatusIconProps,
|
||||
getConfigDialogProps,
|
||||
checkEffectivePermission,
|
||||
|
|
|
|||
45
package-lock.json
generated
45
package-lock.json
generated
|
|
@ -61,7 +61,7 @@
|
|||
"@librechat/api": "*",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@microsoft/microsoft-graph-client": "^3.0.7",
|
||||
"@modelcontextprotocol/sdk": "^1.21.0",
|
||||
"@modelcontextprotocol/sdk": "^1.24.3",
|
||||
"@node-saml/passport-saml": "^5.1.0",
|
||||
"@smithy/node-http-handler": "^4.4.5",
|
||||
"axios": "^1.12.1",
|
||||
|
|
@ -1467,9 +1467,9 @@
|
|||
}
|
||||
},
|
||||
"api/node_modules/jose": {
|
||||
"version": "6.0.11",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.0.11.tgz",
|
||||
"integrity": "sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==",
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz",
|
||||
"integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
|
|
@ -18362,9 +18362,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.21.0.tgz",
|
||||
"integrity": "sha512-YFBsXJMFCyI1zP98u7gezMFKX4lgu/XpoZJk7ufI6UlFKXLj2hAMUuRlQX/nrmIPOmhRrG6tw2OQ2ZA/ZlXYpQ==",
|
||||
"version": "1.24.3",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.24.3.tgz",
|
||||
"integrity": "sha512-YgSHW29fuzKKAHTGe9zjNoo+yF8KaQPzDC2W9Pv41E7/57IfY+AMGJ/aDFlgTLcVVELoggKE4syABCE75u3NCw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ajv": "^8.17.1",
|
||||
|
|
@ -18376,23 +18376,37 @@
|
|||
"eventsource-parser": "^3.0.0",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"jose": "^6.1.1",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
"zod": "^3.25 || ^4.0",
|
||||
"zod-to-json-schema": "^3.25.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@cfworker/json-schema": "^4.1.1"
|
||||
"@cfworker/json-schema": "^4.1.1",
|
||||
"zod": "^3.25 || ^4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@cfworker/json-schema": {
|
||||
"optional": true
|
||||
},
|
||||
"zod": {
|
||||
"optional": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk/node_modules/jose": {
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz",
|
||||
"integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/@mongodb-js/saslprep": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.3.1.tgz",
|
||||
|
|
@ -48069,11 +48083,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.24.3",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz",
|
||||
"integrity": "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A==",
|
||||
"version": "3.25.0",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz",
|
||||
"integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==",
|
||||
"license": "ISC",
|
||||
"peerDependencies": {
|
||||
"zod": "^3.24.1"
|
||||
"zod": "^3.25 || ^4"
|
||||
}
|
||||
},
|
||||
"node_modules/zwitch": {
|
||||
|
|
@ -48129,7 +48144,7 @@
|
|||
"@langchain/core": "^0.3.79",
|
||||
"@librechat/agents": "^3.0.50",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.21.0",
|
||||
"@modelcontextprotocol/sdk": "^1.24.3",
|
||||
"axios": "^1.12.1",
|
||||
"connect-redis": "^8.1.0",
|
||||
"diff": "^7.0.0",
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
"@langchain/core": "^0.3.79",
|
||||
"@librechat/agents": "^3.0.50",
|
||||
"@librechat/data-schemas": "*",
|
||||
"@modelcontextprotocol/sdk": "^1.21.0",
|
||||
"@modelcontextprotocol/sdk": "^1.24.3",
|
||||
"axios": "^1.12.1",
|
||||
"connect-redis": "^8.1.0",
|
||||
"diff": "^7.0.0",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { EModelEndpoint, extractEnvVariable, normalizeEndpointName } from 'librechat-data-provider';
|
||||
import type { TCustomEndpoints, TEndpoint, TConfig } from 'librechat-data-provider';
|
||||
import type { TCustomEndpoints, TEndpoint } from 'librechat-data-provider';
|
||||
import type { TCustomEndpointsConfig } from '~/types/endpoints';
|
||||
import { isUserProvided } from '~/utils';
|
||||
|
||||
|
|
@ -45,7 +45,7 @@ export function loadCustomEndpointsConfig(
|
|||
type: EModelEndpoint.custom,
|
||||
userProvide: isUserProvided(resolvedApiKey),
|
||||
userProvideURL: isUserProvided(resolvedBaseURL),
|
||||
customParams: customParams as TConfig['customParams'],
|
||||
customParams,
|
||||
modelDisplayLabel,
|
||||
iconURL,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -259,14 +259,20 @@ export class MCPConnectionFactory {
|
|||
attempts++;
|
||||
|
||||
if (this.useOAuth && this.isOAuthError(error)) {
|
||||
// Only handle OAuth if this is a user connection (has oauthStart handler)
|
||||
// For returnOnOAuth mode, let the event handler (handleOAuthEvents) deal with OAuth
|
||||
// We just need to stop retrying and let the error propagate
|
||||
if (this.returnOnOAuth) {
|
||||
logger.info(
|
||||
`${this.logPrefix} OAuth required (return on OAuth mode), stopping retries`,
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
|
||||
// Normal flow - wait for OAuth to complete
|
||||
if (this.oauthStart && !oauthHandled) {
|
||||
const errorWithFlag = error as (Error & { isOAuthError?: boolean }) | undefined;
|
||||
if (errorWithFlag?.isOAuthError) {
|
||||
oauthHandled = true;
|
||||
logger.info(`${this.logPrefix} Handling OAuth`);
|
||||
await this.handleOAuthRequired();
|
||||
}
|
||||
oauthHandled = true;
|
||||
logger.info(`${this.logPrefix} Handling OAuth`);
|
||||
await this.handleOAuthRequired();
|
||||
}
|
||||
// Don't retry on OAuth errors - just throw
|
||||
logger.info(`${this.logPrefix} OAuth required, stopping connection attempts`);
|
||||
|
|
@ -288,15 +294,29 @@ export class MCPConnectionFactory {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check for SSE error with 401 status
|
||||
if ('message' in error && typeof error.message === 'string') {
|
||||
return error.message.includes('401') || error.message.includes('Non-200 status code (401)');
|
||||
}
|
||||
|
||||
// Check for error code
|
||||
if ('code' in error) {
|
||||
const code = (error as { code?: number }).code;
|
||||
return code === 401 || code === 403;
|
||||
if (code === 401 || code === 403) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check message for various auth error indicators
|
||||
if ('message' in error && typeof error.message === 'string') {
|
||||
const message = error.message.toLowerCase();
|
||||
// Check for 401 status
|
||||
if (message.includes('401') || message.includes('non-200 status code (401)')) {
|
||||
return true;
|
||||
}
|
||||
// Check for invalid_token (OAuth servers return this for expired/revoked tokens)
|
||||
if (message.includes('invalid_token')) {
|
||||
return true;
|
||||
}
|
||||
// Check for authentication required
|
||||
if (message.includes('authentication required') || message.includes('unauthorized')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -170,8 +170,6 @@ describe('formatToolContent', () => {
|
|||
uri: 'ui://carousel',
|
||||
mimeType: 'application/json',
|
||||
text: '{"items": []}',
|
||||
name: 'carousel',
|
||||
description: 'A carousel component',
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
@ -184,8 +182,6 @@ describe('formatToolContent', () => {
|
|||
text:
|
||||
'Resource Text: {"items": []}\n' +
|
||||
'Resource URI: ui://carousel\n' +
|
||||
'Resource: carousel\n' +
|
||||
'Resource Description: A carousel component\n' +
|
||||
'Resource MIME Type: application/json',
|
||||
},
|
||||
]);
|
||||
|
|
@ -196,8 +192,6 @@ describe('formatToolContent', () => {
|
|||
uri: 'ui://carousel',
|
||||
mimeType: 'application/json',
|
||||
text: '{"items": []}',
|
||||
name: 'carousel',
|
||||
description: 'A carousel component',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -211,8 +205,6 @@ describe('formatToolContent', () => {
|
|||
type: 'resource',
|
||||
resource: {
|
||||
uri: 'file://document.pdf',
|
||||
name: 'Document',
|
||||
description: 'Important document',
|
||||
mimeType: 'application/pdf',
|
||||
text: 'Document content',
|
||||
},
|
||||
|
|
@ -227,8 +219,6 @@ describe('formatToolContent', () => {
|
|||
text:
|
||||
'Resource Text: Document content\n' +
|
||||
'Resource URI: file://document.pdf\n' +
|
||||
'Resource: Document\n' +
|
||||
'Resource Description: Important document\n' +
|
||||
'Resource MIME Type: application/pdf',
|
||||
},
|
||||
]);
|
||||
|
|
@ -242,7 +232,6 @@ describe('formatToolContent', () => {
|
|||
type: 'resource',
|
||||
resource: {
|
||||
uri: 'https://example.com/resource',
|
||||
name: 'Example Resource',
|
||||
text: '',
|
||||
},
|
||||
},
|
||||
|
|
@ -253,7 +242,7 @@ describe('formatToolContent', () => {
|
|||
expect(content).toEqual([
|
||||
{
|
||||
type: 'text',
|
||||
text: 'Resource URI: https://example.com/resource\n' + 'Resource: Example Resource',
|
||||
text: 'Resource URI: https://example.com/resource',
|
||||
},
|
||||
]);
|
||||
expect(artifacts).toBeUndefined();
|
||||
|
|
@ -275,7 +264,6 @@ describe('formatToolContent', () => {
|
|||
type: 'resource',
|
||||
resource: {
|
||||
uri: 'file://data.csv',
|
||||
name: 'Data file',
|
||||
text: '',
|
||||
},
|
||||
},
|
||||
|
|
@ -291,8 +279,7 @@ describe('formatToolContent', () => {
|
|||
'Resource Text: {"label": "Click me"}\n' +
|
||||
'Resource URI: ui://button\n' +
|
||||
'Resource MIME Type: application/json\n\n' +
|
||||
'Resource URI: file://data.csv\n' +
|
||||
'Resource: Data file',
|
||||
'Resource URI: file://data.csv',
|
||||
},
|
||||
]);
|
||||
expect(artifacts).toEqual({
|
||||
|
|
@ -397,8 +384,6 @@ describe('formatToolContent', () => {
|
|||
type: 'resource',
|
||||
resource: {
|
||||
uri: 'https://api.example.com/data',
|
||||
name: 'API Data',
|
||||
description: 'External data source',
|
||||
text: '',
|
||||
},
|
||||
},
|
||||
|
|
@ -417,9 +402,7 @@ describe('formatToolContent', () => {
|
|||
'Resource Text: {"type": "bar"}\n' +
|
||||
'Resource URI: ui://chart\n' +
|
||||
'Resource MIME Type: application/json\n\n' +
|
||||
'Resource URI: https://api.example.com/data\n' +
|
||||
'Resource: API Data\n' +
|
||||
'Resource Description: External data source',
|
||||
'Resource URI: https://api.example.com/data',
|
||||
},
|
||||
{ type: 'text', text: 'Conclusion' },
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -759,15 +759,29 @@ export class MCPConnection extends EventEmitter {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check for SSE error with 401 status
|
||||
if ('message' in error && typeof error.message === 'string') {
|
||||
return error.message.includes('401') || error.message.includes('Non-200 status code (401)');
|
||||
}
|
||||
|
||||
// Check for error code
|
||||
if ('code' in error) {
|
||||
const code = (error as { code?: number }).code;
|
||||
return code === 401 || code === 403;
|
||||
if (code === 401 || code === 403) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check message for various auth error indicators
|
||||
if ('message' in error && typeof error.message === 'string') {
|
||||
const message = error.message.toLowerCase();
|
||||
// Check for 401 status
|
||||
if (message.includes('401') || message.includes('non-200 status code (401)')) {
|
||||
return true;
|
||||
}
|
||||
// Check for invalid_token (OAuth servers return this for expired/revoked tokens)
|
||||
if (message.includes('invalid_token')) {
|
||||
return true;
|
||||
}
|
||||
// Check for authentication required
|
||||
if (message.includes('authentication required') || message.includes('unauthorized')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -88,10 +88,21 @@ export class MCPOAuthHandler {
|
|||
logger.debug(
|
||||
`[MCPOAuth] Discovering OAuth metadata from ${sanitizeUrlForLogging(authServerUrl)}`,
|
||||
);
|
||||
const rawMetadata = await discoverAuthorizationServerMetadata(authServerUrl, {
|
||||
let rawMetadata = await discoverAuthorizationServerMetadata(authServerUrl, {
|
||||
fetchFn,
|
||||
});
|
||||
|
||||
// If discovery failed and we're using a path-based URL, try the base URL
|
||||
if (!rawMetadata && authServerUrl.pathname !== '/') {
|
||||
const baseUrl = new URL(authServerUrl.origin);
|
||||
logger.debug(
|
||||
`[MCPOAuth] Discovery failed with path, trying base URL: ${sanitizeUrlForLogging(baseUrl)}`,
|
||||
);
|
||||
rawMetadata = await discoverAuthorizationServerMetadata(baseUrl, {
|
||||
fetchFn,
|
||||
});
|
||||
}
|
||||
|
||||
if (!rawMetadata) {
|
||||
/**
|
||||
* No metadata discovered - create fallback metadata using default OAuth endpoint paths.
|
||||
|
|
@ -165,6 +176,8 @@ export class MCPOAuthHandler {
|
|||
response_types: ['code'] as string[],
|
||||
token_endpoint_auth_method: 'client_secret_basic',
|
||||
scope: undefined as string | undefined,
|
||||
logo_uri: undefined as string | undefined,
|
||||
tos_uri: undefined as string | undefined,
|
||||
};
|
||||
|
||||
const supportedGrantTypes = metadata.grant_types_supported || ['authorization_code'];
|
||||
|
|
|
|||
|
|
@ -56,18 +56,12 @@ function parseAsString(result: t.MCPToolCallResponse): string {
|
|||
}
|
||||
if (item.type === 'resource') {
|
||||
const resourceText = [];
|
||||
if (item.resource.text != null && item.resource.text) {
|
||||
if ('text' in item.resource && item.resource.text != null && item.resource.text) {
|
||||
resourceText.push(item.resource.text);
|
||||
}
|
||||
if (item.resource.uri) {
|
||||
resourceText.push(`Resource URI: ${item.resource.uri}`);
|
||||
}
|
||||
if (item.resource.name) {
|
||||
resourceText.push(`Resource: ${item.resource.name}`);
|
||||
}
|
||||
if (item.resource.description) {
|
||||
resourceText.push(`Description: ${item.resource.description}`);
|
||||
}
|
||||
if (item.resource.mimeType != null && item.resource.mimeType) {
|
||||
resourceText.push(`Type: ${item.resource.mimeType}`);
|
||||
}
|
||||
|
|
@ -143,18 +137,12 @@ export function formatToolContent(
|
|||
}
|
||||
|
||||
const resourceText = [];
|
||||
if (item.resource.text != null && item.resource.text) {
|
||||
if ('text' in item.resource && item.resource.text != null && item.resource.text) {
|
||||
resourceText.push(`Resource Text: ${item.resource.text}`);
|
||||
}
|
||||
if (item.resource.uri.length) {
|
||||
resourceText.push(`Resource URI: ${item.resource.uri}`);
|
||||
}
|
||||
if (item.resource.name) {
|
||||
resourceText.push(`Resource: ${item.resource.name}`);
|
||||
}
|
||||
if (item.resource.description) {
|
||||
resourceText.push(`Resource Description: ${item.resource.description}`);
|
||||
}
|
||||
if (item.resource.mimeType != null && item.resource.mimeType) {
|
||||
resourceText.push(`Resource MIME Type: ${item.resource.mimeType}`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,9 +8,16 @@ import {
|
|||
WebSocketOptionsSchema,
|
||||
StreamableHTTPOptionsSchema,
|
||||
} from 'librechat-data-provider';
|
||||
import type {
|
||||
EmbeddedResource,
|
||||
ListToolsResult,
|
||||
ImageContent,
|
||||
AudioContent,
|
||||
TextContent,
|
||||
Tool,
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { SearchResultData, UIResource, TPlugin } from 'librechat-data-provider';
|
||||
import type { TokenMethods, JsonSchemaType, IUser } from '@librechat/data-schemas';
|
||||
import type * as t from '@modelcontextprotocol/sdk/types.js';
|
||||
import type { FlowStateManager } from '~/flow/manager';
|
||||
import type { RequestBody } from '~/types/http';
|
||||
import type * as o from '~/mcp/oauth/types';
|
||||
|
|
@ -57,10 +64,10 @@ export interface MCPPrompt {
|
|||
|
||||
export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'error';
|
||||
|
||||
export type MCPTool = z.infer<typeof t.ToolSchema>;
|
||||
export type MCPToolListResponse = z.infer<typeof t.ListToolsResultSchema>;
|
||||
export type ToolContentPart = t.TextContent | t.ImageContent | t.EmbeddedResource | t.AudioContent;
|
||||
export type ImageContent = Extract<ToolContentPart, { type: 'image' }>;
|
||||
export type MCPTool = Tool;
|
||||
export type MCPToolListResponse = ListToolsResult;
|
||||
export type ToolContentPart = TextContent | ImageContent | EmbeddedResource | AudioContent;
|
||||
export type { TextContent, ImageContent, EmbeddedResource, AudioContent };
|
||||
export type MCPToolCallResponse =
|
||||
| undefined
|
||||
| {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import type { ClientOptions, OpenAIClientOptions } from '@librechat/agents';
|
||||
import type { TEndpoint } from 'librechat-data-provider';
|
||||
import type { TConfig } from 'librechat-data-provider';
|
||||
import type { EndpointTokenConfig, ServerRequest } from '~/types';
|
||||
|
||||
export type TCustomEndpointsConfig = Partial<{ [key: string]: Omit<TEndpoint, 'order'> }>;
|
||||
export type TCustomEndpointsConfig = Partial<{ [key: string]: Omit<TConfig, 'order'> }>;
|
||||
|
||||
/**
|
||||
* Interface for user key values retrieved from the database
|
||||
|
|
|
|||
|
|
@ -324,7 +324,8 @@ export const endpointSchema = baseEndpointSchema.merge(
|
|||
defaultParamsEndpoint: z.string().default('custom'),
|
||||
paramDefinitions: z.array(z.record(z.any())).optional(),
|
||||
})
|
||||
.strict(),
|
||||
.strict()
|
||||
.optional(),
|
||||
customOrder: z.number().optional(),
|
||||
directEndpoint: z.boolean().optional(),
|
||||
titleMessageRole: z.string().optional(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue