🏷️ feat: Request Placeholders for Custom Endpoint & MCP Headers (#9095)

* feat: Add conversation ID support to custom endpoint headers

- Add LIBRECHAT_CONVERSATION_ID to customUserVars when provided
- Pass conversation ID to header resolution for dynamic headers
- Add comprehensive test coverage

Enables custom endpoints to access conversation context using {{LIBRECHAT_CONVERSATION_ID}} placeholder.

* fix: filter out unresolved placeholders from headers (thanks @MrunmayS)

* feat: add support for request body placeholders in custom endpoint headers

- Add {{LIBRECHAT_BODY_*}} placeholders for conversationId, parentMessageId, messageId
- Update tests to reflect new body placeholder functionality

* refactor resolveHeaders

* style: minor styling cleanup

* fix: type error in unit test

* feat: add body to other endpoints

* feat: add body for mcp tool calls

* chore: remove changes that unnecessarily increase scope after clarification of requirements

* refactor: move http.ts to packages/api and have RequestBody intersect with Express request body

* refactor: processMCPEnv now uses single object argument pattern

* refactor: update processMCPEnv to use 'options' parameter and align types across MCP connection classes

* feat: enhance MCP connection handling with dynamic request headers to pass request body fields

---------

Co-authored-by: Gopal Sharma <gopalsharma@gopal.sharma1>
Co-authored-by: s10gopal <36487439+s10gopal@users.noreply.github.com>
Co-authored-by: Dustin Healy <dustinhealy1@gmail.com>
This commit is contained in:
Danny Avila 2025-08-16 20:45:55 -04:00 committed by GitHub
parent 627f0bffe5
commit d7d02766ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 353 additions and 171 deletions

View file

@ -6,11 +6,13 @@ import type { TokenMethods } from '@librechat/data-schemas';
import type { FlowStateManager } from '~/flow/manager';
import type { TUser } from 'librechat-data-provider';
import type { MCPOAuthTokens } from '~/mcp/oauth';
import type { RequestBody } from '~/types';
import type * as t from './types';
import { UserConnectionManager } from '~/mcp/UserConnectionManager';
import { ConnectionsRepository } from '~/mcp/ConnectionsRepository';
import { formatToolContent } from './parsers';
import { MCPConnection } from './connection';
import { processMCPEnv } from '~/utils/env';
import { CONSTANTS } from './enum';
/**
@ -179,6 +181,7 @@ Please follow these instructions when using tools from the respective MCP server
toolArguments,
options,
tokenMethods,
requestBody,
flowManager,
oauthStart,
oauthEnd,
@ -190,6 +193,7 @@ Please follow these instructions when using tools from the respective MCP server
provider: t.Provider;
toolArguments?: Record<string, unknown>;
options?: RequestOptions;
requestBody?: RequestBody;
tokenMethods?: TokenMethods;
customUserVars?: Record<string, string>;
flowManager: FlowStateManager<MCPOAuthTokens | null>;
@ -214,6 +218,7 @@ Please follow these instructions when using tools from the respective MCP server
oauthEnd,
signal: options?.signal,
customUserVars,
requestBody,
});
} else {
/** App-level connection */
@ -234,6 +239,17 @@ Please follow these instructions when using tools from the respective MCP server
);
}
const rawConfig = this.getRawConfig(serverName) as t.MCPOptions;
const currentOptions = processMCPEnv({
user,
options: rawConfig,
customUserVars: customUserVars,
body: requestBody,
});
if ('headers' in currentOptions) {
connection.setRequestHeaders(currentOptions.headers || {});
}
const result = await connection.client.request(
{
method: 'tools/call',