From cc45641d7e7f45051e9a555c1dfee2e1afb0f7cb Mon Sep 17 00:00:00 2001
From: Dustin Healy <54083382+dustinhealy@users.noreply.github.com>
Date: Tue, 23 Jun 2026 20:27:30 -0700
Subject: [PATCH] =?UTF-8?q?fix(mcp):=20address=20Codex=20P1/P2=20findings?=
=?UTF-8?q?=20=E2=80=94=20visibility=20filter,=20header=20clobber,=20base?=
=?UTF-8?q?=20path,=20inline=20text?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
App-only tools (visibility: ['app']) were not filtered in MCPServerInspector.getToolFunctions,
so initializeMCPs → getAppToolFunctions → mergeAppTools was silently exposing them to the LLM
tool cache at startup, bypassing the filter that updateMCPServerTools correctly applies.
Applied the same visibility guard that updateMCPServerTools uses.
appToolCall was calling processMCPEnv without customUserVars for DB-sourced servers, then
setRequestHeaders — overwriting the connection's already-correctly-resolved headers with
unresolved {{MCP_API_KEY}} placeholders. Skipped the re-resolve for DB-sourced servers
since the connection carries valid headers from the original callTool setup.
callMCPAppTool and readMCPResource used hardcoded /api/... paths without the apiBaseUrl()
prefix; subdirectory deployments would miss those routes. apiBaseUrl was already imported for
getMCPSandboxUrl — extended it to both API calls for consistency.
MCPAppCard (carousel) and MCPAppView (ToolCall) both checked toolName && serverName first
when deciding to use the app bridge, but parsers.ts now sets those fields on all UIResources
including inline ui:// resources with text content. Resources with text were therefore silently
routed through the app bridge instead of being rendered directly as srcDoc iframes.
Added !resource.text / !app.text guard so inline HTML resources take the correct path.
---
.../Chat/Messages/Content/ToolCall.tsx | 13 ++++++++++++
.../Messages/Content/UIResourceCarousel.tsx | 2 +-
client/src/utils/mcpApps.ts | 4 ++--
packages/api/src/mcp/MCPManager.ts | 21 ++++++++++++-------
.../src/mcp/registry/MCPServerInspector.ts | 11 ++++++++++
5 files changed, 40 insertions(+), 11 deletions(-)
diff --git a/client/src/components/Chat/Messages/Content/ToolCall.tsx b/client/src/components/Chat/Messages/Content/ToolCall.tsx
index f8e83978aa..3325a34b05 100644
--- a/client/src/components/Chat/Messages/Content/ToolCall.tsx
+++ b/client/src/components/Chat/Messages/Content/ToolCall.tsx
@@ -70,6 +70,19 @@ const MCPAppView = React.memo(function MCPAppView({
useAppBridge(iframeRef, app, toolArgs, toolResult, handleSizeChanged);
+ if (app.text && (app.mimeType ?? 'text/html').includes('html')) {
+ return (
+
+
+
+ );
+ }
+
return (
{!loaded && !timedOut && (
diff --git a/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx b/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx
index afa1a0468b..1e67b52c12 100644
--- a/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx
+++ b/client/src/components/Chat/Messages/Content/UIResourceCarousel.tsx
@@ -50,7 +50,7 @@ function MCPAppCard({
handleSizeChanged,
);
- if (resource.toolName && resource.serverName) {
+ if (resource.toolName && resource.serverName && !resource.text) {
return (
<>
{!loaded && (
diff --git a/client/src/utils/mcpApps.ts b/client/src/utils/mcpApps.ts
index b0e884c8f9..6c354f2472 100644
--- a/client/src/utils/mcpApps.ts
+++ b/client/src/utils/mcpApps.ts
@@ -11,7 +11,7 @@ export async function callMCPAppTool(
toolName: string,
args: Record,
) {
- return request.post('/api/mcp/app-tool-call', {
+ return request.post(`${apiBaseUrl()}/api/mcp/app-tool-call`, {
serverName,
toolName,
arguments: args,
@@ -40,7 +40,7 @@ export async function readMCPResource(serverName: string, uri: string, userId?:
}
}
- const promise = request.post('/api/mcp/resources/read', { serverName, uri });
+ const promise = request.post(`${apiBaseUrl()}/api/mcp/resources/read`, { serverName, uri });
resourceCache.set(key, { promise, ts: now });
promise.catch(() => resourceCache.delete(key));
return promise;
diff --git a/packages/api/src/mcp/MCPManager.ts b/packages/api/src/mcp/MCPManager.ts
index 61914d8647..da8341b3a8 100644
--- a/packages/api/src/mcp/MCPManager.ts
+++ b/packages/api/src/mcp/MCPManager.ts
@@ -747,14 +747,19 @@ Please follow these instructions when using tools from the respective MCP server
`${logPrefix} Server "${serverName}" requires Graph API token resolution which is not supported for app tool calls.`,
);
}
- const currentOptions = processMCPEnv({
- user,
- dbSourced: isDbSourced,
- options: rawConfig as t.MCPOptions,
- });
- const resolvedHeaders: Record =
- 'headers' in currentOptions ? { ...(currentOptions.headers || {}) } : {};
- connection.setRequestHeaders(resolvedHeaders);
+ // DB-sourced servers have their customUserVars (e.g. {{MCP_API_KEY}}) resolved during
+ // the original callTool setup. Re-processing without customUserVars would overwrite
+ // those resolved headers with unresolved placeholders, so skip for DB-sourced servers.
+ if (!isDbSourced) {
+ const currentOptions = processMCPEnv({
+ user,
+ dbSourced: false,
+ options: rawConfig as t.MCPOptions,
+ });
+ const resolvedHeaders: Record =
+ 'headers' in currentOptions ? { ...(currentOptions.headers || {}) } : {};
+ connection.setRequestHeaders(resolvedHeaders);
+ }
}
const result = await connection.client.request(
diff --git a/packages/api/src/mcp/registry/MCPServerInspector.ts b/packages/api/src/mcp/registry/MCPServerInspector.ts
index 4e0b9438e4..03f7e49a97 100644
--- a/packages/api/src/mcp/registry/MCPServerInspector.ts
+++ b/packages/api/src/mcp/registry/MCPServerInspector.ts
@@ -175,6 +175,17 @@ export class MCPServerInspector {
const toolFunctions: t.LCAvailableTools = {};
tools.forEach((tool) => {
+ const uiMeta = (tool._meta as Record)?.ui as
+ | Record
+ | undefined;
+ const visibility = uiMeta?.visibility as string[] | undefined;
+ if (
+ Array.isArray(visibility) &&
+ visibility.includes('app') &&
+ !visibility.includes('model')
+ ) {
+ return;
+ }
const name = `${tool.name}${Constants.mcp_delimiter}${serverName}`;
toolFunctions[name] = {
type: 'function',