Skip to content

Conversation

@tmeschter
Copy link
Member

@tmeschter tmeschter commented Jan 9, 2026

What does this PR do?

The AppLens tool has a very low success rate. Looking through the code there are several common, non-exceptional cases where we're throwing an exception that escapes the tool. The MCP server infrastructure captures these exceptions and treats them as failures.

For example, we will currently throw an exception if the resource can't be found. This is pretty normal--the user may have mistyped the name, or the resource exists but not in the specified subscription. Rather than throwing exceptions we should simply return a message telling the LM that the resource wasn't found and to check the spelling.

Most of the changes here are concerned with bubbling existing messages up to the LM in this manner. This is not a complete solution to the problem but does address some low-hanging fruit. Beyond this we'll need to invest in better telemetry for AppLens so that we can see where things tend to go sideways.

GitHub issue number?

https://github.com/microsoft/mcp-pr/issues/172

Pre-merge Checklist

  • Required for All PRs
    • Read contribution guidelines
    • PR title clearly describes the change
    • Commit history is clean with descriptive messages (cleanup guide)
    • Added comprehensive tests for new/modified functionality
    • Updated servers/Azure.Mcp.Server/CHANGELOG.md and/or servers/Fabric.Mcp.Server/CHANGELOG.md for product changes (features, bug fixes, UI/UX, updated dependencies)
  • For MCP tool changes:
    • One tool per PR: This PR adds or modifies only one MCP tool for faster review cycles
    • Updated servers/Azure.Mcp.Server/README.md and/or servers/Fabric.Mcp.Server/README.md documentation
    • Validate README.md changes using script at eng/scripts/Process-PackageReadMe.ps1. See Package README
    • Updated command list in /servers/Azure.Mcp.Server/docs/azmcp-commands.md and/or /docs/fabric-commands.md
    • Run .\eng\scripts\Update-AzCommandsMetadata.ps1 to update tool metadata in azmcp-commands.md (required for CI)
    • For new or modified tool descriptions, ran ToolDescriptionEvaluator and obtained a score of 0.4 or more and a top 3 ranking for all related test prompts
    • For tools with new names, including new tools or renamed tools, update consolidated-tools.json
    • For new tools associated with Azure services or publicly available tools/APIs/products, add URL to documentation in the PR description
  • Extra steps for Azure MCP Server tool changes:
    • Updated test prompts in /servers/Azure.Mcp.Server/docs/e2eTestPrompts.md
    • 👉 For Community (non-Microsoft team member) PRs:
      • Security review: Reviewed code for security vulnerabilities, malicious code, or suspicious activities before running tests (crypto mining, spam, data exfiltration, etc.)
      • Manual tests run: added comment /azp run mcp - pullrequest - live to run Live Test Pipeline

In AppLens calls there are a number of operations that may "fail" in non-exceptional ways: we may not be able to find the specified resource, or we find multiple resources of the same name, or the resource type isn't supported, etc. Currently we have several operation-specific type hierarchies representing this, with concrete types for the success and failure cases sharing an abstract base type.

Since the pattern is always the same this change replaces them with a general purpose `Result<T>` base type and `Success<T>` and `Failure<T>` concrete types. `T` represents the type of the data returned in a successful operation, while `Failure<T>` exposes a `Message` field containing more information about the failure.
The term "result" is somewhat overloaded in the AppLens code. To better distinguish actual data produced by an operation from the success or failure of the operation itself, this change renames `DiagnosticResult` to `AppLensInsights`. This is a better reflection of what the type holds, anyway.
There are a few places where we're turning a `Failure<>` into an exception. This is generally not what we want to do. For one thing, if the issue were truly exceptional we should have already thrown the exception rather than returning `Failure<>`. Also, a local "failure" doesn't always mean we should treat the overall tool call as failed; we may just need to provide more guidance to the LM so it can try again.

Here we replace a couple of `throw`s with `Failure<>`s, and update `IAppLensService/AppLensService` to return `Result<AppLensInsight>`. The calling code in `ResourceDiagnoseCommand` is, in turn, updated to return the strings from any `Failure<>` results.
@tmeschter tmeschter force-pushed the dev/tomescht/260108-RemoveAppLensExceptions branch from 8db8b53 to 075cc0c Compare January 12, 2026 17:15
@tmeschter tmeschter marked this pull request as ready for review January 12, 2026 17:49
@tmeschter tmeschter requested review from a team and msalaman as code owners January 12, 2026 17:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request refactors the AppLens tool to reduce exception throwing by introducing a Result pattern for error handling. Instead of throwing exceptions for common failures (like resource not found), the code now returns structured failure results that can be communicated to the language model as informative messages.

Changes:

  • Introduced a generic Result pattern with Success and Failure types to replace custom result types
  • Updated AppLensService to return Result instead of throwing exceptions for common failure scenarios
  • Modified ResourceDiagnoseCommand to handle both success and failure results appropriately
  • Updated tests to work with the new Success wrapper type

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/Azure.Mcp.Tools.AppLens/src/Models/AppLensModels.cs Introduces Result, Success, and Failure records; removes old custom result types; renames DiagnosticResult to AppLensInsights; adds Message field to ResourceDiagnoseCommandResult
tools/Azure.Mcp.Tools.AppLens/src/Services/IAppLensService.cs Updates interface signature to return Result instead of DiagnosticResult
tools/Azure.Mcp.Tools.AppLens/src/Services/AppLensService.cs Implements new Result pattern throughout; converts exception throws to failure returns; updates method signatures and return types
tools/Azure.Mcp.Tools.AppLens/src/Commands/Resource/ResourceDiagnoseCommand.cs Adds switch expression to handle Success and Failure cases; maps results to appropriate command responses
tools/Azure.Mcp.Tools.AppLens/src/Models/AppLensJsonContext.cs Updates JSON serialization context to reference AppLensInsights instead of DiagnosticResult
tools/Azure.Mcp.Tools.AppLens/tests/Azure.Mcp.Tools.AppLens.UnitTests/Resource/ResourceDiagnoseCommandTests.cs Updates test mocks and assertions to use Success wrapper
servers/Azure.Mcp.Server/CHANGELOG.md Documents the bug fix for AppLens exceptions

{
Success<AppLensInsights> success => ResponseResult.Create(new(success.Data, Message: null), AppLensJsonContext.Default.ResourceDiagnoseCommandResult),
Failure<AppLensInsights> failure => ResponseResult.Create(new ResourceDiagnoseCommandResult(null, failure.Message), AppLensJsonContext.Default.ResourceDiagnoseCommandResult),
_ => throw new InvalidOperationException($"Unexpected result type from ${nameof(service.DiagnoseResourceAsync)}.")
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The string interpolation syntax is incorrect. It uses $ followed by ${nameof(...)}, which will not perform the interpolation correctly. The correct syntax should use {nameof(...)} inside a string prefixed with $. This will result in an error message that literally contains "${nameof(service.DiagnoseResourceAsync)}" instead of the actual method name "DiagnoseResourceAsync".

Suggested change
_ => throw new InvalidOperationException($"Unexpected result type from ${nameof(service.DiagnoseResourceAsync)}.")
_ => throw new InvalidOperationException($"Unexpected result type from {nameof(service.DiagnoseResourceAsync)}.")

Copilot uses AI. Check for mistakes.
Comment on lines +90 to +95
context.Response.Results = result switch
{
Success<AppLensInsights> success => ResponseResult.Create(new(success.Data, Message: null), AppLensJsonContext.Default.ResourceDiagnoseCommandResult),
Failure<AppLensInsights> failure => ResponseResult.Create(new ResourceDiagnoseCommandResult(null, failure.Message), AppLensJsonContext.Default.ResourceDiagnoseCommandResult),
_ => throw new InvalidOperationException($"Unexpected result type from ${nameof(service.DiagnoseResourceAsync)}.")
};
Copy link

Copilot AI Jan 12, 2026

Choose a reason for hiding this comment

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

The new failure path introduced in the Result pattern (when DiagnoseResourceAsync returns a Failure result) lacks test coverage. While the tests check for exceptions being thrown, there are no tests that verify the behavior when the service returns a Failure result containing an error message, such as when a resource is not found or when the AppLens session creation fails. Consider adding tests that mock the service to return Failure results and verify that the command properly handles these cases and returns appropriate responses to the user.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@alzimmermsft alzimmermsft left a comment

Choose a reason for hiding this comment

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

Does this change create larger implications on what we determine to be a successful or failed MCP tool call? For example, does the failure to find a resource change to a successful tool call but the operation itself failed due to the resource not existing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants