Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Agents provisioned before this release need `Agent365.Observability.OtelWrite` g
**Option B — CLI** (`a365 setup admin`) has been removed in this release. Use Option A above, or copy the PowerShell instructions printed in the `a365 setup all` summary output.

### Added
- `develop-mcp get-agent-instances` command lists agent instance service principals for a given blueprint ID.
- `develop-mcp add-byo-scopes` command grants oauth2 delegated permissions for BYO MCP servers to agent instances.
- Log separator written at the start of each CLI invocation now redacts values for secret-bearing options (e.g. `--idp-client-secret`) so they are not written to the log file in plain text.
- Authentication context (tenant and user) is now logged at the `Information` level whenever the resolved sign-in identity changes, giving operators a clear audit trail in the log file of who the CLI is acting as, without exposing credentials.
- `a365 develop-mcp evaluate` command for evaluating MCP server tool schema quality — runs deterministic and semantic checks (via GitHub Copilot or Claude Code CLIs), computes maturity scoring, and generates an interactive HTML report
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Microsoft.Agents.A365.DevTools.Cli.Commands.DevelopSubcommands;
using Microsoft.Agents.A365.DevTools.Cli.Models;
using Microsoft.Agents.A365.DevTools.Cli.Services;
using Microsoft.Agents.A365.DevTools.Cli.Services.Evaluate;
Expand All @@ -22,7 +23,8 @@ public static Command CreateCommand(
ILogger logger,
IAgent365ToolingService toolingService,
IEvaluationPipelineService? evaluationPipelineService = null,
GraphApiService? graphApiService = null)
GraphApiService? graphApiService = null,
AgentBlueprintService? agentBlueprintService = null)
{
var developMcpCommand = new Command("develop-mcp", "Manage MCP servers in Dataverse environments");

Expand All @@ -40,6 +42,16 @@ public static Command CreateCommand(
developMcpCommand.AddCommand(CreateUnpublishSubcommand(logger, toolingService));
developMcpCommand.AddCommand(CreateRegisterExternalMcpServerSubcommand(logger, toolingService, graphApiService));

if (agentBlueprintService is not null)
{
developMcpCommand.AddCommand(CreateGetAgentInstancesSubcommand(logger, agentBlueprintService));

if (graphApiService is not null)
{
developMcpCommand.AddCommand(CreateAddByoScopesSubcommand(logger, toolingService, agentBlueprintService, graphApiService));
}
}

if (evaluationPipelineService is not null)
{
developMcpCommand.AddCommand(CreateEvaluateSubcommand(logger, evaluationPipelineService));
Expand Down Expand Up @@ -635,6 +647,108 @@ private static Command CreateRegisterExternalMcpServerSubcommand(
return command;
}

/// <summary>
/// Creates the get-agent-instances subcommand.
/// </summary>
private static Command CreateGetAgentInstancesSubcommand(
ILogger logger,
AgentBlueprintService agentBlueprintService)
{
var command = new Command("get-agent-instances", "List agent instance service principals for a given blueprint");

var blueprintIdOption = new Option<string>(
["--blueprint-id", "-b"],
description: "Agent Identity Blueprint ID (GUID)")
{
IsRequired = true,
};
command.AddOption(blueprintIdOption);

var tenantIdOption = new Option<string?>(
"--tenant-id",
description: "Azure AD tenant ID. Auto-detected from 'az account show' if not provided.");
command.AddOption(tenantIdOption);

command.AddOption(new Option<bool>(["--verbose", "-v"], description: "Enable verbose logging"));

command.SetHandler(async (context) =>
{
var blueprintId = context.ParseResult.GetValueForOption(blueprintIdOption)!;
var tenantId = context.ParseResult.GetValueForOption(tenantIdOption);
var ct = context.GetCancellationToken();

var executor = new GetAgentInstancesExecutor(logger, agentBlueprintService);
var success = await executor.ExecuteAsync(blueprintId, tenantId, ct);
if (!success)
{
context.ExitCode = 1;
}
});
Comment on lines +674 to +686

return command;
}

/// <summary>
/// Creates the add-byo-scopes subcommand.
/// </summary>
private static Command CreateAddByoScopesSubcommand(
ILogger logger,
IAgent365ToolingService toolingService,
AgentBlueprintService agentBlueprintService,
GraphApiService graphApiService)
{
var command = new Command("add-byo-scopes", "Grant oauth2 delegated permissions for BYO MCP servers to agent instances");

var serverNamesOption = new Option<string>(
["--server-names", "-s"],
description: "Comma-separated list of BYO server names (e.g. ext_server1,ext_server2)")
{
IsRequired = true,
};
command.AddOption(serverNamesOption);

var blueprintIdOption = new Option<string?>(
["--blueprint-id", "-b"],
description: "Blueprint ID to auto-resolve all agent instances");
command.AddOption(blueprintIdOption);

var agentInstancesOption = new Option<string?>(
["--agent-instances", "-i"],
description: "Comma-separated list of agent instance SP IDs (alternative to --blueprint-id, or to filter within blueprint)");
command.AddOption(agentInstancesOption);

var tenantIdOption = new Option<string?>(
"--tenant-id",
description: "Azure AD tenant ID. Auto-detected from 'az account show' if not provided.");
command.AddOption(tenantIdOption);

var dryRunOption = new Option<bool>(
"--dry-run",
description: "Show what would be done without executing");
command.AddOption(dryRunOption);

command.AddOption(new Option<bool>(["--verbose", "-v"], description: "Enable verbose logging"));

command.SetHandler(async (context) =>
{
var serverNames = context.ParseResult.GetValueForOption(serverNamesOption)!;
var blueprintId = context.ParseResult.GetValueForOption(blueprintIdOption);
var agentInstances = context.ParseResult.GetValueForOption(agentInstancesOption);
var tenantId = context.ParseResult.GetValueForOption(tenantIdOption);
var dryRun = context.ParseResult.GetValueForOption(dryRunOption);
var ct = context.GetCancellationToken();

var executor = new AddByoScopesExecutor(logger, toolingService, agentBlueprintService, graphApiService);
var success = await executor.ExecuteAsync(serverNames, blueprintId, agentInstances, tenantId, dryRun, ct);
if (!success)
{
context.ExitCode = 1;
}
});
Comment on lines +732 to +747

return command;
}

internal static void WriteLabel(string label)
{
var prevColor = Console.ForegroundColor;
Expand Down
Loading
Loading