Skip to content

feat: Microsoft Teams channel integration adapter (Bot Framework) #582

Description

@ginccc

Problem / Motivation

Microsoft Teams (320M+ monthly active users) is the dominant enterprise communication platform. EDDI's multi-agent orchestration capabilities - especially group discussions with debate, peer review, and round table styles - are a natural fit for enterprise Teams channels.

WARNING: Teams has the most complex authentication model of all target platforms. The Bot Framework uses Azure AD JWT tokens for incoming message verification, requiring OpenID metadata fetching, key rotation handling, and multi-claim validation. Plan for this complexity.

Key Teams constraints:

  • No threading in Group Chats - Threads (reply chains) are only supported in Channels. Group Chats are flat message streams. Multi-agent debates MUST deploy in Channels.
  • @mention-gated by default - Bots only receive messages when explicitly @mentioned unless Resource-Specific Consent (RSC) is configured with ChannelMessage.Read.Group permission.
  • Adaptive Cards (v1.5) - Teams uses Adaptive Cards for rich formatting. Schema version 1.5 is the maximum supported by Teams.
  • Azure AD registration required - Each bot needs an Azure AD app registration with appId + appPassword.

Architecture Principle

No platform SDKs. All adapters use java.net.http.HttpClient + Jackson for raw HTTP/JSON - no Bot Framework SDK. The Bot Framework REST API is well-documented enough for direct integration, and raw HTTP keeps the dependency tree lean, the single-JAR deployment simple, and gives full control over retry logic, error handling, and message formatting. See the existing SlackWebApiClient (245 lines, zero external dependencies) as the reference pattern.

Proposed Solution

Create ai.labs.eddi.integrations.teams package:

Class Responsibility Slack Equivalent
RestTeamsWebhook JAX-RS @POST /integrations/teams/messages. Receives Bot Framework Activity payloads, validates JWT, dispatches async. RestSlackWebhook
TeamsTokenVerifier Validates incoming Bot Framework JWT tokens against Azure AD OpenID metadata. Fetches signing keys from https://login.botframework.com/v1/.well-known/openidconfiguration. Caches keys, handles rotation. SlackSignatureVerifier
TeamsEventHandler Routes Teams activities to EDDI via ChannelTargetRouter. Handles message, conversationUpdate activity types. Strips <at> mention tags. Maps Teams conversations -> EDDI conversations. SlackEventHandler
TeamsApiClient Posts messages/Adaptive Cards back via Bot Framework REST API (POST /v3/conversations/{id}/activities). Acquires access tokens from login.microsoftonline.com. Converts Markdown -> Adaptive Card JSON (v1.5) for rich responses, plain text for simple ones. SlackWebApiClient
TeamsGroupDiscussionListener Implements GroupDiscussionEventListener. Posts agent contributions as reply chain activities in the channel. Uses replyToId for threading. SlackGroupDiscussionListener
TeamsDeliveryException Retryable delivery failure. SlackDeliveryException

Configuration Model

{
  "name": "Engineering Teams Channel",
  "channelType": "teams",
  "platformConfig": {
    "channelId": "19:abc123@thread.tacv2",
    "appId": "${vault:teams-app-id}",
    "appPassword": "${vault:teams-app-password}",
    "tenantId": "your-tenant-id",
    "serviceUrl": "https://smba.trafficmanager.net/teams/"
  },
  "defaultTargetName": "assistant",
  "targets": [...]
}

Key Design Decisions

  1. Channel-only deployment - Document clearly that Teams Group Chats do NOT support threading and are incompatible with multi-agent debates. Channels are mandatory.
  2. JWT verification complexity - This is the hardest verification of all platforms. Recommended: full validation - verify JWT signature against Bot Framework's published keys (OpenID discovery), validate audience (appId), issuer, expiration, serviceUrl claim. Cache OpenID metadata with refresh on 401.
  3. Adaptive Card rendering - For rich responses (tables, structured data, code blocks), generate Adaptive Card JSON (v1.5). For simple text responses, use plain text with basic HTML formatting (<b>, <i>, <code>). Teams does NOT support standard Markdown in bot messages.
  4. RSC permissions - Document required manifest entry for ambient listening without @mentions. Without RSC, the bot only receives @mentioned messages (which may be sufficient for trigger-keyword use cases).
  5. Outgoing auth - Bot must acquire access tokens from https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token using client credentials flow. Cache tokens until expiry.
  6. Message size - ~28KB for text, separate limits for Adaptive Card payloads. Implement chunking for long agent responses.
  7. Loop prevention - Filter activities where activity.from.id matches the bot's appId.

Deliverables

  • 6 Java classes in ai.labs.eddi.integrations.teams
  • Unit tests (JWT verification testable with crafted test tokens)
  • Add "teams" to REGISTERED_CHANNEL_TYPES
  • Documentation page: docs/teams-integration.md
  • Azure AD app registration guide in docs

Alternatives Considered

  • Microsoft Bot Framework SDK for Java - Adds heavy dependency; EDDI's approach is raw HTTP (no SDK for Slack either), consistent with the no-SDK principle. The Bot Framework REST API is well-documented enough for direct integration.
  • Azure Bot Service as proxy - Adds infrastructure complexity and cost. Direct webhook is simpler and keeps EDDI self-contained.

Additional Context

Acceptance Criteria

  • All classes listed in Deliverables are implemented and compile without warnings
  • Unit tests for every class (JWT verification testable with crafted test tokens)
  • "teams" added to REGISTERED_CHANNEL_TYPES in RestChannelIntegrationStore
  • ./mvnw test passes with zero failures
  • Code coverage meets project targets: >90% instruction coverage, >80% branch coverage (measured by JaCoCo)
  • Documentation page docs/teams-integration.md exists (following docs/slack-integration.md structure)
  • Azure AD app registration guide included in docs
  • PR description includes screenshots or curl transcripts demonstrating:
    • Bot Framework Activity received and routed to an EDDI agent
    • EDDI agent response delivered back to the Teams channel
    • JWT token verification rejecting an invalid/expired token
  • PR submitted for review - do NOT merge without approval

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementhelp wantedintegrationChannel integration adapters (Slack, Teams, Discord, Telegram, WhatsApp)javaPull requests that update java codesize: LLarge: full day+, significant scope, design discussion likely

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions