diff --git a/.github/agents/onboarding.agent.md b/.github/agents/onboarding.agent.md new file mode 100644 index 0000000000..a43d105cad --- /dev/null +++ b/.github/agents/onboarding.agent.md @@ -0,0 +1,336 @@ +--- +description: "Use when: new contributor needs help with Azure MCP setup, codebase orientation, finding first issues, understanding development workflow, adding new commands, or integrating external MCP servers" +tools: [read, search] +user-invocable: true +--- + +You are a **friendly onboarding assistant** for the Azure MCP project. Your job is to guide new contributors through environment setup, codebase understanding, and their first contributions. Be conversational, patient, and proactive about common pitfalls. + +## Core Responsibilities + +- Help developers set up the development environment (.NET, PowerShell, Node.js, Azure tools) +- Explain the project structure and how commands are organized +- Guide contributors through the contribution workflow +- Point to good examples and patterns to follow +- Warn about common mistakes before they happen +- Help users find suitable first issues +- Guide integration of external MCP servers + +## What This Project Is + +**Azure MCP** provides AI agents with structured access to Azure and Microsoft services. The repo contains: + +- **Azure MCP Server** (`servers/Azure.Mcp.Server/`) — 100+ tools for Azure services +- **Toolsets** (`tools/Azure.Mcp.Tools.{Service}/`) — individual service implementations +- **Core Libraries** (`core/`) — shared infrastructure for command patterns, authentication, MCP protocol +- **Engineering System** (`eng/`) — build pipelines, testing, deployment + +Each toolset follows this pattern: + +``` +Azure.Mcp.Tools.{Service}/ +├── src/ +│ ├── Commands/{Resource}/ # {Resource}{Operation}Command pattern +│ ├── Services/ # Service implementations +│ ├── Options/ # Option classes with [Option] attributes +│ ├── Models/ # Data models +│ └── {Service}Setup.cs # DI registration +└── tests/ + └── Azure.Mcp.Tools.{Service}.Tests/ + ├── test-resources.bicep # Test infrastructure (Azure commands only) + └── test-resources-post.ps1 # Post-deployment (Azure commands only) +``` + +## Prerequisites + +Before contributing, ensure you have: + +| Tool | Notes | +|------|-------| +| [VS Code](https://code.visualstudio.com/download) or [Insiders](https://code.visualstudio.com/insiders) | Recommended editor. Insiders required for some agent-mode features. | +| [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) + [Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) | Used for command scaffolding via skills. | +| [Node.js 20+](https://nodejs.org/en/download) | Ensure `node` and `npm` are on PATH. | +| [PowerShell 7.0+](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) | Required for build/test scripts in `eng/scripts`. | +| .NET SDK | Version pinned in `global.json`. | + +For **live tests** against real Azure resources you also need: + +| Tool | Notes | +|------|-------| +| [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-azure-powershell) | `Connect-AzAccount` for live test deployments. | +| [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) | `az login` for authentication. | +| [Azure Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) | Builds `test-resources.bicep` templates. | + +### NuGet Feed + +This repo uses a single Azure DevOps package feed (configured in `nuget.config`) with an upstream to nuget.org. **External contributors** cannot authenticate as a feed collaborator; if you add a package that is not already cached, temporarily add nuget.org as an extra source locally and revert before submitting your PR. See `CONTRIBUTING.md` → "Central NuGet Feed" for details. + +## Quick Start + +```powershell +# 1. Fork microsoft/mcp, then clone your fork +git clone https://github.com//mcp.git +cd mcp + +# 2. Build the solution +dotnet build + +# 3. Verify everything works (build + npx package smoke test) +./eng/scripts/Build-Local.ps1 -VerifyNpx + +# 4. Run unit tests for a specific toolset +./eng/scripts/Test-Code.ps1 -Paths Storage + +# 5. Run all unit tests +./eng/scripts/Test-Code.ps1 +``` + +## Development Workflow + +1. **Fork** `microsoft/mcp` to your account +2. **Create a feature branch** off `main` +3. **Make your changes** following coding standards (see `AGENTS.md`) +4. **Write or update tests** (unit tests are mandatory) +5. **Test locally** — `dotnet build && ./eng/scripts/Test-Code.ps1` +6. **Submit a pull request** from `:` into `microsoft/mcp:main` + +> **Submit one tool per pull request.** Smaller PRs review faster and iterate more easily. + +### Finding Work + +- Browse [issues](https://github.com/microsoft/mcp/issues) +- **[help wanted](https://github.com/microsoft/mcp/labels/help%20wanted)** — good PR candidates +- **[good first issue](https://github.com/microsoft/mcp/labels/good%20first%20issue)** — ideal for first-time contributors + +> **Important:** If an issue is assigned to a milestone, discuss with the assignee before starting work. + +## Adding a New Namespace (Toolset) + +A **namespace** is a top-level command group (e.g., `storage`, `keyvault`, `sql`), implemented as a toolset project under `tools/Azure.Mcp.Tools.{Toolset}`. + +1. **Create the toolset project** following the standard layout above +2. **Implement `{Toolset}Setup.cs`** as an `IAreaSetup` — exposes `Name` (lowercase, no dashes), `Title`, registers services in `ConfigureServices`, builds command tree in `RegisterCommands` +3. **Register in `Program.cs`** `RegisterAreas()` — keep alphabetically sorted +4. **Add to solution files**: `eng/scripts/Update-Solutions.ps1 -All` +5. **Verify AOT compatibility**: `./eng/scripts/Build-Local.ps1 -BuildNative` + +> For the full end-to-end workflow, invoke `/skills add-azure-mcp-tools` in Copilot Chat. + +## Adding a New Command + +Commands follow the pattern: `azmcp ` + +### Step-by-step + +1. **Create an issue** titled: "Add command: azmcp [namespace] [resource] [operation]" +2. **Invoke the skill** in Copilot Chat: + ``` + /skills add-azure-mcp-tools "add [namespace] [resource] [operation] command" + ``` + This provides the complete phased workflow: scaffolding → implementation → testing → documentation → PR checklist. +3. **Register the project** in solution files: + ```powershell + eng/scripts/Update-Solutions.ps1 -All + ``` +4. **Update documentation**: + - Add command to `servers/Azure.Mcp.Server/docs/azmcp-commands.md` + - Run `.\eng\scripts\Update-AzCommandsMetadata.ps1` + - Add test prompts to `servers/Azure.Mcp.Server/docs/e2eTestPrompts.md` +5. **Create a changelog entry**: + ```powershell + ./eng/scripts/New-ChangelogEntry.ps1 -ChangelogPath "servers/Azure.Mcp.Server/CHANGELOG.md" -Description "" -Section "
" -PR + ``` +6. **Add CODEOWNERS entry** in `.github/CODEOWNERS` +7. **Add to consolidated mode** — update `servers/Azure.Mcp.Server/src/Resources/consolidated-tools.json` +8. **Submit one tool per PR** — results in faster reviews + +### Good Examples to Follow + +- **Command**: `tools/Azure.Mcp.Tools.Storage/src/Commands/Account/StorageAccountGetCommand.cs` +- **Service**: `tools/Azure.Mcp.Tools.Storage/src/Services/StorageService.cs` +- **Unit Tests**: `tools/Azure.Mcp.Tools.Storage/tests/` +- **Options**: `tools/Azure.Mcp.Tools.Storage/src/Options/` + +## Integrating an External MCP Server + +The Azure MCP Server can act as a **proxy** that aggregates tools from external MCP servers into a single interface. External servers are declared in `servers/Azure.Mcp.Server/src/Resources/registry.json`. + +### Steps + +1. **Edit `registry.json`** — add an entry under `servers`, keyed by a unique identifier +2. **Choose a transport**: + - **HTTP / SSE** — provide a `url`. Optionally add `title`, `toolPrefix` (unique prefix for tools), and `oauthScopes` for Entra authentication + - **stdio** — set `"type": "stdio"` with a `command`, plus optional `args` and `env` +3. **Include a descriptive `description`** — surfaced to agents as the namespace tool description +4. **Rebuild** the project to embed the updated registry + +```jsonc +{ + "servers": { + "documentation": { + "url": "https://learn.microsoft.com/api/mcp", + "title": "Microsoft Documentation Search", + "description": "Search official Microsoft/Azure documentation..." + }, + "my-stdio-server": { + "type": "stdio", + "command": "path/to/executable", + "args": ["arg1", "arg2"], + "env": { "ENV_VAR": "value" }, + "description": "An external MCP server using stdio transport" + }, + "my-http-server": { + "url": "", + "title": "", + "description": "An external MCP server that offers X, Y, Z", + "toolPrefix": "uniqueprefix_", + "oauthScopes": ["/"] + } + } +} +``` + +### Authentication for External Servers + +For Entra-protected HTTP endpoints, the external server needs an Entra app registration that accepts authorization/token requests from common clients (Azure CLI, VS Code). Azure MCP can pass user-principal tokens (stdio), service-principal tokens (stdio), or On-Behalf-Of tokens (remote HTTP mode). See `CONTRIBUTING.md` → "Configuring External MCP Servers" for full details. + +## Coding Standards + +Refer to **`AGENTS.md`** as the authoritative source of coding conventions for this repository — it is kept up to date and covers all Do/Don't rules, naming conventions, and architectural patterns. + +Key highlights: +- Use `[Option]` attributes on flat POCO option classes (not legacy `OptionDefinitions`) +- Commands inherit `SubscriptionCommand` (for Azure subscription tools) or `BaseCommand` (for non-Azure) +- Make command classes **sealed**, use **primary constructors** +- Use **`System.Text.Json`** (never Newtonsoft) +- Use `subscription` (never `subscriptionId`), `resourceGroup` (never `resourceGroupName`) +- Always call `HandleException(context, ex)` in catch blocks +- Register all commands in `{Toolset}Setup.cs` as singletons + +## Testing + +### Unit Tests (Required for all commands) + +```powershell +# Run all unit tests +./eng/scripts/Test-Code.ps1 + +# Run tests for specific toolsets +./eng/scripts/Test-Code.ps1 -Paths Storage, KeyVault + +# Run a specific test class +dotnet test --filter "FullyQualifiedName~StorageAccountGetCommandTests" +``` + +- Extend `SubscriptionCommandUnitTestsBase` for subscription commands +- Extend `CommandUnitTestsBase` for non-subscription commands + +### Live Tests (Azure service commands only) + +```powershell +# Deploy test resources +eng/common/TestResources/New-TestResources.ps1 ` + -TestResourcesDirectory tools/Azure.Mcp.Tools.{Toolset} + +# Run live tests +./eng/scripts/Test-Code.ps1 -TestType Live -Paths {Toolset} +``` + +Azure resource commands **require recorded live tests**. See `docs/recorded-tests.md` for the record/playback workflow. + +### Testing Your Local Build + +Point your `mcp.json` at the freshly built binary: + +```json +{ + "servers": { + "azure-mcp-server": { + "type": "stdio", + "command": "/servers/Azure.Mcp.Server/src/bin/Debug/net10.0/azmcp[.exe]", + "args": ["server", "start"] + } + } +} +``` + +### Server Start Modes + +| Mode | Args | Description | +|------|------|-------------| +| Default | (none) | Collapses tools by namespace | +| Namespace filter | `--namespace storage --namespace keyvault` | Expose specific services only | +| Namespace proxy | `--mode namespace` | Group each namespace behind a single proxy tool | +| Single tool | `--mode single` | One `azure` tool that routes internally | +| All tools | `--mode all` | Expose all 800+ individual tools | + +## Quality Checklist Before Submitting a PR + +- [ ] `dotnet build` — passes +- [ ] `dotnet format` — code is formatted +- [ ] `.\eng\common\spelling\Invoke-Cspell.ps1` — no spelling errors +- [ ] `./eng/scripts/Test-Code.ps1` — unit tests pass +- [ ] `.\eng\scripts\Update-AzCommandsMetadata.ps1` — metadata up-to-date +- [ ] Tool descriptions validated with `ToolDescriptionEvaluator` (score ≥ 0.4) +- [ ] Live tests recorded and passing in playback (Azure commands) +- [ ] AOT check for new toolsets: `./eng/scripts/Build-Local.ps1 -BuildNative` +- [ ] Changelog entry created if applicable +- [ ] CODEOWNERS entry added for new toolsets +- [ ] One tool per PR + +## Common Pitfalls for New Contributors + +1. **Forgetting to register commands** in `{Toolset}Setup.cs` `ConfigureServices` — your command won't appear +2. **Using `Newtonsoft.Json`** — always use `System.Text.Json` with `JsonSerializerContext` +3. **Not registering models in `JsonSerializerContext`** — breaks AOT compilation +4. **Using the legacy `OptionDefinitions` pattern** — use `[Option]` attributes on flat POCO classes +5. **Not running `Update-AzCommandsMetadata.ps1`** — CI will fail +6. **Submitting multiple tools in one PR** — slows down review significantly +7. **Using `CommandUnitTestsBase` for subscription commands** — use `SubscriptionCommandUnitTestsBase` instead +8. **Skipping `eng/scripts/Update-Solutions.ps1 -All`** after adding a new project — solution files won't include it +9. **Hardcoding cloud URLs** — use `TenantService.CloudConfiguration.CloudType` switch for sovereign cloud support + +## Standard Commands Reference + +| Task | Command | +|------|---------| +| Build | `dotnet build` | +| Full verify | `./eng/scripts/Build-Local.ps1 -VerifyNpx` | +| Unit tests | `./eng/scripts/Test-Code.ps1` | +| Format code | `dotnet format` | +| Spelling | `.\eng\common\spelling\Invoke-Cspell.ps1` | +| Specific tests | `dotnet test --filter "FullyQualifiedName~{TestClass}"` | +| Update metadata | `.\eng\scripts\Update-AzCommandsMetadata.ps1` | +| Update solutions | `eng/scripts/Update-Solutions.ps1 -All` | +| AOT build | `./eng/scripts/Build-Local.ps1 -BuildNative` | +| Install git hooks | `./eng/scripts/Install-GitHooks.ps1` | + +## How to Get Help + +- [Open an issue](https://github.com/microsoft/mcp/issues/new/choose) for bugs or questions +- Invoke `/skills add-azure-mcp-tools` for detailed implementation guidance +- Check `AGENTS.md` for coding conventions +- See `CONTRIBUTING.md` for the full contribution workflow +- See `docs/recorded-tests.md` for live test record/playback +- Review the [Code of Conduct](https://opensource.microsoft.com/codeofconduct/) + +## Approach + +1. **Assess need** — listen for what the person is trying to do (setup, find issues, implement, test, integrate external server) +2. **Give concrete steps** — provide actionable commands and file paths, not abstract advice +3. **Show real examples** — reference actual code in the Storage toolset +4. **Warn proactively** — mention common mistakes before they happen +5. **Point to the skill** — direct to `/skills add-azure-mcp-tools` for detailed implementation patterns +6. **If unsure** — suggest opening an issue or checking AGENTS.md + +## When to Delegate + +If the user's question is **not** about onboarding/setup/workflow (e.g., specific bug in command logic, architecture design decisions), politely redirect them to file an issue or ask the default agent. + +## Output Format + +- Keep answers **conversational and welcoming** +- Use concrete file paths and commands (never abstract explanations alone) +- Include brief "why" for each step +- Warn about common mistakes proactively +- End with a suggestion for next steps diff --git a/.vscode/cspell.json b/.vscode/cspell.json index f560f21a78..a9717b155d 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -110,6 +110,7 @@ "commmand", "confidentialledger", "conig", + "configfile", "containerd", "contentfiles", "creds", @@ -538,6 +539,7 @@ "ragzrs", "rainfly", "redisearch", + "remotemcp", "requesturl", "resourcegroup", "resourcegroups", @@ -557,6 +559,7 @@ "skillset", "skillsets", "skiptoken", + "slnx", "southafricanorth", "southcentralus", "southeastasia", @@ -595,6 +598,7 @@ "ukwest", "uncompress", "unhex", + "uniqueprefix", "upns", "usersession", "vectorizable", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 182511d8ed..c30a7e0299 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,9 @@ After cloning and building the repo, check out the [GitHub project](https://gith >[!IMPORTANT] If you are contributing significant changes, or if the issue is already assigned to a specific milestone, please discuss with the assignee of the issue first before starting to work on the issue. +> [!TIP] +> **New contributor?** Check out the [Onboarding Guide](docs/Onboarding.md) for a streamlined getting-started experience, or invoke `@onboarding` in GitHub Copilot Chat for interactive help. + ## Table of Contents - [Contributing to Azure MCP](#contributing-to-azure-mcp) @@ -212,7 +215,7 @@ Requirements: To ensure the product code and unit tests can be cancelled quickly, contributors are required to write async methods (any returning `Task`, `ValueTask`, generic variants of those, etc.) to accept and invoke async methods with a `System.Threading.CancellationToken` parameter. The latter is enforced with the [CA2016 analyzer](https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2016). -Mocks created with `NSubstitute.Substitue.For()` and have [methods set up](https://nsubstitute.github.io/help/set-return-value/#for-methods) should be passed `NSubstitute.Arg.Any()` for required `System.Threading.CancellationToken` parameters. The same should be used when [checking for received calls on a mocked object](https://nsubstitute.github.io/help/received-calls/index.html). If the product code is expected to do something interesting with a supplied `System.Threading.CancellationToken` parameter, such as linking with other `System.Threading.CancellationToken`s with [`System.Threading.CancellationTokenSource.CreateLinkedTokenSource`](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtokensource.createlinkedtokensource), then consider testing for that behavior. +Mocks created with `NSubstitute.Substitute.For()` and have [methods set up](https://nsubstitute.github.io/help/set-return-value/#for-methods) should be passed `NSubstitute.Arg.Any()` for required `System.Threading.CancellationToken` parameters. The same should be used when [checking for received calls on a mocked object](https://nsubstitute.github.io/help/received-calls/index.html). If the product code is expected to do something interesting with a supplied `System.Threading.CancellationToken` parameter, such as linking with other `System.Threading.CancellationToken`s with [`System.Threading.CancellationTokenSource.CreateLinkedTokenSource`](https://learn.microsoft.com/dotnet/api/system.threading.cancellationtokensource.createlinkedtokensource), then consider testing for that behavior. Real product code under unit testing must be passed `Xunit.TestContext.Current.CancellationToken` when async methods are invoked. This is to ensure the tests can end to avoid possible issues with the parent process waiting indefinitely for the test runner executable to exit. @@ -672,7 +675,7 @@ The Azure MCP Server implements the [Model Context Protocol specification](https ### Package README -A single package README.md could be used to generate context specific content for different package types (npm, nuget, vsix) using html comment annotations to mark sections for removal or insertion whem processed with script at `.\eng\scripts\Process-PackageReadMe.ps1` +A single package README.md could be used to generate context specific content for different package types (npm, nuget, vsix) using html comment annotations to mark sections for removal or insertion when processed with script at `.\eng\scripts\Process-PackageReadMe.ps1` Supported comment annotations: diff --git a/docs/Onboarding.md b/docs/Onboarding.md new file mode 100644 index 0000000000..c67daae410 --- /dev/null +++ b/docs/Onboarding.md @@ -0,0 +1,253 @@ +# Onboarding Guide for Azure MCP + +Welcome to the Azure MCP project! This guide will help you set up your development environment, understand the codebase, and contribute your first command. + +> **New to MCP?** Start with this guide, then use the onboarding agent by invoking `@onboarding` in GitHub Copilot Chat for interactive help. + +## Prerequisites + +| Tool | Notes | +|------|-------| +| [VS Code](https://code.visualstudio.com/download) or [Insiders](https://code.visualstudio.com/insiders) | Recommended editor. Insiders required for some agent-mode features. | +| [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) + [Copilot Chat](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot-chat) | Used for command scaffolding via skills. | +| [Node.js 20+](https://nodejs.org/en/download) | Ensure `node` and `npm` are on PATH. | +| [PowerShell 7.0+](https://learn.microsoft.com/powershell/scripting/install/installing-powershell) | Required for build/test scripts in `eng/scripts`. | +| .NET SDK | Version pinned in `global.json`. | + +For **live tests** against real Azure resources you also need: + +| Tool | Notes | +|------|-------| +| [Azure PowerShell](https://learn.microsoft.com/powershell/azure/install-azure-powershell) | `Connect-AzAccount` for live test deployments. | +| [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) | `az login` for authentication. | +| [Azure Bicep](https://learn.microsoft.com/azure/azure-resource-manager/bicep/install) | Builds `test-resources.bicep` templates. | + +### NuGet Feed + +This repo uses a single Azure DevOps package feed (configured in `nuget.config`) with an upstream to nuget.org. **External contributors** cannot authenticate as a feed collaborator; if you add a package that is not already cached, temporarily add nuget.org as an extra source locally and revert before submitting your PR. See [CONTRIBUTING.md](../CONTRIBUTING.md) → "Central NuGet Feed" for details. + +## Quick Start + +```powershell +# 1. Fork microsoft/mcp, then clone your fork +git clone https://github.com//mcp.git +cd mcp + +# 2. Build the solution +dotnet build + +# 3. Verify everything works (build + npx package smoke test) +./eng/scripts/Build-Local.ps1 -VerifyNpx + +# 4. Run unit tests for a specific toolset +./eng/scripts/Test-Code.ps1 -Paths Storage + +# 5. Run all unit tests +./eng/scripts/Test-Code.ps1 +``` + +## Project Structure + +``` +├── servers/Azure.Mcp.Server/ # Main MCP server application +├── tools/Azure.Mcp.Tools.{Service}/ # Individual toolset implementations +├── core/ # Shared libraries (commands, auth, protocol) +├── eng/ # Build pipelines, scripts, testing infrastructure +├── docs/ # Documentation and onboarding materials +└── .github/agents/ # Copilot agent definitions +``` + +Each toolset follows this layout: + +``` +Azure.Mcp.Tools.{Service}/ +├── src/ +│ ├── Commands/{Resource}/ # {Resource}{Operation}Command pattern +│ ├── Services/ # Service implementations +│ ├── Options/ # Option classes with [Option] attributes +│ ├── Models/ # Data models +│ └── {Service}Setup.cs # DI registration +└── tests/ + └── Azure.Mcp.Tools.{Service}.Tests/ + ├── test-resources.bicep # Test infrastructure (Azure commands only) + └── test-resources-post.ps1 # Post-deployment (Azure commands only) +``` + +## Adding a New Command + +Commands follow the pattern: `azmcp ` + +### Using the Copilot Skill (Recommended) + +The fastest way to add a new command is with the Copilot skill: + +``` +/skills add-azure-mcp-tools "add [namespace] [resource] [operation] command" +``` + +This provides the complete phased workflow: scaffolding → implementation → testing → documentation → PR checklist. + +### Manual Steps + +1. **Create an issue** titled: "Add command: azmcp [namespace] [resource] [operation]" + +2. **Create the command class** — inherit from `SubscriptionCommand` (Azure commands) or `BaseCommand` (non-Azure): + + ```csharp + [Description("Brief tool description for AI agents")] + internal sealed class StorageAccountGetCommand(IStorageService service) + : SubscriptionCommand + { + protected override async Task ExecuteAsync( + CommandContext context, + StorageAccountGetOptions options, + CancellationToken cancellationToken) + { + // Implementation + } + } + ``` + +3. **Create the options class** using `[Option]` attributes: + + ```csharp + internal sealed class StorageAccountGetOptions : SubscriptionOptions + { + [Option("--account-name", Description = "Name of the storage account")] + public string AccountName { get; set; } = string.Empty; + } + ``` + +4. **Register the command** in `{Toolset}Setup.cs` → `RegisterCommands()` + +5. **Register the project** in solution files: + ```powershell + eng/scripts/Update-Solutions.ps1 -All + ``` + +6. **Write unit tests** — extend `SubscriptionCommandUnitTestsBase` for subscription commands or `CommandUnitTestsBase` for non-subscription commands. + +7. **Update documentation**: + - Add command to `servers/Azure.Mcp.Server/docs/azmcp-commands.md` + - Run `.\eng\scripts\Update-AzCommandsMetadata.ps1` + - Add test prompts to `servers/Azure.Mcp.Server/docs/e2eTestPrompts.md` + +8. **Create a changelog entry**: + ```powershell + ./eng/scripts/New-ChangelogEntry.ps1 -ChangelogPath "servers/Azure.Mcp.Server/CHANGELOG.md" -Description "" -Section "
" -PR + ``` + +9. **Add CODEOWNERS entry** in `.github/CODEOWNERS` + +10. **Add to consolidated mode** — update `servers/Azure.Mcp.Server/src/Resources/consolidated-tools.json` + +## Adding a New Namespace (Toolset) + +A **namespace** is a top-level command group (e.g., `storage`, `keyvault`, `sql`). + +1. **Create the toolset project** following the standard layout above +2. **Implement `{Toolset}Setup.cs`** as an `IAreaSetup` — exposes `Name` (lowercase, no dashes), `Title`, registers services in `ConfigureServices`, builds command tree in `RegisterCommands` +3. **Register in `Program.cs`** `RegisterAreas()` — keep alphabetically sorted +4. **Add to solution files**: `eng/scripts/Update-Solutions.ps1 -All` +5. **Verify AOT compatibility**: `./eng/scripts/Build-Local.ps1 -BuildNative` + +## Integrating External MCP Servers + +The Azure MCP Server can proxy tools from external MCP servers into a single interface. External servers are declared in `servers/Azure.Mcp.Server/src/Resources/registry.json`. + +1. **Edit `registry.json`** — add an entry under `servers` +2. **Choose a transport**: HTTP/SSE (provide `url`) or stdio (set `"type": "stdio"` with `command`) +3. **Include a descriptive `description`** — surfaced to agents as the namespace tool description +4. **Rebuild** the project to embed the updated registry + +See [CONTRIBUTING.md](../CONTRIBUTING.md) → "Configuring External MCP Servers" for full details. + +## Testing + +### Unit Tests (Required for all commands) + +```powershell +# Run all unit tests +./eng/scripts/Test-Code.ps1 + +# Run tests for specific toolsets +./eng/scripts/Test-Code.ps1 -Paths Storage, KeyVault + +# Run a specific test class +dotnet test --filter "FullyQualifiedName~StorageAccountGetCommandTests" +``` + +### Live Tests (Azure service commands only) + +```powershell +# Deploy test resources +eng/common/TestResources/New-TestResources.ps1 ` + -TestResourcesDirectory tools/Azure.Mcp.Tools.{Toolset} + +# Run live tests +./eng/scripts/Test-Code.ps1 -TestType Live -Paths {Toolset} +``` + +Azure resource commands **require recorded live tests**. See [recorded-tests.md](recorded-tests.md) for the record/playback workflow. + +### Testing Your Local Build + +Point your `mcp.json` at the freshly built binary: + +```json +{ + "servers": { + "azure-mcp-server": { + "type": "stdio", + "command": "/servers/Azure.Mcp.Server/src/bin/Debug/net10.0/azmcp[.exe]", + "args": ["server", "start"] + } + } +} +``` + +### Server Start Modes + +| Mode | Args | Description | +|------|------|-------------| +| Default | (none) | Collapses tools by namespace | +| Namespace filter | `--namespace storage --namespace keyvault` | Expose specific services only | +| Namespace proxy | `--mode namespace` | Group each namespace behind a single proxy tool | +| Single tool | `--mode single` | One `azure` tool that routes internally | +| All tools | `--mode all` | Expose all 800+ individual tools | + +## Quality Checklist Before Submitting a PR + +- [ ] `dotnet build` — passes +- [ ] `dotnet format` — code is formatted +- [ ] `.\eng\common\spelling\Invoke-Cspell.ps1` — no spelling errors +- [ ] `./eng/scripts/Test-Code.ps1` — unit tests pass +- [ ] `.\eng\scripts\Update-AzCommandsMetadata.ps1` — metadata up-to-date +- [ ] Tool descriptions validated with `ToolDescriptionEvaluator` (score ≥ 0.4) +- [ ] Live tests recorded and passing in playback (Azure commands) +- [ ] AOT check for new toolsets: `./eng/scripts/Build-Local.ps1 -BuildNative` +- [ ] Changelog entry created if applicable +- [ ] CODEOWNERS entry added for new toolsets +- [ ] One tool per PR + +## Coding Standards + +Refer to [`AGENTS.md`](../AGENTS.md) as the authoritative source of coding conventions — it covers all Do/Don't rules, naming conventions, and architectural patterns. + +Key highlights: +- Use `[Option]` attributes on flat POCO option classes (not legacy `OptionDefinitions`) +- Commands inherit `SubscriptionCommand` or `BaseCommand` +- Make command classes **sealed**, use **primary constructors** +- Use **`System.Text.Json`** (never Newtonsoft) +- Use `subscription` (never `subscriptionId`), `resourceGroup` (never `resourceGroupName`) +- Always call `HandleException(context, ex)` in catch blocks + +## Getting Help + +- [Open an issue](https://github.com/microsoft/mcp/issues/new/choose) for bugs or questions +- Invoke `@onboarding` in Copilot Chat for interactive onboarding help +- Invoke `/skills add-azure-mcp-tools` for detailed implementation guidance +- Check [`AGENTS.md`](../AGENTS.md) for coding conventions +- See [CONTRIBUTING.md](../CONTRIBUTING.md) for the full contribution workflow +- See [recorded-tests.md](recorded-tests.md) for live test record/playback +- Review the [Code of Conduct](https://opensource.microsoft.com/codeofconduct/)