Skip to content

feat(teams): Microsoft Teams integration via Graph API#292

Closed
Shubhank-Jonnada wants to merge 1 commit into
masterfrom
feature/teams-integration
Closed

feat(teams): Microsoft Teams integration via Graph API#292
Shubhank-Jonnada wants to merge 1 commit into
masterfrom
feature/teams-integration

Conversation

@Shubhank-Jonnada
Copy link
Copy Markdown
Contributor

Summary

  • Adds a brand new Microsoft Teams integration built on the Microsoft Graph API (replaces the broken Bot Framework approach)
  • Full 2-way support — agents can now both read and send messages
  • 14 actions across teams, channels, messages, chats, and members
  • OAuth2 authentication via Microsoft platform provider

Actions

Action Description
list_teams List all teams the user belongs to
get_team Get details of a specific team
list_channels List channels in a team
get_channel Get a specific channel
create_channel Create a new channel
list_messages Read messages from a channel
get_message Get a specific channel message
send_channel_message Send a message to a channel
list_members List team members
add_member Add a user to a team
remove_member Remove a user from a team
list_chats List 1:1 and group chats
list_chat_messages Read messages from a chat
send_chat_message Send a message to a chat

Why this replaces the previous integration

The previous Teams integration (in Teams.zip) used the Microsoft Bot Framework SDK with hardcoded bot credentials. It could not read messages, had no real auth flow for customers, and send_message would fail without a pre-existing bot conversation context. This integration uses the standard Graph API with proper OAuth2, giving full read+write capability.

Test plan

  • Connect a Microsoft account with Teams access
  • Run python tests/test_teams.py <access_token> — verifies list_teams, list_chats, list_channels, list_messages
  • Manually test send_channel_message and send_chat_message
  • Verify add_member / remove_member with an admin-consented account

14 actions: list/get teams, list/get/create channels, list/get/send
channel messages, list/add/remove members, list chats, list/send
chat messages. Full 2-way read+write support using OAuth2 + Graph API.
@github-actions
Copy link
Copy Markdown

🔍 Integration Validation Results

Commit: e038cd5c3a1e2691fd3f5e9ab608d66b2e5032e9 · feat(teams): add Microsoft Teams integration via Graph API
Updated: 2026-04-29T01:49:20Z

Changed directories: teams

Check Result
Structure ⚠️ Passed with warnings
Code ⚠️ Passed with warnings
Tests ⚠️ Passed with warnings
README ✅ Passed
Version ✅ Passed
⚠️ Structure Check output
Validating 1 integration(s)...

============================================================
Integration: teams
============================================================

Warnings (1):
  ⚠️ Potentially unused scopes (please verify): ChannelMessage.Read.All, TeamMember.Read.All, TeamMember.ReadWrite.All

============================================================
SUMMARY
============================================================
Integrations validated: 1
Total errors: 0
Total warnings: 1

⚠️ Validation passed with warnings - please review
⚠️ Code Check output

[notice] A new release of pip is available: 26.0.1 -> 26.1
[notice] To update, run: pip install --upgrade pip
----------------------------------------
Checking: teams
----------------------------------------

📦 Installing dependencies...

🐍 Checking Python syntax...
   ✅ Syntax OK

📥 Checking imports...
   ✅ Imports OK

📄 Checking JSON files...
   ✅ JSON files OK

🔍 Linting with ruff...
   ✅ Lint OK

🎨 Checking formatting with ruff...
   ✅ Formatting OK

🔒 Scanning for security issues with bandit...
   ✅ Security OK

🛡️ Checking dependencies for vulnerabilities with pip-audit...
   ✅ Dependencies OK

🔗 Checking config-code sync...
   ⚠️  Action 'get_team': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'list_channels': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'get_channel': parameter 'channel_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'get_channel': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'create_channel': parameter 'display_name' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'create_channel': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'list_messages': parameter 'channel_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'list_messages': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'get_message': parameter 'channel_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'get_message': parameter 'message_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'get_message': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'send_channel_message': parameter 'channel_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'send_channel_message': parameter 'message' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'send_channel_message': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'list_members': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'add_member': parameter 'user_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'add_member': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'remove_member': parameter 'membership_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'remove_member': parameter 'team_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'list_chat_messages': parameter 'chat_id' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'send_chat_message': parameter 'message' is required in schema but accessed with inputs.get() (safe for missing)
   ⚠️  Action 'send_chat_message': parameter 'chat_id' is required in schema but accessed with inputs.get() (safe for missing)
   ✅ Config-code sync OK

🔄 Checking fetch patterns...
   ✅ Fetch patterns OK

========================================
✅ CODE CHECK PASSED
========================================
⚠️ Tests output
⚠️  No unit tests (test_*_unit.py) found in: teams
⚠️  No unit tests to run
✅ README Check output
========================================
✅ README CHECK PASSED
========================================
✅ Version Check output
✅ teams: New integration with version 1.0.0

========================================
✅ VERSION CHECK PASSED
========================================

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e038cd5c3a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread teams/teams.py
Comment on lines +150 to +154
response = await context.fetch(
f"{BASE_URL}/teams/{team_id}/channels",
method="POST",
headers=_headers(context),
body=body,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use JSON/data args for POST payloads in context.fetch

These POST actions pass request content via body=..., but integration code in this repo consistently uses json=/data= with ExecutionContext.fetch; using body here can cause the payload to be dropped (or raise an unexpected-argument error), so write operations fail or send malformed requests. This impacts all new write paths that use this pattern, including create_channel, send_channel_message, add_member, and send_chat_message.

Useful? React with 👍 / 👎.

Comment thread teams/teams.py
Comment on lines +273 to +276
"@odata.type": "#microsoft.graph.aadUserConversationMember",
"roles": roles,
"user@odata.bind": f"https://graph.microsoft.com/v1.0/users/{user_id}",
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Build user@odata.bind using OData key format

The member-add payload binds the user as .../users/{user_id} even though this endpoint expects an OData entity reference form (users('{id-or-upn}')). As written, valid user_id inputs (especially UPN/email-style values) can be rejected as an invalid bind URL, causing add_member to fail despite otherwise correct permissions.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

@TheRealAgentK TheRealAgentK left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tackling the broken Bot-Framework Teams integration with a Graph API rewrite — the action surface looks reasonable. There are several blockers before this can land though, with the test gap being the most pressing.

Unit tests — missing

There are no pytest-collectable unit tests for any of the 14 new actions. teams/tests/test_teams.py is a manual asyncio script intended to be run as python test_teams.py <token> against the live Microsoft Graph API — it's not a pytest module, has no markers, no mocked ExecutionContext, and is not picked up by our python_files = ["test_*_unit.py"] filter. Locally pytest teams/ -v collects 0 tests.

For a brand-new 1255-line integration with 14 actions and zero unit coverage, this is below the bar set by every other recent integration in the repo (e.g. salesforce 56, hubspot 264, harvest 42).

Action required: add teams/tests/test_teams_unit.py following the writing-unit-tests skill. Each action should have at minimum: happy path, request verification (URL/method/headers/body), and at least one error path. Target 4–5 tests per action like the rest of the SDK-v2 PRs in this batch (#284 float = 71, #287 freshdesk = 88, #290 xero = 100).

Integration tests — missing

No teams/tests/test_teams_integration.py. The current test_teams.py script is the closest thing but it doesn't follow the live_context fixture pattern, doesn't use the integration marker, and isn't excluded from CI through the standard double-mechanism (file glob + marker filter).

Action required: convert test_teams.py into a proper test_teams_integration.py per the writing-integration-tests skill, with pytestmark = pytest.mark.integration, env-var token (TEAMS_ACCESS_TOKEN), and @pytest.mark.destructive on add_member / remove_member / send_*_message.

Other blockers I want addressed in the same PR

These aren't about tests but they're shipping defects that would force an immediate follow-up PR:

  1. SDK 1.1.1 instead of 2.0.0. requirements.txt pins autohive-integrations-sdk~=1.1.1. Every other new integration in the last 2 weeks ships on 2.0.0, and we're actively migrating off the 1.x error pattern across this batch (#274, #287, #288, #289, #290) because it crashes Lambda. Please bump to ~=2.0.0 and follow the upgrading-sdk-v2 skill.
  2. All error paths use ActionResult(data={"result": False, "error": str(e)}). This is the exact pattern that just caused 232 production failures in #274 (github), several in #289 (sheets) and #290 (xero). On SDK 2.0.0 it fails output-schema validation and returns "Unhandled" to the caller. Switch to ActionError(message=...) and remove result/error from the output_schema blocks.
  3. No ConnectedAccountHandler. Same defect as #273 (stripe) — the platform calls the connected-account endpoint after OAuth and will raise ValidationError: No connected account handler registered, surfacing as a connect-time crash in Raygun. Add a TeamsConnectedAccountHandler that calls /me on Graph and returns ConnectedAccountInfo(username=display_name, user_id=id).
  4. README claims this "replaces the previous Teams integration" but I can't find a teams/ folder in master or in any prior closed PR. Please clarify what's being replaced — if there's a private-repo predecessor, link it.

Process

  • Branch name feature/teams-integration doesn't follow the <type>/<issue-number>/<short-description> convention from AGENTS.md.
  • No linked GitHub issue.

Local CI status

validate_integration.py ✅, check_code.py ✅, ruff check ✅, ruff format ✅, pytest teams/ ⚠️ (0 tests collected). The validators pass only because the v1-style schemas declare result/error fields — once you bump to v2 and remove them, the existing error returns will start failing schema validation, which is exactly the prod issue we want to surface in tests.

@Shubhank-Jonnada
Copy link
Copy Markdown
Contributor Author

Closing this for now, reworking the Teams bot to handle admin approval flow.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants