Skip to content

feat(msteams): Implement read and search message actions for Teams channel #40865

@BradGroux

Description

@BradGroux

Summary

The message tool's read and search actions are defined in the action name enum (CHANNEL_MESSAGE_ACTION_NAMES) and fully implemented for Discord (readMessages, searchMessages in discord-actions-messaging.ts), but the Microsoft Teams channel plugin only declares ["poll"] as its supported action.

This means agents connected via Teams cannot read channel message history or search messages — they can only send and react. The agent is blind to any channel activity not directly routed as an inbound message.

Current State

What exists:

  • CHANNEL_MESSAGE_ACTION_NAMES includes "read" and "search" (source)
  • Discord fully implements both:
    • readMessages — reads channel history with limit, before, after, around params
    • searchMessages — searches by content, author, channel with filtering
    • (source)

What's missing:

  • MSTeams listActions returns only ["poll"] (source)
  • No readMessages or searchMessages handler in the Teams handleAction
  • The Graph API endpoints needed (/teams/{teamId}/channels/{channelId}/messages and /chats/{chatId}/messages) are well-documented and available

Proposed Implementation

1. Add read and search to Teams listActions:

```typescript
listActions: ({ cfg }) => {
// ...existing enabled check...
return ["poll", "read", "search"] satisfies ChannelMessageActionName[];
},
```

2. Implement readMessages in handleAction:

Using Microsoft Graph API:

  • Channel messages: GET /teams/{teamId}/channels/{channelId}/messages?$top={limit}
  • Chat messages: GET /chats/{chatId}/messages?$top={limit}
  • Support limit, before/after pagination (Graph uses $skipToken or @odata.nextLink)

3. Implement searchMessages in handleAction:

Using Microsoft Graph Search API:

  • POST /search/query with entityTypes: ["chatMessage"]
  • Filter by channelIdentity, from, date range

4. Normalize response format:

Match the Discord implementation's response shape for consistency:
```typescript
return jsonResult({
ok: true,
messages: messages.map((msg) => ({
id: msg.id,
author: msg.from?.user?.displayName,
authorId: msg.from?.user?.id,
timestamp: msg.createdDateTime,
text: msg.body?.content,
attachments: msg.attachments?.map(a => ({ name: a.name, contentType: a.contentType })),
replyTo: msg.replyToId,
})),
});
```

Graph API Permissions Required

The bot registration likely needs:

  • ChannelMessage.Read.All (application) — for channel messages
  • Chat.Read (delegated) or Chat.Read.All (application) — for chat messages
  • ChatMessage.Read — for search

These may already be consented depending on the bot's existing Graph permissions. Worth documenting which are needed in the setup guide.

Why This Matters

  1. Parity with Discord — Discord agents can read and search; Teams agents cannot. This is the most-requested enterprise channel.
  2. Agent workspace integration — Teams is often the primary workspace for enterprise users. An agent that can only write but not read operates half-blind.
  3. Reduces webhook dependency — On-demand read eliminates the need to wire every channel with real-time event forwarding for the agent to have awareness.
  4. Heartbeat/monitoring workflows — Agents doing proactive monitoring sweeps need to check channels for activity without requiring every message to be individually routed.

Environment

  • OpenClaw version: 2026.3.7
  • Channel: msteams
  • Existing actions available: send, react, poll
  • Missing: read, search

Related

  • Discord implementation: src/agents/tools/discord-actions-messaging.ts (lines 220-260, 473-520)
  • Action names enum: src/channels/plugins/message-action-names.ts
  • Teams channel plugin: extensions/msteams/src/channel.ts

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions