mirror of
https://github.com/danny-avila/LibreChat.git
synced 2026-04-07 08:25:23 +02:00
fix: only require MCP tool approval when tool matches toolApproval config
The validation flow in MCP.js was running unconditionally for all MCP tool calls. Now checks requiresApproval() against the toolApproval config using the full tool key (toolName_mcp_serverName) before initiating the approval flow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6f4eafeb5f
commit
5ee53e258f
1 changed files with 55 additions and 40 deletions
|
|
@ -9,6 +9,7 @@ const {
|
|||
const {
|
||||
sendEvent,
|
||||
MCPOAuthHandler,
|
||||
requiresApproval,
|
||||
isMCPDomainAllowed,
|
||||
normalizeServerName,
|
||||
normalizeJsonSchema,
|
||||
|
|
@ -17,7 +18,14 @@ const {
|
|||
buildOAuthToolCallName,
|
||||
MCPToolCallValidationHandler,
|
||||
} = require('@librechat/api');
|
||||
const { Time, CacheKeys, Constants, isAssistantsEndpoint } = require('librechat-data-provider');
|
||||
const {
|
||||
Time,
|
||||
CacheKeys,
|
||||
Constants,
|
||||
ContentTypes,
|
||||
EModelEndpoint,
|
||||
isAssistantsEndpoint,
|
||||
} = require('librechat-data-provider');
|
||||
const {
|
||||
getOAuthReconnectionManager,
|
||||
getMCPServersRegistry,
|
||||
|
|
@ -625,59 +633,66 @@ function createToolInstance({
|
|||
derivedSignal.addEventListener('abort', abortHandler, { once: true });
|
||||
}
|
||||
|
||||
// Tool call validation flow - requires user approval before executing
|
||||
const validationFlowType = MCPToolCallValidationHandler.getFlowType();
|
||||
const { validationId, flowMetadata } =
|
||||
await MCPToolCallValidationHandler.initiateValidationFlow(
|
||||
userId,
|
||||
serverName,
|
||||
toolName,
|
||||
typeof toolArguments === 'string' ? { input: toolArguments } : toolArguments,
|
||||
);
|
||||
// Tool call validation flow - only if tool requires approval
|
||||
const appConfig = await getAppConfig({ role: config?.configurable?.user?.role });
|
||||
const toolApprovalConfig = appConfig?.endpoints?.[EModelEndpoint.agents]?.toolApproval;
|
||||
const toolKey = `${toolName}${Constants.mcp_delimiter}${normalizeServerName(serverName)}`;
|
||||
const needsApproval = requiresApproval(toolKey, toolApprovalConfig);
|
||||
|
||||
/** @type {{ id: string; delta: AgentToolCallDelta }} */
|
||||
const validationData = {
|
||||
id: stepId,
|
||||
delta: {
|
||||
type: StepTypes.TOOL_CALLS,
|
||||
tool_calls: [{ ...toolCall, args: '' }],
|
||||
validation: validationId,
|
||||
expires_at: Date.now() + Time.TEN_MINUTES,
|
||||
},
|
||||
};
|
||||
|
||||
if (streamId) {
|
||||
await GenerationJobManager.emitChunk(streamId, {
|
||||
event: GraphEvents.ON_RUN_STEP_DELTA,
|
||||
data: validationData,
|
||||
});
|
||||
} else {
|
||||
sendEvent(res, { event: GraphEvents.ON_RUN_STEP_DELTA, data: validationData });
|
||||
}
|
||||
|
||||
try {
|
||||
await flowManager.createFlow(validationId, validationFlowType, flowMetadata, derivedSignal);
|
||||
if (needsApproval) {
|
||||
const validationFlowType = MCPToolCallValidationHandler.getFlowType();
|
||||
const { validationId, flowMetadata } =
|
||||
await MCPToolCallValidationHandler.initiateValidationFlow(
|
||||
userId,
|
||||
serverName,
|
||||
toolName,
|
||||
typeof toolArguments === 'string' ? { input: toolArguments } : toolArguments,
|
||||
);
|
||||
|
||||
/** @type {{ id: string; delta: AgentToolCallDelta }} */
|
||||
const successData = {
|
||||
const validationData = {
|
||||
id: stepId,
|
||||
delta: {
|
||||
type: StepTypes.TOOL_CALLS,
|
||||
tool_calls: [{ ...toolCall }],
|
||||
tool_calls: [{ ...toolCall, args: '' }],
|
||||
validation: validationId,
|
||||
expires_at: Date.now() + Time.TEN_MINUTES,
|
||||
},
|
||||
};
|
||||
|
||||
if (streamId) {
|
||||
await GenerationJobManager.emitChunk(streamId, {
|
||||
event: GraphEvents.ON_RUN_STEP_DELTA,
|
||||
data: successData,
|
||||
data: validationData,
|
||||
});
|
||||
} else {
|
||||
sendEvent(res, { event: GraphEvents.ON_RUN_STEP_DELTA, data: successData });
|
||||
sendEvent(res, { event: GraphEvents.ON_RUN_STEP_DELTA, data: validationData });
|
||||
}
|
||||
|
||||
try {
|
||||
await flowManager.createFlow(validationId, validationFlowType, flowMetadata, derivedSignal);
|
||||
|
||||
/** @type {{ id: string; delta: AgentToolCallDelta }} */
|
||||
const successData = {
|
||||
id: stepId,
|
||||
delta: {
|
||||
type: StepTypes.TOOL_CALLS,
|
||||
tool_calls: [{ ...toolCall }],
|
||||
},
|
||||
};
|
||||
if (streamId) {
|
||||
await GenerationJobManager.emitChunk(streamId, {
|
||||
event: GraphEvents.ON_RUN_STEP_DELTA,
|
||||
data: successData,
|
||||
});
|
||||
} else {
|
||||
sendEvent(res, { event: GraphEvents.ON_RUN_STEP_DELTA, data: successData });
|
||||
}
|
||||
} catch (validationError) {
|
||||
throw new Error(
|
||||
`Tool call validation required for ${serverName}/${toolName}. User rejected or validation timed out.`,
|
||||
);
|
||||
}
|
||||
} catch (validationError) {
|
||||
throw new Error(
|
||||
`Tool call validation required for ${serverName}/${toolName}. User rejected or validation timed out.`,
|
||||
);
|
||||
}
|
||||
|
||||
const customUserVars =
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue