mirror of
https://github.com/microsoft/mcp.git
synced 2026-11-04 12:01:31 +00:00
* docs: make Fabric tool description evaluation optional Keep Azure and other non-Fabric evaluation gates, including stricter toolset requirements, and all other validation unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: use absolute links for Fabric evaluation policy Fix the three AGENTS.md policy links rejected by repository link-format guidance. Policy scope is unchanged. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> * docs: minimize Fabric evaluator exception wording Keep only short Fabric qualifiers in existing guidance, preserving the original requirements and examples. Remove the expanded policy section and links as requested. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
788 lines
34 KiB
Markdown
788 lines
34 KiB
Markdown
# AGENTS.md
|
|
|
|
## Do
|
|
- Use primary constructors for all C# classes
|
|
- Use `System.Text.Json`
|
|
- Make command classes sealed unless designed for inheritance
|
|
- Make members static when possible for AOT compatibility
|
|
- Put each class and interface in separate files
|
|
- Use `subscription` parameter (never `subscriptionId`) - supports both IDs and names
|
|
- Use `resourceGroup` (not `resourceGroupName`)
|
|
- Use singular nouns for resource names (e.g., `server`, not `serverName`)
|
|
- Run `dotnet build` after making changes
|
|
- Follow the `{Resource}{Operation}Command` naming pattern
|
|
- Use the two-generic pattern `SubscriptionCommand<TOptions, TResult>` for new commands
|
|
- Use `[Option]` attributes on flat POCO option classes for option declaration
|
|
- Use `ISubscriptionOption` interface on options classes that need subscription/tenant
|
|
- Always call `HandleException(context, ex)` in catch blocks
|
|
- Include live tests for all commands that interact with Azure resources
|
|
- Create Bicep templates for Azure service commands (`test-resources.bicep`)
|
|
- Include post-deployment scripts (`test-resources-post.ps1`)
|
|
- Record all live tests according to the guidelines in `/docs/recorded-tests.md`
|
|
- Submit one tool per pull request
|
|
- Use `BaseAzureResourceService` for Resource Graph queries when possible
|
|
- Register all response models in JSON serialization context for AOT safety
|
|
- Register all commands in the appropriate Setup.cs file
|
|
- Use concatenated lowercase for command group names (no dashes)
|
|
- Prefer file-scoped changes over project-wide modifications when possible
|
|
- Always review your own code for consistency, maintainability, and testability
|
|
- Always ask for clarifications if the request is ambiguous or lacks sufficient context
|
|
- Write transport-agnostic commands that work in both stdio and HTTP modes
|
|
- Keep commands stateless and thread-safe for multi-user remote scenarios
|
|
- Test commands with different RBAC permissions for OBO scenarios
|
|
|
|
## Don't
|
|
- Use `subscriptionId` parameter name
|
|
- Add unnecessary "-name" suffixes (use `--account` vs `--account-name`)
|
|
- Use the old one-generic `RegisterOptions`/`BindOptions` pattern for new commands (see `/docs/option-conversion.md`)
|
|
- Use `OptionDefinitions` static classes for new commands (use `[Option]` attributes instead)
|
|
- Use options class inheritance hierarchies (use flat POCOs with interface constraints)
|
|
- Skip live tests, live test infrastructure, or test recordings for Azure service commands
|
|
- Redefine base class properties in Options classes
|
|
- Leave commands unregistered
|
|
- Skip error handling or comprehensive tests
|
|
- Use dashes in command group names (use concatenated lowercase)
|
|
- Make project-wide changes when file-scoped changes suffice
|
|
- Check transport type in commands (stdio vs HTTP)
|
|
- Store per-request state in command instance fields
|
|
- Access HttpContext directly from commands
|
|
- Make transport-specific decisions in command logic
|
|
- Assume single-user scenarios when implementing services
|
|
- Use `git stash` in linked worktrees; the stash stack is shared across worktrees and can collide during concurrent work
|
|
|
|
## Commands
|
|
|
|
### File-scoped commands (preferred for faster feedback)
|
|
```powershell
|
|
# Build single project
|
|
dotnet build tools/Azure.Mcp.Tools.Storage/src
|
|
|
|
# Format specific files
|
|
dotnet format --include="tools/Azure.Mcp.Tools.Storage/**/*.cs"
|
|
|
|
# Test specific class
|
|
dotnet test --filter "FullyQualifiedName~StorageAccountListCommandTests"
|
|
|
|
# Type check and validate
|
|
./eng/scripts/Build-Local.ps1 -VerifyNpx
|
|
|
|
# Note: Don't run local builds to check pipeline YAML files (e.g., files in `eng/pipelines/` with `.yml` extension)
|
|
```
|
|
|
|
### Project-wide commands (use sparingly)
|
|
```powershell
|
|
# Full build (when explicitly requested)
|
|
dotnet build
|
|
|
|
# All tests (when needed)
|
|
./eng/scripts/Test-Code.ps1
|
|
|
|
# AOT compatibility check (for new toolsets)
|
|
./eng/scripts/Build-Local.ps1 -BuildNative
|
|
```
|
|
|
|
## Safety and Permissions
|
|
|
|
### Allowed without prompt
|
|
- Read files, list directories
|
|
- Single file builds (`dotnet build path/to/project`)
|
|
- Code formatting (`dotnet format --include="specific/path/**"`)
|
|
- Spelling checks (`.\eng\common\spelling\Invoke-Cspell.ps1`)
|
|
- Unit tests for specific classes
|
|
- Creating/updating documentation
|
|
|
|
### Ask first
|
|
- Installing new packages or dependencies
|
|
- Running project-wide builds or tests
|
|
- Modifying `.csproj`, `.slnx`, or configuration files
|
|
- Deploying test resources (`New-TestResources.ps1`)
|
|
- Making breaking changes to public APIs
|
|
- Adding new toolsets to the solution
|
|
|
|
## Project Overview
|
|
Microsoft MCP (Model Context Protocol) servers provide AI agents with structured access to Azure, Microsoft Fabric, and other Microsoft services. This repository contains the core libraries, multiple MCP servers, service-specific tools, and comprehensive testing infrastructure for building agent-integrated Microsoft service interactions.
|
|
|
|
**Key Components:**
|
|
- **Azure MCP Server**: Complete Azure service integration with 100+ tools
|
|
- **Microsoft Fabric MCP Server**: Fabric workspace and data platform operations
|
|
- **Core Libraries**: Shared infrastructure for command patterns, authentication, and MCP protocol
|
|
- **Toolsets**: Individual Azure service implementations (Storage, SQL, KeyVault, etc.)
|
|
- **Engineering System**: Build pipelines, testing infrastructure, and deployment automation
|
|
|
|
## Project Structure
|
|
|
|
### Key directories
|
|
- `core/Azure.Mcp.Core/` - Azure MCP core library with shared infrastructure
|
|
- `servers/Azure.Mcp.Server/` - Main Azure MCP server implementation
|
|
- `tools/Azure.Mcp.Tools.{Service}/` - Individual service toolsets (Storage, SQL, etc.)
|
|
- `eng/scripts/` - Build, test, and deployment PowerShell scripts
|
|
- `.github/skills/add-azure-mcp-tools/SKILL.md` - Implementation guide for new commands
|
|
- `docs/option-conversion.md` - Guide for converting to two-generic option pattern
|
|
- `CONTRIBUTING.md` - Contribution guidelines and workflows
|
|
|
|
### Good examples to follow
|
|
- Command implementation: `tools/Azure.Mcp.Tools.Storage/src/Commands/Account/AccountGetCommand.cs`
|
|
- Service pattern: `tools/Azure.Mcp.Tools.Storage/src/Services/StorageService.cs`
|
|
- Unit tests: `tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.Tests/Account/StorageAccountGetCommandTests.cs`
|
|
- Integration tests: `tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.Tests/StorageCommandTests.cs`
|
|
- Live test infrastructure: `tools/Azure.Mcp.Tools.Storage/tests/test-resources.bicep`
|
|
- Option conversion guide: `docs/option-conversion.md`
|
|
|
|
### Legacy patterns to avoid
|
|
- Old one-generic `RegisterOptions`/`BindOptions` pattern (use two-generic with `[Option]` attributes)
|
|
- `OptionDefinitions` static classes with `.AsRequired()`/`.AsOptional()` (use `[Option]` on flat POCOs)
|
|
- Options class inheritance hierarchies (use flat classes with interface constraints)
|
|
- Commands without proper error handling
|
|
- Missing live test infrastructure for Azure services
|
|
- Non-sealed command classes
|
|
|
|
## Development Environment Setup
|
|
|
|
### Prerequisites
|
|
1. **Visual Studio Code**: [VS Code Stable](https://code.visualstudio.com/download) or [Insiders](https://code.visualstudio.com/insiders)
|
|
2. **GitHub Copilot**: Install [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) and [GitHub Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) extensions
|
|
3. **Node.js**: [Latest Node.js LTS](https://nodejs.org/en/download) (ensure `node` and `npm` are in PATH)
|
|
4. **PowerShell**: [PowerShell 7.0+](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) (required for build/test scripts)
|
|
5. **.NET SDK**: .NET 10 SDK (configured in `global.json`)
|
|
6. **Azure PowerShell**: For live tests - [Install Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-azure-powershell)
|
|
7. **Azure Bicep**: For test infrastructure - [Install Azure Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install#install-manually)
|
|
|
|
### Quick Start Commands
|
|
```powershell
|
|
# Clone and build the project
|
|
git clone https://github.com/microsoft/mcp.git
|
|
cd mcp
|
|
dotnet build
|
|
|
|
# Verify everything works
|
|
./eng/scripts/Build-Local.ps1 -VerifyNpx
|
|
|
|
# Run unit tests for specific toolset
|
|
./eng/scripts/Test-Code.ps1 -Paths Storage
|
|
|
|
# Run all unit tests
|
|
./eng/scripts/Test-Code.ps1
|
|
```
|
|
|
|
## API Docs and References
|
|
- API documentation: `/servers/Azure.Mcp.Server/docs/azmcp-commands.md` - Complete command reference
|
|
- Implementation guide: `/.github/skills/add-azure-mcp-tools/SKILL.md` - Step-by-step command creation
|
|
- Option conversion: `/docs/option-conversion.md` - Converting to two-generic option pattern
|
|
- Test prompts: `/servers/Azure.Mcp.Server/docs/e2eTestPrompts.md` - Example prompts for testing
|
|
- Recorded tests: `/docs/recorded-tests.md` - Guide for converting live tests to recorded (playback) tests
|
|
- Contributing guide: `/CONTRIBUTING.md` - Development workflow and standards
|
|
- Code guidelines: `/.github/copilot-instructions.md` - Specific coding standards
|
|
|
|
## When Stuck
|
|
- Ask clarifying questions about Azure service requirements or command patterns
|
|
- Propose a short plan before implementing complex features
|
|
- Reference existing commands in similar services as templates
|
|
- Check `/.github/skills/add-azure-mcp-tools/SKILL.md` for implementation patterns
|
|
- Use GitHub Copilot Chat with `"create [service] [resource] [operation] command using /skills/add-azure-mcp-tools as a reference"`
|
|
- Check `/docs/option-conversion.md` for the two-generic option pattern
|
|
|
|
## PR Checklist
|
|
- Format and type check: `dotnet format && dotnet build` - all green
|
|
- Unit tests: Add comprehensive tests following existing patterns
|
|
- Live test infrastructure: Include Bicep template and post-deployment script for Azure services
|
|
- Live tests: Include live tests for all commands that interact with Azure resources
|
|
- Recorded tests: All live tests **must** be recorded for playback (see `/docs/recorded-tests.md`)
|
|
- Documentation: Update `/servers/Azure.Mcp.Server/docs/azmcp-commands.md` and add test prompts to `/servers/Azure.Mcp.Server/docs/e2eTestPrompts.md`
|
|
- Tool validation (optional for Fabric MCP tools): Run `ToolDescriptionEvaluator` for command descriptions (target: top 3 ranking, ≥0.4 confidence)
|
|
- Spelling check: `.\eng\common\spelling\Invoke-Cspell.ps1`
|
|
- Changelog: Create changelog entry YAML file if the change is a new feature, bug fix, or breaking change. See `/docs/changelog-entries.md` for instructions. Always use the `-ChangelogPath` parameter (e.g., `/servers/Azure.Mcp.Server/CHANGELOG.md` or `/servers/Fabric.Mcp.Server/CHANGELOG.md`).
|
|
- One tool per PR: Submit single toolsets for faster review cycles
|
|
|
|
## Architecture and Project Structure
|
|
|
|
### Repository Organization
|
|
```
|
|
├── core/ # Core libraries and shared components
|
|
│ ├── Azure.Mcp.Core/ # Azure MCP core library
|
|
│ ├── Microsoft.Mcp.Core/ # Base MCP protocol implementation
|
|
│ └── Fabric.Mcp.Core/ # Fabric-specific core (extends Azure.Mcp.Core)
|
|
├── servers/ # Individual MCP servers
|
|
│ ├── Azure.Mcp.Server/ # Azure MCP server implementation
|
|
│ ├── Fabric.Mcp.Server/ # Microsoft Fabric MCP server
|
|
│ └── Template.Mcp.Server/ # Template for new MCP servers
|
|
├── tools/ # Service-specific toolset implementations
|
|
│ ├── Azure.Mcp.Tools.Storage/ # Azure Storage operations
|
|
│ ├── Azure.Mcp.Tools.KeyVault/ # Azure Key Vault operations
|
|
│ ├── Azure.Mcp.Tools.Sql/ # Azure SQL operations
|
|
│ └── [60+ other Azure services] # Each Azure service has its own toolset
|
|
├── eng/ # Engineering system and build infrastructure
|
|
│ ├── scripts/ # Build, test, and deployment scripts
|
|
│ ├── pipelines/ # Azure DevOps pipeline definitions
|
|
│ └── tools/ # Development and validation tools
|
|
└── docs/ # Documentation and implementation guides
|
|
```
|
|
|
|
### Toolset Architecture Pattern
|
|
Each Azure service follows a consistent pattern:
|
|
```
|
|
Azure.Mcp.Tools.{Service}/
|
|
├── src/
|
|
│ ├── Commands/ # Command implementations
|
|
│ │ └── {Resource}/ # Resource-specific commands
|
|
│ ├── Services/ # Service layer implementations
|
|
│ ├── Options/ # Command option definitions
|
|
│ ├── Models/ # Data models and DTOs
|
|
│ └── {Service}Setup.cs # Service registration and configuration
|
|
└── tests/
|
|
├── Azure.Mcp.Tools.{Service}.Tests/ # Unit tests (no Azure resources) and Integration tests (requires Azure)
|
|
├── test-resources.bicep # Test infrastructure template
|
|
└── test-resources-post.ps1 # Post-deployment setup script
|
|
```
|
|
|
|
### Command Naming Convention
|
|
Commands follow the pattern: `azmcp <service> <resource> <operation>`
|
|
```bash
|
|
# Examples
|
|
azmcp storage account get # Get storage accounts
|
|
azmcp sql database show # Show SQL database details
|
|
azmcp keyvault secret get # Get Key Vault secret
|
|
azmcp resourcegroup list # List resource groups
|
|
```
|
|
|
|
## Build Commands and Development Workflow
|
|
|
|
### Core Build Commands
|
|
```powershell
|
|
# Basic build (fastest for development)
|
|
dotnet build
|
|
|
|
# Full verification build (recommended before PR)
|
|
./eng/scripts/Build-Local.ps1 -VerifyNpx
|
|
|
|
# AOT-compatible build (tests native compilation)
|
|
./eng/scripts/Build-Local.ps1 -BuildNative
|
|
|
|
# Build with debugging symbols
|
|
./eng/scripts/Build-Local.ps1
|
|
|
|
# Docker image build
|
|
./eng/scripts/Build-Docker.ps1 -ServerName "Azure.Mcp.Server"
|
|
```
|
|
|
|
### Testing Commands
|
|
```powershell
|
|
# Unit tests only (no Azure resources required)
|
|
./eng/scripts/Test-Code.ps1
|
|
|
|
# Specific toolset unit tests
|
|
./eng/scripts/Test-Code.ps1 -Paths Storage, KeyVault
|
|
|
|
# Deploy test infrastructure for live tests
|
|
eng/common/TestResources/New-TestResources.ps1 -TestResourcesDirectory tools/Azure.Mcp.Tools.Storage
|
|
|
|
# Live tests (requires Azure authentication and resources)
|
|
./eng/scripts/Test-Code.ps1 -TestType Live -Paths Storage
|
|
|
|
# Run tests from specific directory
|
|
pushd 'tools/Azure.Mcp.Tools.Storage/tests/Azure.Mcp.Tools.Storage.Tests'
|
|
dotnet test --filter "FullyQualifiedName~StorageAccountGetCommandTests"
|
|
popd
|
|
```
|
|
|
|
### Code Quality and Validation
|
|
```powershell
|
|
# Format code and remove unused using statements
|
|
dotnet format
|
|
|
|
# Format specific toolset
|
|
dotnet format --include="tools/Azure.Mcp.Tools.Storage/**/*.cs"
|
|
|
|
# Spelling check
|
|
.\eng\common\spelling\Invoke-Cspell.ps1
|
|
|
|
# AOT compatibility analysis
|
|
./eng/scripts/Analyze-AOT-Compact.ps1
|
|
|
|
# Tool description quality validation (optional for Fabric MCP tools)
|
|
pushd 'eng/tools/ToolDescriptionEvaluator/src'
|
|
dotnet run -- --validate --tool-description "Your command description" --prompt "user query" --test-single-tool 'your-tool-name'
|
|
popd
|
|
```
|
|
|
|
## Testing Strategy and Patterns
|
|
|
|
### Unit Testing Requirements
|
|
All commands must include comprehensive unit tests:
|
|
|
|
```csharp
|
|
// Required test patterns for every command
|
|
[Fact] public void Constructor_InitializesCommandCorrectly()
|
|
[Theory] public async Task ExecuteAsync_ValidatesInputCorrectly(string args, bool shouldSucceed)
|
|
[Fact] public async Task ExecuteAsync_DeserializationValidation()
|
|
[Fact] public async Task ExecuteAsync_HandlesServiceErrors()
|
|
[Fact] public void BindOptions_BindsOptionsCorrectly()
|
|
```
|
|
|
|
Command unit tests should extend `SubscriptionCommandUnitTestsBase<TCommand, TService>` for subscription commands or `CommandUnitTestsBase<TCommand, TService>` for non-subscription commands.
|
|
|
|
### Live Testing Requirements
|
|
Azure service commands require live tests to validate functionality against actual Azure resources. Live tests must be recorded for playback using `RecordedCommandTestsBase`. See `/docs/recorded-tests.md` for the full recording workflow, sanitizer configuration, and migration guide.
|
|
|
|
Tests for tools marked `LocalRequired = true` need special handling in classes extending `RecordedCommandTestsBase`. Start each such test with `AssertLocalToolIsUnavailableInHttpMode(toolName)` and return early when it returns `true`; the helper verifies that the tool is unavailable in remote HTTP mode before local-only behavior is tested in other transports.
|
|
|
|
### Live Test Infrastructure
|
|
Azure service commands require Bicep templates for test resources:
|
|
```powershell
|
|
# Deploy test infrastructure
|
|
eng/common/TestResources/New-TestResources.ps1 -TestResourcesDirectory tools/Azure.Mcp.Tools.{Toolset}
|
|
|
|
# Required files for Azure service toolsets:
|
|
# - tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources.bicep
|
|
# - tools/Azure.Mcp.Tools.{Toolset}/tests/test-resources-post.ps1
|
|
```
|
|
|
|
### Authentication for Live Tests
|
|
```powershell
|
|
# Azure authentication setup
|
|
Connect-AzAccount
|
|
az login
|
|
|
|
# Test resource deployment with proper RBAC
|
|
eng/common/TestResources/New-TestResources.ps1 -TestResourcesDirectory tools/Azure.Mcp.Tools.Storage
|
|
```
|
|
|
|
### Testing with vally
|
|
|
|
When testing the MCP server with vally, assume that the user has already been authenticated. Assume that `az login` has been called. DO NOT call `subscription_list`.
|
|
|
|
## Code Style and Standards
|
|
|
|
### C# Coding Standards
|
|
- **Always use primary constructors** for dependency injection
|
|
- **Always use `System.Text.Json`** over Newtonsoft.Json
|
|
- **Make all command classes sealed** unless designed for inheritance
|
|
- **Always make members static** when possible for AOT compatibility
|
|
- **Put new classes and interfaces in separate files**
|
|
- **Always run `dotnet build`** after making changes
|
|
- **All generated code must be AOT-safe**
|
|
|
|
### File and Class Naming Patterns
|
|
```csharp
|
|
// Command naming: {Resource}{Operation}Command
|
|
public sealed class StorageAccountGetCommand // ✅ Correct
|
|
public sealed class GetStorageAccountCommand // ❌ Wrong order
|
|
|
|
// Options naming: {Resource}{Operation}Options
|
|
public class StorageAccountGetOptions // ✅ Correct
|
|
|
|
// Test naming: {Command}Tests
|
|
public class StorageAccountGetCommandTests // ✅ Correct
|
|
```
|
|
|
|
### Option Handling Pattern (Two-Generic)
|
|
```csharp
|
|
// Options are flat POCOs with [Option] attributes — no RegisterOptions/BindOptions needed
|
|
public class AccountGetOptions : ISubscriptionOption
|
|
{
|
|
[Option(Description = "The name of the Azure Storage account.")]
|
|
public string? Account { get; set; }
|
|
|
|
[Option(Description = OptionDescriptions.Subscription)]
|
|
public string? Subscription { get; set; }
|
|
|
|
[Option(Description = OptionDescriptions.Tenant)]
|
|
public string? Tenant { get; set; }
|
|
}
|
|
|
|
// Commands use two-generic base: SubscriptionCommand<TOptions, TResult>
|
|
public sealed class AccountGetCommand(
|
|
ILogger<AccountGetCommand> logger,
|
|
IStorageService storageService,
|
|
ISubscriptionResolver subscriptionResolver)
|
|
: SubscriptionCommand<AccountGetOptions, AccountGetCommand.AccountGetResult>(subscriptionResolver)
|
|
{
|
|
// ExecuteAsync receives pre-bound, pre-validated options
|
|
public override async Task<CommandResponse> ExecuteAsync(
|
|
CommandContext context, AccountGetOptions options, CancellationToken cancellationToken)
|
|
{
|
|
// options are already bound — just use them directly
|
|
}
|
|
|
|
internal record AccountGetResult(List<StorageAccountInfo> Accounts);
|
|
}
|
|
```
|
|
|
|
> See `/docs/option-conversion.md` for the full conversion guide from one-generic to two-generic pattern.
|
|
|
|
### Parameter Naming Standards
|
|
- **Use `subscription`** (never `subscriptionId`) - supports both IDs and names
|
|
- **Use `resourceGroup`** (not `resourceGroupName`)
|
|
- **Use singular nouns** for resource names (e.g., `server`, not `serverName`)
|
|
- **Remove unnecessary "-name" suffixes** (e.g., `--account` vs `--account-name`)
|
|
|
|
## Error Handling Patterns
|
|
|
|
### Standard Error Response Format
|
|
```csharp
|
|
// Override error handling for service-specific context
|
|
protected override string GetErrorMessage(Exception ex) => ex switch
|
|
{
|
|
Azure.RequestFailedException reqEx when reqEx.Status == 404 =>
|
|
"Resource not found. Verify the resource exists and you have access.",
|
|
Azure.RequestFailedException reqEx when reqEx.Status == 403 =>
|
|
$"Authorization failed accessing the resource. Details: {reqEx.Message}",
|
|
Azure.Identity.AuthenticationFailedException =>
|
|
"Authentication failed. Please run 'az login' to sign in.",
|
|
_ => base.GetErrorMessage(ex)
|
|
};
|
|
|
|
protected override int GetStatusCode(Exception ex) => ex switch
|
|
{
|
|
Azure.RequestFailedException reqEx => reqEx.Status,
|
|
Azure.Identity.AuthenticationFailedException => 401,
|
|
ValidationException => 400,
|
|
_ => base.GetStatusCode(ex)
|
|
};
|
|
```
|
|
|
|
### Exception Handling in Commands
|
|
```csharp
|
|
try
|
|
{
|
|
// Command execution logic
|
|
var results = await service.GetResourcesAsync(options.Subscription!, cancellationToken);
|
|
context.Response.Results = ResponseResult.Create(new(results ?? []), ServiceJsonContext.Default.CommandResult);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error in {Operation}. Subscription: {Subscription}", Name, options.Subscription);
|
|
HandleException(context, ex); // Always call base handler
|
|
}
|
|
```
|
|
|
|
**DO NOT** log `{@Options}` as this may log sensitive information. Only log parameters that are known to be safe.
|
|
|
|
## Service Implementation Patterns
|
|
|
|
### Base Service Classes
|
|
Choose the appropriate base class based on operations:
|
|
|
|
**For Azure Resource Read Operations (recommended):**
|
|
```csharp
|
|
public class StorageService(IAzureService azureService)
|
|
: BaseAzureResourceService(azureService), IStorageService
|
|
{
|
|
public async Task<ResourceQueryResults<StorageAccount>> ListAccountsAsync(
|
|
string subscription,
|
|
string? resourceGroup,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
return await ExecuteResourceQueryAsync(
|
|
"Microsoft.Storage/storageAccounts",
|
|
resourceGroup,
|
|
subscription,
|
|
null,
|
|
ConvertToStorageAccountModel,
|
|
cancellationToken: cancellationToken);
|
|
}
|
|
}
|
|
```
|
|
|
|
**For Azure Resource Write Operations:**
|
|
```csharp
|
|
public class StorageService(IAzureService azureService)
|
|
: BaseAzureService(azureService), IStorageService
|
|
{
|
|
public async Task<StorageAccountResult> CreateStorageAccount(
|
|
string account,
|
|
string resourceGroup,
|
|
string location,
|
|
string subscription,
|
|
string? sku = null,
|
|
string? accessTier = null,
|
|
bool? enableHierarchicalNamespace = null,
|
|
string? tenant = null,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
var subscriptionResource = await AzureService.GetSubscription(subscription, tenant, cancellationToken: cancellationToken);
|
|
// Use subscriptionResource for write operations
|
|
}
|
|
}
|
|
```
|
|
|
|
### JSON Serialization Context (AOT Requirement)
|
|
```csharp
|
|
// All response models must be registered for AOT compatibility
|
|
[JsonSerializable(typeof(StorageAccountGetCommand.StorageAccountListCommandResult))]
|
|
[JsonSerializable(typeof(StorageAccount))]
|
|
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
|
|
internal partial class StorageJsonContext : JsonSerializerContext;
|
|
|
|
// Usage in commands
|
|
context.Response.Results = ResponseResult.Create(new(results), StorageJsonContext.Default.StorageAccountGetCommandResult);
|
|
```
|
|
|
|
### Long-running operations
|
|
|
|
Long-running operations (at this time) don't offer the ability to configure polling intervals, and even if they were
|
|
able to there is a limit on how small of a polling interval can be used. Due to this, to prevent long-running operations
|
|
with a significant number of polls from wasting CPU time waiting during testing, all long-running operations should use
|
|
a two call pattern. The first call is the service method starting the polling operation, that should pass
|
|
`WaitUntil.Started` to simply begin the operation. Then waiting for completion should call
|
|
`BaseAzureService.WaitForLroCompletionAsync` to wait for completion in a way that testing can ignore the polling
|
|
interval to prevent CPU wait loops that aren't necessary when playback testing.
|
|
|
|
```csharp
|
|
var lroOperation = Service.LroAsync(WaitUntil.Started, cancellationToken);
|
|
await WaitForLroCompletionAsync(lroOperation, cancellationToken);
|
|
```
|
|
|
|
## Adding New Commands and Services
|
|
|
|
### Development Process
|
|
1. **Create issue**: "Add command: azmcp [service] [resource] [operation]"
|
|
2. **Use Copilot for generation**: Execute in Copilot Chat: `"create [service] [resource] [operation]" command using "/skills add-azure-mcp-tools" as a reference`
|
|
3. **Follow implementation guidelines** in `/.github/skills/add-azure-mcp-tools/SKILL.md` and **two-generic pattern** in `/docs/option-conversion.md`
|
|
4. **Create live test infrastructure** (if Azure service): Bicep template and post-deployment script
|
|
5. **Submit one tool per pull request** for faster review cycles
|
|
|
|
### Required Files for New Commands
|
|
```
|
|
tools/Azure.Mcp.Tools.{Service}/
|
|
├── src/
|
|
│ ├── Options/{Resource}/{Operation}Options.cs # Flat POCO with [Option] attributes
|
|
│ ├── Commands/{Resource}/{Resource}{Operation}Command.cs # Two-generic command
|
|
│ ├── Services/I{Service}Service.cs # Service interface
|
|
│ ├── Services/{Service}Service.cs # Service implementation
|
|
│ └── Commands/{Service}JsonContext.cs # JSON serialization context
|
|
└── tests/
|
|
├── Azure.Mcp.Tools.{Service}.Tests/{Resource}/{Resource}{Operation}CommandTests.cs # Unit tests (no Azure resources)
|
|
├── Azure.Mcp.Tools.{Service}.Tests/{Service}CommandTests.cs # Integration tests (requires Azure)
|
|
├── test-resources.bicep # Test infrastructure (Azure services only)
|
|
└── test-resources-post.ps1 # Post-deployment script (Azure services only)
|
|
```
|
|
|
|
### Tool Description Quality Validation
|
|
```powershell
|
|
# Validate command descriptions for AI agent compatibility
|
|
pushd 'eng/tools/ToolDescriptionEvaluator/src'
|
|
|
|
# Single prompt validation
|
|
dotnet run -- --validate --tool-description "Get storage accounts in a subscription" --prompt "show me my storage accounts"
|
|
|
|
# Multiple prompt validation
|
|
dotnet run -- --validate \
|
|
--tool-description "Get storage accounts in a subscription" \
|
|
--prompt "show storage accounts" \
|
|
--prompt "list my storage" \
|
|
--prompt "what storage do I have"
|
|
|
|
# Custom files validation
|
|
dotnet run -- --tools-file my-tools.json --prompts-file my-prompts.md
|
|
popd
|
|
|
|
# Target: Top 3 ranking and confidence score ≥ 0.4 (optional for Fabric MCP tools)
|
|
```
|
|
|
|
## Local Development and Testing
|
|
|
|
### Running Azure MCP Server Locally
|
|
mcp.json configuration for local development:
|
|
```json
|
|
{
|
|
"servers": {
|
|
"azure-mcp-server": {
|
|
"type": "stdio",
|
|
"command": "C:/code/mcp/servers/Azure.Mcp.Server/bin/Debug/net10.0/azmcp.exe",
|
|
"args": ["server", "start"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Server Mode Configurations
|
|
|
|
**Namespace filtering** (specific services only):
|
|
```json
|
|
"args": ["server", "start", "--namespace", "storage", "--namespace", "keyvault"]
|
|
```
|
|
|
|
**Namespace proxy mode** (groups tools, helpful for VS Code 128-tool limit):
|
|
```json
|
|
"args": ["server", "start", "--mode", "namespace"]
|
|
```
|
|
|
|
**Single tool mode** (single "azure" tool with internal routing):
|
|
```json
|
|
"args": ["server", "start", "--mode", "single"]
|
|
```
|
|
|
|
**Combined mode** (filter + proxy):
|
|
```json
|
|
"args": ["server", "start", "--namespace", "storage", "--mode", "namespace"]
|
|
```
|
|
|
|
### Docker Development
|
|
```powershell
|
|
# Build local Docker image
|
|
./eng/scripts/Build-Docker.ps1 -ServerName "Azure.Mcp.Server"
|
|
|
|
# Use in mcp.json
|
|
{
|
|
"servers": {
|
|
"Azure MCP Server": {
|
|
"command": "docker",
|
|
"args": ["run", "-i", "--rm", "--env-file", "/path/to/.env", "azure-sdk/azure-mcp:<version-number-of-docker-image>"]
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Performance and Compatibility
|
|
|
|
### AOT (Ahead-of-Time) Compilation
|
|
All new toolsets must be AOT-compatible or excluded from native builds:
|
|
|
|
```powershell
|
|
# Test AOT compatibility
|
|
./eng/scripts/Build-Local.ps1 -BuildNative
|
|
|
|
# If AOT fails (common for new Azure services), exclude toolset:
|
|
# 1. Move setup call in Program.cs under #if !BUILD_NATIVE
|
|
# 2. Add ProjectReference-Remove condition in Azure.Mcp.Server.csproj
|
|
```
|
|
|
|
### Caching and Performance
|
|
- Use `ICacheService` for expensive Azure operations
|
|
- Implement `BaseAzureResourceService` for efficient Resource Graph queries
|
|
|
|
## Remote MCP Server Architecture
|
|
|
|
Azure MCP Server supports **stdio** (local) and **HTTP** (remote) transports with different authentication models.
|
|
|
|
### Key Differences: Stdio vs Remote HTTP
|
|
|
|
| Aspect | Stdio Mode | Remote HTTP Mode |
|
|
|--------|-----------|------------------|
|
|
| **Concurrency** | Single user | Multiple concurrent users |
|
|
| **State Management** | Can use instance fields | Must be stateless |
|
|
| **Deployment** | Local binaries | Cloud hosting (App Service, AKS) |
|
|
| **Configuration** | Simple (no auth) | Requires Entra ID app registration |
|
|
|
|
### Authentication Strategies
|
|
|
|
**On-Behalf-Of (OBO) Flow:**
|
|
- Per-user authorization with audit trails
|
|
- User's RBAC permissions enforced
|
|
- Requires API permissions and admin consent
|
|
- Command: `--run-as-remote-http-service --outgoing-auth-strategy UseOnBehalfOf`
|
|
|
|
**Hosting Environment Identity:**
|
|
- Service-level permissions using Managed Identity
|
|
- Simpler configuration, no token exchange overhead
|
|
- All users share server's permissions
|
|
- Command: `--run-as-remote-http-service --outgoing-auth-strategy UseHostingEnvironmentIdentity`
|
|
|
|
### Command Implementation for Remote Mode
|
|
|
|
**Critical Requirements:**
|
|
- Write transport-agnostic commands (work in both stdio and HTTP modes)
|
|
- Use `IAzureTokenCredentialProvider` for all Azure authentication
|
|
- Keep commands stateless and thread-safe (no instance field state)
|
|
- Test with different RBAC permissions for OBO scenarios
|
|
- Provide context-aware error messages for remote scenarios
|
|
|
|
**Key Patterns:**
|
|
```csharp
|
|
// ✅ Correct: Authentication provider handles both modes
|
|
var credential = await GetCredentialAsync(tenant, CancellationToken.None);
|
|
var armClient = new ArmClient(credential);
|
|
|
|
// ❌ Wrong: Don't check transport type or access HttpContext
|
|
if (Environment.GetEnvironmentVariable("ASPNETCORE_URLS") != null) { }
|
|
var httpContext = _httpContextAccessor.HttpContext;
|
|
```
|
|
|
|
### Security Best Practices
|
|
|
|
1. Always use HTTPS in production
|
|
2. Implement least privilege RBAC
|
|
3. Use OBO for multi-tenant scenarios (preserves user identity)
|
|
4. Secure configuration secrets with Azure Key Vault
|
|
5. Enable Application Insights for monitoring
|
|
6. Validate token claims (audience, issuer, scopes)
|
|
7. Use Managed Identity when possible
|
|
|
|
## External MCP Server Integration
|
|
|
|
The Azure MCP Server can proxy to external MCP servers via `registry.json`:
|
|
|
|
```json
|
|
// core/Azure.Mcp.Core/src/Areas/Server/Resources/registry.json
|
|
{
|
|
"servers": {
|
|
"documentation": {
|
|
"url": "https://learn.microsoft.com/api/mcp",
|
|
"description": "Search official Microsoft/Azure documentation"
|
|
},
|
|
"github-server": {
|
|
"type": "stdio",
|
|
"command": "npx",
|
|
"args": ["-y", "@modelcontextprotocol/server-github@latest"],
|
|
"description": "GitHub repository operations"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Documentation and Compliance
|
|
|
|
### Required Documentation Updates
|
|
When adding new commands:
|
|
1. **Update `/servers/Azure.Mcp.Server/docs/azmcp-commands.md`** with new command details
|
|
2. **Add test prompts to `/servers/Azure.Mcp.Server/docs/e2eTestPrompts.md`** (maintain alphabetical order)
|
|
3. **Update toolset README.md** with new functionality
|
|
4. **Create changelog entry** if user-facing or critical change. See `docs/changelog-entries.md` for instructions. Always use the `-ChangelogPath` parameter (e.g., `servers/Azure.Mcp.Server/CHANGELOG.md` or `servers/Fabric.Mcp.Server/CHANGELOG.md`).
|
|
5. **Add CODEOWNERS entry** for new toolset
|
|
|
|
### Spelling and Content Validation
|
|
```powershell
|
|
# Check spelling across codebase
|
|
.\eng\common\spelling\Invoke-Cspell.ps1
|
|
|
|
# Add project-specific terms to that project's cspell.yaml.
|
|
# Add cross-cutting terms to .vscode/cspell.json.
|
|
```
|
|
|
|
## Git Workflow and Automation
|
|
|
|
### Git Hooks for Quality
|
|
```powershell
|
|
# Install pre-push hook (runs dotnet format automatically)
|
|
./eng/scripts/Install-GitHooks.ps1
|
|
|
|
# Remove git hooks
|
|
./eng/scripts/Remove-GitHooks.ps1
|
|
```
|
|
|
|
### Git Worktree Safety
|
|
When working in a linked git worktree (multiple worktrees sharing one repository common git directory), do **not** use `git stash`. Git stores stash entries in the shared `refs/stash`, so stash operations from concurrent worktrees share the same stack and can apply or drop the wrong changes. To compare against a baseline, use `git diff <ref>` (e.g., `git diff main -- <files>`) or create a throwaway worktree of the base branch with `git worktree add <path> <base-ref>` (e.g., `git worktree add ../mcp-main main`). When committing, stage intended changes explicitly (`git add -p` or specific paths) rather than `git add -A`.
|
|
|
|
### Pull Request Guidelines
|
|
- **Run all tests**: `./eng/scripts/Test-Code.ps1`
|
|
- **Format code**: `dotnet format`
|
|
- **Check spelling**: `.\eng\common\spelling\Invoke-Cspell.ps1`
|
|
- **Validate tool descriptions**: Use ToolDescriptionEvaluator (optional for Fabric MCP tools)
|
|
- **Follow contribution guidelines**: See `CONTRIBUTING.md`
|
|
- **One tool per PR**: Submit single toolsets for faster review
|
|
|
|
## Advanced Configuration
|
|
|
|
### External Tool Integration
|
|
The server supports integration with external MCP servers through registry configuration, enabling aggregation of tools from multiple sources into a unified interface.
|
|
|
|
### Namespace-Based Tool Organization
|
|
Commands are organized by Azure service namespace, allowing for fine-grained control over exposed functionality and helping manage VS Code's 128-tool display limit.
|
|
|
|
### Telemetry and Monitoring
|
|
The server includes comprehensive telemetry integration with proper tag propagation for monitoring tool usage and performance across different deployment scenarios.
|
|
|
|
---
|
|
|
|
This documentation provides AI agents with comprehensive guidance for working effectively with the Microsoft MCP codebase. For additional details, see `/.github/skills/add-azure-mcp-tools/SKILL.md` for implementation specifics and `CONTRIBUTING.md` for contribution workflows.
|