Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8ce6af3
initial changes
Jan 27, 2026
6cca5d9
adds tests
Jan 28, 2026
cd2e769
Merge branch 'users/jterrazas/setup-copilotstudio-permissions' into u…
Jan 28, 2026
000c26b
Merge branch 'main' into users/jterrazas/develop-add-permission-mcs
Feb 2, 2026
5111635
add more than mcp permissions in add-permissions subcommand
Feb 2, 2026
386f550
remove unused setupall flag
Feb 2, 2026
151caf7
Merge branch 'users/jterrazas/setup-copilotstudio-permissions' into u…
Feb 3, 2026
84816a1
initial code
Feb 6, 2026
f77a2fb
try cleaning diff
Feb 6, 2026
9c4cc44
remove test cases that don't test any code from source
Feb 6, 2026
06618a8
restore tests
Feb 6, 2026
5586563
Address code review feedback for resource token acquisition (#226)
Copilot Feb 6, 2026
4739fc5
update endpoint resolution
Feb 6, 2026
904ff12
Merge branch 'users/jterrazas/resource-flag-get-token' of https://git…
Feb 6, 2026
4ecfd43
Merge branch 'main' into users/jterrazas/resource-flag-get-token
Feb 6, 2026
78755f2
Merge branch 'main' into users/jterrazas/develop-add-permission-mcs
Feb 6, 2026
6620c94
Merge branch 'users/jterrazas/resource-flag-get-token' into users/jte…
Feb 7, 2026
cdfe391
merge get-token code for shared functionality
Feb 7, 2026
c7d4966
Merge branch 'main' into users/jterrazas/develop-add-permission-mcs
Feb 10, 2026
96f1f34
Add ResolveResource for duplicate code
Feb 10, 2026
6c042bc
update messages and comments
Feb 10, 2026
d119463
Update option description
Feb 10, 2026
e7e2ea1
Update docs/commands/develop/develop-addpermissions.md
JesuTerraz Feb 10, 2026
b29512c
Update src/Microsoft.Agents.A365.DevTools.Cli/Constants/ErrorMessages.cs
JesuTerraz Feb 10, 2026
8524a9f
update doc
Feb 10, 2026
9053662
Update docs to be consistent about explicit scopes with resource flags
Feb 10, 2026
2aed990
Specify resource / scope behavior a little more
Feb 10, 2026
e38f0ea
update tests
Feb 10, 2026
71ba355
Update docs/commands/develop/develop-addpermissions.md
JesuTerraz Feb 10, 2026
928f4ad
add more example usage
Feb 10, 2026
bf65ea5
Merge branch 'users/jterrazas/develop-add-permission-mcs' of https://…
Feb 10, 2026
1c7a0bf
Merge branch 'main' into users/jterrazas/develop-add-permission-mcs
Feb 12, 2026
377697b
add missing using directive
Feb 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 39 additions & 6 deletions docs/commands/develop/develop-addpermissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

## Overview

The `a365 develop add-permissions` command adds MCP (Model Context Protocol) server API permissions to Azure AD applications. This command is designed for **development scenarios** where you need to configure custom applications (not agent blueprints) to access MCP servers.
The `a365 develop add-permissions` command adds API permissions to Azure AD applications. This command supports:

- **MCP (Model Context Protocol) server permissions** - Default resource for Agent365 Tools
- **Power Platform API permissions** - For CopilotStudio and other Power Platform services

This command is designed for **development scenarios** where you need to configure custom applications (not agent blueprints) to access various APIs.

## Usage

Expand All @@ -15,12 +20,19 @@ a365 develop add-permissions [options]
| Option | Alias | Description | Default |
|--------|-------|-------------|---------|
| `--config` | `-c` | Configuration file path | `a365.config.json` |
| `--manifest` | `-m` | Path to ToolingManifest.json | `<deploymentProjectPath>/ToolingManifest.json` |
| `--manifest` | `-m` | Path to ToolingManifest.json (for mcp resource) | `<deploymentProjectPath>/ToolingManifest.json` |
| `--app-id` | | Application (client) ID to add permissions to | `clientAppId` from config |
| `--scopes` | | Specific scopes to add (space-separated) | All scopes from ToolingManifest.json |
| `--resource` | `-r` | Target resource API: 'mcp' (default), 'powerplatform' | `mcp` |
| `--resource-id` | | Resource application ID (GUID) to add permissions to. Requires `--scopes` | None |
| `--scopes` | | Specific scopes to add (space-separated). Required when using `--resource` or `--resource-id` | All scopes from ToolingManifest.json (mcp) or required defaults |
| `--verbose` | `-v` | Show detailed output | `false` |
| `--dry-run` | | Show what would be done without making changes | `false` |

> **Resource and scopes behavior**:
> - The `--resource` and `--resource-id` flags are mutually exclusive.
> - If you specify either `--resource` or `--resource-id`, you must provide `--scopes` explicitly.
> - If you wish to use the scopes specified in the ToolingManifest.json, please omit the resource flags.

## When to Use This Command

### Development Scenarios
Expand All @@ -29,7 +41,8 @@ a365 develop add-permissions [options]
- Third-party integrations calling MCP servers

### NOT for Agent Blueprints
- Use `a365 setup permissions mcp` for agent blueprint setup
- Use `a365 setup permissions mcp` for agent blueprint MCP setup
- Use `a365 setup permissions copilotstudio` for agent blueprint Power Platform setup

## Understanding the Application ID

Expand All @@ -50,7 +63,7 @@ The application you're adding permissions to can be the **same application** you
## Prerequisites

1. **Azure CLI Authentication**: `az login` with appropriate permissions
2. **Client Application**:
2. **Client Application**:
- Must exist in Azure AD
- Must have `Application.ReadWrite.All` permission (to modify app registrations)
- Can be configured in `a365.config.json` as `clientAppId` OR provided via `--app-id`
Expand Down Expand Up @@ -96,6 +109,12 @@ a365 develop add-permissions --app-id 87654321-4321-4321-4321-210987654321
a365 develop add-permissions --scopes McpServers.Mail.All McpServers.Calendar.All
```

### Add permissions from different resources
```bash
# Add CopilotStudio.Copilots.Invoke permission to the app in config
a365 develop add-permissions --resource powerplatform --scopes CopilotStudio.Copilots.Invoke
```

### Combine options with dry-run
```bash
# Preview changes to a specific app with specific scopes
Expand All @@ -106,4 +125,18 @@ a365 develop add-permissions --app-id 12345678-1234-1234-1234-123456789abc --sco
```bash
# When no config exists, you must provide --app-id
a365 develop add-permissions --app-id 12345678-1234-1234-1234-123456789abc --scopes McpServers.Mail.All
```
```

## Resource Types

### MCP Resource (Default)
- **Resource Key**: `mcp` (default)
- **Target**: Agent 365 Tools API
- **Scope Source**: ToolingManifest.json or explicit `--scopes`
- **Example Scopes**: `McpServers.Mail.All`, `McpServers.Calendar.All`

### Power Platform Resource
- **Resource Key**: `powerplatform`
- **Target**: Power Platform API (`8578e004-a5c6-46e7-913e-12f58912df43`)
- **Scope Source**: **Required** explicit `--scopes` (no defaults)
- **Example Scopes**: `CopilotStudio.Copilots.Invoke`
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace Microsoft.Agents.A365.DevTools.Cli.Commands.DevelopSubcommands;

/// <summary>
/// AddPermissions subcommand - Adds MCP server API permissions to a custom application
/// AddPermissions subcommand - Adds API permissions to a custom application
/// </summary>
internal static class AddPermissionsSubcommand
{
Expand All @@ -22,7 +22,7 @@ public static Command CreateCommand(
{
var command = new Command(
"add-permissions",
"Add MCP server API permissions to a custom application");
"Add API permissions to a custom application");

var configOption = new Option<FileInfo>(
["--config", "-c"],
Expand Down Expand Up @@ -51,6 +51,22 @@ public static Command CreateCommand(
["--verbose", "-v"],
description: "Show detailed output");

var resourceOption = new Option<string?>(
["--resource", "-r"],
description: "Target resource API: 'mcp' (default), 'powerplatform'. " +
"When specified, --scopes is required.")
{
IsRequired = false
};

var resourceIdOption = new Option<string?>(
["--resource-id"],
description: "Resource application ID (GUID) to add permissions for. " +
"When specified, --scopes is required.")
{
IsRequired = false
};

var dryRunOption = new Option<bool>(
["--dry-run"],
description: "Show what would be done without executing");
Expand All @@ -59,30 +75,34 @@ public static Command CreateCommand(
command.AddOption(manifestOption);
command.AddOption(appIdOption);
command.AddOption(scopesOption);
command.AddOption(resourceOption);
command.AddOption(resourceIdOption);
command.AddOption(verboseOption);
command.AddOption(dryRunOption);

command.SetHandler(async (config, manifest, appId, scopes, verbose, dryRun) =>
command.SetHandler(async (config, manifest, appId, scopes, resource, resourceId, verbose, dryRun) =>
{
try
{
logger.LogInformation("Adding MCP server permissions to application...");
logger.LogInformation("Adding API permissions to application...");
logger.LogInformation("");

// Check if config file exists or if --app-id was provided
var setupConfig = File.Exists(config.FullName)
? await configService.LoadAsync(config.FullName)
var setupConfig = File.Exists(config.FullName)
? await configService.LoadAsync(config.FullName)
: null;

if (setupConfig == null && string.IsNullOrWhiteSpace(appId))
{
logger.LogError("Configuration file not found: {ConfigPath}", config.FullName);
logger.LogInformation("");
logger.LogInformation("To add MCP server permissions, you must either:");
logger.LogInformation("To add API permissions, you must either:");
logger.LogInformation(" 1. Create a config file using: a365 config init");
logger.LogInformation(" 2. Specify the application ID using: a365 develop addpermissions --app-id <your-app-id>");
logger.LogInformation(" 2. Specify the application ID using: a365 develop add-permissions --app-id <your-app-id>");
logger.LogInformation("");
logger.LogInformation("Example: a365 develop addpermissions --app-id 12345678-1234-1234-1234-123456789abc --scopes McpServers.Mail.All");
logger.LogInformation("Examples:");
logger.LogInformation(" a365 develop add-permissions --app-id 12345678-1234-1234-1234-123456789abc --scopes McpServers.Mail.All");
logger.LogInformation(" a365 develop add-permissions --app-id 12345678-1234-1234-1234-123456789abc --resource powerplatform --scopes CopilotStudio.Copilots.Invoke");
Environment.Exit(1);
return;
}
Expand All @@ -103,25 +123,64 @@ public static Command CreateCommand(
{
logger.LogError("No application ID specified. Use --app-id or ensure ClientAppId is set in config.");
logger.LogInformation("");
logger.LogInformation("Example: a365 develop addpermissions --app-id <your-app-id>");
logger.LogInformation("Example: a365 develop add-permissions --app-id <your-app-id>");
Environment.Exit(1);
return;
}

// Determine manifest path
var manifestPath = manifest?.FullName
var manifestPath = manifest?.FullName
?? Path.Combine(setupConfig?.DeploymentProjectPath ?? Environment.CurrentDirectory, McpConstants.ToolingManifestFileName);

var environment = setupConfig?.Environment ?? "prod";

// Resolve resource app ID
ResolvedResource resolvedResource;
try
{
resolvedResource = ResourceResolutionHelper.ResolveResource(resourceId, resource, environment);
}
catch (ArgumentException ex)
{
logger.LogError("Resource resolution error: {ErrorMessage}", ex.Message);
logger.LogInformation("");
logger.LogInformation("Example: a365 develop add-permissions --resource-id 12345678-1234-1234-1234-123456789abc --scopes .default");
logger.LogInformation("Example: a365 develop add-permissions --resource powerplatform --scopes .default");
logger.LogInformation("Example: a365 develop add-permissions");
Environment.Exit(1);
return;
}

var resourceAppId = resolvedResource.ResourceAppId;
var resourceName = resolvedResource.DisplayName;

logger.LogInformation("Target resource: {ResourceName} ({ResourceAppId})", resourceName, resourceAppId);
logger.LogInformation("");

// Determine if custom resource is being used
bool isCustomResource = !string.IsNullOrWhiteSpace(resource) || !string.IsNullOrWhiteSpace(resourceId);

// Determine which scopes to add
string[] requestedScopes;

if (scopes != null && scopes.Length > 0)
{
// User provided explicit scopes
requestedScopes = scopes;
logger.LogInformation("Using user-specified scopes: {Scopes}", string.Join(", ", requestedScopes));
logger.LogInformation("");
}
else if (isCustomResource)
{
logger.LogError("The --scopes option is required when using --resource or --resource-id.");
logger.LogInformation("");
logger.LogInformation("Manifest-based scopes are only supported for the default flow.");
logger.LogInformation("Please omit the --resource and --resource-id options if you'd like to use manifest-based scopes.");
logger.LogInformation("");
logger.LogInformation("Example: a365 develop add-permissions --resource powerplatform --scopes .default");
Environment.Exit(1);
return;
}
else
{
// Read scopes from ToolingManifest.json
Expand All @@ -132,7 +191,7 @@ public static Command CreateCommand(
logger.LogInformation("Please ensure ToolingManifest.json exists in your project directory");
logger.LogInformation("or specify scopes explicitly with --scopes option.");
logger.LogInformation("");
logger.LogInformation("Example: a365 develop addpermissions --scopes McpServers.Mail.All McpServers.Calendar.All");
logger.LogInformation("Example: a365 develop add-permissions --scopes McpServers.Mail.All McpServers.Calendar.All");
Environment.Exit(1);
return;
}
Expand All @@ -150,23 +209,17 @@ public static Command CreateCommand(
return;
}

logger.LogInformation("Collected {Count} unique scope(s) from manifest: {Scopes}",
logger.LogInformation("Collected {Count} unique scope(s) from manifest: {Scopes}",
requestedScopes.Length, string.Join(", ", requestedScopes));
}

var environment = setupConfig?.Environment ?? "prod";
var resourceAppId = ConfigConstants.GetAgent365ToolsResourceAppId(environment);

logger.LogInformation("Target resource: Agent 365 Tools ({ResourceAppId})", resourceAppId);
logger.LogInformation("");

// Dry run mode
if (dryRun)
{
logger.LogInformation("DRY RUN: Add MCP Server Permissions");
logger.LogInformation("DRY RUN: Add API Permissions");
logger.LogInformation("Would add the following permissions to application {AppId}:", targetAppId);
logger.LogInformation("");
logger.LogInformation("Resource: {ResourceAppId}", resourceAppId);
logger.LogInformation("Resource: {ResourceName} ({ResourceAppId})", resourceName, resourceAppId);
logger.LogInformation(" Scopes: {Scopes}", string.Join(", ", requestedScopes));
logger.LogInformation("");
logger.LogInformation("No changes made (dry run mode)");
Expand All @@ -181,7 +234,7 @@ public static Command CreateCommand(
string tenantId = await TenantDetectionHelper.DetectTenantIdAsync(setupConfig, logger) ?? string.Empty;

logger.LogInformation("Processing resource: {ResourceAppId}", resourceAppId);

bool success;
try
{
Expand All @@ -207,7 +260,7 @@ public static Command CreateCommand(
logger.LogDebug(" {StackTrace}", ex.StackTrace);
success = false;
}

logger.LogInformation("");

// Summary
Expand All @@ -227,11 +280,12 @@ public static Command CreateCommand(
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to add MCP server permissions: {Message}", ex.Message);
logger.LogError(ex, "Failed to add API permissions: {Message}", ex.Message);
Environment.Exit(1);
}
}, configOption, manifestOption, appIdOption, scopesOption, verboseOption, dryRunOption);
}, configOption, manifestOption, appIdOption, scopesOption, resourceOption, resourceIdOption, verboseOption, dryRunOption);

return command;
}

}
Loading
Loading