Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ The Microsoft Agent 365 DevTools CLI can be used through the developer journey o
- **develop**: Manage MCP tool servers for agent development
- **develop-mcp**: Manage MCP servers in Dataverse environments
- **setup**: Set up your Agent 365 environment by creating Azure resources, configuring permissions, and registering your agent blueprint for deployment
- **publish**: Update agent manifest and publish package to MOS (Microsoft Online Services); configure federated identity and app role assignments. After publishing, hire your agent through Teams to complete onboarding.
- **publish**: Update agent manifest IDs and package the manifest for upload to the Microsoft 365 Admin Center. After uploading, hire your agent through Teams to complete onboarding.
- **deploy**: Deploy Agent 365 application binaries to the configured Azure App Service and update Agent 365 Tool permissions
- **config**: Configure Azure subscription, resource settings, and deployment options for Agent 365 CLI commands
- **query-entra**: Query Microsoft Entra ID for agent information (scopes, permissions, consent status)
Expand Down
2 changes: 1 addition & 1 deletion docs/ai-workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ AI workflows are structured markdown documents that guide AI agents (like GitHub
- Setup commands (infrastructure, blueprint, permissions)
- Development commands (MCP management, Dataverse integration)
- Deployment to Azure (multi-platform support)
- Publishing to MOS (Microsoft Online Services)
- Publishing agent manifests for upload to Microsoft 365 Admin Center
- Query operations (Entra ID scopes and permissions)
- Cleanup operations (blueprint, instance, Azure resources)
- Error handling and edge cases
Expand Down
10 changes: 4 additions & 6 deletions docs/ai-workflows/integration-test-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -638,7 +638,7 @@ a365 deploy mcp
---

### Section 9: Publish Command
**Objective**: Test manifest publication to MOS
**Objective**: Test manifest packaging and upload preparation

#### Test 9.1: Create Manifest File
```bash
Expand Down Expand Up @@ -679,15 +679,13 @@ a365 publish --dry-run

#### Test 9.3: Publish Manifest (Actual)
```bash
# Publish to MOS
# Package manifest for upload
a365 publish

# Expected:
# - Manifest updated with blueprint IDs
# - Package created
# - Published to MOS (Microsoft Online Services)
# - Federated identity configured
# - App role assignments updated
# - Package created (manifest.zip)
# - Upload package to Microsoft 365 Admin Center manually

# Record: Publish succeeded (Yes/No)
# Note: After publish, hire agent through Teams to complete onboarding
Expand Down
3 changes: 1 addition & 2 deletions docs/ai-workflows/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ a365 deploy mcp # Update MCP only

### Publish
```bash
a365 publish # Publish to MOS
a365 publish # Package manifest for upload to Microsoft 365 Admin Center
a365 publish --dry-run # Show publish plan
```

Expand Down Expand Up @@ -472,7 +472,6 @@ $env:AGENT365_TEST_MANAGER_EMAIL

# CLI configuration (optional)
$env:AGENT365_CONFIG_PATH # Override default config location
$env:MOS_TITLES_URL # Override MOS endpoint (testing)
```

---
Expand Down
4 changes: 2 additions & 2 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ flowchart TB
Azure["Azure Resource Manager<br/>(App Service, Web Apps)"]
Graph["Microsoft Graph API<br/>(Entra ID, Permissions)"]
Bot["Azure Bot Service<br/>(Messaging Endpoints)"]
MOS["MOS Titles Service<br/>(Agent Publishing)"]
M365["Microsoft 365 Admin Center<br/>(Agent Upload)"]
Dataverse["Dataverse<br/>(MCP Server Management)"]
end

Expand All @@ -65,7 +65,7 @@ flowchart TB
Services --> Azure
Services --> Graph
Services --> Bot
Services --> MOS
Services --> M365
Services --> Dataverse
Services --> Config
```
Expand Down
125 changes: 23 additions & 102 deletions src/DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,32 +148,26 @@ a365 develop-mcp list-servers -e "myenv" --verbose

### Publish Command

The `publish` command packages and publishes your agent manifest to the MOS (Microsoft Online Services) Titles service. It uses **embedded templates** for complete portability - no external file dependencies required.
The `publish` command updates manifest IDs from your agent blueprint and packages the manifest files into a zip ready for upload to the Microsoft 365 Admin Center. It uses **embedded templates** for complete portability no external file dependencies required.

**Key Features:**
- **Embedded Templates**: Manifest templates (JSON + PNG) are embedded in the CLI binary
- **Fully Portable**: No external file dependencies - works from any directory
- **Automatic ID Updates**: Updates both `manifest.json` and `agenticUserTemplateManifest.json` with agent blueprint ID
- **Interactive Customization**: Prompts for manifest customization before upload
- **Graceful Degradation**: Falls back to manual upload if permissions are insufficient
- **Graph API Integration**: Configures federated identity credentials and role assignments
- **Fully Portable**: No external file dependencies — works from any directory
- **Automatic ID Updates**: Updates both `manifest.json` and `agenticUserTemplateManifest.json` with the agent blueprint ID

**Command Options:**
- `a365 publish` — Publish agent manifest with embedded templates
- `a365 publish --dry-run` — Preview changes without uploading
- `a365 publish --skip-graph` — Skip Graph API operations (federated identity, role assignments)
- `a365 publish --mos-env <env>` — Target specific MOS environment (default: prod)
- `a365 publish --mos-token <token>` — Override MOS authentication token
- `a365 publish` — Update manifest IDs and create the manifest zip package
- `a365 publish --dry-run` — Preview changes without writing files or creating the zip

**Manifest Structure:**

The publish command works with two manifest files:

1. **`manifest.json`** - Teams app manifest with agent metadata
- Updated fields: `id`, `name.short`, `name.full`, `bots[0].botId`
- Updated fields: `id`, `copilotAgents.customEngineAgents[0].id`, `bots[0].botId`

2. **`agenticUserTemplateManifest.json`** - Agent identity blueprint configuration
- Updated fields: `agentIdentityBlueprintId` (replaces old `webApplicationInfo.id`)
- Updated field: `agentIdentityBlueprintId`

**Workflow:**

Expand All @@ -184,26 +178,11 @@ a365 config display
# 2. Run setup to create agent blueprint (if not already done)
a365 setup all

# 3. Publish the manifest
# 3. Package the manifest
a365 publish
```

**Interactive Customization Prompt:**

Before uploading, you'll be prompted to customize:
- **Version**: Must increment for republishing (e.g., 1.0.0 → 1.0.1)
- **Agent Name**: Short (≤30 chars) and full display names
- **Descriptions**: Short (1-2 sentences) and full capabilities
- **Developer Info**: Name, website URL, privacy URL
- **Icons**: Custom branding (color.png, outline.png)

**Manual Upload Fallback:**

If you receive an authorization error (401/403), the CLI will:
1. Create the manifest package locally in a temporary directory
2. Display the package location
3. Provide instructions for manual upload to MOS Titles portal
4. Reference documentation for detailed steps
# 4. Upload the generated manifest.zip to the Microsoft 365 Admin Center
```

**Example:**

Expand All @@ -213,90 +192,32 @@ a365 publish

# Dry run to preview changes
a365 publish --dry-run

# Skip Graph API operations
a365 publish --skip-graph

# Use custom MOS environment
$env:MOS_TITLES_URL = "https://titles.dev.mos.microsoft.com"
a365 publish
```

**Manual Upload Instructions:**

If automated upload fails due to insufficient privileges:

1. Locate the generated `manifest.zip` file (path shown in error message)
2. Navigate to MOS Titles portal: `https://titles.prod.mos.microsoft.com`
3. Go to Packages section
4. Upload the manifest.zip file
5. Follow the portal workflow to complete publishing

For detailed MOS upload instructions, see the [MOS Titles Documentation](https://aka.ms/mos-titles-docs).

**MOS Token Authentication:**

The publish command uses **custom client app** authentication to acquire MOS (Microsoft Office Store) tokens:

- **MosTokenService**: Native C# service using MSAL.NET for interactive authentication
- **Custom Client App**: Uses the client app ID configured during `a365 config init` (not hardcoded Microsoft IDs)
- **Tenant-Specific Authorities**: Uses `https://login.microsoftonline.com/{tenantId}` for single-tenant app support (not `/common` endpoint)
- **Token Caching**: Caches tokens locally in `.mos-token-cache.json` to reduce auth prompts
- **MOS Environments**: Supports prod, sdf, test, gccm, gcch, and dod environments
- **Redirect URI**: Uses `http://localhost:8400/` for OAuth callback (aligns with custom client app configuration)

**Important:** Single-tenant apps (created after October 15, 2018) cannot use the `/common` endpoint due to Azure policy. The CLI automatically uses tenant-specific authority URLs built from the `TenantId` in your configuration to ensure compatibility.

**MOS Prerequisites (Auto-Configured):**

On first run, `a365 publish` automatically configures MOS API access:

1. **Service Principal Creation**: Creates service principals for MOS resource apps in your tenant:
- `6ec511af-06dc-4fe2-b493-63a37bc397b1` (TPS AppServices 3p App - MOS publishing)
- `8578e004-a5c6-46e7-913e-12f58912df43` (Power Platform API - MOS token acquisition)
- `e8be65d6-d430-4289-a665-51bf2a194bda` (MOS Titles API - titles.prod.mos.microsoft.com access)

2. **Idempotency Check**: Skips setup if MOS permissions already exist in custom client app

3. **Admin Consent Detection**: Checks OAuth2 permission grants and prompts user to grant admin consent if missing
After `a365 publish` completes:

4. **Fail-Fast on Privilege Errors**: If you lack Application Administrator/Cloud Application Administrator/Global Administrator role, the CLI shows manual service principal creation commands:
```bash
az ad sp create --id 6ec511af-06dc-4fe2-b493-63a37bc397b1
az ad sp create --id 8578e004-a5c6-46e7-913e-12f58912df43
az ad sp create --id e8be65d6-d430-4289-a665-51bf2a194bda
```
1. Locate the generated `manifest.zip` file (path shown in output)
2. Go to [Microsoft 365 Admin Center](https://admin.microsoft.com) > Settings > Integrated apps
3. Upload the `manifest.zip` file
4. Follow the portal workflow to complete publishing

**Architecture Details:**

- **MosConstants.cs**: Centralized constants for MOS resource app IDs, environment scopes, authorities, redirect URI
- **MosTokenService.cs**: Handles token acquisition using MSAL.NET PublicClientApplication with tenant-specific authorities:
- Validates both `ClientAppId` and `TenantId` from configuration
- Builds authority URL dynamically: `https://login.microsoftonline.com/{tenantId}`
- Government cloud: `https://login.microsoftonline.us/{tenantId}`
- Returns null if TenantId is missing (fail-fast validation)
- **PublishHelpers.EnsureMosPrerequisitesAsync**: Just-in-time provisioning of MOS prerequisites with idempotency and error handling
- **ManifestTemplateService**: Handles embedded resource extraction and manifest customization
- **ManifestTemplateService**: Handles embedded resource extraction and manifest ID updates
- **Embedded Resources**: 4 files embedded at build time:
- `manifest.json` - Base Teams app manifest
- `agenticUserTemplateManifest.json` - Agent identity blueprint manifest
- `color.png` - Color icon (192x192)
- `outline.png` - Outline icon (32x32)
- **Temporary Working Directory**: Templates extracted to temp directory, customized, then zipped
- **Automatic Cleanup**: Temp directory removed after successful publish
- `manifest.json` Base Teams app manifest
- `agenticUserTemplateManifest.json` Agent identity blueprint manifest
- `color.png` Color icon (192x192)
- `outline.png` Outline icon (32x32)
- **Temporary Working Directory**: Templates extracted to temp directory, IDs updated, then zipped
- **Automatic Cleanup**: Temp directory removed after successful packaging

**Error Handling:**

- **AADSTS650052 (Missing Service Principal/Admin Consent)**: Shows Portal URL for admin consent or prompts interactive consent
- **AADSTS50194 (Single-Tenant App / Multi-Tenant Endpoint)**: Fixed by using tenant-specific authority URLs instead of `/common` endpoint
- **MOS Prerequisites Failure**: Displays manual `az ad sp create` commands for all three MOS resource apps if automatic creation fails
- **401 Unauthorized / 403 Forbidden**: Graceful fallback with manual upload instructions
- **Missing Blueprint ID**: Clear error message directing user to run `a365 setup`
- **Missing TenantId**: MosTokenService returns null if TenantId is not configured (fail-fast validation)
- **Invalid Manifest**: JSON validation errors with specific field information
- **Network Errors**: Detailed HTTP status codes and response bodies for troubleshooting
- **Consistent Error Codes**: Uses `ErrorCodes.MosTokenAcquisitionFailed`, `ErrorCodes.MosPrerequisitesFailed`, `ErrorCodes.MosAdminConsentRequired`
- **Centralized Messages**: Error guidance from `ErrorMessages.GetMosServicePrincipalMitigation()` and `ErrorMessages.GetMosAdminConsentMitigation()`

## Permissions Architecture

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -520,11 +520,11 @@ private static void LogDeprecationError(ILogger logger, string commandName)
{
logger.LogError("ERROR: Command '{Command}' has been deprecated.", commandName);
logger.LogError("");
logger.LogError("This command bypasses the Microsoft Online Services (MOS) workflow,");
logger.LogError("This command bypasses the standard agent registration workflow,");
logger.LogError("which prevents proper agent registration and event propagation.");
logger.LogError("");
logger.LogError("Use the recommended workflow instead:");
logger.LogError(" 1. Run 'a365 publish' to publish your agent to MOS");
logger.LogError(" 1. Run 'a365 publish' to package and upload your agent manifest");
logger.LogError(" 2. Run 'a365 deploy' to deploy your application (if Azure-hosted)");
logger.LogError(" 3. Create an agent instance through Microsoft Teams");
logger.LogError("");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This folder contains CLI command implementations. Each command inherits from `As
| **create-instance** | `CreateInstanceCommand.cs` | Agent identity, licenses, and notifications setup |
| **deploy** | `DeployCommand.cs` | Multiplatform deployment to Azure App Service |
| **cleanup** | `CleanupCommand.cs` | Delete agent resources (blueprint, instance, Azure) |
| **publish** | `PublishCommand.cs` | Publish agent manifest to MOS Titles service |
| **publish** | `PublishCommand.cs` | Package agent manifest for upload to Microsoft 365 Admin Center |
| **query-entra** | `QueryEntraCommand.cs` | Query Entra ID scopes for blueprints and instances |
| **develop** | `DevelopCommand.cs` | Development utilities (tokens, permissions, mock server) |
| **develop-mcp** | `DevelopMcpCommand.cs` | MCP server management in Dataverse environments |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ await SetupHelpers.EnsureResourcePermissionsAsync(
cancellationToken);

// Configure Power Platform API permissions using unified method
// Note: Using the MOS Power Platform API (8578e004-a5c6-46e7-913e-12f58912df43) which is
// Note: Using the Power Platform API (8578e004-a5c6-46e7-913e-12f58912df43) which is
// the Power Platform API for agent operations. This API exposes Connectivity.Connections.Read
// for reading Power Platform connections.
// Similar to Messaging Bot API, we skip addToRequiredResourceAccess because the scopes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,6 @@ public static class ErrorCodes
public const string RetryExhausted = "RETRY_EXHAUSTED";
public const string SetupValidationFailed = "SETUP_VALIDATION_FAILED";
public const string ClientAppValidationFailed = "CLIENT_APP_VALIDATION_FAILED";
public const string MosTokenAcquisitionFailed = "MOS_TOKEN_ACQUISITION_FAILED";
public const string MosPrerequisitesFailed = "MOS_PREREQUISITES_FAILED";
public const string MosAdminConsentRequired = "MOS_ADMIN_CONSENT_REQUIRED";
public const string MosServicePrincipalCreationFailed = "MOS_SERVICE_PRINCIPAL_CREATION_FAILED";
public const string MosInsufficientPrivileges = "MOS_INSUFFICIENT_PRIVILEGES";
public const string MosPermissionUpdateFailed = "MOS_PERMISSION_UPDATE_FAILED";
public const string DotNetSdkVersionMismatch = "DOTNET_SDK_VERSION_MISMATCH";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,32 +155,4 @@ public static List<string> GetGenericAppServicePlanMitigation()

#endregion

#region MOS Token and Prerequisites Messages

public const string MosClientAppIdMissing =
"Custom client app ID not found in configuration. Run 'a365 config init' first.";

public const string MosClientAppNotFound =
"Custom client app not found in tenant. Verify the app exists and you have access.";

public const string MosTokenAcquisitionFailed =
"Failed to acquire MOS token. Check your authentication and permissions.";

public const string MosAdminConsentRequired =
"Admin consent required for MOS API permissions. Visit the Azure Portal to grant consent.";

/// <summary>
/// Gets mitigation steps for MOS service principal creation failures.
/// </summary>
public static List<string> GetMosServicePrincipalMitigation(string appId)
{
return new List<string>
{
$"Insufficient privileges to create service principal for {appId}.",
"Required role: Application Administrator, Cloud Application Administrator, or Global Administrator.",
$"Ask your tenant administrator to run: az ad sp create --id {appId}"
};
}

#endregion
}
30 changes: 0 additions & 30 deletions src/Microsoft.Agents.A365.DevTools.Cli/Constants/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ This folder contains centralized constant definitions used throughout the CLI. C
| **AuthenticationConstants.cs** | OAuth scopes, redirect URIs, authority URLs | Authentication services |
| **ConfigConstants.cs** | Configuration-related constants, environment URLs | ConfigService, endpoint resolution |
| **McpConstants.cs** | MCP (Model Context Protocol) constants | Agent 365 Tools App IDs, MCP endpoints |
| **MosConstants.cs** | MOS (Microsoft Online Services) Titles constants | PublishCommand, MosTokenService |
| **GraphApiConstants.cs** | Microsoft Graph API constants | GraphApiService, permission configuration |
| **CommandNames.cs** | CLI command name strings | Command registration, help text |

Expand Down Expand Up @@ -57,11 +56,6 @@ public static class ErrorMessages
public static string ConfigFileNotFound(string path)
=> $"Configuration file not found: {path}";

public static string GetMosServicePrincipalMitigation()
=> "Run the following commands to create required service principals:...";

public static string GetMosAdminConsentMitigation(string clientAppId)
=> $"Admin consent required. Visit: https://login.microsoftonline.com/...";
}
```

Expand Down Expand Up @@ -118,30 +112,6 @@ public static class McpConstants

---

## MosConstants

MOS Titles service constants:

```csharp
public static class MosConstants
{
// MOS Resource App IDs (for service principal creation - see source for actual values)
public const string TpsAppServicesAppId = "...";
public const string PowerPlatformApiAppId = "...";
public const string MosTitlesApiAppId = "...";

// Environment-specific scopes
public static string GetMosScope(string environment) => environment switch
{
"prod" => "api://...",
"sdf" => "api://...",
_ => "api://..."
};
}
```

---

## Cross-References

- **[CLI Design](../design.md)** - Architecture overview
Expand Down
Loading
Loading