🔍 Search commands
Message extension search commands allow users to search external systems and insert the results of that search into a message in the form of a card.
Search command invocation locations
There are two different areas search commands can be invoked from:
- Compose Area
- Compose Box
Compose Area and Box

Setting up your Teams app manifest
To use search commands you have to define them in the Teams app manifest. Here is an example:
"composeExtensions": [
{
"botId": "${{BOT_ID}}",
"commands": [
{
"id": "searchQuery",
"context": [
"compose",
"commandBox"
],
"description": "Test command to run query",
"title": "Search query",
"type": "query",
"parameters": [
{
"name": "searchQuery",
"title": "Search Query",
"description": "Your search query",
"inputType": "text"
}
]
}
]
}
]
Here we are defining the searchQuery search (or query) command.
Handle submission
Handle the search query submission when the searchQuery search command is invoked.
from microsoft.teams.api import AdaptiveCardAttachment, MessageExtensionQueryInvokeActivity, ThumbnailCardAttachment, card_attachment, InvokeResponse, AttachmentLayout, MessagingExtensionAttachment, MessagingExtensionInvokeResponse, MessagingExtensionResult, MessagingExtensionResultType
# ...
@app.on_message_ext_query
async def handle_message_ext_query(ctx: ActivityContext[MessageExtensionQueryInvokeActivity]):
command_id = ctx.activity.value.command_id
search_query = ""
if ctx.activity.value.parameters and len(ctx.activity.value.parameters) > 0:
search_query = ctx.activity.value.parameters[0].value or ""
if command_id == "searchQuery":
cards = await create_dummy_cards(search_query)
attachments: list[MessagingExtensionAttachment] = []
for card_data in cards:
main_attachment = card_attachment(AdaptiveCardAttachment(content=card_data["card"]))
preview_attachment = card_attachment(ThumbnailCardAttachment(content=card_data["thumbnail"]))
attachment = MessagingExtensionAttachment(
content_type=main_attachment.content_type, content=main_attachment.content, preview=preview_attachment
)
attachments.append(attachment)
result = MessagingExtensionResult(
type=MessagingExtensionResultType.RESULT, attachment_layout=AttachmentLayout.LIST, attachments=attachments
)
return MessagingExtensionInvokeResponse(compose_extension=result)
return InvokeResponse[MessagingExtensionInvokeResponse](status=400)
create_dummy_cards() method
from typing import Any, Dict, List
from microsoft.teams.cards import AdaptiveCard
# ...
async def create_dummy_cards(search_query: str) -> List[Dict[str, Any]]:
"""Create dummy cards for search results."""
dummy_items = [
{
"title": "Item 1",
"description": f"This is the first item and this is your search query: {search_query}",
},
{"title": "Item 2", "description": "This is the second item"},
{"title": "Item 3", "description": "This is the third item"},
{"title": "Item 4", "description": "This is the fourth item"},
{"title": "Item 5", "description": "This is the fifth item"},
]
cards: List[Dict[str, Any]] = []
for item in dummy_items:
card_data: Dict[str, Any] = {
"card": AdaptiveCard.model_validate(
{
"type": "AdaptiveCard",
"version": "1.4",
"body": [
{
"type": "TextBlock",
"text": item["title"],
"size": "Large",
"weight": "Bolder",
"color": "Accent",
"style": "heading",
},
{"type": "TextBlock", "text": item["description"], "wrap": True, "spacing": "Medium"},
],
}
),
"thumbnail": {
"title": item["title"],
"text": item["description"],
},
}
cards.append(card_data)
return cards
The search results include both a full adaptive card and a preview card. The preview card appears as a list item in the search command area:

When a user clicks on a list item the dummy adaptive card is added to the compose box:

To implement custom actions when a user clicks on a search result item, you can add the tap property to the preview card. This allows you to handle the click event with custom logic:
from microsoft.teams.api import MessageExtensionSelectItemInvokeActivity, AttachmentLayout, MessagingExtensionInvokeResponse, MessagingExtensionResult, MessagingExtensionResultType
from microsoft.teams.apps import ActivityContext
# ...
@app.on_message_ext_select_item
async def handle_message_ext_select_item(ctx: ActivityContext[MessageExtensionSelectItemInvokeActivity]):
option = getattr(ctx.activity.value, "option", None)
await ctx.send(f"Selected item: {option}")
result = MessagingExtensionResult(
type=MessagingExtensionResultType.RESULT, attachment_layout=AttachmentLayout.LIST, attachments=[]
)
return MessagingExtensionInvokeResponse(compose_extension=result)