From da24f1ac57d129a3a24b0cda12cfd459f6b17058 Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Tue, 20 Jan 2026 09:00:54 -0800 Subject: [PATCH 1/2] feat: Add design documents --- docs/design.md | 565 ++++++++++++++++++ .../agents-a365-notifications/docs/design.md | 318 ++++++++++ .../docs/design.md | 207 +++++++ .../docs/design.md | 230 +++++++ .../agents-a365-observability/docs/design.md | 422 +++++++++++++ packages/agents-a365-runtime/docs/design.md | 227 +++++++ .../docs/design.md | 203 +++++++ .../docs/design.md | 215 +++++++ .../docs/design.md | 208 +++++++ packages/agents-a365-tooling/docs/design.md | 269 +++++++++ 10 files changed, 2864 insertions(+) create mode 100644 docs/design.md create mode 100644 packages/agents-a365-notifications/docs/design.md create mode 100644 packages/agents-a365-observability-extensions-openai/docs/design.md create mode 100644 packages/agents-a365-observability-hosting/docs/design.md create mode 100644 packages/agents-a365-observability/docs/design.md create mode 100644 packages/agents-a365-runtime/docs/design.md create mode 100644 packages/agents-a365-tooling-extensions-claude/docs/design.md create mode 100644 packages/agents-a365-tooling-extensions-langchain/docs/design.md create mode 100644 packages/agents-a365-tooling-extensions-openai/docs/design.md create mode 100644 packages/agents-a365-tooling/docs/design.md diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 00000000..5985fb6b --- /dev/null +++ b/docs/design.md @@ -0,0 +1,565 @@ +# Microsoft Agent 365 SDK for Node.js - Architecture and Design + +This document describes the architecture and design of the Microsoft Agent 365 SDK for Node.js/TypeScript. It is intended to help developers and coding agents understand the project structure and quickly get started reading, writing, and reviewing code. + +## Overview + +The Microsoft Agent 365 SDK extends the [Microsoft 365 Agents SDK](https://github.com/Microsoft/Agents-for-js) with enterprise-grade capabilities for building sophisticated AI agents. It provides comprehensive tooling for: + +- **Observability**: OpenTelemetry-based tracing, monitoring, and context propagation +- **Notifications**: Agent notification services and activity routing for Microsoft 365 workloads +- **Runtime**: Core utilities for agent operations and Power Platform integration +- **Tooling**: MCP (Model Context Protocol) server configuration and tool discovery + +The SDK supports production deployment across Microsoft 365, Teams, Copilot Studio, and Webchat platforms. + +## Repository Structure + +``` +Agent365-nodejs/ +├── packages/ # Core packages (9 total) +│ ├── agents-a365-runtime/ +│ ├── agents-a365-tooling/ +│ ├── agents-a365-observability/ +│ ├── agents-a365-notifications/ +│ ├── agents-a365-observability-hosting/ +│ ├── agents-a365-observability-extensions-openai/ +│ ├── agents-a365-tooling-extensions-claude/ +│ ├── agents-a365-tooling-extensions-langchain/ +│ └── agents-a365-tooling-extensions-openai/ +├── tests/ # Test suite +├── tests-agent/ # Sample agent applications +├── docs/ # Documentation +└── package.json # Workspace configuration +``` + +### Package Layout + +Each package follows a consistent structure: + +``` +packages/agents-a365-/ +├── src/ +│ ├── index.ts # Public API exports +│ ├── .ts # Core implementation +│ └── / # Sub-modules +├── package.json # Package configuration +├── tsconfig.json # TypeScript configuration +└── docs/ + └── design.md # Package-specific design doc +``` + +## Core Packages + +> **Note**: Each package has its own detailed design document in `packages//docs/design.md`. The sections below provide an overview; refer to the package-specific documents for implementation details. + +### 1. Runtime (`@microsoft/agents-a365-runtime`) + +> **Detailed documentation**: [packages/agents-a365-runtime/docs/design.md](../packages/agents-a365-runtime/docs/design.md) + +Core utilities shared across the SDK. + +**Key Classes:** + +| Class | Purpose | +|-------|---------| +| `Utility` | Token decoding, agent identity resolution, user-agent generation | +| `AgenticAuthenticationService` | Token exchange for MCP platform authentication | +| `PowerPlatformApiDiscovery` | Endpoint discovery for different cloud environments | + +**Environment Utilities:** + +| Function | Purpose | +|----------|---------| +| `getObservabilityAuthenticationScope()` | Get auth scopes for observability service | +| `getClusterCategory()` | Get environment classification (prod, dev, local) | +| `isDevelopmentEnvironment()` | Check if running in development mode | +| `getMcpPlatformAuthenticationScope()` | Get MCP platform authentication scope | + +**Usage Example:** + +```typescript +import { + Utility, + PowerPlatformApiDiscovery, + getClusterCategory, +} from '@microsoft/agents-a365-runtime'; + +// Decode agent identity from JWT token +const appId = Utility.GetAppIdFromToken(jwtToken); + +// Resolve agent identity from context +const agentId = Utility.ResolveAgentIdentity(turnContext, authToken); + +// Discover Power Platform endpoints +const discovery = new PowerPlatformApiDiscovery('prod'); +const endpoint = discovery.getTenantIslandClusterEndpoint(tenantId); + +// Generate User-Agent header +const userAgent = Utility.GetUserAgentHeader('MyOrchestrator'); +``` + +### 2. Observability (`@microsoft/agents-a365-observability`) + +> **Detailed documentation**: [packages/agents-a365-observability/docs/design.md](../packages/agents-a365-observability/docs/design.md) + +The foundation for distributed tracing in agent applications. Built on OpenTelemetry. + +**Key Classes:** + +| Class | Purpose | +|-------|---------| +| `ObservabilityManager` | Main entry point, singleton pattern for telemetry configuration | +| `ObservabilityBuilder` | Fluent API for configuring telemetry | +| `InvokeAgentScope` | Trace agent invocation lifecycle (entry point for agent requests) | +| `InferenceScope` | Trace LLM/AI model inference calls | +| `ExecuteToolScope` | Trace tool execution operations | +| `BaggageBuilder` | Fluent API for context propagation across async boundaries | + +**Data Classes:** + +| Interface | Purpose | +|-----------|---------| +| `InvokeAgentDetails` | Agent endpoint, session ID, and invocation metadata | +| `AgentDetails` | Agent identification and metadata | +| `TenantDetails` | Tenant identification for multi-tenant scenarios | +| `InferenceDetails` | Model name, tokens, provider information | +| `ToolCallDetails` | Tool name, arguments, endpoint | +| `CallerDetails` | Caller identification and context | + +**Usage Example:** + +```typescript +import { + ObservabilityManager, + InvokeAgentScope, + BaggageBuilder, + ExecutionType, +} from '@microsoft/agents-a365-observability'; + +// Initialize telemetry +ObservabilityManager.start({ + serviceName: 'my-agent', + tokenResolver: (agentId, tenantId) => getAuthToken(), + clusterCategory: 'prod' +}); + +// Set context for child spans +const scope = new BaggageBuilder() + .tenantId(tenantId) + .agentId(agentId) + .correlationId(correlationId) + .build(); + +scope.run(() => { + // Trace agent invocation + using agentScope = InvokeAgentScope.start( + invokeAgentDetails, + tenantDetails, + callerAgentDetails, + callerDetails + ); + + // Agent logic here + agentScope.recordResponse('result'); +}); +``` + +### 3. Observability Extensions + +Framework-specific instrumentations that integrate with the observability core: + +| Package | Purpose | Design Doc | +|---------|---------|------------| +| `observability-extensions-openai` | Instrument OpenAI Agents SDK | [design.md](../packages/agents-a365-observability-extensions-openai/docs/design.md) | +| `observability-hosting` | Hosting-specific observability utilities | [design.md](../packages/agents-a365-observability-hosting/docs/design.md) | + +### 4. Tooling (`@microsoft/agents-a365-tooling`) + +> **Detailed documentation**: [packages/agents-a365-tooling/docs/design.md](../packages/agents-a365-tooling/docs/design.md) + +MCP (Model Context Protocol) tool server configuration and discovery. + +**Key Classes:** + +| Class | Purpose | +|-------|---------| +| `McpToolServerConfigurationService` | Discover and configure MCP tool servers | +| `Utility` | Header composition, token validation, URL construction | + +**Interfaces:** + +| Interface | Purpose | +|-----------|---------| +| `MCPServerConfig` | Tool server configuration (name, URL, headers) | +| `McpClientTool` | Tool metadata from MCP servers | +| `ToolOptions` | Tool request options with orchestrator name | + +**Dual-Mode Configuration:** + +The tooling package supports two modes based on the `NODE_ENV` variable: + +- **Development** (`NODE_ENV=Development`): Loads tool servers from a local `ToolingManifest.json` file +- **Production** (default): Discovers tool servers from the Agent365 gateway endpoint + +**Usage Example:** + +```typescript +import { + McpToolServerConfigurationService, + Utility, +} from '@microsoft/agents-a365-tooling'; + +const service = new McpToolServerConfigurationService(); + +// Discover available tool servers +const servers = await service.listToolServers( + agenticAppId, + bearerToken, + { orchestratorName: 'MyOrchestrator' } +); + +for (const server of servers) { + console.log(`Tool: ${server.mcpServerName}`); + console.log(`URL: ${server.url}`); + + // Get tools from the server + const tools = await service.getMcpClientTools( + server.mcpServerName, + server + ); +} +``` + +### 5. Tooling Extensions + +Framework-specific adapters for MCP tool integration: + +| Package | Purpose | Design Doc | +|---------|---------|------------| +| `tooling-extensions-claude` | Claude SDK integration | [design.md](../packages/agents-a365-tooling-extensions-claude/docs/design.md) | +| `tooling-extensions-langchain` | LangChain integration | [design.md](../packages/agents-a365-tooling-extensions-langchain/docs/design.md) | +| `tooling-extensions-openai` | OpenAI Agents SDK integration | [design.md](../packages/agents-a365-tooling-extensions-openai/docs/design.md) | + +### 6. Notifications (`@microsoft/agents-a365-notifications`) + +> **Detailed documentation**: [packages/agents-a365-notifications/docs/design.md](../packages/agents-a365-notifications/docs/design.md) + +Agent notification and lifecycle event handling. + +**Extension Methods:** + +| Method | Purpose | +|--------|---------| +| `onAgentNotification()` | Generic agent notification routing | +| `onAgenticEmailNotification()` | Email notification handler | +| `onAgenticWordNotification()` | Word document notification handler | +| `onAgenticExcelNotification()` | Excel notification handler | +| `onAgenticPowerPointNotification()` | PowerPoint notification handler | +| `onLifecycleNotification()` | All lifecycle events handler | +| `onAgenticUserCreatedNotification()` | User creation event handler | +| `onAgenticUserWorkloadOnboardingNotification()` | User onboarding event handler | +| `onAgenticUserDeletedNotification()` | User deletion event handler | + +**Usage Example:** + +```typescript +import '@microsoft/agents-a365-notifications'; +import { AgentApplication } from '@microsoft/agents-hosting'; + +const app = new AgentApplication(); + +// Handle email notifications +app.onAgenticEmailNotification(async (turnContext, turnState, notification) => { + const emailRef = notification.emailReference; + console.log(`Received email notification: ${emailRef?.subject}`); +}); + +// Handle lifecycle events +app.onLifecycleNotification(async (turnContext, turnState, notification) => { + console.log(`Lifecycle event: ${notification.notificationType}`); +}); +``` + +## Design Patterns + +### 1. Singleton Pattern + +`ObservabilityManager` uses singleton pattern to ensure a single tracer provider per application: + +```typescript +export class ObservabilityManager { + private static instance?: ObservabilityBuilder; + + public static start(options?: BuilderOptions): ObservabilityBuilder { + const builder = new ObservabilityBuilder(); + // Configure builder... + builder.start(); + ObservabilityManager.instance = builder; + return builder; + } + + public static getInstance(): ObservabilityBuilder | null { + return ObservabilityManager.instance || null; + } +} +``` + +### 2. Disposable Pattern + +All scope classes implement the `Disposable` interface for automatic span lifecycle management: + +```typescript +using scope = InvokeAgentScope.start(details, tenantDetails); +// Span is active +scope.recordResponse('result'); +// Span automatically ends when scope is disposed +``` + +### 3. Builder Pattern + +`BaggageBuilder` and `ObservabilityBuilder` provide fluent APIs for configuration: + +```typescript +const scope = new BaggageBuilder() + .tenantId(tenantId) + .agentId(agentId) + .correlationId(correlationId) + .build(); +``` + +### 4. Strategy Pattern + +`McpToolServerConfigurationService` selects between manifest-based (dev) and gateway-based (prod) configuration loading based on environment: + +```typescript +async listToolServers(agenticAppId: string, authToken: string): Promise { + return this.isDevScenario() + ? this.getMCPServerConfigsFromManifest() + : this.getMCPServerConfigsFromToolingGateway(agenticAppId, authToken); +} +``` + +### 5. Extension Methods Pattern + +The notifications package uses TypeScript declaration merging to extend `AgentApplication`: + +```typescript +declare module '@microsoft/agents-hosting' { + interface AgentApplication { + onAgenticEmailNotification(handler: AgentNotificationHandler): void; + } +} + +AgentApplication.prototype.onAgenticEmailNotification = function(...) { ... }; +``` + +## Data Flow + +### Agent Invocation Tracing Flow + +``` +Application + │ + ▼ +BaggageBuilder.build().run() ← Set tenant, agent, correlation IDs + │ + ▼ +InvokeAgentScope.start() ← Create root span + │ + ├──▶ SpanProcessor.onStart() ← Copy baggage to span attributes + │ + ▼ +[Agent execution] + │ + ├──▶ InferenceScope.start() ← Child span for LLM calls + │ └── recordInputTokens() + │ └── recordOutputTokens() + │ + ├──▶ ExecuteToolScope.start() ← Child span for tool execution + │ └── recordResponse() + │ + ▼ +scope.recordResponse() ← Record final response + │ + ▼ +BatchSpanProcessor ← Accumulate spans + │ + ▼ +Agent365Exporter.export() ← Send to backend + ├── Partition by (tenant_id, agent_id) + ├── Resolve endpoint via PowerPlatformApiDiscovery + └── POST to observability service +``` + +### MCP Tool Discovery Flow + +``` +Application + │ + ▼ +McpToolServerConfigurationService.listToolServers() + │ + ▼ +isDevScenario()? + │ + ├── YES ─▶ getMCPServerConfigsFromManifest() + │ ├── Find ToolingManifest.json + │ ├── Parse JSON configuration + │ └── Return MCPServerConfig[] + │ + └── NO ──▶ getMCPServerConfigsFromToolingGateway() + ├── Build gateway URL + ├── HTTP GET with auth token + └── Return MCPServerConfig[] +``` + +## Configuration + +### Environment Variables + +| Variable | Purpose | Values | +|----------|---------|--------| +| `NODE_ENV` | Controls development vs production behavior | `Development`, `production` (default) | +| `CLUSTER_CATEGORY` | Environment classification | `local`, `dev`, `prod`, `gov`, etc. | +| `A365_OBSERVABILITY_SCOPES_OVERRIDE` | Override observability auth scopes | Space-separated scope strings | +| `MCP_PLATFORM_AUTHENTICATION_SCOPE` | MCP platform auth scope | Scope string | +| `MCP_PLATFORM_ENDPOINT` | MCP platform base URL | URL string | + +### Cluster Categories + +The SDK supports multiple deployment environments via `ClusterCategory`: + +| Category | Description | +|----------|-------------| +| `local` | Local development | +| `dev` | Development environment | +| `test` | Test environment | +| `preprod` | Pre-production | +| `firstrelease` | First release ring | +| `prod` | Production | +| `gov` | Government cloud | +| `high` | High security government | +| `dod` | Department of Defense | +| `mooncake` | China sovereign cloud | +| `ex` | Sovereign cloud (EagleX) | +| `rx` | Sovereign cloud (RX) | + +## Testing + +### Test Structure + +``` +tests/ +├── observability/ +├── runtime/ +├── tooling/ +└── jest.config.cjs +``` + +### Running Tests + +```bash +# Run all tests +pnpm test + +# Run with coverage +pnpm test:coverage + +# Run specific package tests +cd packages/agents-a365-observability && pnpm test + +# Run integration tests +pnpm test:integration +``` + +### Test Conventions + +- **Framework**: Jest +- **Pattern**: AAA (Arrange → Act → Assert) +- **Naming**: `.test.ts` or `test_.ts` + +## Development + +### Prerequisites + +- Node.js >= 18.0.0 +- pnpm 10.20.0+ +- Git + +### Setup + +```bash +# Clone repository +git clone https://github.com/microsoft/Agent365-nodejs.git +cd Agent365-nodejs + +# Install dependencies +pnpm install + +# Build all packages +pnpm build +``` + +### Building + +```bash +# Build all packages +pnpm build + +# Build specific package +cd packages/agents-a365-observability && pnpm build + +# Watch mode (development) +pnpm build:watch +``` + +### Code Quality + +The project uses ESLint with TypeScript support: + +```bash +# Check linting +pnpm lint + +# Auto-fix issues +pnpm lint:fix +``` + +### Package Dependencies + +Dependencies between packages are managed via pnpm workspace: + +``` +runtime ◄─── observability + │ │ + │ ▼ + │ observability-hosting + │ │ + │ ▼ + │ observability-extensions-openai + │ + ▼ +tooling ◄─── tooling-extensions-* + │ + ▼ +notifications +``` + +## Key Files Reference + +| File | Purpose | +|------|---------| +| [package.json](../package.json) | Workspace configuration, scripts | +| [packages/agents-a365-observability/src/index.ts](../packages/agents-a365-observability/src/index.ts) | Observability public API | +| [packages/agents-a365-tooling/src/index.ts](../packages/agents-a365-tooling/src/index.ts) | Tooling public API | +| [packages/agents-a365-runtime/src/index.ts](../packages/agents-a365-runtime/src/index.ts) | Runtime public API | +| [packages/agents-a365-notifications/src/index.ts](../packages/agents-a365-notifications/src/index.ts) | Notifications public API | + +## External Resources + +- [Microsoft Agent 365 Developer Documentation](https://learn.microsoft.com/en-us/javascript/api/agent365-sdk-node/agent365-overview?view=agent365-sdk-node-latest) +- [Microsoft 365 Agents SDK Documentation](https://learn.microsoft.com/microsoft-365/agents-sdk/) +- [OpenTelemetry Node.js Documentation](https://opentelemetry.io/docs/languages/js/) +- [Model Context Protocol Specification](https://modelcontextprotocol.io/) +- [GitHub Repository](https://github.com/microsoft/Agent365-nodejs) diff --git a/packages/agents-a365-notifications/docs/design.md b/packages/agents-a365-notifications/docs/design.md new file mode 100644 index 00000000..a5879b0e --- /dev/null +++ b/packages/agents-a365-notifications/docs/design.md @@ -0,0 +1,318 @@ +# Notifications - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-notifications` package. + +## Overview + +The notifications package provides agent notification and lifecycle event handling for Microsoft 365 workloads. It extends the `AgentApplication` class with methods to handle notifications from Email, Word, Excel, PowerPoint, and agent lifecycle events. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ @microsoft/agents-hosting │ +│ AgentApplication │ +└─────────────────────────────────────────────────────────────────┘ + │ + (extends via prototype) + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Notification Extension Methods │ +│ │ +│ onAgentNotification() onAgenticEmailNotification() │ +│ onAgenticWordNotification() onAgenticExcelNotification() │ +│ onAgenticPowerPointNotification() onLifecycleNotification() │ +│ onAgenticUserCreatedNotification() ... │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ AgentNotificationHandler │ +│ (turnContext, turnState, agentNotificationActivity) => void │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### Extension Methods ([agent-notification.ts](../src/agent-notification.ts)) + +The package extends `AgentApplication` with notification handlers: + +```typescript +import '@microsoft/agents-a365-notifications'; +import { AgentApplication } from '@microsoft/agents-hosting'; + +const app = new AgentApplication(); + +// Handle all agent notifications +app.onAgentNotification('agents:email', async (turnContext, turnState, notification) => { + console.log('Received notification:', notification.notificationType); +}); + +// Handle email notifications specifically +app.onAgenticEmailNotification(async (turnContext, turnState, notification) => { + const emailRef = notification.emailReference; + console.log(`Email from: ${emailRef?.from}`); + console.log(`Subject: ${emailRef?.subject}`); +}); + +// Handle Word document notifications +app.onAgenticWordNotification(async (turnContext, turnState, notification) => { + const comment = notification.wpxComment; + console.log(`Comment: ${comment?.comment}`); +}); + +// Handle Excel notifications +app.onAgenticExcelNotification(async (turnContext, turnState, notification) => { + // Handle Excel-specific notification +}); + +// Handle PowerPoint notifications +app.onAgenticPowerPointNotification(async (turnContext, turnState, notification) => { + // Handle PowerPoint-specific notification +}); + +// Handle all lifecycle events +app.onLifecycleNotification(async (turnContext, turnState, notification) => { + console.log('Lifecycle event:', notification.notificationType); +}); + +// Handle specific lifecycle events +app.onAgenticUserCreatedNotification(async (turnContext, turnState, notification) => { + console.log('User identity created'); +}); + +app.onAgenticUserWorkloadOnboardingNotification(async (turnContext, turnState, notification) => { + console.log('User workload onboarding updated'); +}); + +app.onAgenticUserDeletedNotification(async (turnContext, turnState, notification) => { + console.log('User deleted'); +}); +``` + +### AgentNotificationHandler Type ([agent-notification-handler.ts](../src/extensions/agent-notification-handler.ts)) + +```typescript +type AgentNotificationHandler = ( + turnContext: TurnContext, + turnState: TState, + agentNotificationActivity: AgentNotificationActivity +) => Promise; +``` + +### Constants ([constants.ts](../src/constants.ts)) + +**Channel Constants:** + +| Constant | Value | Purpose | +|----------|-------|---------| +| `AGENTS_CHANNEL` | `agents` | Base channel prefix | +| `AGENTS_EMAIL_SUBCHANNEL` | `agents:email` | Email notifications | +| `AGENTS_EXCEL_SUBCHANNEL` | `agents:excel` | Excel notifications | +| `AGENTS_WORD_SUBCHANNEL` | `agents:word` | Word notifications | +| `AGENTS_POWERPOINT_SUBCHANNEL` | `agents:powerpoint` | PowerPoint notifications | + +**Lifecycle Constants:** + +| Constant | Value | Purpose | +|----------|-------|---------| +| `AGENT_LIFECYCLE` | `agentlifecycle` | Lifecycle event activity name | +| `USER_CREATED_LIFECYCLE_EVENT` | `agenticuseridentitycreated` | User creation event | +| `USER_WORKLOAD_ONBOARDING_LIFECYCLE_EVENT` | `agenticuserworkloadonboardingupdated` | User onboarding event | +| `USER_DELETED_LIFECYCLE_EVENT` | `agenticuserdeleted` | User deletion event | + +## Data Models + +### AgentNotificationActivity + +Wrapper for notification activity with typed notification data: + +```typescript +interface AgentNotificationActivity { + activity: Activity; + notificationType: NotificationType; + emailReference?: EmailReference; + emailResponse?: EmailResponse; + wpxComment?: WPXComment; +} +``` + +### NotificationType ([notification-type.ts](../src/models/notification-type.ts)) + +```typescript +enum NotificationType { + Email = 'Email', + Word = 'Word', + Excel = 'Excel', + PowerPoint = 'PowerPoint', + Lifecycle = 'Lifecycle', + Unknown = 'Unknown' +} +``` + +### EmailReference ([email-reference.ts](../src/models/email-reference.ts)) + +```typescript +interface EmailReference { + messageId?: string; + from?: string; + to?: string[]; + cc?: string[]; + subject?: string; + receivedDateTime?: string; + bodyPreview?: string; + webLink?: string; +} +``` + +### WPXComment ([wpx-comment.ts](../src/models/wpx-comment.ts)) + +Used for Word, PowerPoint, and Excel comment notifications: + +```typescript +interface WPXComment { + comment?: string; + documentId?: string; + documentName?: string; + webLink?: string; + author?: string; + createdDateTime?: string; +} +``` + +## Design Patterns + +### Extension Methods Pattern + +The package uses TypeScript declaration merging to extend `AgentApplication`: + +```typescript +// Declare the interface extension +declare module '@microsoft/agents-hosting' { + interface AgentApplication { + onAgenticEmailNotification( + routeHandler: AgentNotificationHandler, + rank?: number, + autoSignInHandlers?: string[] + ): void; + } +} + +// Implement via prototype +AgentApplication.prototype.onAgenticEmailNotification = function(...) { ... }; +``` + +### Route Selector Pattern + +Each notification handler uses a selector function to filter activities: + +```typescript +const routeSelector: Selector = async (turnContext) => { + const activity = turnContext.activity; + + // Check for agentic channel + if (!activity.channelId?.toLowerCase().startsWith('agents')) { + return false; + } + + // Check for specific subchannel + if (activity.channelId.toLowerCase() !== channelId.toLowerCase()) { + return false; + } + + return true; +}; +``` + +### Agentic Request Filtering + +All notification handlers verify the request is from an agentic source: + +```typescript +function isAgenticRequest(turnContext: TurnContext): boolean { + const role = turnContext.activity?.recipient?.role; + return role === 'agenticAppInstance' || role === 'agenticUser'; +} +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +├── agent-notification.ts # Extension methods implementation +├── constants.ts # Channel and lifecycle constants +├── extensions/ +│ ├── index.ts # Extension exports +│ └── agent-notification-handler.ts # Handler type definition +└── models/ + ├── index.ts # Model exports + ├── notification-type.ts # NotificationType enum + ├── agent-notification-activity.ts # Activity wrapper + ├── email-reference.ts # Email notification model + ├── email-response.ts # Email response model + └── wpx-comment.ts # Word/PowerPoint/Excel comment model +``` + +## Usage Examples + +### Email Notification Handler + +```typescript +app.onAgenticEmailNotification(async (turnContext, turnState, notification) => { + const email = notification.emailReference; + + if (email) { + console.log(`New email from: ${email.from}`); + console.log(`Subject: ${email.subject}`); + console.log(`Preview: ${email.bodyPreview}`); + + // Process the email + await processEmail(email); + + // Send response + await turnContext.sendActivity(`Processed email: ${email.subject}`); + } +}); +``` + +### Lifecycle Event Handler + +```typescript +app.onAgenticUserCreatedNotification(async (turnContext, turnState, notification) => { + // User identity was created - initialize user state + const activity = notification.activity; + const userId = activity.from?.id; + + await initializeUserState(userId); + console.log(`Initialized state for user: ${userId}`); +}); +``` + +### Generic Channel Handler + +```typescript +// Handle all notifications from a specific channel +app.onAgentNotification('agents:email', async (turnContext, turnState, notification) => { + switch (notification.notificationType) { + case NotificationType.Email: + // Handle email + break; + default: + console.log('Unknown notification type'); + } +}); + +// Handle all agentic notifications with wildcard +app.onAgentNotification('agents:*', async (turnContext, turnState, notification) => { + console.log(`Received ${notification.notificationType} notification`); +}); +``` + +## Dependencies + +- `@microsoft/agents-hosting` - AgentApplication, TurnContext, TurnState +- `@microsoft/agents-a365-runtime` - Runtime utilities (internal) +- `@microsoft/agents-activity` - Activity types diff --git a/packages/agents-a365-observability-extensions-openai/docs/design.md b/packages/agents-a365-observability-extensions-openai/docs/design.md new file mode 100644 index 00000000..2a48e907 --- /dev/null +++ b/packages/agents-a365-observability-extensions-openai/docs/design.md @@ -0,0 +1,207 @@ +# Observability Extensions - OpenAI - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-observability-extensions-openai` package. + +## Overview + +The OpenAI observability extensions package provides automatic instrumentation for the OpenAI Agents SDK. It wraps the OpenAI Agents SDK tracer to integrate with Agent 365 Observability, converting OpenAI trace events to OpenTelemetry spans. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ OpenAI Agents SDK │ +│ (@openai/agents) │ +└─────────────────────────────────────────────────────────────────┘ + │ + (trace events) + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ OpenAIAgentsTraceInstrumentor │ +│ (InstrumentationBase implementation) │ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐│ +│ │ OpenAIAgentsTraceProcessor ││ +│ │ ││ +│ │ Converts OpenAI trace events → OpenTelemetry spans ││ +│ └─────────────────────────────────────────────────────────────┘│ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ OpenTelemetry SDK │ +│ (via ObservabilityManager) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### OpenAIAgentsTraceInstrumentor ([OpenAIAgentsTraceInstrumentor.ts](../src/OpenAIAgentsTraceInstrumentor.ts)) + +Extends OpenTelemetry's `InstrumentationBase` to provide automatic tracing for OpenAI Agents: + +```typescript +import { ObservabilityManager } from '@microsoft/agents-a365-observability'; +import { OpenAIAgentsTraceInstrumentor } from '@microsoft/agents-a365-observability-extensions-openai'; + +// First, configure observability +ObservabilityManager.start({ + serviceName: 'my-openai-agent', + tokenResolver: async (agentId, tenantId) => getToken(), + clusterCategory: 'prod' +}); + +// Then create and enable the instrumentor +const instrumentor = new OpenAIAgentsTraceInstrumentor({ + enabled: true, + tracerName: 'my-openai-agent-tracer', + tracerVersion: '1.0.0', + suppressInvokeAgentInput: false // Set true to hide LLM inputs +}); + +// Enable instrumentation +instrumentor.enable(); + +// ... run your OpenAI agent ... + +// Disable when done +instrumentor.disable(); +``` + +**Configuration Options:** + +```typescript +interface OpenAIAgentsInstrumentationConfig extends InstrumentationConfig { + enabled?: boolean; // Enable/disable instrumentation + tracerName?: string; // Custom tracer name + tracerVersion?: string; // Custom tracer version + suppressInvokeAgentInput?: boolean; // Hide LLM input messages +} +``` + +**Methods:** + +| Method | Purpose | +|--------|---------| +| `enable()` | Enable tracing for OpenAI Agents SDK | +| `disable()` | Disable instrumentation and cleanup | +| `getProcessor()` | Access the trace processor instance | + +### OpenAIAgentsTraceProcessor ([OpenAIAgentsTraceProcessor.ts](../src/OpenAIAgentsTraceProcessor.ts)) + +Custom trace processor that converts OpenAI Agents trace events to OpenTelemetry spans: + +```typescript +import { OpenAIAgentsTraceProcessor } from '@microsoft/agents-a365-observability-extensions-openai'; + +const processor = new OpenAIAgentsTraceProcessor(tracer, { + suppressInvokeAgentInput: false +}); + +// The processor is automatically registered with the OpenAI Agents SDK +// when using OpenAIAgentsTraceInstrumentor +``` + +The processor handles various OpenAI Agents trace events and converts them to appropriate OpenTelemetry span types: + +- Agent runs → `InvokeAgentScope` +- LLM calls → `InferenceScope` +- Tool executions → `ExecuteToolScope` + +## Prerequisites + +The instrumentor requires `ObservabilityManager` to be configured before initialization: + +```typescript +// This will throw an error +const instrumentor = new OpenAIAgentsTraceInstrumentor(); +// Error: ObservabilityManager is not configured yet + +// Correct usage +ObservabilityManager.start({ serviceName: 'my-agent' }); +const instrumentor = new OpenAIAgentsTraceInstrumentor(); +``` + +## Integration with OpenAI Agents SDK + +The instrumentor integrates with the OpenAI Agents SDK's tracing system: + +```typescript +import { setTraceProcessors, setTracingDisabled } from '@openai/agents'; + +// In enable(): +setTracingDisabled(false); // Enable tracing in OpenAI SDK +setTraceProcessors([this.processor]); // Register our processor + +// In disable(): +setTraceProcessors([]); // Clear processors +``` + +## Usage Example + +Complete example with an OpenAI agent: + +```typescript +import { Agent, run } from '@openai/agents'; +import { ObservabilityManager } from '@microsoft/agents-a365-observability'; +import { OpenAIAgentsTraceInstrumentor } from '@microsoft/agents-a365-observability-extensions-openai'; + +// 1. Configure observability +ObservabilityManager.start({ + serviceName: 'my-openai-agent', + tokenResolver: async () => getObservabilityToken(), + clusterCategory: 'prod' +}); + +// 2. Enable instrumentation +const instrumentor = new OpenAIAgentsTraceInstrumentor({ + enabled: true, + suppressInvokeAgentInput: false +}); +instrumentor.enable(); + +// 3. Create and run your agent +const agent = new Agent({ + name: 'MyAgent', + model: 'gpt-4o', + instructions: 'You are a helpful assistant.' +}); + +const result = await run(agent, 'Hello, world!'); +console.log(result.finalOutput); + +// 4. Cleanup +instrumentor.disable(); +await ObservabilityManager.shutdown(); +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +├── OpenAIAgentsTraceInstrumentor.ts # Main instrumentor class +└── OpenAIAgentsTraceProcessor.ts # Trace event processor +``` + +## Dependencies + +- `@microsoft/agents-a365-observability` - ObservabilityManager, scope classes +- `@openai/agents` - OpenAI Agents SDK, trace types +- `@opentelemetry/api` - OpenTelemetry API +- `@opentelemetry/instrumentation` - InstrumentationBase class + +## Supported OpenAI Agents SDK Versions + +The instrumentor supports OpenAI Agents SDK versions `>=0.1.5`. + +```typescript +protected init(): InstrumentationModuleDefinition { + return { + name: '@openai/agents', + supportedVersions: ['>=0.1.5'], + files: [], + }; +} +``` diff --git a/packages/agents-a365-observability-hosting/docs/design.md b/packages/agents-a365-observability-hosting/docs/design.md new file mode 100644 index 00000000..d9a17aba --- /dev/null +++ b/packages/agents-a365-observability-hosting/docs/design.md @@ -0,0 +1,230 @@ +# Observability Hosting - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-observability-hosting` package. + +## Overview + +The observability hosting package provides hosting-specific utilities for integrating observability with the Microsoft Agents Hosting SDK. It bridges the gap between `TurnContext` and OpenTelemetry baggage/scope creation. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ TurnContext │ +│ (@microsoft/agents-hosting) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Utility Classes │ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ TurnContextUtils │ │ BaggageBuilderUtils │ │ +│ │ │ │ │ │ +│ │ Extract baggage │───▶│ Populate builder │ │ +│ │ pairs from context │ │ from context │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ BaggageBuilder │ +│ (@microsoft/agents-a365-observability) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### BaggageBuilderUtils ([BaggageBuilderUtils.ts](../src/utils/BaggageBuilderUtils.ts)) + +Utilities to populate `BaggageBuilder` from a `TurnContext`: + +```typescript +import { BaggageBuilderUtils } from '@microsoft/agents-a365-observability-hosting'; +import { BaggageBuilder } from '@microsoft/agents-a365-observability'; + +const builder = new BaggageBuilder(); + +// Populate all baggage from turn context +BaggageBuilderUtils.fromTurnContext(builder, turnContext); + +// Or populate specific baggage categories +BaggageBuilderUtils.setCallerBaggage(builder, turnContext); +BaggageBuilderUtils.setExecutionTypeBaggage(builder, turnContext); +BaggageBuilderUtils.setTargetAgentBaggage(builder, turnContext); +BaggageBuilderUtils.setTenantIdBaggage(builder, turnContext); +BaggageBuilderUtils.setSourceMetadataBaggage(builder, turnContext); +BaggageBuilderUtils.setConversationIdBaggage(builder, turnContext); + +// Build and use the scope +const scope = builder.build(); +scope.run(() => { + // All child spans inherit the baggage +}); +``` + +**Methods:** + +| Method | Purpose | +|--------|---------| +| `fromTurnContext(builder, turnContext)` | Populate all supported baggage pairs | +| `setCallerBaggage(builder, turnContext)` | Set caller ID, name, UPN, tenant | +| `setExecutionTypeBaggage(builder, turnContext)` | Set execution type (HumanToAgent, Agent2Agent) | +| `setTargetAgentBaggage(builder, turnContext)` | Set target agent ID, name, description | +| `setTenantIdBaggage(builder, turnContext)` | Set tenant ID from recipient or channel data | +| `setSourceMetadataBaggage(builder, turnContext)` | Set channel name and subchannel | +| `setConversationIdBaggage(builder, turnContext)` | Set conversation ID and item link | + +### TurnContextUtils ([TurnContextUtils.ts](../src/utils/TurnContextUtils.ts)) + +Low-level utilities to extract OpenTelemetry baggage pairs from `TurnContext`: + +```typescript +import { + getCallerBaggagePairs, + getExecutionTypePair, + getTargetAgentBaggagePairs, + getTenantIdPair, + getSourceMetadataBaggagePairs, + getConversationIdAndItemLinkPairs +} from '@microsoft/agents-a365-observability-hosting'; + +// Extract caller information +const callerPairs = getCallerBaggagePairs(turnContext); +// => [['gen_ai.caller.id', 'aad-object-id'], ['gen_ai.caller.name', 'John'], ...] + +// Extract execution type +const executionPair = getExecutionTypePair(turnContext); +// => [['gen_ai.execution.type', 'HumanToAgent']] + +// Extract target agent information +const agentPairs = getTargetAgentBaggagePairs(turnContext); +// => [['gen_ai.agent.id', 'agent-123'], ['gen_ai.agent.name', 'MyAgent'], ...] +``` + +**Functions:** + +| Function | Extracted Keys | +|----------|----------------| +| `getCallerBaggagePairs()` | `gen_ai.caller.id`, `gen_ai.caller.name`, `gen_ai.caller.upn`, `gen_ai.caller.tenant_id`, `gen_ai.agent.blueprint_id` | +| `getExecutionTypePair()` | `gen_ai.execution.type` | +| `getTargetAgentBaggagePairs()` | `gen_ai.agent.id`, `gen_ai.agent.name`, `gen_ai.agent.description`, `gen_ai.agent.auid` | +| `getTenantIdPair()` | `tenant_id` | +| `getSourceMetadataBaggagePairs()` | `gen_ai.execution.source.name`, `gen_ai.execution.source.description` | +| `getConversationIdAndItemLinkPairs()` | `gen_ai.conversation.id`, `gen_ai.conversation.item_link` | + +### AgenticTokenCacheInstance ([AgenticTokenCache.ts](../src/caching/AgenticTokenCache.ts)) + +Token caching for improved performance: + +```typescript +import { AgenticTokenCacheInstance } from '@microsoft/agents-a365-observability-hosting'; + +// Cache token with key +AgenticTokenCacheInstance.set('cache-key', 'token-value', ttlMs); + +// Retrieve cached token +const token = AgenticTokenCacheInstance.get('cache-key'); +``` + +## Execution Type Detection + +The package automatically detects the execution type based on caller role: + +```typescript +function getExecutionTypePair(turnContext: TurnContext): Array<[string, string]> { + const from = turnContext.activity.from; + let executionType = ExecutionType.HumanToAgent; + + if (from.role) { + switch (from.role) { + case RoleTypes.AgenticUser: + executionType = ExecutionType.Agent2Agent; + break; + case RoleTypes.User: + executionType = ExecutionType.HumanToAgent; + break; + } + } + + return [[OpenTelemetryConstants.GEN_AI_EXECUTION_TYPE_KEY, executionType]]; +} +``` + +## Tenant ID Resolution + +The package extracts tenant ID from multiple sources: + +```typescript +function getTenantIdPair(turnContext: TurnContext): Array<[string, string]> { + // Try recipient first + let tenantId = turnContext.activity?.recipient?.tenantId; + + // Fallback to channelData + if (!tenantId && turnContext.activity?.channelData) { + const channelData = typeof turnContext.activity.channelData === 'string' + ? JSON.parse(turnContext.activity.channelData) + : turnContext.activity.channelData; + + tenantId = channelData?.tenant?.id; + } + + return tenantId ? [[OpenTelemetryConstants.TENANT_ID_KEY, tenantId]] : []; +} +``` + +## Usage Example + +Complete example of setting up observability from a turn context: + +```typescript +import { BaggageBuilderUtils } from '@microsoft/agents-a365-observability-hosting'; +import { BaggageBuilder, InvokeAgentScope } from '@microsoft/agents-a365-observability'; + +// In your agent's message handler +async function onMessage(turnContext: TurnContext, turnState: TurnState) { + // Build baggage from turn context + const builder = new BaggageBuilder(); + BaggageBuilderUtils.fromTurnContext(builder, turnContext); + const baggageScope = builder.build(); + + // Execute agent logic within baggage context + return baggageScope.run(async () => { + // Create agent invocation scope + using scope = InvokeAgentScope.start( + { + agentId: turnContext.activity.recipient?.agenticAppId, + agentName: turnContext.activity.recipient?.name, + sessionId: turnContext.activity.conversation?.id + }, + { tenantId: turnContext.activity.recipient?.tenantId } + ); + + // Agent processing... + const response = await processMessage(turnContext.activity.text); + scope.recordResponse(response); + + await turnContext.sendActivity(response); + }); +} +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +├── utils/ +│ ├── BaggageBuilderUtils.ts # BaggageBuilder population utilities +│ ├── TurnContextUtils.ts # Low-level extraction functions +│ └── ScopeUtils.ts # Scope creation utilities +└── caching/ + └── AgenticTokenCache.ts # Token caching +``` + +## Dependencies + +- `@microsoft/agents-a365-observability` - BaggageBuilder, OpenTelemetryConstants, ExecutionType +- `@microsoft/agents-a365-runtime` - Runtime utilities +- `@microsoft/agents-hosting` - TurnContext type +- `@microsoft/agents-activity` - RoleTypes enum diff --git a/packages/agents-a365-observability/docs/design.md b/packages/agents-a365-observability/docs/design.md new file mode 100644 index 00000000..f73c5148 --- /dev/null +++ b/packages/agents-a365-observability/docs/design.md @@ -0,0 +1,422 @@ +# Observability - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-observability` package. + +## Overview + +The observability package provides OpenTelemetry-based distributed tracing infrastructure for AI agent applications. It enables comprehensive observability by tracing agent invocations, LLM inference calls, and tool executions. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Public API │ +│ ObservabilityManager | Builder | Scopes | BaggageBuilder │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ ObservabilityBuilder │ +│ (Fluent configuration API) │ +│ - Service name/version │ +│ - Token resolver │ +│ - Cluster category │ +│ - Exporter options │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌──────────────────┼──────────────────┐ + ▼ ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ SpanProcessor │ │ BatchSpanProcessor│ │ Agent365Exporter │ +│ (Baggage to │ │ (OTEL SDK) │ │ (HTTP export) │ +│ attributes) │ │ │ │ │ +└──────────────────┘ └──────────────────┘ └──────────────────┘ +``` + +## Key Components + +### ObservabilityManager ([ObservabilityManager.ts](../src/ObservabilityManager.ts)) + +Main entry point using singleton pattern: + +```typescript +import { ObservabilityManager } from '@microsoft/agents-a365-observability'; + +// Option 1: Simple start with options +ObservabilityManager.start({ + serviceName: 'my-agent', + serviceVersion: '1.0.0', + tokenResolver: async (agentId, tenantId) => getAuthToken(), + clusterCategory: 'prod' +}); + +// Option 2: Configure with callback +ObservabilityManager.configure((builder) => { + builder + .withService('my-agent', '1.0.0') + .withTokenResolver(tokenResolver) + .withClusterCategory('prod'); +}).start(); + +// Get current instance +const instance = ObservabilityManager.getInstance(); + +// Shutdown +await ObservabilityManager.shutdown(); +``` + +### ObservabilityBuilder ([ObservabilityBuilder.ts](../src/ObservabilityBuilder.ts)) + +Fluent API for configuring telemetry: + +```typescript +import { Builder } from '@microsoft/agents-a365-observability'; + +const builder = new Builder() + .withService('my-agent', '1.0.0') + .withTokenResolver(async (agentId, tenantId) => getToken()) + .withClusterCategory('prod') + .withExporterOptions({ + maxQueueSize: 2048, + scheduledDelayMilliseconds: 5000 + }); + +builder.build(); +builder.start(); +``` + +**Builder Methods:** + +| Method | Purpose | +|--------|---------| +| `withService(name, version?)` | Set service name and version | +| `withTokenResolver(resolver)` | Set token resolver for exporter auth | +| `withClusterCategory(category)` | Set environment cluster | +| `withExporterOptions(options)` | Configure exporter options | +| `build()` | Initialize OpenTelemetry SDK | +| `start()` | Start the SDK | +| `shutdown()` | Graceful shutdown | + +### Scope Classes + +#### OpenTelemetryScope (Base Class) ([OpenTelemetryScope.ts](../src/tracing/scopes/OpenTelemetryScope.ts)) + +Base class for all tracing scopes, implementing `Disposable`: + +```typescript +abstract class OpenTelemetryScope implements Disposable { + // Make span active for async callback + withActiveSpanAsync(callback: () => Promise): Promise; + + // Record an error + recordError(error: Error): void; + + // Record multiple attributes + recordAttributes(attributes: Record): void; + + // Set attribute if value is not null + protected setTagMaybe(name: string, value: T | null | undefined): void; + + // Dispose and end span + [Symbol.dispose](): void; + dispose(): void; +} +``` + +#### InvokeAgentScope ([InvokeAgentScope.ts](../src/tracing/scopes/InvokeAgentScope.ts)) + +Traces agent invocation operations: + +```typescript +import { InvokeAgentScope, InvokeAgentDetails, TenantDetails } from '@microsoft/agents-a365-observability'; + +using scope = InvokeAgentScope.start( + { + agentId: 'agent-123', + agentName: 'MyAgent', + endpoint: { host: 'api.example.com', port: 443 }, + sessionId: 'session-456', + request: { + content: 'Hello', + executionType: ExecutionType.HumanToAgent + } + }, + { tenantId: 'tenant-789' }, + callerAgentDetails, // Optional caller agent + callerDetails // Optional caller user +); + +scope.recordInputMessages(['Hello']); +// ... agent processing ... +scope.recordResponse('Hi there!'); +scope.recordOutputMessages(['Hi there!']); +``` + +**Span attributes recorded:** +- Server address and port +- Session ID +- Execution type and source metadata +- Input/output messages +- Caller details (ID, UPN, name, tenant, client IP) +- Caller agent details (if agent-to-agent) + +#### InferenceScope ([InferenceScope.ts](../src/tracing/scopes/InferenceScope.ts)) + +Traces LLM/AI model inference calls: + +```typescript +import { InferenceScope, InferenceDetails, InferenceOperationType } from '@microsoft/agents-a365-observability'; + +using scope = InferenceScope.start( + { + operationName: InferenceOperationType.CHAT, + model: 'gpt-4', + providerName: 'openai' + }, + agentDetails, + tenantDetails, + conversationId, + sourceMetadata +); + +scope.recordInputMessages(['User message']); +// ... LLM call ... +scope.recordOutputMessages(['Assistant response']); +scope.recordInputTokens(100); +scope.recordOutputTokens(50); +scope.recordResponseId('resp-123'); +scope.recordFinishReasons(['stop']); +``` + +#### ExecuteToolScope ([ExecuteToolScope.ts](../src/tracing/scopes/ExecuteToolScope.ts)) + +Traces tool execution operations: + +```typescript +import { ExecuteToolScope, ToolCallDetails } from '@microsoft/agents-a365-observability'; + +using scope = ExecuteToolScope.start( + { + toolName: 'search', + arguments: JSON.stringify({ query: 'weather' }), + toolCallId: 'call-123', + toolType: 'mcp', + endpoint: { host: 'tools.example.com' } + }, + agentDetails, + tenantDetails, + conversationId, + sourceMetadata +); + +// ... tool execution ... +scope.recordResponse('Tool result'); +``` + +### BaggageBuilder ([BaggageBuilder.ts](../src/tracing/middleware/BaggageBuilder.ts)) + +Fluent API for setting OpenTelemetry baggage: + +```typescript +import { BaggageBuilder } from '@microsoft/agents-a365-observability'; + +// Full builder pattern +const scope = new BaggageBuilder() + .tenantId('tenant-123') + .agentId('agent-456') + .correlationId('corr-789') + .callerId('user-abc') + .sessionId('session-xyz') + .callerUpn('user@example.com') + .conversationId('conv-123') + .build(); + +// Execute code within baggage context +scope.run(() => { + // All child spans inherit this baggage +}); + +// Convenience method +const scope2 = BaggageBuilder.setRequestContext( + 'tenant-123', + 'agent-456', + 'corr-789' +); +``` + +**Available baggage setters:** + +| Method | Baggage Key | +|--------|-------------| +| `tenantId(value)` | `tenant_id` | +| `agentId(value)` | `gen_ai.agent.id` | +| `agentAuid(value)` | `gen_ai.agent.auid` | +| `agentUpn(value)` | `gen_ai.agent.upn` | +| `correlationId(value)` | `correlation_id` | +| `callerId(value)` | `gen_ai.caller.id` | +| `sessionId(value)` | `session_id` | +| `conversationId(value)` | `gen_ai.conversation.id` | +| `callerUpn(value)` | `gen_ai.caller.upn` | +| `sourceMetadataName(value)` | `gen_ai.execution.source.name` | + +## Data Interfaces + +### InvokeAgentDetails + +```typescript +interface InvokeAgentDetails extends AgentDetails { + request?: AgentRequest; + endpoint?: ServiceEndpoint; + sessionId?: string; +} +``` + +### AgentDetails + +```typescript +interface AgentDetails { + agentId: string; + conversationId?: string; + agentName?: string; + agentType?: string; + agentDescription?: string; + iconUri?: string; + platformId?: string; + agentAUID?: string; + agentUPN?: string; + agentBlueprintId?: string; + tenantId?: string; + agentClientIP?: string; +} +``` + +### InferenceDetails + +```typescript +interface InferenceDetails { + operationName: InferenceOperationType; + model: string; + providerName?: string; + inputTokens?: number; + outputTokens?: number; + finishReasons?: string[]; + responseId?: string; +} +``` + +### ToolCallDetails + +```typescript +interface ToolCallDetails { + toolName: string; + arguments?: string; + toolCallId?: string; + description?: string; + toolType?: string; + endpoint?: ServiceEndpoint; +} +``` + +### Enums + +```typescript +enum ExecutionType { + HumanToAgent = 'HumanToAgent', + Agent2Agent = 'Agent2Agent', + EventToAgent = 'EventToAgent', + Unknown = 'Unknown' +} + +enum InvocationRole { + Human = 'Human', + Agent = 'Agent', + Event = 'Event', + Unknown = 'Unknown' +} + +enum InferenceOperationType { + CHAT = 'Chat', + TEXT_COMPLETION = 'TextCompletion', + GENERATE_CONTENT = 'GenerateContent' +} +``` + +## Design Patterns + +### Singleton Pattern + +`ObservabilityManager` ensures a single telemetry configuration per application: + +```typescript +export class ObservabilityManager { + private static instance?: ObservabilityBuilder; + + public static getInstance(): ObservabilityBuilder | null { + return ObservabilityManager.instance || null; + } +} +``` + +### Disposable Pattern + +All scope classes implement `Disposable` for automatic span lifecycle: + +```typescript +using scope = InvokeAgentScope.start(...); +// Span is active +scope.recordResponse('result'); +// Span automatically ends when scope is disposed +``` + +### Builder Pattern + +`ObservabilityBuilder` and `BaggageBuilder` use method chaining: + +```typescript +const builder = new ObservabilityBuilder() + .withService('my-agent') + .withTokenResolver(resolver) + .withClusterCategory('prod'); +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +├── ObservabilityManager.ts # Main entry point +├── ObservabilityBuilder.ts # Configuration builder +├── tracing/ +│ ├── constants.ts # OpenTelemetry attribute keys +│ ├── contracts.ts # Data interfaces and enums +│ ├── scopes/ +│ │ ├── OpenTelemetryScope.ts # Base scope class +│ │ ├── InvokeAgentScope.ts # Agent invocation tracing +│ │ ├── InferenceScope.ts # LLM inference tracing +│ │ └── ExecuteToolScope.ts # Tool execution tracing +│ ├── middleware/ +│ │ └── BaggageBuilder.ts # Baggage context builder +│ ├── processors/ +│ │ └── SpanProcessor.ts # Baggage-to-attribute processor +│ └── exporter/ +│ ├── Agent365Exporter.ts # Custom HTTP exporter +│ ├── Agent365ExporterOptions.ts # Exporter configuration +│ └── utils.ts # Exporter utilities +└── utils/ + └── logging.ts # Internal logging +``` + +## Dependencies + +- `@opentelemetry/api` - OpenTelemetry API interfaces +- `@opentelemetry/sdk-node` - OpenTelemetry SDK +- `@opentelemetry/sdk-trace-base` - Span processors and exporters +- `@opentelemetry/resources` - Resource configuration +- `@opentelemetry/semantic-conventions` - Semantic attribute keys +- `@microsoft/agents-a365-runtime` - Cluster category type + +## Environment Variables + +| Variable | Purpose | +|----------|---------| +| `ENABLE_A365_OBSERVABILITY_EXPORTER` | Enable/disable Agent365 exporter | diff --git a/packages/agents-a365-runtime/docs/design.md b/packages/agents-a365-runtime/docs/design.md new file mode 100644 index 00000000..033d3056 --- /dev/null +++ b/packages/agents-a365-runtime/docs/design.md @@ -0,0 +1,227 @@ +# Runtime - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-runtime` package. + +## Overview + +The runtime package provides foundational utilities shared across the Microsoft Agent 365 SDK. It offers Power Platform endpoint discovery, JWT token handling, environment configuration, and agent identity resolution. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Public API │ +│ Utility | AgenticAuthenticationService | PowerPlatformApiDiscovery │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌──────────────────┼──────────────────┐ + ▼ ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ Utility │ │ Authentication │ │ API Discovery │ +│ │ │ │ │ │ +│ - Token decode │ │ - Token exchange │ │ - Endpoint URLs │ +│ - Agent identity │ │ - Scopes │ │ - Cluster config │ +│ - User-Agent │ │ │ │ │ +└──────────────────┘ └──────────────────┘ └──────────────────┘ +``` + +## Key Components + +### Utility Class ([utility.ts](../src/utility.ts)) + +The `Utility` class provides static helper methods for common agent operations: + +```typescript +import { Utility } from '@microsoft/agents-a365-runtime'; + +// Decode App ID from JWT token +const appId = Utility.GetAppIdFromToken(jwtToken); + +// Resolve agent identity from turn context or token +const agentId = Utility.ResolveAgentIdentity(turnContext, authToken); + +// Generate User-Agent header +const userAgent = Utility.GetUserAgentHeader('MyOrchestrator'); +// => "Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; MyOrchestrator)" +``` + +**Methods:** + +| Method | Purpose | +|--------|---------| +| `GetAppIdFromToken(token)` | Decode JWT and extract `appid` or `azp` claim | +| `ResolveAgentIdentity(context, authToken)` | Get agent identity from agentic request or token | +| `GetUserAgentHeader(orchestrator?)` | Generate formatted User-Agent string | + +### AgenticAuthenticationService ([agentic-authorization-service.ts](../src/agentic-authorization-service.ts)) + +Handles token exchange for MCP platform authentication: + +```typescript +import { AgenticAuthenticationService } from '@microsoft/agents-a365-runtime'; + +const token = await AgenticAuthenticationService.GetAgenticUserToken( + authorization, + authHandlerName, + turnContext +); +``` + +The service retrieves the MCP platform authentication scope from environment configuration and exchanges the user's token for a scoped access token. + +### PowerPlatformApiDiscovery ([power-platform-api-discovery.ts](../src/power-platform-api-discovery.ts)) + +Handles cluster-based endpoint resolution for Power Platform APIs: + +```typescript +import { PowerPlatformApiDiscovery } from '@microsoft/agents-a365-runtime'; + +const discovery = new PowerPlatformApiDiscovery('prod'); + +// Get token audience +const audience = discovery.getTokenAudience(); +// => "https://api.powerplatform.com" + +// Get tenant endpoint +const endpoint = discovery.getTenantEndpoint(tenantId); + +// Get tenant island cluster endpoint +const islandEndpoint = discovery.getTenantIslandClusterEndpoint(tenantId); +``` + +**Supported Cluster Categories:** + +| Category | API Hostname Suffix | +|----------|---------------------| +| `local` | `api.powerplatform.localhost` | +| `dev`, `test`, `preprod` | `api.powerplatform.com` | +| `firstrelease`, `prod` | `api.powerplatform.com` | +| `gov` | `api.gov.powerplatform.microsoft.us` | +| `high` | `api.high.powerplatform.microsoft.us` | +| `dod` | `api.appsplatform.us` | +| `mooncake` | `api.powerplatform.partner.microsoftonline.cn` | +| `ex` | `api.powerplatform.eaglex.ic.gov` | +| `rx` | `api.powerplatform.microsoft.scloud` | + +### Environment Utilities ([environment-utils.ts](../src/environment-utils.ts)) + +Helper functions for environment-specific configuration: + +```typescript +import { + getObservabilityAuthenticationScope, + getClusterCategory, + isDevelopmentEnvironment, + getMcpPlatformAuthenticationScope, +} from '@microsoft/agents-a365-runtime'; + +// Get observability auth scopes (supports override via env var) +const scopes = getObservabilityAuthenticationScope(); +// => ["https://api.powerplatform.com/.default"] + +// Get cluster category from CLUSTER_CATEGORY env var +const cluster = getClusterCategory(); +// => "prod" (default) + +// Check if running in development +const isDev = isDevelopmentEnvironment(); +// => true if cluster is "local" or "dev" + +// Get MCP platform auth scope +const mcpScope = getMcpPlatformAuthenticationScope(); +``` + +**Environment Variables:** + +| Variable | Purpose | Default | +|----------|---------|---------| +| `A365_OBSERVABILITY_SCOPES_OVERRIDE` | Override observability auth scopes | Production scope | +| `CLUSTER_CATEGORY` | Environment cluster category | `prod` | +| `MCP_PLATFORM_AUTHENTICATION_SCOPE` | MCP platform auth scope | Production scope | + +## Type Definitions + +### ClusterCategory + +```typescript +type ClusterCategory = + | 'local' + | 'dev' + | 'test' + | 'preprod' + | 'firstrelease' + | 'prod' + | 'gov' + | 'high' + | 'dod' + | 'mooncake' + | 'ex' + | 'rx'; +``` + +## Design Patterns + +### Static Utility Methods + +The `Utility` class uses static methods for stateless operations, making it easy to use without instantiation: + +```typescript +// No instantiation needed +const appId = Utility.GetAppIdFromToken(token); +``` + +### Environment-Based Configuration + +Configuration is environment-aware with sensible defaults: + +```typescript +export function getClusterCategory(): string { + const clusterCategory = process.env.CLUSTER_CATEGORY; + return clusterCategory?.toLowerCase() ?? 'prod'; +} +``` + +### Defensive Token Handling + +Token decoding handles edge cases gracefully: + +```typescript +public static GetAppIdFromToken(token: string): string { + if (!token || token.trim() === '') { + return '00000000-0000-0000-0000-000000000000'; + } + + try { + const decoded = jwt.decode(token) as jwt.JwtPayload; + return decoded?.['appid'] || decoded?.['azp'] || ''; + } catch { + return ''; + } +} +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +├── utility.ts # Utility class +├── agentic-authorization-service.ts # Token exchange service +├── power-platform-api-discovery.ts # Endpoint discovery +├── environment-utils.ts # Environment helpers +└── version.ts # Package version constant +``` + +## Dependencies + +- `@microsoft/agents-hosting` - TurnContext and Authorization types +- `jsonwebtoken` - JWT token decoding +- `os` - Operating system information for User-Agent + +## Integration with Other Packages + +The runtime package is a foundational dependency used by: + +- `@microsoft/agents-a365-tooling` - For agent identity resolution and headers +- `@microsoft/agents-a365-observability` - For cluster category and environment utilities +- All tooling extension packages - For authentication and identity resolution diff --git a/packages/agents-a365-tooling-extensions-claude/docs/design.md b/packages/agents-a365-tooling-extensions-claude/docs/design.md new file mode 100644 index 00000000..248e0382 --- /dev/null +++ b/packages/agents-a365-tooling-extensions-claude/docs/design.md @@ -0,0 +1,203 @@ +# Tooling Extensions - Claude - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-tooling-extensions-claude` package. + +## Overview + +The Claude tooling extensions package provides integration between the Microsoft Agent 365 MCP tooling system and the Claude (Anthropic) Agent SDK. It enables automatic discovery and registration of MCP tool servers with Claude agents. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Claude Agent SDK │ +│ (@anthropic-ai/claude-agent-sdk) │ +└─────────────────────────────────────────────────────────────────┘ + ▲ + │ + (Options with mcpServers) + │ +┌─────────────────────────────────────────────────────────────────┐ +│ McpToolRegistrationService │ +│ │ +│ 1. Discover MCP servers via tooling package │ +│ 2. Transform to Claude-compatible format │ +│ 3. Prefix tool names with mcp____ │ +│ 4. Add to agent options │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ McpToolServerConfigurationService │ +│ (@microsoft/agents-a365-tooling) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### McpToolRegistrationService ([McpToolRegistrationService.ts](../src/McpToolRegistrationService.ts)) + +Main service for registering MCP tools with Claude agents: + +```typescript +import { McpToolRegistrationService } from '@microsoft/agents-a365-tooling-extensions-claude'; +import { Options } from '@anthropic-ai/claude-agent-sdk'; + +const registrationService = new McpToolRegistrationService(); + +// Create Claude agent options +const agentOptions: Options = { + name: 'MyClaudeAgent', + model: 'claude-3-opus-20240229', + instructions: 'You are a helpful assistant.' +}; + +// Register MCP tools with the agent +await registrationService.addToolServersToAgent( + agentOptions, + authorization, + authHandlerName, + turnContext, + authToken // Optional, will be obtained if not provided +); + +// agentOptions now contains: +// - mcpServers: { serverName: { type: 'http', url: '...', headers: {...} } } +// - allowedTools: ['mcp__serverName__toolName', ...] +``` + +**Method Signature:** + +```typescript +async addToolServersToAgent( + agentOptions: Options, // Claude agent options to update + authorization: Authorization, // Authorization object for token exchange + authHandlerName: string, // Auth handler name + turnContext: TurnContext, // Current turn context + authToken: string // Bearer token (optional) +): Promise +``` + +## Tool Name Prefixing + +The service prefixes tool names to match Claude's MCP tool naming convention: + +```typescript +// Original tool name: "search" +// Server name: "webTools" +// Claude tool name: "mcp__webTools__search" + +clientTools = clientTools.map((tool) => ({ + name: 'mcp__' + server.mcpServerName + '__' + tool.name, + description: tool.description, + inputSchema: tool.inputSchema +})); +``` + +## Configuration Format + +The service transforms MCP server configs to Claude's expected format: + +```typescript +// Input (from McpToolServerConfigurationService) +const mcpConfig: MCPServerConfig = { + mcpServerName: 'mailTools', + url: 'https://agent365.svc.cloud.microsoft/agents/servers/mailTools' +}; + +// Output (for Claude SDK) +const claudeConfig: McpServerConfig = { + type: 'http', + url: 'https://agent365.svc.cloud.microsoft/agents/servers/mailTools', + headers: { + 'Authorization': 'Bearer ', + 'x-ms-channel-id': 'teams', + 'x-ms-subchannel-id': 'email', + 'User-Agent': 'Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; Claude)' + } +}; +``` + +## Usage Example + +Complete example with a Claude agent: + +```typescript +import { Claude } from '@anthropic-ai/claude-agent-sdk'; +import { McpToolRegistrationService } from '@microsoft/agents-a365-tooling-extensions-claude'; + +// In your agent's message handler +async function onMessage(turnContext: TurnContext, authorization: Authorization) { + const registrationService = new McpToolRegistrationService(); + + // Prepare Claude agent options + const options = { + name: 'MyAssistant', + model: 'claude-3-opus-20240229', + instructions: 'You are a helpful assistant with access to Microsoft 365 tools.' + }; + + // Register MCP tools + await registrationService.addToolServersToAgent( + options, + authorization, + 'myAuthHandler', + turnContext, + authToken + ); + + // Create and run the agent + const agent = new Claude(options); + const response = await agent.run(turnContext.activity.text); + + await turnContext.sendActivity(response); +} +``` + +## Authentication Flow + +The service handles authentication automatically: + +```typescript +// If authToken is not provided, get it via token exchange +if (!authToken) { + authToken = await AgenticAuthenticationService.GetAgenticUserToken( + authorization, + authHandlerName, + turnContext + ); +} + +// Validate the token before use +Utility.ValidateAuthToken(authToken); + +// Resolve agent identity for server discovery +const agenticAppId = RuntimeUtility.ResolveAgentIdentity(turnContext, authToken); +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +└── McpToolRegistrationService.ts # Main registration service +``` + +## Dependencies + +- `@microsoft/agents-a365-tooling` - MCP server discovery and configuration +- `@microsoft/agents-a365-runtime` - Authentication service, agent identity resolution +- `@microsoft/agents-hosting` - TurnContext, Authorization types +- `@anthropic-ai/claude-agent-sdk` - Claude SDK types (McpServerConfig, Options) +- `@modelcontextprotocol/sdk` - MCP client types + +## Orchestrator Identification + +The service identifies itself as "Claude" in User-Agent headers: + +```typescript +private readonly orchestratorName: string = "Claude"; + +// Results in User-Agent header: +// "Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; Claude)" +``` diff --git a/packages/agents-a365-tooling-extensions-langchain/docs/design.md b/packages/agents-a365-tooling-extensions-langchain/docs/design.md new file mode 100644 index 00000000..882b55e1 --- /dev/null +++ b/packages/agents-a365-tooling-extensions-langchain/docs/design.md @@ -0,0 +1,215 @@ +# Tooling Extensions - LangChain - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-tooling-extensions-langchain` package. + +## Overview + +The LangChain tooling extensions package provides integration between the Microsoft Agent 365 MCP tooling system and LangChain. It enables automatic discovery and registration of MCP tool servers with LangChain agents using the `@langchain/mcp-adapters` package. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ LangChain Agent │ +│ (ReactAgent) │ +└─────────────────────────────────────────────────────────────────┘ + ▲ + │ + (Agent with MCP tools) + │ +┌─────────────────────────────────────────────────────────────────┐ +│ McpToolRegistrationService │ +│ │ +│ 1. Discover MCP servers via tooling package │ +│ 2. Create MultiServerMCPClient │ +│ 3. Get tools via MCP adapters │ +│ 4. Merge with existing agent tools │ +│ 5. Return new agent with combined tools │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + ▼ ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ Tooling Service │ │ MultiServerMCP │ │ LangChain Tools │ +│ │ │ Client │ │ │ +│ Server discovery │ │ (@langchain/ │ │ Tool definitions │ +│ │ │ mcp-adapters) │ │ for agent │ +└──────────────────┘ └──────────────────┘ └──────────────────┘ +``` + +## Key Components + +### McpToolRegistrationService ([McpToolRegistrationService.ts](../src/McpToolRegistrationService.ts)) + +Main service for registering MCP tools with LangChain agents: + +```typescript +import { McpToolRegistrationService } from '@microsoft/agents-a365-tooling-extensions-langchain'; +import { createAgent, ReactAgent } from 'langchain'; + +const registrationService = new McpToolRegistrationService(); + +// Create initial LangChain agent +let agent = createAgent({ + llm: chatModel, + tools: existingTools +}); + +// Register MCP tools with the agent +agent = await registrationService.addToolServersToAgent( + agent, + authorization, + authHandlerName, + turnContext, + authToken // Optional, will be obtained if not provided +); + +// Agent now has access to both existing tools and MCP tools +const result = await agent.invoke({ input: 'Search for documents' }); +``` + +**Method Signature:** + +```typescript +async addToolServersToAgent( + agent: ReactAgent, // LangChain agent to update + authorization: Authorization, // Authorization object for token exchange + authHandlerName: string, // Auth handler name + turnContext: TurnContext, // Current turn context + authToken: string // Bearer token (optional) +): Promise // Returns new agent with MCP tools +``` + +## LangChain MCP Integration + +The service uses `@langchain/mcp-adapters` for MCP integration: + +```typescript +import { ClientConfig, Connection, MultiServerMCPClient } from '@langchain/mcp-adapters'; + +// Build MCP server connections +const mcpServers: Record = {}; +for (const server of servers) { + mcpServers[server.mcpServerName] = { + type: 'http', + url: server.url, + headers: Utility.GetToolRequestHeaders(authToken, turnContext, options) + }; +} + +// Create MCP client +const mcpClientConfig: ClientConfig = { mcpServers }; +const multiServerMcpClient = new MultiServerMCPClient(mcpClientConfig); + +// Get LangChain-compatible tools +const mcpTools = await multiServerMcpClient.getTools(); +``` + +## Tool Merging + +The service preserves existing tools when adding MCP tools: + +```typescript +// Get existing tools from agent +const existingTools = agent.options.tools ?? []; + +// Get MCP tools from servers +const mcpTools = await multiServerMcpClient.getTools(); + +// Combine all tools +const allTools = [...existingTools, ...mcpTools]; + +// Create new agent with combined tools +return createAgent({ + ...agent.options, + tools: allTools +}); +``` + +## Usage Example + +Complete example with a LangChain agent: + +```typescript +import { ChatOpenAI } from '@langchain/openai'; +import { createAgent } from 'langchain'; +import { McpToolRegistrationService } from '@microsoft/agents-a365-tooling-extensions-langchain'; + +// In your agent's message handler +async function onMessage(turnContext: TurnContext, authorization: Authorization) { + const registrationService = new McpToolRegistrationService(); + + // Create LangChain chat model + const chatModel = new ChatOpenAI({ + modelName: 'gpt-4', + temperature: 0 + }); + + // Create initial agent + let agent = createAgent({ + llm: chatModel, + tools: [] // Start with no tools + }); + + // Register MCP tools + agent = await registrationService.addToolServersToAgent( + agent, + authorization, + 'myAuthHandler', + turnContext, + authToken + ); + + // Run the agent + const result = await agent.invoke({ + input: turnContext.activity.text + }); + + await turnContext.sendActivity(result.output); +} +``` + +## Connection Configuration + +The service creates HTTP connections for each MCP server: + +```typescript +// MCP server connection format +const connection: Connection = { + type: 'http', + url: server.url, + headers: { + 'Authorization': 'Bearer ', + 'x-ms-channel-id': 'teams', + 'x-ms-subchannel-id': 'email', + 'User-Agent': 'Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; LangChain)' + } +}; +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +└── McpToolRegistrationService.ts # Main registration service +``` + +## Dependencies + +- `@microsoft/agents-a365-tooling` - MCP server discovery and configuration +- `@microsoft/agents-a365-runtime` - Authentication service, agent identity resolution +- `@microsoft/agents-hosting` - TurnContext, Authorization types +- `@langchain/mcp-adapters` - MCP adapters for LangChain (ClientConfig, Connection, MultiServerMCPClient) +- `langchain` - LangChain core (createAgent, ReactAgent) + +## Orchestrator Identification + +The service identifies itself as "LangChain" in User-Agent headers: + +```typescript +private readonly orchestratorName: string = "LangChain"; + +// Results in User-Agent header: +// "Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; LangChain)" +``` diff --git a/packages/agents-a365-tooling-extensions-openai/docs/design.md b/packages/agents-a365-tooling-extensions-openai/docs/design.md new file mode 100644 index 00000000..c340ef3d --- /dev/null +++ b/packages/agents-a365-tooling-extensions-openai/docs/design.md @@ -0,0 +1,208 @@ +# Tooling Extensions - OpenAI - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-tooling-extensions-openai` package. + +## Overview + +The OpenAI tooling extensions package provides integration between the Microsoft Agent 365 MCP tooling system and the OpenAI Agents SDK. It enables automatic discovery and registration of MCP tool servers with OpenAI agents using the native `MCPServerStreamableHttp` class. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ OpenAI Agent │ +│ (@openai/agents) │ +└─────────────────────────────────────────────────────────────────┘ + ▲ + │ + (Agent with mcpServers) + │ +┌─────────────────────────────────────────────────────────────────┐ +│ McpToolRegistrationService │ +│ │ +│ 1. Discover MCP servers via tooling package │ +│ 2. Create MCPServerStreamableHttp instances │ +│ 3. Add to agent.mcpServers array │ +│ 4. Return updated agent │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ McpToolServerConfigurationService │ +│ (@microsoft/agents-a365-tooling) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### McpToolRegistrationService ([McpToolRegistrationService.ts](../src/McpToolRegistrationService.ts)) + +Main service for registering MCP tools with OpenAI agents: + +```typescript +import { McpToolRegistrationService } from '@microsoft/agents-a365-tooling-extensions-openai'; +import { Agent } from '@openai/agents'; + +const registrationService = new McpToolRegistrationService(); + +// Create OpenAI agent +let agent = new Agent({ + name: 'MyAgent', + model: 'gpt-4o', + instructions: 'You are a helpful assistant.' +}); + +// Register MCP tools with the agent +agent = await registrationService.addToolServersToAgent( + agent, + authorization, + authHandlerName, + turnContext, + authToken // Optional, will be obtained if not provided +); + +// Agent now has access to MCP tool servers +const result = await run(agent, 'Search for documents'); +``` + +**Method Signature:** + +```typescript +async addToolServersToAgent( + agent: Agent, // OpenAI agent to update + authorization: Authorization, // Authorization object for token exchange + authHandlerName: string, // Auth handler name + turnContext: TurnContext, // Current turn context + authToken: string // Bearer token (optional) +): Promise // Returns updated agent +``` + +## OpenAI Agents SDK Integration + +The service uses the native `MCPServerStreamableHttp` class from the OpenAI Agents SDK: + +```typescript +import { Agent, MCPServerStreamableHttp } from '@openai/agents'; + +// Create MCP server instances for OpenAI agents +const mcpServers: MCPServerStreamableHttp[] = []; + +for (const server of servers) { + const mcpServer = new MCPServerStreamableHttp({ + url: server.url, + name: server.mcpServerName, + requestInit: { + headers: Utility.GetToolRequestHeaders(authToken, turnContext, options) + } + }); + + mcpServers.push(mcpServer); +} + +// Add to agent +agent.mcpServers = agent.mcpServers ?? []; +agent.mcpServers.push(...mcpServers); +``` + +## Server Configuration + +The service creates `MCPServerStreamableHttp` instances with proper authentication: + +```typescript +const mcpServer = new MCPServerStreamableHttp({ + url: 'https://agent365.svc.cloud.microsoft/agents/servers/mailTools', + name: 'mailTools', + requestInit: { + headers: { + 'Authorization': 'Bearer ', + 'x-ms-channel-id': 'teams', + 'x-ms-subchannel-id': 'email', + 'User-Agent': 'Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; OpenAI)' + } + } +}); +``` + +## Usage Example + +Complete example with an OpenAI agent: + +```typescript +import { Agent, run } from '@openai/agents'; +import { McpToolRegistrationService } from '@microsoft/agents-a365-tooling-extensions-openai'; + +// In your agent's message handler +async function onMessage(turnContext: TurnContext, authorization: Authorization) { + const registrationService = new McpToolRegistrationService(); + + // Create OpenAI agent + let agent = new Agent({ + name: 'MyAssistant', + model: 'gpt-4o', + instructions: 'You are a helpful assistant with access to Microsoft 365 tools.' + }); + + // Register MCP tools + agent = await registrationService.addToolServersToAgent( + agent, + authorization, + 'myAuthHandler', + turnContext, + authToken + ); + + // Run the agent + const result = await run(agent, turnContext.activity.text); + + await turnContext.sendActivity(result.finalOutput); +} +``` + +## Preserving Existing MCP Servers + +The service preserves any existing MCP servers on the agent: + +```typescript +// Ensure mcpServers array exists +agent.mcpServers = agent.mcpServers ?? []; + +// Add new servers (doesn't replace existing ones) +agent.mcpServers.push(...mcpServers); + +return agent; +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +└── McpToolRegistrationService.ts # Main registration service +``` + +## Dependencies + +- `@microsoft/agents-a365-tooling` - MCP server discovery and configuration +- `@microsoft/agents-a365-runtime` - Authentication service, agent identity resolution +- `@microsoft/agents-hosting` - TurnContext, Authorization types +- `@openai/agents` - OpenAI Agents SDK (Agent, MCPServerStreamableHttp) + +## Orchestrator Identification + +The service identifies itself as "OpenAI" in User-Agent headers: + +```typescript +private readonly orchestratorName: string = "OpenAI"; + +// Results in User-Agent header: +// "Agent365SDK/1.0.0 (Windows_NT; Node.js v18.0.0; OpenAI)" +``` + +## Comparison with Other Extensions + +| Feature | OpenAI Extension | Claude Extension | LangChain Extension | +|---------|------------------|------------------|---------------------| +| Server Class | `MCPServerStreamableHttp` | `McpServerConfig` | `Connection` | +| Tool Discovery | Automatic via SDK | Manual via client | Via MCP adapters | +| Tool Naming | Native MCP names | `mcp__server__tool` | Native MCP names | +| Return Type | Updated `Agent` | `void` (modifies options) | New `ReactAgent` | diff --git a/packages/agents-a365-tooling/docs/design.md b/packages/agents-a365-tooling/docs/design.md new file mode 100644 index 00000000..6068daab --- /dev/null +++ b/packages/agents-a365-tooling/docs/design.md @@ -0,0 +1,269 @@ +# Tooling - Design Document + +This document describes the architecture and design of the `@microsoft/agents-a365-tooling` package. + +## Overview + +The tooling package provides MCP (Model Context Protocol) tool server configuration and discovery services. It enables agents to dynamically discover and connect to tool servers for extending agent capabilities. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Public API │ +│ McpToolServerConfigurationService | Utility | Contracts │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ McpToolServerConfigurationService │ +│ │ +│ ┌─────────────────────┐ ┌─────────────────────┐ │ +│ │ Development Mode │ │ Production Mode │ │ +│ │ │ │ │ │ +│ │ ToolingManifest.json│ │ Tooling Gateway │ │ +│ │ (local file) │ │ (HTTP endpoint) │ │ +│ └─────────────────────┘ └─────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ MCPServerConfig[] │ +│ { mcpServerName, url, headers? } │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Components + +### McpToolServerConfigurationService ([McpToolServerConfigurationService.ts](../src/McpToolServerConfigurationService.ts)) + +The main service for discovering and configuring MCP tool servers. + +```typescript +import { McpToolServerConfigurationService } from '@microsoft/agents-a365-tooling'; + +const service = new McpToolServerConfigurationService(); + +// Discover tool servers +const servers = await service.listToolServers( + agenticAppId, + bearerToken, + { orchestratorName: 'MyOrchestrator' } +); + +// Get tools from a specific server +const tools = await service.getMcpClientTools( + server.mcpServerName, + server +); +``` + +#### Environment Detection + +The service automatically selects the configuration source based on the `NODE_ENV` variable: + +| Environment | Source | Description | +|-------------|--------|-------------| +| `Development` | `ToolingManifest.json` | Local file-based configuration | +| Other (default) | Tooling Gateway | HTTP endpoint discovery | + +```typescript +private isDevScenario(): boolean { + const environment = process.env.NODE_ENV || ''; + return environment.toLowerCase() === 'development'; +} +``` + +#### Development Mode: Manifest-Based Configuration + +In development mode, the service reads from `ToolingManifest.json`: + +```json +{ + "mcpServers": [ + { + "mcpServerName": "mailMCPServer", + "mcpServerUniqueName": "mcp_MailTools" + }, + { + "mcpServerName": "sharePointMCPServer", + "mcpServerUniqueName": "mcp_SharePointTools" + } + ] +} +``` + +**Search locations for manifest file:** +1. Current working directory (`process.cwd()`) +2. Directory of the main script (`process.argv[1]`) + +#### Production Mode: Gateway-Based Configuration + +In production mode, the service calls the tooling gateway endpoint: + +``` +GET https://agent365.svc.cloud.microsoft/agents/{agenticAppId}/mcpServers +Authorization: Bearer {authToken} +User-Agent: Agent365SDK/x.x.x (...) +``` + +### Utility Class ([Utility.ts](../src/Utility.ts)) + +Helper functions for URL construction, token validation, and header composition: + +```typescript +import { Utility } from '@microsoft/agents-a365-tooling'; + +// Compose standard headers for MCP requests +const headers = Utility.GetToolRequestHeaders( + authToken, + turnContext, + { orchestratorName: 'MyOrchestrator' } +); + +// Validate JWT token (throws if invalid or expired) +Utility.ValidateAuthToken(authToken); + +// Build tooling gateway URL +const gatewayUrl = Utility.GetToolingGatewayForDigitalWorker(agenticAppId); +// => "https://agent365.svc.cloud.microsoft/agents/{agenticAppId}/mcpServers" + +// Build MCP server URL +const serverUrl = Utility.BuildMcpServerUrl('MyServer'); +// => "https://agent365.svc.cloud.microsoft/agents/servers/MyServer" +``` + +**Header Constants:** + +| Constant | Header Name | +|----------|-------------| +| `HEADER_CHANNEL_ID` | `x-ms-channel-id` | +| `HEADER_SUBCHANNEL_ID` | `x-ms-subchannel-id` | +| `HEADER_USER_AGENT` | `User-Agent` | + +## Data Models + +### MCPServerConfig ([contracts.ts](../src/contracts.ts)) + +```typescript +interface MCPServerConfig { + mcpServerName: string; // Display name of the tool server + url: string; // Full URL endpoint for the MCP server + headers?: Record; // Optional request headers +} +``` + +### McpClientTool + +```typescript +interface McpClientTool { + name: string; // Tool name + description?: string; // Tool description + inputSchema: InputSchema; // JSON schema for tool inputs +} +``` + +### InputSchema + +```typescript +interface InputSchema { + type: string; + properties?: Record; + required?: string[]; + additionalProperties?: boolean; +} +``` + +### ToolOptions + +```typescript +interface ToolOptions { + orchestratorName?: string; // Name for User-Agent header +} +``` + +## Design Patterns + +### Strategy Pattern + +The service uses the Strategy pattern to select between configuration sources: + +```typescript +async listToolServers(agenticAppId: string, authToken: string): Promise { + return this.isDevScenario() + ? this.getMCPServerConfigsFromManifest() // Strategy A + : this.getMCPServerConfigsFromToolingGateway(); // Strategy B +} +``` + +### MCP Client Integration + +The service uses the official MCP SDK for tool discovery: + +```typescript +async getMcpClientTools(serverName: string, config: MCPServerConfig): Promise { + const transport = new StreamableHTTPClientTransport( + new URL(config.url), + { requestInit: { headers: config.headers } } + ); + + const client = new Client({ name: serverName + ' Client', version: '1.0' }); + await client.connect(transport); + const tools = await client.listTools(); + await client.close(); + + return tools.tools; +} +``` + +## File Structure + +``` +src/ +├── index.ts # Public API exports +├── McpToolServerConfigurationService.ts # Main service +├── Utility.ts # Helper utilities +└── contracts.ts # Type definitions +``` + +## Environment Variables + +| Variable | Purpose | Default | +|----------|---------|---------| +| `NODE_ENV` | Controls dev vs prod mode | Production | +| `MCP_PLATFORM_ENDPOINT` | Base URL for MCP platform | `https://agent365.svc.cloud.microsoft` | + +## Error Handling + +The service provides detailed error messages: + +```typescript +// Token validation errors +Error('Authentication token is required') +Error('Invalid JWT token format') +Error('Failed to decode JWT token payload') +Error('Authentication token has expired') +Error('Authentication token does not contain expiration claim') + +// Gateway errors +Error(`Failed to read MCP servers from endpoint: ${code} ${message}`) +``` + +## Dependencies + +- `@microsoft/agents-a365-runtime` - Agent identity resolution, User-Agent generation +- `@microsoft/agents-hosting` - TurnContext type +- `@modelcontextprotocol/sdk` - MCP client and transport +- `axios` - HTTP client for gateway communication + +## Integration with Framework Extensions + +The tooling package is extended by framework-specific packages: + +| Extension Package | Purpose | +|-------------------|---------| +| `tooling-extensions-claude` | Claude SDK integration | +| `tooling-extensions-langchain` | LangChain integration | +| `tooling-extensions-openai` | OpenAI Agents SDK integration | + +These extensions adapt the `MCPServerConfig` objects to framework-specific tool definitions. From cd675886e44509e867f3cdc70a18028c2b96691c Mon Sep 17 00:00:00 2001 From: Johan Broberg Date: Tue, 20 Jan 2026 09:36:54 -0800 Subject: [PATCH 2/2] feat: Add CLAUDE.md for project guidance and documentation --- CLAUDE.md | 258 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..b6e6a3dd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,258 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +The Microsoft Agent 365 SDK extends the Microsoft 365 Agents SDK with enterprise-grade capabilities for building sophisticated AI agents. This Node.js/TypeScript monorepo provides comprehensive tooling across four core areas: + +- **Observability**: OpenTelemetry-based tracing and monitoring for agent applications +- **Notifications**: Agent notification services and lifecycle event handling +- **Runtime**: Core utilities for agent operations and Power Platform integration +- **Tooling**: MCP (Model Context Protocol) server configuration and tool discovery + +## Essential Commands + +### Package Management +This project uses **pnpm** (version 10.20.0+) as its package manager: + +```bash +# Install all dependencies +pnpm install + +# Build all packages +pnpm build + +# Build and watch for changes +pnpm build:watch + +# Clean build artifacts +pnpm clean +``` + +### Testing + +```bash +# Run all unit tests (excludes integration tests) +pnpm test + +# Run tests with coverage +pnpm test:coverage + +# Run tests in watch mode +pnpm test:watch + +# Run integration tests +pnpm test:integration + +# Run tests for a specific package +cd packages/agents-a365-observability && pnpm test +``` + +### Code Quality + +```bash +# Run linter +pnpm lint + +# Auto-fix linting issues +pnpm lint:fix +``` + +### Package Building and Distribution + +```bash +# Build and create .tgz files for distribution +pnpm build +cd packages && pnpm pack --workspaces + +# The .tgz files will be created in the root directory +``` + +## Architecture + +### Monorepo Structure + +This is a pnpm workspace monorepo with 9 packages in `packages/`: + +``` +packages/ +├── agents-a365-runtime/ # Core utilities (no external deps) +├── agents-a365-observability/ # OpenTelemetry tracing (depends on runtime) +├── agents-a365-observability-hosting/ # Hosting-specific observability +├── agents-a365-observability-extensions-openai/ # OpenAI instrumentation +├── agents-a365-notifications/ # Agent notification services +├── agents-a365-tooling/ # MCP server configuration +├── agents-a365-tooling-extensions-claude/ # Claude/Anthropic integration +├── agents-a365-tooling-extensions-langchain/ # LangChain integration +└── agents-a365-tooling-extensions-openai/ # OpenAI Agents SDK integration +``` + +**Dependency Flow:** +``` +runtime ──► observability ──► observability-hosting ──► observability-extensions-openai + │ + └─────────► tooling ──► tooling-extensions-* (claude, langchain, openai) + │ + └─────────► notifications +``` + +### Package Structure + +Each package follows this convention: + +``` +packages/agents-a365-/ +├── src/ +│ ├── index.ts # Public API exports (ONLY place for exports) +│ └── +├── dist/ # Build output +│ ├── cjs/ # CommonJS build +│ └── esm/ # ES Module build +├── package.json +├── tsconfig.json # Base config +├── tsconfig.cjs.json # CommonJS build config +├── tsconfig.esm.json # ESM build config +└── docs/design.md # Package-specific design doc +``` + +**Build System:** +- Each package builds to **both CJS and ESM** formats +- `npm run build` runs `build:cjs && build:esm` +- TypeScript uses separate tsconfig files for each module format + +### Key Design Patterns + +1. **Singleton Pattern**: `ObservabilityManager` ensures single tracer provider per application +2. **Disposable Pattern**: Scope classes (`InvokeAgentScope`, `InferenceScope`, `ExecuteToolScope`) implement `Disposable` for automatic span lifecycle with `using` keyword +3. **Builder Pattern**: `ObservabilityBuilder` and `BaggageBuilder` provide fluent APIs +4. **Strategy Pattern**: `McpToolServerConfigurationService` switches between manifest (dev) and gateway (prod) based on `NODE_ENV` +5. **Extension Methods**: `notifications` package extends `AgentApplication` via TypeScript declaration merging + +## Core Package Functionality + +### Runtime (`@microsoft/agents-a365-runtime`) +Foundation package with no SDK dependencies. Provides: +- **`Utility`**: Token decoding (`GetAppIdFromToken`), agent identity resolution (`ResolveAgentIdentity`), User-Agent generation (`GetUserAgentHeader`) +- **`AgenticAuthenticationService`**: Token exchange for MCP platform auth +- **`PowerPlatformApiDiscovery`**: Endpoint discovery for different clouds (prod, gov, dod, mooncake, etc.) +- **Environment utilities**: `getClusterCategory()`, `isDevelopmentEnvironment()`, `getObservabilityAuthenticationScope()`, `getMcpPlatformAuthenticationScope()` + +### Observability (`@microsoft/agents-a365-observability`) +OpenTelemetry-based distributed tracing: +- **`ObservabilityManager`**: Main entry point (singleton) +- **`ObservabilityBuilder`**: Fluent configuration API +- **Scope classes**: + - `InvokeAgentScope`: Trace agent invocations (root span) + - `InferenceScope`: Trace LLM/AI inference calls + - `ExecuteToolScope`: Trace tool execution +- **`BaggageBuilder`**: Context propagation across async boundaries (tenant ID, agent ID, correlation ID) +- **Data contracts**: `InvokeAgentDetails`, `AgentDetails`, `TenantDetails`, `InferenceDetails`, `ToolCallDetails` + +**Tracing Flow:** +``` +BaggageBuilder.build().run() + └─► InvokeAgentScope.start() [Root span - agent request] + ├─► InferenceScope.start() [Child span - LLM call] + ├─► ExecuteToolScope.start() [Child span - tool execution] + └─► Agent365Exporter.export() [Batched HTTP export] +``` + +### Tooling (`@microsoft/agents-a365-tooling`) +MCP tool server discovery and configuration: +- **`McpToolServerConfigurationService`**: Discover/configure MCP tool servers + - Dev mode (`NODE_ENV=Development`): Loads from `ToolingManifest.json` + - Prod mode (default): Discovers from Agent365 gateway endpoint +- **`Utility`**: Header composition, token validation, URL construction +- **Interfaces**: `MCPServerConfig`, `McpClientTool`, `ToolOptions` + +### Notifications (`@microsoft/agents-a365-notifications`) +Extends `AgentApplication` with notification handlers via declaration merging: +- `onAgenticEmailNotification()`, `onAgenticWordNotification()`, `onAgenticExcelNotification()`, `onAgenticPowerPointNotification()` +- `onLifecycleNotification()`: All lifecycle events +- `onAgenticUserCreatedNotification()`, `onAgenticUserWorkloadOnboardingNotification()`, `onAgenticUserDeletedNotification()` + +## Important Development Rules + +### Copyright Headers +**CRITICAL**: All `.js`, `.ts`, `.jsx`, `.tsx` files must include a Microsoft copyright header. Check `.github/copilot-instructions.md` for the exact format: + +```typescript +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +``` + +Exceptions: Test files, config files (`.json`, `.yaml`, `.md`), third-party code, build output. + +### Legacy Keyword Check +The keyword "Kairo" is legacy and should not appear in any code. Flag and remove if found. + +### Code Standards +- **Unused variables**: Prefix with `_` to avoid ESLint errors (configured in `eslint.config.mjs`) +- **Module format**: This is an ESM project (`"type": "module"` in root `package.json`) +- **Node.js version**: Requires Node.js >= 18.0.0 + +## Environment Variables + +| Variable | Purpose | Values | +|----------|---------|--------| +| `NODE_ENV` | Dev vs prod mode | `Development`, `production` (default) | +| `CLUSTER_CATEGORY` | Environment classification | `local`, `dev`, `test`, `preprod`, `prod`, `gov`, `high`, `dod`, `mooncake`, `ex`, `rx` | +| `A365_OBSERVABILITY_SCOPES_OVERRIDE` | Override observability auth scopes | Space-separated scope strings | +| `MCP_PLATFORM_AUTHENTICATION_SCOPE` | MCP platform auth scope | Scope string | +| `MCP_PLATFORM_ENDPOINT` | MCP platform base URL | URL string | + +## Testing + +### Test Configuration +- **Framework**: Jest with ts-jest preset +- **Config**: `tests/jest.config.cjs` +- **Coverage**: HTML, text, lcov, cobertura formats +- **Pattern**: Tests can be named `.test.ts` or `.spec.ts` + +### Module Resolution in Tests +The Jest config includes module name mappers to resolve workspace packages: +```javascript +'@microsoft/agents-a365-runtime$': '/packages/agents-a365-runtime/src' +'@microsoft/agents-a365-observability$': '/packages/agents-a365-observability/src' +// etc. +``` + +This allows tests to import from package names directly even though they're not published. + +## Version Management + +The project uses [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning): + +```bash +# Update version +npm run version + +# Update to local dev version +npm run version:local + +# Dry run (check what would change) +npm run version:check +``` + +## Additional Resources + +### Design Documents + +- **Overall Architecture**: [docs/design.md](docs/design.md) - Main SDK architecture and design +- **Runtime Package**: [packages/agents-a365-runtime/docs/design.md](packages/agents-a365-runtime/docs/design.md) +- **Observability Package**: [packages/agents-a365-observability/docs/design.md](packages/agents-a365-observability/docs/design.md) +- **Observability Hosting**: [packages/agents-a365-observability-hosting/docs/design.md](packages/agents-a365-observability-hosting/docs/design.md) +- **Observability Extensions (OpenAI)**: [packages/agents-a365-observability-extensions-openai/docs/design.md](packages/agents-a365-observability-extensions-openai/docs/design.md) +- **Notifications Package**: [packages/agents-a365-notifications/docs/design.md](packages/agents-a365-notifications/docs/design.md) +- **Tooling Package**: [packages/agents-a365-tooling/docs/design.md](packages/agents-a365-tooling/docs/design.md) +- **Tooling Extensions (Claude)**: [packages/agents-a365-tooling-extensions-claude/docs/design.md](packages/agents-a365-tooling-extensions-claude/docs/design.md) +- **Tooling Extensions (LangChain)**: [packages/agents-a365-tooling-extensions-langchain/docs/design.md](packages/agents-a365-tooling-extensions-langchain/docs/design.md) +- **Tooling Extensions (OpenAI)**: [packages/agents-a365-tooling-extensions-openai/docs/design.md](packages/agents-a365-tooling-extensions-openai/docs/design.md) + +### External Documentation + +- **Microsoft Agent 365 Docs**: https://learn.microsoft.com/microsoft-agent-365/developer/ +- **Microsoft 365 Agents SDK**: https://github.com/Microsoft/Agents-for-js +- **OpenTelemetry Node.js**: https://opentelemetry.io/docs/languages/js/ +- **Model Context Protocol**: https://modelcontextprotocol.io/