Skip to content

Fix MCP UAMI persistence and dynamic-values identity threading#9265

Open
AbodeSaafan wants to merge 5 commits into
mainfrom
absaafan-microsoft/fix-mcp-uami-bugs
Open

Fix MCP UAMI persistence and dynamic-values identity threading#9265
AbodeSaafan wants to merge 5 commits into
mainfrom
absaafan-microsoft/fix-mcp-uami-bugs

Conversation

@AbodeSaafan

@AbodeSaafan AbodeSaafan commented Jun 9, 2026

Copy link
Copy Markdown

Commit Type

  • feature - New functionality
  • fix - Bug fix
  • refactor - Code restructuring without behavior change
  • perf - Performance improvement
  • docs - Documentation update
  • test - Test-related changes
  • chore - Maintenance/tooling

Risk Level

  • Low - Minor changes, limited scope
  • Medium - Moderate changes, some user impact
  • High - Major changes, significant user/system impact

What & Why

Two MCP connection bugs in the Consumption designer:

Bug 1: MCP server node reverts to system-assigned after saving a UAMI connection. The connection thunk re-derived the identity from the Logic App via WorkflowService().getAppIdentity() with a strict equals(type, 'UserAssigned') check, dropping the user's UAMI selection (and failing entirely for hybrid SystemAssigned, UserAssigned identities), so the canvas node fell back to system-assigned.

Bug 2: listMcpTools was called without the UAMI identity in v1. getListDynamicValues hardcoded the identity to undefined, so both the cache key and the service call omitted the user's selected identity, causing tool listings to use the wrong auth.

Fix:

  • New getManagedIdentityFromConnection helper reads the stored identity from both connection-storage shapes (multi-auth parameterValueSet.values.identity.value and single-auth parameterValues.identity).
  • getConnectionPropertiesIfRequired prefers the stored connection identity, falling back to the Logic App identity only when none is stored.
  • getListDynamicValues accepts an optional identity arg, includes it (lowercased) in the cache key, and forwards it to the service.
  • dynamicdata.ts extracts the identity from connectionReference.connectionProperties.authentication.identity and threads it through.
  • Service-layer defense-in-depth: the consumption connector _buildMcpAuthentication accepts an identityOverride and the caller computes an effectiveIdentity priority chain.

Impact of Change

  • Users: MCP server connections in the Consumption designer now correctly persist the selected User-Assigned Managed Identity on the canvas node, and listMcpTools calls use the right identity.
  • Developers: New getManagedIdentityFromConnection helper. getListDynamicValues and _buildMcpAuthentication gained an optional identity arg/override.
  • System: No perf or dependency impact.

Test Plan

  • Unit tests added/updated
  • E2E tests added/updated
  • Manual testing completed
  • Tested in:

13 new unit tests across 3 files (1 new spec), all passing:

  • connections.spec.ts (new) — 5 tests for the helper across both storage shapes
  • connector.spec.ts (designer) — 3 tests for identity threading, cache-key separation, and case-insensitive cache reuse
  • connector.spec.ts (shared) — 5 tests for _buildMcpAuthentication priority and caller identity selection

Full targeted runs green. Full suites (873 designer + 2105 shared) still pass.

Contributors

Screenshots/Videos

…mption)

Bug 1: After picking a UAMI in the MCP connection creation panel, the MCP server node on the canvas reverted to system-assigned identity. The connection-update thunk re-derived identity from the Logic App's identity config, which failed for hybrid 'SystemAssigned, UserAssigned' apps. Now reads the identity stored on the connection first.

Bug 2: listMcpTools (and other dynamic-value calls) in the v1 designer ran without the selected identity. Now threads the identity from connectionReference.connectionProperties.authentication.identity through getListDynamicValues, and _buildMcpAuthentication prefers an explicit override over the Logic App identity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 9, 2026 21:53
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

🔒 AI Validation Pending

This PR is from an external contributor. AI validation will be performed after manual review by a maintainer.

Maintainers: Add the external-approved label to this PR after reviewing the changes to enable AI validation.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes managed identity (UAMI) propagation for MCP server connections and MCP dynamic-values in the v1 designer by preferring the identity stored on the connection (across both single-auth and multi-auth storage shapes) and threading the selected identity through dynamic-values calls and service-layer authentication building.

Changes:

  • Add getManagedIdentityFromConnection helper and use it to preserve a user-selected UAMI when re-hydrating connection properties.
  • Thread an optional identity through getDynamicValuesgetListDynamicValues (query + cache key) → connector service calls.
  • Add/extend unit tests validating identity persistence, cache separation by identity, and service-layer MSI auth priority behavior.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
libs/logic-apps-shared/src/designer-client-services/lib/consumption/connector.ts Adds identity override/effective identity selection so MCP MSI auth keeps the selected UAMI (including hybrid identity apps).
libs/logic-apps-shared/src/designer-client-services/lib/consumption/tests/connector.spec.ts Adds tests covering identity propagation and MSI identity resolution priority for MCP auth.
libs/designer/src/lib/core/utils/parameters/dynamicdata.ts Extracts selected identity from connectionReference and forwards it into dynamic-values fetching.
libs/designer/src/lib/core/utils/connectors/connections.ts Introduces getManagedIdentityFromConnection helper to read identity from both connection parameter storage shapes.
libs/designer/src/lib/core/utils/connectors/test/connections.spec.ts New unit tests validating the helper across multi-auth/single-auth shapes and precedence.
libs/designer/src/lib/core/queries/connector.ts Extends getListDynamicValues to accept identity, include it in the cache key, and forward it to the connector service.

Comment on lines 116 to 124
[
'listdynamicvalues',
(connectionId ?? '').toLowerCase(),
connectorId.toLowerCase(),
operationId.toLowerCase(),
dynamicState.operationId?.toLowerCase(),
getParametersKey({ ...dynamicState.parameters, ...parameters }),
identity ?? '',
],

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.

Good catch normalized to lowercase in the cache key (a85b8e5) so case-variant ARM IDs collapse to the same entry. The service call still passes the raw identity to preserve the canonical ID. Added a test in connector.spec.ts that asserts a case-variant second call is a cache hit.

@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

📊 Coverage check completed. See workflow run for details.

AbodeSaafan and others added 3 commits June 9, 2026 15:37
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Three v2-specific gaps still left UAMI broken after publish:

- mcpToolWizard.tsx: getListDynamicValues was missing the identity arg, so listMcpTools fetched tools without the selected UAMI.

- consumption connector managed branch used bare identity instead of the effective identity resolved from parameter values/connection.

- ParseReduxAction reconstructed connectionReferences from $connections but dropped connectionProperties, connectionRuntimeUrl, authentication, and impersonation. This explained the 'works only after publish' symptom: dynamic data could not see the chosen identity until a fresh load via the API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… in v2

Built-in MCP connections returned from Azure store their parameters in

parameterValueSet.values.<key>.value when a non-default authentication set

is selected (e.g. managedServiceIdentity). The consumption connector's

getListDynamicValues branch only checked the flat parameterValues shape, so

UAMI connections fell through to the managed-MCP path and listMcpTools was

called without identity.

Add a _flattenConnectionParameters helper that merges both shapes

(parameterValueSet first, then parameterValues for any keys not already set)

and use it for branch detection and authentication building. Updated tests

exercise the real parameterValueSet-only Azure shape instead of the

redundant dual-shape fixture that previously masked the bug.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AbodeSaafan

Copy link
Copy Markdown
Author

Found another root cause for the v2 issue you hit at https://localhost:4200/v2 pushed as 09a799c12.

Root cause: Built-in MCP connections that use UAMI are returned by Azure in the parameterValueSet.values.<key>.value shape (with parameterValueSet.name === 'managedServiceIdentity'), not the flat parameterValues shape. The shared consumption/connector.ts::getListDynamicValues branch detection only inspected parameterValues?.mcpServerUrl, so UAMI connections fell through into the managed-MCP path and listMcpTools POSTed without identity. This explains why intercepting the request only worked after publishing pre-publish design-time was occasionally serving the flat shape, but the persisted Azure state comes back in the parameterValueSet shape.

Fix: Added _flattenConnectionParameters helper that merges both shapes (parameterValueSet wins, fallback to parameterValues for unset keys) and use it for branch detection + _buildMcpAuthentication. Existing UAMI tests had a redundant parameterValues block alongside parameterValueSet that masked the bug stripped those so the tests exercise the real Azure response shape. All 25 connector.spec.ts tests pass.

No changes needed to the v2 queries/connector.ts wrapper or dynamicdata.ts both already thread the identity correctly. The issue was entirely in the shared service consumed by v1 and v2.

Managed MCP connections persist UAMI at properties.parameterValues.authentication.identity, which the existing reader helpers (flat parameterValueSet / parameterValues paths only) missed. As a result, listMcpTools was posted with no identity even after the user had explicitly picked a UAMI on the existing connection.

Extends both getManagedIdentityFromConnection (v1 + v2) and the consumption connector's listMcpTools identity resolution with a third fallback that reads parameterValues.authentication.identity, gated on authentication.type === 'ManagedServiceIdentity' to avoid misreading other auth shapes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@AbodeSaafan

Copy link
Copy Markdown
Author

Pushed 313a51d with a fix for the v2 issue you reported (calling listMcpTools with the wrong identity even though the saved connection had a UAMI).

Root cause: createManagedMcpConnection writes the UAMI to properties.parameterValues.authentication.identity, but getManagedIdentityFromConnection and the consumption connector's listMcpTools resolver only checked the flat parameterValueSet.values.identity.value / parameterValues.identity paths. So selecting an existing UAMI-backed connection produced a request body with the connection ID but no identity.

Fix: added a third fallback that reads parameterValues.authentication.identity, gated on authentication.type === ''ManagedServiceIdentity'' so we don''t misread other auth shapes. Applied in 3 sites for v1/v2 parity:

  • libs/designer-v2/src/lib/core/utils/connectors/connections.ts
  • libs/designer/src/lib/core/utils/connectors/connections.ts
  • libs/logic-apps-shared/src/designer-client-services/lib/consumption/connector.ts

Added 5 tests (positive nested-shape + negative type-guard in v1/v2 helpers, plus a connector test mirroring the existing managed-MCP UAMI test with the nested shape). All 40 tests in the 3 specs pass.

Out of scope: the pre-publish 401 you saw when manually injecting the identity is a backend requirement (workflow must be published before listMcpTools will accept the call) not something we can fix from the designer.

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.

2 participants