Skip to content

Provision A365 proxy app on develop-mcp publish - #499

Open
deepaligargms wants to merge 3 commits into
microsoft:mainfrom
deepaligargms:deepaligarg-microsoft-publish-a365-proxy-app
Open

deepaligargms wants to merge 3 commits into
microsoft:mainfrom
deepaligargms:deepaligarg-microsoft-publish-a365-proxy-app

Conversation

@deepaligargms

@deepaligargms deepaligargms commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Wire develop-mcp publish to provision the A365 proxy Entra app (confidential app + secret) and forward its credentials to the platform, so custom (non-Dataverse) MCP servers actually get a Power Platform connector created at publish time.

Previously publish created only the PublicClients app, so the platform's connector-creation step logged A365ProxyConnectorCreation=SkippedNoCredentials and skipped. The register flow already provisions this proxy app; this mirrors it in publish.

  • CreateEntraAppsAsync now creates {server}-A365Proxy (confidential, with secret) first, failing the publish if it can't be created; CreateProxyAppAsync self-cleans its own orphan on partial failure.
  • PublishMcpServerRequest carries a365ProxyClientId / a365ProxyClientSecret; PublishMcpServerResponse reads A365ProxyRedirectUri (+ A365ProxyConnectorId).
  • After publish, the proxy app's redirect URIs are set from A365ProxyRedirectUri (tc/non-tc list), mirroring register; warns if absent.
  • The McpServer required-resource-access grant is added onto both the A365 proxy app and the PublicClients app. The platform wires the connector with the proxy app as its OAuth client and McpServerAppId as the resource, so the proxy app must hold this grant or Entra rejects the connector token request with AADSTS650057.
  • a365ProxyClientSecret is added to RedactSecretFields so the new secret is masked as ***REDACTED*** in verbose request-payload logging (alongside the other client secrets).
  • RollbackEntraAppsAsync now deletes both apps.

Paired MCP-Platform change (connector create at publish, tenant-publish at approve) is already merged.

Tests: new PublishCommandExecutorEntraAppTests (incl. grant-on-both-apps assertion), RedactSecretsFromPayload_RedactsA365ProxyClientSecret; updated regression + dry-run tests. Full suite green (2019 passed).

Publish now creates the confidential A365 proxy Entra app (app + secret) alongside the PublicClients app and forwards its credentials to the platform, so custom (non-Dataverse) MCP servers get a Power Platform connector created at publish time instead of the platform logging A365ProxyConnectorCreation=SkippedNoCredentials. Mirrors the register flow: proxy app created first (fatal on failure, with self-cleanup), request carries the proxy clientId/secret, proxy redirect URIs are updated post-publish, and rollback deletes both apps.

Co-authored-by: Copilot App <[email protected]>
@deepaligargms
deepaligargms requested review from a team as code owners September 22, 2026 04:55
Copilot AI lite review requested due to automatic review settings September 22, 2026 04:55

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.

Copilot review overview

🟡 Changes recommended

One or more issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 2 High severity

Open (2)
What changed in this PR

Adds A365 proxy app provisioning to develop-mcp publish, forwards credentials for connector creation, configures redirect URIs, and adds rollback/test coverage.

Changes:

  • Provisions confidential proxy apps with secrets.
  • Extends publish request/response models.
  • Updates rollback, redirect URI handling, tests, and changelog.
File Description
src/​Tests/​Microsoft.Agents.A365.DevTools.Cli.Tests/​Commands/​PublishCommandExecutorEntraAppTests.cs Updated as part of this pull request.
src/​Tests/​Microsoft.Agents.A365.DevTools.Cli.Tests/​Commands/​PublishCommandExecutorDryRunTests.cs Updated as part of this pull request.
src/​Tests/​Microsoft.Agents.A365.DevTools.Cli.Tests/​Commands/​DevelopMcpCommandRegressionTests.cs Updated as part of this pull request.
src/​Microsoft.Agents.A365.DevTools.Cli/​Models/​PublishMcpServerResponse.cs Updated as part of this pull request.
src/​Microsoft.Agents.A365.DevTools.Cli/​Models/​PublishMcpServerRequest.cs Updated as part of this pull request.
src/​Microsoft.Agents.A365.DevTools.Cli/​Commands/​PublishCommandExecutor.cs Updated as part of this pull request.
CHANGELOG.md Updated as part of this pull request.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

The platform wires the A365 proxy connector with the proxy app as its OAuth client and McpServerAppId as the resource, so the proxy app must hold the McpServerScope required-resource-access grant or Entra rejects the connector's token request with AADSTS650057. ConfigureEntraAppsAsync previously granted this only on the PublicClients app; now it grants on both the proxy app and the PublicClients app, mirroring register.

Co-authored-by: Copilot App <[email protected]>
Copilot AI review requested due to automatic review settings September 22, 2026 05:39

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.

Copilot review overview

🟡 Changes recommended

Address the cleanup, secret-redaction, and failing dry-run assertion issues.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 3 High severity

Open (3)

The publish request now carries the newly created A365 proxy Entra app secret as a365ProxyClientSecret. RedactSecretFields only masked clientApp1Secret/clientApp2Secret/clientSecret, so verbose request-payload logging wrote the live client secret in plaintext. Add a365ProxyClientSecret to the redaction key set with a regression test.

Co-authored-by: Copilot App <[email protected]>
Copilot AI review requested due to automatic review settings September 22, 2026 18:00

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.

Copilot review overview

🔵 Needs a closer look

Unresolved moderate findings affect cleanup, dry-run accuracy, permission handling, and cancellation behavior.

Review effort: Lite
Findings: None

Resolved since last review (3)
Previously missed (2)

In code that hasn't changed since last review

Medium severity Include proxy configuration in dry-run output

src/​Microsoft.Agents.A365.DevTools.Cli/​Commands/​PublishCommandExecutor.cs:92

The real publish path now also grants the MCP server permission to both apps and, when returned, writes the proxy redirect URIs, but this dry-run output still says it only back-fills the PPMI scope. That makes --dry-run under-report the changes users are previewing; update this message and its assertion to describe the new proxy configuration as well.

Medium severity Roll back proxy app when public client creation fails

src/​Microsoft.Agents.A365.DevTools.Cli/​Commands/​PublishCommandExecutor.cs:335

The proxy app is now created before CreatePublicClientsAppAsync, but an exception from that call can escape CreateEntraAppsAsync: its Graph app-creation call is outside the provisioner's catch, and ExecuteAsync has no rollback around app creation. A transient Graph/network failure here therefore leaves the newly created A365 proxy registration and secret orphaned. Catch this failure (while preserving cancellation), delete the proxy app, and return a failed publish; add a regression test for this partial-creation path.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Requesting changes. The main concern is credential hygiene: publish now mints a confidential app with a secret on every run, and it can be left behind on partial failure. Details inline.


// Confidential A365 proxy app + secret. Required for custom (non-Dataverse) servers: the
// platform creates the Power Platform connector only when its credentials are supplied.
var a365ProxyApp = await provisioner.CreateProxyAppAsync(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The proxy app and secret are created unconditionally, but the comment says they're only required for custom (non-Dataverse) servers. For Dataverse / first-party servers this mints a credential nobody uses. Please gate it on server type.

Also, the secret gets Graph's default lifetime and publish has no --secret-lifetime-months like register does, so tenants with stricter app management policies can now fail publish where it used to succeed. Please add/pass that option.

// platform creates the Power Platform connector only when its credentials are supplied.
var a365ProxyApp = await provisioner.CreateProxyAppAsync(
input.ServerName, tenantId, suffix: "A365Proxy", roleDisplay: "A365 Proxy",
serviceTreeId: null, ct: ct);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

serviceTreeId: null is hard-coded, so this will fail with ServiceTreeValueMissing in tenants that enforce ServiceTree (same issue as #496). Please expose --service-tree-id on publish and pass it through here and to the public clients app.

serviceTreeId: null, ct: ct);
if (a365ProxyApp is null) return null;

var publicClients = await provisioner.CreatePublicClientsAppAsync(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

If this throws (Graph error, throttling, cancellation), the proxy app and its secret created above are left in the tenant; RollbackEntraAppsAsync only runs when the platform call fails. Please catch here, delete the proxy app, return failure, and add a test for this path.

}
else
{
var msg = "A365 Proxy redirect URI was not returned by publish. Redirect URI configuration skipped.";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This warning fires on every non-custom publish, since only custom servers get a redirect URI back, so valid Dataverse / first-party publishes will look degraded. Please only warn when a redirect URI was expected.

{
_logger.LogInformation("[DRY RUN] Would create Entra app '{PublicClients}' in tenant", $"{input.ServerName}-PublicClients");
_logger.LogInformation("[DRY RUN] Would create Entra apps '{PublicClients}' and '{A365Proxy}' in tenant", $"{input.ServerName}-PublicClients", $"{input.ServerName}-A365Proxy");
_logger.LogInformation("[DRY RUN] Would call publish endpoint and back-fill PPMI scope on the created app");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Dry run still only mentions the PPMI back-fill. It should also list the proxy app creation and the permission / redirect URI configuration.

Comment thread CHANGELOG.md
**Option B — CLI** (`a365 setup admin`) has been removed in this release. Use Option A above, or copy the PowerShell instructions printed in the `a365 setup all` summary output.

### Added
- `develop-mcp publish` now creates the A365 proxy Entra app and sends its credentials to the platform, so custom (non-Dataverse) MCP servers get a Power Platform connector created at publish time.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing the (#499) reference.

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