Skip to content

fix(workflow-executor): match MCP tools by config id#1584

Merged
hercemer42 merged 6 commits into
fix/prd-357-fetchremotetools-record-shapefrom
fix/prd-362-match-mcp-tools-by-id
May 20, 2026
Merged

fix(workflow-executor): match MCP tools by config id#1584
hercemer42 merged 6 commits into
fix/prd-357-fetchremotetools-record-shapefrom
fix/prd-362-match-mcp-tools-by-id

Conversation

@hercemer42
Copy link
Copy Markdown

@hercemer42 hercemer42 commented May 19, 2026

Summary

  • ai-proxy: threads a stable DB id from each ToolConfig entry through McpClient, ForestIntegrationClient and the integration tool factories (getZendeskTools, getKolarTools, getSnowflakeTools) onto RemoteTool.id
  • workflow-executor: McpStepExecutor.getFilteredTools now matches tool.id === step.mcpServerId (was tool.sourceId), so workflows targeting a specific MCP server actually resolve their tools instead of always hitting NoMcpToolsError
  • workflow-executor: NoMcpToolsError carries the misconfigured mcpServerId and the list of loaded config ids in its technical message; user-facing message stays generic per the dual-message convention

fixes PRD-362

Stacked on

Targets fix/prd-357-fetchremotetools-record-shape (#1583). PR #1583 introduces the Record<string, ToolConfig> shape this change consumes. Once #1583 merges into feat/prd-214-server-step-mapper, rebase this onto the same base.

Prerequisite

End-to-end resolution also depends on the orchestrator (forestadmin-server, PRD-360) exposing the DB id on each Record<string, ToolConfig> entry, including Forest-connector entries. Until that lands, the executor still fires NoMcpToolsError with an enriched diagnostic message — which is now actionable from the activity log.

Test plan

  • CI passes (ai-proxy + workflow-executor)
  • Manual: run a workflow whose MCP step targets a specific MCP server config id — tool resolves and runs
  • Manual: run a workflow whose MCP step targets a Forest-connector-backed config (Zendesk/Kolar/Snowflake) — tool resolves and runs
  • Manual: deliberately set mcpServerId to a non-existent id and check the activity log carries the requested id + loaded id list

Note

Fix MCP tool filtering in McpStepExecutor to match by config id instead of sourceId

  • Adds an optional id field to McpServerConfig and ForestIntegrationConfig, which is threaded through tool construction so each RemoteTool instance carries a matching mcpServerId property.
  • Updates McpStepExecutor.getFilteredTools to filter tools by tool.mcpServerId instead of sourceId, fixing cross-config tool leakage.
  • Improves NoMcpToolsError to include the requested mcpServerId and list of loaded IDs in its technical message for easier diagnostics.
  • Behavioral Change: steps that previously matched tools by sourceId will now only match tools whose mcpServerId aligns with the step's mcpServerId; steps relying on the old sourceId matching will no longer find tools.

Changes since #1584 opened

  • Renamed MCP server ID variables and map properties to explicitly indicate they store MCP server identifiers [ceacd35]
  • Added explanatory comment to mcpServerId property in RemoteTool class documenting shared usage across McpServerRemoteTool and ServerRemoteTool [095113b]

Macroscope summarized 4edc2e4.

@linear
Copy link
Copy Markdown

linear Bot commented May 19, 2026

PRD-362

Comment thread packages/ai-proxy/src/mcp-client.ts Outdated
Comment thread packages/ai-proxy/src/forest-integration-client.ts Outdated
Comment thread packages/ai-proxy/src/forest-integration-client.ts
Comment thread packages/ai-proxy/src/mcp-client.ts Outdated
Comment thread packages/ai-proxy/src/remote-tool.ts Outdated
Comment thread packages/ai-proxy/src/forest-integration-client.ts
Comment thread packages/workflow-executor/src/executors/mcp-step-executor.ts Outdated
Comment thread packages/workflow-executor/src/errors.ts
Comment thread packages/workflow-executor/test/executors/mcp-step-executor.test.ts Outdated
Comment thread packages/workflow-executor/test/executors/mcp-step-executor.test.ts Outdated
Comment thread packages/workflow-executor/test/errors.test.ts Outdated
@qltysh
Copy link
Copy Markdown

qltysh Bot commented May 19, 2026

Qlty


Coverage Impact

⬆️ Merging this pull request will increase total coverage on fix/prd-357-fetchremotetools-record-shape by 0.01%.

Modified Files with Diff Coverage (8)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/integrations/snowflake/tools.ts100.0%
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/remote-tool.ts100.0%
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/integrations/zendesk/tools.ts100.0%
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/forest-integration-client.ts100.0%
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/mcp-client.ts100.0%
Coverage rating: A Coverage rating: A
packages/ai-proxy/src/integrations/kolar/tools.ts100.0%
Coverage rating: A Coverage rating: A
packages/workflow-executor/src/errors.ts100.0%
Coverage rating: A Coverage rating: A
packages/workflow-executor/src/executors/mcp-step-executor.ts100.0%
Total100.0%
🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

@hercemer42 hercemer42 marked this pull request as ready for review May 19, 2026 18:41
@hercemer42 hercemer42 force-pushed the fix/prd-357-fetchremotetools-record-shape branch from 32c1afb to 480148c Compare May 20, 2026 08:30
Comment thread packages/ai-proxy/src/mcp-client.ts Outdated
const extendedTools = loadedTools.map(
tool => new McpServerRemoteTool({ tool, sourceId: name }),
tool =>
new McpServerRemoteTool({ tool, sourceId: name, id: this.idsByServerName[name] }),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think id is badly named in the code, It should be name mcpServerId, what do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, for me 'id' always means database id, but I can rename if you prefer.

Copy link
Copy Markdown
Member

@Scra3 Scra3 May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes i'm agree but we are in the tool class, not in the mcp, by reading id we can understand that the id of the tool but it's the mcp's id.

Copy link
Copy Markdown
Author

@hercemer42 hercemer42 May 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok fair point, agreed. I made the changes - I changed ID to mcpServerId only where it concerns the private maps, and left it alone in the type and wiring - otherwise I'll have to change it in the orchestrator contract, and I think it's correct there as we call the DB on that layer. Is that ok ?

@@ -4,15 +4,18 @@ export default abstract class RemoteTool<ToolType = unknown> {
base: StructuredToolInterface<ToolType>;
sourceId: string;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sourceId is currently the mcp server name. And Id the mcp server id. Could we improve it by adding a commentary or an alias or ... on other idea?
Maybe id could be sourcerMcpServerId and sourceId => sourceMcpServerName but maybe it's breaking, we can maybe find a way to add a getter to encapsulate the wrong naming ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get sourceMcpServerName() => return sourceId.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is RemoteTool.mcpServerId ok to keep consistent with the other changes ? As for sourceId we can but it'll make more changes so if you really want to do that I suggest we open a refacto ticket.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I'm reading this right, mcpServerId is accurate on McpServerRemoteTool but inherited by ServerRemoteTool (Zendesk, Snowflake, ...), whose value is the DB id of an ai_mcp_configs row with isForestConnector: true. The name reads accurately on half the subclasses, ambiguously on the other half. A dev seeing tool.mcpServerId === step.mcpServerId on a Snowflake tool might wonder "since when is Snowflake an MCP server?".

Would a short JSDoc here clarifying that it covers both user MCP and Forest connectors be enough, or are you leaning toward the broader sourceId rename you mentioned? Happy either way.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the Forest connectors and user MCP servers live in the same ai_mcp_configs so currently we treat them all as MCP servers. But yes I think a comment is suitable. Not pushing for the broader sourceId change because its opportunistic and out of scope for this PR, happy to do it in another ticket as mentioned to Alban.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added the comment.

The executor's filter compared `tool.sourceId` (server display name) against
`step.mcpServerId` (DB id written by the frontend), so any workflow that
specified an MCP server failed with NoMcpToolsError regardless of configuration.

Threads the stable DB id from each ToolConfig entry through ai-proxy
(McpClient, ForestIntegrationClient and the integration factories) onto
RemoteTool.id, and switches the executor to match by id. Enriches
NoMcpToolsError's technical message with the requested id and the loaded
config id list so misconfigurations are diagnosable from the activity log;
the user-facing message stays generic per the dual-message convention.
Surface the requested mcpServerId and loaded-id list in structured logs
when the MCP step filter misses. The dual-message convention keeps the
activity log generic for end users, so without this the enriched
technical message would only live on the Error object and never become
observable to engineers.
…ig.id optional, drop redundant comments

Aligns ForestIntegrationConfig with RemoteTool/McpServerConfig where id is
already optional, and removes WHAT-explaining comments that the named code
already documents.
`RemoteTool.id` was misleading — the field carries the foreign-key id of
the originating MCP server config, not the tool's own identity. The
consumer-site comparison `tool.mcpServerId === step.mcpServerId` now reads
the relationship plainly.

McpServerConfig.id and ForestIntegrationConfig.id stay unchanged — those
legitimately are the entry's own DB id.
@hercemer42 hercemer42 force-pushed the fix/prd-362-match-mcp-tools-by-id branch from a61082e to 4edc2e4 Compare May 20, 2026 11:02
The config types (`ForestIntegrationConfig.id`, `McpServerConfig.id`) keep
the wire-shape field name `id`, but the local pass-through bindings inside
`ForestIntegrationClient.loadTools` and `McpClient` are renamed via
destructure-aliasing so the variables and the private map read symmetric
with the `RemoteTool.mcpServerId` they ultimately feed.
…ubclasses

The orchestrator stores Forest connectors alongside user MCP servers in the
same `ai_mcp_configs` table, so the FK lives on the base class.
@hercemer42 hercemer42 merged commit 53618a7 into fix/prd-357-fetchremotetools-record-shape May 20, 2026
30 checks passed
@hercemer42 hercemer42 deleted the fix/prd-362-match-mcp-tools-by-id branch May 20, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants