11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- using Microsoft . Agents . A365 . DevTools . Cli . Constants ;
54using Microsoft . Agents . A365 . DevTools . Cli . Helpers ;
65using Microsoft . Agents . A365 . DevTools . Cli . Services ;
76using Microsoft . Extensions . Logging ;
@@ -51,10 +50,21 @@ public static Command CreateCommand(
5150 [ "--verbose" , "-v" ] ,
5251 description : "Show detailed output" ) ;
5352
54- var resourceOption = new Option < string > (
53+ var resourceOption = new Option < string ? > (
5554 [ "--resource" , "-r" ] ,
56- getDefaultValue : ( ) => "mcp" ,
57- description : "Target resource API: 'mcp' (default), 'powerplatform'" ) ;
55+ description : "Target resource API: 'mcp' (default), 'powerplatform'. " +
56+ "When specified, --scopes is required for non-mcp resources." )
57+ {
58+ IsRequired = false
59+ } ;
60+
61+ var resourceIdOption = new Option < string ? > (
62+ [ "--resource-id" ] ,
63+ description : "Resource application ID (GUID) to add permissions for. " +
64+ "When specified, --scopes is required." )
65+ {
66+ IsRequired = false
67+ } ;
5868
5969 var dryRunOption = new Option < bool > (
6070 [ "--dry-run" ] ,
@@ -65,10 +75,11 @@ public static Command CreateCommand(
6575 command . AddOption ( appIdOption ) ;
6676 command . AddOption ( scopesOption ) ;
6777 command . AddOption ( resourceOption ) ;
78+ command . AddOption ( resourceIdOption ) ;
6879 command . AddOption ( verboseOption ) ;
6980 command . AddOption ( dryRunOption ) ;
7081
71- command . SetHandler ( async ( config , manifest , appId , scopes , resource , verbose , dryRun ) =>
82+ command . SetHandler ( async ( config , manifest , appId , scopes , resource , resourceId , verbose , dryRun ) =>
7283 {
7384 try
7485 {
@@ -120,6 +131,32 @@ public static Command CreateCommand(
120131 var manifestPath = manifest ? . FullName
121132 ?? Path . Combine ( setupConfig ? . DeploymentProjectPath ?? Environment . CurrentDirectory , "ToolingManifest.json" ) ;
122133
134+ var environment = setupConfig ? . Environment ?? "prod" ;
135+
136+ // Resolve resource app ID
137+ ResolvedResource resolvedResource ;
138+ try
139+ {
140+ resolvedResource = ResourceResolutionHelper . ResolveResource ( resourceId , resource , environment ) ;
141+ }
142+ catch ( ArgumentException ex )
143+ {
144+ logger . LogError ( "Resource resolution error: {ErrorMessage}" , ex . Message ) ;
145+ logger . LogInformation ( "" ) ;
146+ logger . LogInformation ( "Example: a365 develop add-permissions --resource-id 12345678-1234-1234-1234-123456789abc --scopes .default" ) ;
147+ Environment . Exit ( 1 ) ;
148+ return ;
149+ }
150+
151+ var resourceAppId = resolvedResource . ResourceAppId ;
152+ var resourceName = resolvedResource . DisplayName ;
153+
154+ logger . LogInformation ( "Target resource: {ResourceName} ({ResourceAppId})" , resourceName , resourceAppId ) ;
155+ logger . LogInformation ( "" ) ;
156+
157+ // Determine if custom resource is being used
158+ bool isCustomResource = ! string . IsNullOrWhiteSpace ( resource ) || ! string . IsNullOrWhiteSpace ( resourceId ) ;
159+
123160 // Determine which scopes to add
124161 string [ ] requestedScopes ;
125162
@@ -130,67 +167,48 @@ public static Command CreateCommand(
130167 logger . LogInformation ( "Using user-specified scopes: {Scopes}" , string . Join ( ", " , requestedScopes ) ) ;
131168 logger . LogInformation ( "" ) ;
132169 }
170+ else if ( isCustomResource )
171+ {
172+ logger . LogError ( "The --scopes option is required when using --resource or --resource-id." ) ;
173+ logger . LogInformation ( "" ) ;
174+ logger . LogInformation ( "Manifest-based scopes are only supported for the default flow." ) ;
175+ logger . LogInformation ( "Please omit the --resource and --resource-id options if you'd like to use manifest-based scopes." ) ;
176+ logger . LogInformation ( "" ) ;
177+ logger . LogInformation ( "Example: a365 develop add-permissions --resource powerplatform --scopes .default" ) ;
178+ Environment . Exit ( 1 ) ;
179+ return ;
180+ }
133181 else
134182 {
135- // Only read scopes from ToolingManifest.json for mcp resource
136- if ( resource . ToLowerInvariant ( ) is "mcp" )
183+ // Default MCP flow: read scopes from ToolingManifest.json
184+ if ( ! File . Exists ( manifestPath ) )
137185 {
138- // Read scopes from ToolingManifest.json
139- if ( ! File . Exists ( manifestPath ) )
140- {
141- logger . LogError ( "ToolingManifest.json not found at: {Path}" , manifestPath ) ;
142- logger . LogInformation ( "" ) ;
143- logger . LogInformation ( "Please ensure ToolingManifest.json exists in your project directory" ) ;
144- logger . LogInformation ( "or specify scopes explicitly with --scopes option." ) ;
145- logger . LogInformation ( "" ) ;
146- logger . LogInformation ( "Example: a365 develop add-permissions --scopes McpServers.Mail.All McpServers.Calendar.All" ) ;
147- Environment . Exit ( 1 ) ;
148- return ;
149- }
150-
151- logger . LogInformation ( "Reading MCP server configuration from: {Path}" , manifestPath ) ;
152-
153- // Use ManifestHelper to extract scopes (includes fallback to mappings and McpServersMetadata.Read.All)
154- requestedScopes = await ManifestHelper . GetRequiredScopesAsync ( manifestPath ) ;
155-
156- if ( requestedScopes . Length == 0 )
157- {
158- logger . LogError ( "No scopes found in ToolingManifest.json" ) ;
159- logger . LogInformation ( "You can specify scopes explicitly with --scopes option." ) ;
160- Environment . Exit ( 1 ) ;
161- return ;
162- }
163-
164- logger . LogInformation ( "Collected {Count} unique scope(s) from manifest: {Scopes}" ,
165- requestedScopes . Length , string . Join ( ", " , requestedScopes ) ) ;
166- }
167- else
168- {
169- // For other resources (like powerplatform), scopes are required
170- logger . LogError ( "--scopes is required when --resource {resource} is specified." , resource ) ;
186+ logger . LogError ( "ToolingManifest.json not found at: {Path}" , manifestPath ) ;
187+ logger . LogInformation ( "" ) ;
188+ logger . LogInformation ( "Please ensure ToolingManifest.json exists in your project directory" ) ;
189+ logger . LogInformation ( "or specify scopes explicitly with --scopes option." ) ;
171190 logger . LogInformation ( "" ) ;
172- logger . LogInformation ( "Example: a365 develop add-permissions --resource {resource} -- scopes ExampleScope.ReadWrite .All" , resource ) ;
191+ logger . LogInformation ( "Example: a365 develop add-permissions --scopes McpServers.Mail .All McpServers.Calendar.All" ) ;
173192 Environment . Exit ( 1 ) ;
174193 return ;
175194 }
176- }
177195
178- var environment = setupConfig ? . Environment ?? "prod" ;
196+ logger . LogInformation ( "Reading MCP server configuration from: {Path}" , manifestPath ) ;
179197
180- // Resolve resource configuration based on --resource option
181- var resolvedResource = ResourceResolutionHelper . ResolveByKeyword ( resource , environment ) ;
182- if ( resolvedResource is null )
183- {
184- logger . LogError ( ErrorMessages . UnknownResourceKeyword , resource ) ;
185- Environment . Exit ( 1 ) ;
186- return ;
187- }
198+ // Use ManifestHelper to extract scopes (includes fallback to mappings and McpServersMetadata.Read.All)
199+ requestedScopes = await ManifestHelper . GetRequiredScopesAsync ( manifestPath ) ;
188200
189- var resourceAppId = resolvedResource . ResourceAppId ;
190- var resourceName = resolvedResource . DisplayName ;
201+ if ( requestedScopes . Length == 0 )
202+ {
203+ logger . LogError ( "No scopes found in ToolingManifest.json" ) ;
204+ logger . LogInformation ( "You can specify scopes explicitly with --scopes option." ) ;
205+ Environment . Exit ( 1 ) ;
206+ return ;
207+ }
191208
192- logger . LogInformation ( "Target resource: {ResourceName} ({ResourceAppId})" , resourceName , resourceAppId ) ;
193- logger . LogInformation ( "" ) ;
209+ logger . LogInformation ( "Collected {Count} unique scope(s) from manifest: {Scopes}" ,
210+ requestedScopes . Length , string . Join ( ", " , requestedScopes ) ) ;
211+ }
194212
195213 // Dry run mode
196214 if ( dryRun )
@@ -262,7 +280,7 @@ public static Command CreateCommand(
262280 logger . LogError ( ex , "Failed to add API permissions: {Message}" , ex . Message ) ;
263281 Environment . Exit ( 1 ) ;
264282 }
265- } , configOption , manifestOption , appIdOption , scopesOption , resourceOption , verboseOption , dryRunOption ) ;
283+ } , configOption , manifestOption , appIdOption , scopesOption , resourceOption , resourceIdOption , verboseOption , dryRunOption ) ;
266284
267285 return command ;
268286 }
0 commit comments