Skip to content

Add manual cleanup block for unpublish - #477

Open
deepaligargms wants to merge 4 commits into
mainfrom
u/deepaligarg/unpublishchanges
Open

Add manual cleanup block for unpublish#477
deepaligargms wants to merge 4 commits into
mainfrom
u/deepaligarg/unpublishchanges

Conversation

@deepaligargms

Copy link
Copy Markdown
Contributor

Summary

When you unpublish a custom MCP server, the platform removes the tenant publication (MOS title + MCC record) but cannot delete the Entra app registration it created for the server (the *-PublicClients app), because the platform's identity has no rights to delete app registrations in the customer tenant. Previously that app was left dangling after unpublish, with nothing telling the user it existed.

The platform now returns those apps on the unpublish response under a self-describing ManualCleanupRequired block. This PR teaches the CLI to consume that block and delete the apps on the user's behalf — mirroring the rollback the publish flow already does when a publish fails.

What changed

UnpublishServerAsync now returns the parsed response (Task<UnpublishMcpServerResponse?> instead of Task), so the command layer can act on the returned apps. Returns null on failure.
New models mirroring the platform contract:

  • UnpublishMcpServerResponse — Status, Message, ManualCleanupRequired, IsSuccess.
  • McpServerManualCleanup — { Reason, Apps[] }.
  • McpServerAppEntry — { AppName, AppId }.
  • GraphApiService.GetAppObjectIdByAppIdAsync — resolves an app's object id from its app (client) id, since the platform returns the client id but Graph's delete needs the object id.
  • develop mcp unpublish now takes the GraphApiService and, after a successful unpublish, deletes each returned app best-effort: each delete is independent, failures are logged with the app id, and the step degrades gracefully (warn, don't fail the command) when Graph or the tenant can't be resolved — falling back to a manual-cleanup message so the user still knows what to remove.

Testing

CLI + test projects build clean (warnings-as-errors).
Full suite: 1923 passed / 0 failed / 12 skipped.
Added coverage: null-response path (unpublish failed → no throw) and the no-Graph-service cleanup path (degrades gracefully).

Copilot AI lite review requested due to automatic review settings July 17, 2026 20:12
@deepaligargms
deepaligargms requested review from a team as code owners July 17, 2026 20:12
@github-actions

Copy link
Copy Markdown

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None

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 updates the develop mcp unpublish flow to consume the platform’s new ManualCleanupRequired response block and (best-effort) delete Entra app registrations that the platform cannot remove on its own.

Changes:

  • Change UnpublishServerAsync to return a parsed UnpublishMcpServerResponse? (instead of bool) so the command layer can act on returned cleanup artifacts.
  • Add response/cleanup models (UnpublishMcpServerResponse, McpServerManualCleanup, McpServerAppEntry) mirroring the platform contract.
  • Add Graph support to resolve an app object id from an app (client) id and use it to delete returned apps after a successful unpublish.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Tests/Microsoft.Agents.A365.DevTools.Cli.Tests/Commands/DevelopMcpCommandRegressionTests.cs Updates mocks for new unpublish return type; adds regression tests for null response and no-Graph cleanup behavior.
src/Microsoft.Agents.A365.DevTools.Cli/Services/IAgent365ToolingService.cs Updates unpublish service contract to return UnpublishMcpServerResponse?.
src/Microsoft.Agents.A365.DevTools.Cli/Services/GraphApiService.cs Adds appId→objectId lookup helper for later deletion.
src/Microsoft.Agents.A365.DevTools.Cli/Services/Agent365ToolingService.cs Parses unpublish response body (when present) and returns cleanup info to the command layer.
src/Microsoft.Agents.A365.DevTools.Cli/Models/UnpublishMcpServerResponse.cs New unpublish response model (Status/Message/ManualCleanupRequired + IsSuccess).
src/Microsoft.Agents.A365.DevTools.Cli/Models/McpServerManualCleanup.cs New model for the ManualCleanupRequired block.
src/Microsoft.Agents.A365.DevTools.Cli/Models/McpServerAppEntry.cs New model for Entra app registrations returned for cleanup.
src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs Wires unpublish to consume cleanup block and invoke best-effort Entra app deletion.
Comments suppressed due to low confidence (1)

src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs:528

  • The unpublish handler logs an error and returns on failure, but it never sets a non-zero exit code. As a result, a365 develop mcp unpublish can fail (null/!IsSuccess response) while still exiting 0, which breaks scripting/CI usage and conflicts with the repo’s command exit-code rule for failure branches.
            var response = await toolingService.UnpublishServerAsync(envId, serverName);

            if (response is null || !response.IsSuccess)
            {
                logger.LogError("Failed to unpublish MCP server {ServerName} from environment {EnvId}", serverName, envId);
                return;
            }

Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Services/Agent365ToolingService.cs Outdated
Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Services/GraphApiService.cs

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot has a few relevant comments that I think should be addressed. Please take a look at them

Copilot AI review requested due to automatic review settings July 21, 2026 17:51

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

Copilot reviewed 8 out of 8 changed files in this pull request and generated 3 comments.

Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs Outdated
Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs Outdated
Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs Outdated
Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs Outdated
Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Services/GraphApiService.cs
Comment thread src/Microsoft.Agents.A365.DevTools.Cli/Services/Agent365ToolingService.cs Outdated

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

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

@deepaligargms
deepaligargms enabled auto-merge (squash) July 24, 2026 21:02
deepaligargms and others added 4 commits August 5, 2026 21:33
The platform now returns the Public Clients Entra app registration(s) it cannot delete in the customer tenant (AppIdsToCleanup) from the unpublish response. Change UnpublishServerAsync to return the parsed UnpublishMcpServerResponse instead of bool, and have the develop-mcp unpublish subcommand delete each returned app via Graph (resolving the object id from the app id), mirroring the publish rollback pattern. Degrades gracefully with manual-cleanup guidance when Graph is unavailable or the tenant can't be detected.

- New models UnpublishMcpServerResponse + McpServerAppEntry.
- New GraphApiService.GetAppObjectIdByAppIdAsync (appId -> application object id).
- Regression tests updated for the new return type; added null-response and no-graph-service cleanup coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow the platform's self-describing unpublish contract: read the Entra
apps the platform cannot delete from response.ManualCleanupRequired.Apps
instead of the removed AppIdsToCleanup list. Adds the mirroring
McpServerManualCleanup model and updates the cleanup regression test.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 6, 2026 04:34
@deepaligargms
deepaligargms force-pushed the u/deepaligarg/unpublishchanges branch from 61aa166 to d49f91a Compare August 6, 2026 04:34

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

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/Microsoft.Agents.A365.DevTools.Cli/Commands/DevelopMcpCommand.cs:546

  • Manual cleanup guidance from the service response is partially dropped: ManualCleanupRequired.Reason is never passed into CleanupEntraAppsAsync, so when Graph/tenant detection is unavailable the CLI warning cannot include the platform-provided reason/instructions. Preserve that reason in the user-facing warning to make the fallback cleanup instructions self-describing.
            // The platform removes the tenant publication but cannot delete Entra app registrations in the
            // customer tenant, so it returns any it created for this server (in ManualCleanupRequired) for
            // the CLI to clean up here.
            await CleanupEntraAppsAsync(logger, graphApiService, response.ManualCleanupRequired?.Apps, serverName);
        });

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.

4 participants