Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a48a27b
fix: ensure copilot agent ends with the action plan (#353)
heyitsaamir Apr 3, 2026
3ece4ba
feat/bug: add custom feedback form & fix pydantic valid error on Mess…
lilyydu Apr 3, 2026
493ce1f
fix: update invalid inbound/outbound activities (#340)
rajan-chari Apr 6, 2026
f0c2721
Move builders from ActivityInput to MessageActivityInput (#363)
lilyydu Apr 6, 2026
99bb1e7
Update generated Adaptive Cards core from latest codegen (#352)
corinagum Apr 7, 2026
2204b31
Bump vite from 6.4.1 to 6.4.2 in /examples/tab/Web (#364)
dependabot[bot] Apr 7, 2026
7ec0009
Remove dead HttpPlugin and jwt_middleware code (#367)
heyitsaamir Apr 7, 2026
250d144
fixes: cookiecutter README, runtime dependencies, graph README (#369)
lilyydu Apr 8, 2026
73c4e3f
Implement stream cancelation (#337)
jerbob92 Apr 8, 2026
8212432
feat: add App.get_app_graph() and App._get_graph_token() (#355)
Copilot Apr 8, 2026
49ada14
address dependabot alerts (#368)
lilyydu Apr 8, 2026
80b98eb
feat: add hatch-nbgv plugin for graceful nbgv fallback (#365)
heyitsaamir Apr 9, 2026
c5bd359
Fix uv lock (#372)
heyitsaamir Apr 9, 2026
662d715
Bump cryptography from 46.0.6 to 46.0.7 (#371)
dependabot[bot] Apr 9, 2026
3ef0871
fix: update graph example README to match actual sample code (#375)
heyitsaamir Apr 9, 2026
a723ab9
Remove workflow_dispatch trigger from issue-analysis workflow (#374)
Copilot Apr 9, 2026
4e800d2
Bump axios from 1.13.5 to 1.15.0 in /examples/tab/Web (#376)
dependabot[bot] Apr 9, 2026
2505059
Merge branch 'alpha/v2.0.0' into main
lilyydu Apr 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .azdo/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ extends:
ob_git_fetchTags: true
ob_sdl_binskim_break: true
ob_git_fetchDepth: -1
NBGV_REQUIRED: '1'

steps:
- checkout: self
Expand Down
10 changes: 5 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ Microsoft Teams SDK for Python is a comprehensive SDK for building Microsoft Tea
**ALWAYS manually validate changes by running at least one complete test application scenario:**

### Basic Teams App Validation
1. Navigate to test app: `cd tests/echo`
1. Navigate to test app: `cd examples/echo`
2. Start the app: `python src/main.py`
3. **Expected output**: App starts on ports 3978 and 3979 with logs:
```
[INFO] @teams/app.HttpPlugin listening on port 3978 🚀
[INFO] Teams app started successfully
[INFO] @teams/app.DevToolsPlugin listening on port 3979 🚀
```
4. **Test endpoints**:
Expand Down Expand Up @@ -106,7 +106,7 @@ pyright # Type checking validation
- **microsoft-teams-openai**: OpenAI integration
- **microsoft-teams-mcpplugin**: MCP protocol integration

### Test Applications (`/tests`)
### Test Applications (`/examples`)
Available test apps for development and validation:
- **echo**: Basic message echo bot (recommended for quick validation)
- **ai-test**: AI functionality testing
Expand All @@ -118,14 +118,14 @@ Available test apps for development and validation:

### Creating New Components
- **New package**: `cookiecutter templates/package -o packages`
- **New test app**: `cookiecutter templates/test -o tests`
- **New test app**: `cookiecutter templates/examples -o examples`

## Common Development Tasks

### Testing Changes
1. **Run commands with UV** (recommended): Use `uv run pytest packages/[package-name]` or **activate virtual environment**: `source .venv/bin/activate`
2. **Run affected tests**: `pytest packages/[package-name]` for specific package (or `uv run pytest packages/[package-name]`)
3. **Validate with test app**: Use `tests/echo` for basic functionality validation (starts a blocking server process)
3. **Validate with test app**: Use `examples/echo` for basic functionality validation (starts a blocking server process)
4. **Check DevTools web app**: Access http://localhost:3979/devtools when app is running

### Debugging and Development
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
matrix:
python-version: ["3.12", "3.13", "3.14"]

env:
NBGV_REQUIRED: "1"

steps:
- name: Harden Runner
uses: step-security/harden-runner@0080882f6c36860b6ba35c610c98ce87d4e2f26f # v2.10.2
Expand Down
63 changes: 32 additions & 31 deletions .github/workflows/issue-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@ name: "Issue Analysis → Teams"
on:
issues:
types: [opened]
workflow_dispatch:
inputs:
issue_number:
description: "Issue number to analyze"
required: true
type: number

# Declare default permissions as read only.
permissions: read-all
Expand All @@ -19,7 +13,7 @@ jobs:
analyze-and-notify:
name: Analyze Issue & Notify Teams
runs-on: ubuntu-latest
if: github.event_name == 'workflow_dispatch' || github.event.issue.performed_via_github_app == null
if: github.event.issue.performed_via_github_app == null

permissions:
issues: read
Expand Down Expand Up @@ -53,8 +47,6 @@ jobs:
id: issue
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
EVENT_NAME: ${{ github.event_name }}
INPUT_NUMBER: ${{ inputs.issue_number }}
# Pass event data through env vars to avoid shell injection
EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
EVENT_ISSUE_TITLE: ${{ github.event.issue.title }}
Expand All @@ -76,22 +68,12 @@ jobs:
} >> "$GITHUB_OUTPUT"
}

if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
ISSUE=$(gh api repos/microsoft/teams.py/issues/"$INPUT_NUMBER")
write_output "number" "$(echo "$ISSUE" | jq -r '.number')"
write_output "title" "$(echo "$ISSUE" | jq -r '.title')"
write_output "author" "$(echo "$ISSUE" | jq -r '.user.login')"
write_output "html_url" "$(echo "$ISSUE" | jq -r '.html_url')"
write_output "labels" "$(echo "$ISSUE" | jq -r '[.labels[].name] | join(",")')"
write_output "body" "$(echo "$ISSUE" | jq -r '.body // ""')"
else
write_output "number" "$EVENT_ISSUE_NUMBER"
write_output "title" "$EVENT_ISSUE_TITLE"
write_output "author" "$EVENT_ISSUE_AUTHOR"
write_output "html_url" "$EVENT_ISSUE_URL"
write_output "labels" "$(echo "$EVENT_ISSUE_LABELS" | jq -r '[.[].name] | join(",")')"
write_output "body" "$EVENT_ISSUE_BODY"
fi
write_output "number" "$EVENT_ISSUE_NUMBER"
write_output "title" "$EVENT_ISSUE_TITLE"
write_output "author" "$EVENT_ISSUE_AUTHOR"
write_output "html_url" "$EVENT_ISSUE_URL"
write_output "labels" "$(echo "$EVENT_ISSUE_LABELS" | jq -r '[.[].name] | join(",")')"
write_output "body" "$EVENT_ISSUE_BODY"

- name: Analyze issue with Copilot CLI
env:
Expand All @@ -105,13 +87,32 @@ jobs:

Issue URL: ${ISSUE_URL}

Read the codebase to understand the architecture, then provide a concrete action plan in markdown:
1. **Root cause** — What's likely going wrong or what's missing
2. **Files to investigate** — Specific file paths to look at (use actual paths you found)
3. **Proposed approach** — Step-by-step what a developer should do to resolve this
4. **Estimated complexity** — Small (< 1 day), Medium (1-3 days), or Large (3+ days)
Follow this workflow:
1. Parse the issue for concrete signals — keywords, error messages, stack traces, config names, package references.
2. Use those signals to search the codebase (grep, glob, read files). Trace execution paths to identify failure points or missing functionality.
3. Do not claim confirmation without code-based evidence.

Be specific. Reference actual files and code you found in the repo." \
Your FINAL message must be the complete analysis in this markdown structure:

## Issue Summary
One-paragraph summary of what the issue is reporting or requesting.

## Evidence
Concrete file pointers with line references and what you found. Quote relevant code snippets.

## Root-Cause Hypothesis
State your hypothesis with a confidence level (high/medium/low). Explain what evidence supports it and what is uncertain.

## Proposed Fix
Step-by-step what a developer should do to resolve this. Reference specific files and functions.

## Estimated Complexity
Small (< 1 day), Medium (1-3 days), or Large (3+ days) — with justification.

## Open Questions
Anything you could not confirm or that needs clarification from the issue author.

Do NOT end with a summary or meta-commentary — end with the structured analysis itself." \
--allow-tool='shell(git:*)' \
--allow-tool='read' \
--allow-tool='glob' \
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ All packages live in `packages/`, each with `src/microsoft_teams/<package>/` lay

```bash
cookiecutter templates/package -o packages # New package
cookiecutter templates/test -o tests # New test package
cookiecutter templates/examples -o examples # New test app
```

## Dependencies and Build
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ A comprehensive SDK for building Microsoft Teams applications, bots, and AI agen

- UV version is >= 0.8.11. Install and upgrade from [docs.astral.sh/uv](https://docs.astral.sh/uv/getting-started/installation/).
- Python version is >= 3.12. Install or upgrade from [python.org/downloads](https://www.python.org/downloads/).
- (Optional) .NET SDK + `nbgv` CLI for real version numbers. Without it, packages build as `0.0.0` which is fine for local development. See [RELEASE.md](RELEASE.md) for details.

### Installation

Expand Down Expand Up @@ -83,10 +84,10 @@ Follow the prompts to name the package and the directory. It should create the p

### Create A New Test Package

Similarly, to create a new test package, run:
Similarly, to create a new test app, run:

```bash
cookiecutter templates/test -o tests
cookiecutter templates/examples -o examples
```

## Test Apps
Expand Down
5 changes: 5 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ This project uses [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.Gi

## Prerequisites

The .NET SDK and `nbgv` CLI are **required for publishing** but **optional for local development**. Without them, packages fall back to version `0.0.0` so you can still build and test locally.

CI pipelines set `NBGV_REQUIRED=1` to ensure builds fail if `nbgv` is unavailable.

```bash
# Optional for local dev, required for releases
dotnet tool install -g nbgv
```

Expand Down
3 changes: 3 additions & 0 deletions examples/ai-test/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"""

import asyncio
import logging
import re
from os import getenv

Expand All @@ -30,6 +31,8 @@

load_dotenv(find_dotenv(usecwd=True))

logging.basicConfig(level=getenv("LOG_LEVEL", "WARNING").upper())


def get_required_env(key: str) -> str:
value = getenv(key)
Expand Down
117 changes: 22 additions & 95 deletions examples/graph/README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,42 @@
> [!CAUTION]
> This project is in public preview. Well do our best to maintain compatibility, but there may be breaking changes in upcoming releases.
> This project is in public preview. We'll do our best to maintain compatibility, but there may be breaking changes in upcoming releases.

# Teams Graph Integration Demo

This demo application showcases how to use Microsoft Graph APIs within a Teams bot.
This demo application showcases how to use Microsoft Graph APIs within a Teams bot, including both delegated (user) and app-only access patterns.

## Features

- **User Authentication**: Teams OAuth integration with automatic token management
- **Token Implementation**: Uses callable-based tokens for exact expiration handling
- **Profile Information**: Retrieve and display user profile data
- **Profile Information**: Retrieve and display user profile data via delegated access
- **Email Access**: List recent emails with Mail.Read scope
- **Automatic Token Refresh**: Intelligent token lifecycle management
- **App-Level Graph Access**: Query organization data using app-only permissions (no user sign-in needed) via `app.get_app_graph()` or `ctx.app_graph`

## Commands

- `signin` - Authenticate with Microsoft Graph
- `profile` - Display user profile information (requires User.Read)
- `emails` - Show recent emails (requires Mail.Read permission)
- `app-users` - List organization users via `app.get_app_graph()` (app-only, no sign-in needed)
- `app-users ctx` - List organization users via `ctx.app_graph` (app-only, no sign-in needed)
- `signout` - Sign out of Microsoft Graph
- `help` - Show available commands and implementation details

## Setup

1. Configure OAuth connection in Azure Bot registration
2. Set connection name to "graph" (or update `default_connection_name` in app options)
2. Set connection name to "graph" (or update `CONNECTION_NAME` env var)
3. Configure appropriate Microsoft Graph permissions:
- `User.Read` (for profile access)
- `Mail.Read` (for email access)
4. Create a `.env` file with required environment variables (copy from `sample.env`):
- `User.Read.All` application permission (for app-users commands)
4. Create a `.env` file in `examples/graph/src/` with required environment variables (copy from `sample.env`):
```
CLIENT_ID=<your-azure-bot-app-id>
CLIENT_SECRET=<your-azure-bot-app-secret>
TENANT_ID=<your-tenant-id>
CONNECTION_NAME=graph
# PORT=3979 # Optional: specify custom port (defaults to 3979)
# PORT=3978 # Optional: specify custom port (defaults to 3978)
```

## Configuring a Regional Bot
Expand All @@ -53,105 +58,27 @@ app = App(

## Running

### Option 1: Using the PowerShell Script (Recommended)

From the `examples/graph/` directory:

```powershell
.\run_demo.ps1
```

### Option 2: Manual PYTHONPATH Setup

From the `examples/graph/` directory:

```powershell
# PowerShell
$env:PYTHONPATH="..\..\packages\graph\src;..\..\packages\api\src;..\..\packages\apps\src;..\..\packages\common\src"
python src\main.py
```

```bash
# Bash (Linux/macOS)
PYTHONPATH="../../packages/graph/src:../../packages/api/src:../../packages/apps/src:../../packages/common/src" python src/main.py
```

### Option 3: Install Packages in Development Mode

From the repository root:
From the `examples/graph/` directory (so `.env` is discovered automatically):

```bash
# Install the graph package in development mode
pip install -e packages/graph
pip install -e packages/api
pip install -e packages/app
pip install -e packages/common

# Then run the demo
python examples/graph/src/main.py
cd examples/graph
uv run src/main.py
```

## Architecture

The demo uses the `microsoft_teams.graph` package which provides:

- **Token Integration**: Uses callable tokens for exact expiration handling
- **Automatic Token Resolution**: Seamless integration with Teams OAuth tokens
- **Graph Client Factory**: `get_graph_client()` function for creating authenticated clients

## Example Usage

```python
from microsoft_teams.graph import get_graph_client

# Get user's Graph client using their token
# Delegated access — create Graph client using the user's token
graph = get_graph_client(ctx.user_token)

# Access user profile
me = await graph.me.get()

# Access Teams membership
teams = await graph.me.joined_teams.get()

# Access emails
messages = await graph.me.messages.get()
```

## Token Lifecycle

1. User initiates `signin` command
2. Teams OAuth flow completes and stores user token
3. Graph client created with callable token that:
- Fetches fresh token on each call
- Includes exact expiration metadata
- Handles token refresh automatically
4. Graph API calls use current valid token
5. User can `signout` to clear tokens
# App-only access — no user sign-in needed
graph = app.get_app_graph()
users = await graph.users.get()

This approach provides better reliability and eliminates common token expiration issues.

- **`get_graph_client()`** - Main factory function accepting Token values (strings, callables, etc.)
- **`DirectTokenCredential`** - Azure TokenCredential implementation using the unified Token type

### Key Implementation Details

```python
from microsoft_teams.api.clients.user.params import GetUserTokenParams
from microsoft_teams.graph import get_graph_client

# Get token directly from Teams API
token_params = GetUserTokenParams(
channel_id=ctx.activity.channel_id,
user_id=ctx.activity.from_.id,
connection_name=ctx.connection_name,
)

# Get user token and create Graph client directly
token_response = await ctx.api.users.token.get(token_params)

# Create Graph client with string token (simplest approach)
graph = get_graph_client(token_response.token, connection_name=ctx.connection_name)

# Make Graph API calls
me = await graph.me.get()
# Or via context
users = await ctx.app_graph.users.get()
```
Loading
Loading