Skip to content

Update new-command.md to two-generic options pattern#2707

Open
KarishmaGhiya wants to merge 1 commit into
microsoft:mainfrom
KarishmaGhiya:new-command-options
Open

Update new-command.md to two-generic options pattern#2707
KarishmaGhiya wants to merge 1 commit into
microsoft:mainfrom
KarishmaGhiya:new-command-options

Conversation

@KarishmaGhiya
Copy link
Copy Markdown
Member

Replace the legacy one-generic BaseCommand pattern with the new two-generic SubscriptionCommand<TOptions, TResult> pattern using [Option] attribute-driven binding throughout new-command.md.

Key changes:

  • Options Class section: flat POCOs with [Option] attributes instead of inheritance hierarchies
  • Added Key Principles and Usage Patterns subsections covering 5 patterns (standard, mutually exclusive, enum/constrained, interface constraints, list/query)
  • Command Class section: two-generic base class, ISubscriptionResolver injection, ExecuteAsync takes TOptions directly
  • Base Service Command Classes: interface constraint pattern instead of RegisterOptions/BindOptions overrides
  • Unit Tests: SubscriptionCommandUnitTestsBase instead of CommandUnitTestsBase
  • Common Pitfalls and Best Practices: updated guidance

What does this PR do?

[Provide a clear, concise description of the changes]

[Add additional context, screenshots, or information that helps reviewers]

GitHub issue number?

[Link to the GitHub issue this PR addresses]

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
    • Created a changelog entry if the change falls among the following: new feature, bug fix, UI/UX update, breaking change, or updated dependencies. Follow the changelog entry guide
  • 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 running the script ./eng/scripts/Process-PackageReadMe.ps1. See Package README
    • 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 renamed tools, follow the Tool Rename Checklist and tag the PR with the breaking-change label
    • 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 command list in servers/Azure.Mcp.Server/docs/azmcp-commands.md
    • Ran ./eng/scripts/Update-AzCommandsMetadata.ps1 to update tool metadata in azmcp-commands.md (required for CI)
    • 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

Replace the legacy one-generic BaseCommand<TOptions> pattern with the new
two-generic SubscriptionCommand<TOptions, TResult> pattern using [Option]
attribute-driven binding throughout new-command.md.

Key changes:
- Options Class section: flat POCOs with [Option] attributes instead of
  inheritance hierarchies
- Added Key Principles and Usage Patterns subsections covering 5 patterns
  (standard, mutually exclusive, enum/constrained, interface constraints,
  list/query)
- Command Class section: two-generic base class, ISubscriptionResolver
  injection, ExecuteAsync takes TOptions directly
- Base Service Command Classes: interface constraint pattern instead of
  RegisterOptions/BindOptions overrides
- Unit Tests: SubscriptionCommandUnitTestsBase instead of
  CommandUnitTestsBase
- Common Pitfalls and Best Practices: updated guidance

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
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

Updates servers/Azure.Mcp.Server/docs/new-command.md to document the newer two-generic SubscriptionCommand<TOptions, TResult> approach with attribute-driven option binding, replacing the legacy one-generic command/option-definition pattern.

Changes:

  • Reworks the “Options Class” guidance to use flat POCOs and OptionBinder-based binding with [Option] attributes.
  • Updates command examples to inject ISubscriptionResolver, use ExecuteAsync(CommandContext, TOptions, CancellationToken), and rely on ValidateOptions.
  • Refreshes base-command and unit-test guidance to align with SubscriptionCommandUnitTestsBase.

Invoking Livetests

Copilot submitted PRs are not trustworthy by default. Users with write access to the repo need to validate the contents of this PR before leaving a comment with the text /azp run mcp - pullrequest - live. This will trigger the necessary livetest workflows to complete required validation.

Comment on lines +525 to +528
- Options classes are **flat POCOs** — no class inheritance. Each command has its own options class.
- `OptionBinder` discovers all `[Option]`-attributed properties on the concrete class and handles both registration (adding to the CLI parser) and binding (populating from parse results) automatically.
- **Required vs optional** is determined entirely by nullability: `required` keyword or non-nullable types = required; `?` = optional. No `.AsRequired()`/`.AsOptional()` calls needed.
- **No shared state**: Each command gets its own options instance per request — thread-safe by design.
Comment on lines +527 to +528
- **Required vs optional** is determined entirely by nullability: `required` keyword or non-nullable types = required; `?` = optional. No `.AsRequired()`/`.AsOptional()` calls needed.
- **No shared state**: Each command gets its own options instance per request — thread-safe by design.
Comment on lines +528 to +530
- **No shared state**: Each command gets its own options instance per request — thread-safe by design.
- **Implement `ISubscriptionOption`** if the command needs `Subscription`/`Tenant`. This enables `ISubscriptionResolver` post-processing.
- **Implement additional option interfaces** (e.g., `IStorageAccountOption`) only when base command classes need type-safe access to specific properties for shared behavior like validation.
Comment on lines 727 to 733
```csharp
using System.Net;
using Azure.Mcp.Tools.{Toolset}.Models;
using Azure.Mcp.Tools.{Toolset}.Options; // REQUIRED: For {Toolset}OptionDefinitions
using Azure.Mcp.Tools.{Toolset}.Options.{Resource}; // For resource-specific options
using Azure.Mcp.Tools.{Toolset}.Options; // REQUIRED: For options classes
using Azure.Mcp.Tools.{Toolset}.Services;
using Microsoft.Extensions.Logging;
using Microsoft.Mcp.Core.Commands;
Comment on lines +668 to 669
public class BlobUploadOptions : ISubscriptionOption, IBlobOption
{
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants