mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-07-02 12:22:22 +00:00
Reimplement the MCP Apps ui-meta helpers (RESOURCE_MIME_TYPE, getToolUiResourceUri, isToolVisibilityModelOnly, isToolVisibilityAppOnly) in packages/api/src/mcp/apps.ts so @librechat/api no longer imports the ESM-only @modelcontextprotocol/ext-apps from its CommonJS build. ext-apps remains a client-only dependency, removing the require(ESM) boundary that throws ERR_REQUIRE_ESM on Node versions without synchronous require(esm) support. Add an mcpSettings.apps toggle (enabled unless explicitly false). Thread enableApps through connection creation so the io.modelcontextprotocol/ui capability is advertised only when apps are enabled, and gate the resource and app-tool-call routes with a requireMCPAppsEnabled middleware. Authorize app-driven resources/read against the resources and templates a server advertises, so a sandboxed app cannot proxy arbitrary uris. ui:// resources stay allowed and the check fails closed. Render MCP apps in shared and search transcripts display-only by withholding the host-bound bridge handlers and capabilities in read-only views, so an embedded app cannot call tools or read resources with the viewer's auth while the stored tool result still renders.
61 lines
2.2 KiB
JavaScript
61 lines
2.2 KiB
JavaScript
const mongoose = require('mongoose');
|
|
const { logger } = require('@librechat/data-schemas');
|
|
const { mergeAppTools, getAppConfig } = require('./Config');
|
|
const { createMCPServersRegistry, createMCPManager } = require('~/config');
|
|
|
|
/**
|
|
* Resolves the current request's effective MCP allowlists from the merged (tenant-scoped)
|
|
* config. The registry calls this per inspection/connection so admin-panel `mcpSettings`
|
|
* overrides are honored without a restart. Tenant comes from the ALS context inside
|
|
* `getAppConfig`; `userId`/`role` pick up user/role-scoped overrides when an actor exists.
|
|
* @param {{ userId?: string, role?: string }} [ctx]
|
|
*/
|
|
async function resolveMCPAllowlists(ctx) {
|
|
const appConfig = await getAppConfig({ role: ctx?.role, userId: ctx?.userId });
|
|
return {
|
|
allowedDomains: appConfig?.mcpSettings?.allowedDomains,
|
|
allowedAddresses: appConfig?.mcpSettings?.allowedAddresses,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Initialize MCP servers
|
|
*/
|
|
async function initializeMCPs() {
|
|
const appConfig = await getAppConfig({ baseOnly: true });
|
|
const mcpServers = appConfig.mcpConfig;
|
|
|
|
try {
|
|
createMCPServersRegistry(
|
|
mongoose,
|
|
appConfig?.mcpSettings?.allowedDomains,
|
|
appConfig?.mcpSettings?.allowedAddresses,
|
|
resolveMCPAllowlists,
|
|
appConfig?.mcpSettings?.apps,
|
|
);
|
|
} catch (error) {
|
|
logger.error('[MCP] Failed to initialize MCPServersRegistry:', error);
|
|
throw error;
|
|
}
|
|
|
|
try {
|
|
const mcpManager = await createMCPManager(mcpServers || {});
|
|
|
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
const mcpTools = (await mcpManager.getAppToolFunctions()) || {};
|
|
await mergeAppTools(mcpTools);
|
|
const serverCount = Object.keys(mcpServers).length;
|
|
const toolCount = Object.keys(mcpTools).length;
|
|
logger.info(
|
|
`[MCP] Initialized with ${serverCount} configured ${serverCount === 1 ? 'server' : 'servers'} and ${toolCount} ${toolCount === 1 ? 'tool' : 'tools'}.`,
|
|
);
|
|
} else {
|
|
logger.debug('[MCP] No servers configured. MCPManager ready for UI-based servers.');
|
|
}
|
|
} catch (error) {
|
|
logger.error('[MCP] Failed to initialize MCPManager:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
module.exports = initializeMCPs;
|