| title | Create Search Commands for App |
|---|---|
| author | vikasalmal |
| description | Learn about message extension search commands for Teams apps, to create a search command through app manifest and manually. |
| ms.topic | article |
| ms.author | anclear |
| ms.date | 04/21/2026 |
| ms.localizationpriority | medium |
| ms.owner | slamba |
[!INCLUDE bot-based-me-note]
The search command is invoked from any one or both of the following locations:
- Compose message area: The buttons at the bottom of the compose message area.
- Command box: By using / in the command box. For example, /your-app-name. If you're using the classic Teams, search command is invoked by @mentioning in the command box. For example, @your-app-name.
When a search command is invoked from the compose message area, the user sends the results to the conversation. When a search command is invoked from the command box, the user interacts with the resulting card or copies it for use elsewhere.
The following image displays the invoke locations of the search command:
:::image type="content" source="~/assets/images/messaging-extension/search-command-invoke-locations.png" alt-text="Screenshot shows the invoke locations of a search command in a Teams channel.":::
To add the search command to your app manifest (previously called Teams app manifest), you must add a new composeExtensions object to the top level of your app manifest JSON. You can add the search command either with the help of Developer Portal or manually.
You can create a search message extension using Microsoft 365 Agents Toolkit (previously known as Teams Toolkit) and Developer Portal for Teams.
Before you get started, ensure that you meet the following requirements:
- A supported version of Node.js (16 or 18).
- A Microsoft 365 account for development.
- A dev environment set up for extending Teams apps across Microsoft 365. After you've enrolled your developer tenant in Office 365 Targeted Release, it might take a couple of days for the enrollment to take effect.
- Agents Toolkit for Visual Studio Code Extension (version 5.2.0 or later) or Microsoft 365 Agents Toolkit CLI (previously known as Teams Toolkit CLI).
To create a search-based message extension using Agents Toolkit, follow these steps:
-
Open Visual Studio Code.
-
From the left pane, select Microsoft 365 Agents Toolkit.
-
Select Create a New Agents/App > Teams Agents and Apps > Other Teams Capabilities.
-
Select Message Extension.
-
Select a programming language.
-
Select Default folder.
-
Enter the name of your app/agent and select Enter.
Agents Toolkit scaffolds your project and creates a search message extension.
To run the message extension in Teams, follow these steps:
-
From the left pane, select Microsoft 365 Agents Toolkit.
-
Under ACCOUNTS, sign in with your Microsoft 365 account and Azure account if you haven't already.
:::image type="content" source="../../../assets/images/Copilot/api-based-me-ttk-accounts.png" alt-text="Screenshot shows the Microsoft 365 and Azure sign in option in Agents Toolkit.":::
-
From the left pane, Select Run and Debug (Ctrl+Shift+D).
-
From the launch configuration dropdown, select
Preview in Teams (Edge)orPreview in Teams (Chrome). Agents Toolkit launches Teams web client in a browser window. -
Go to a chat message and select the Actions and apps icon. In the flyout menu, search for your app.
-
Select your message extension from the list and enter a search command in the search box.
-
Select an item from the list. The item unfurls into an Adaptive Card in the message compose area.
-
Select Send. Teams sends the search result as an Adaptive Card in the chat message.
-
Go to Developer Portal for Teams.
-
Go to Apps.
-
Select Create a new app.
-
Under Configure, select App features.
-
Select Messaging extension.
:::image type="content" source="../../../assets/images/Copilot/api-based-me-tdp-app-feature.png" alt-text="Screenshot shows the message extension option in Teams Developer Portal.":::
-
Under Message extension type, select Bot.
- If you get a disclaimer, which reads API Message extension is already in use by users. Would you like to change message extension type to bot?. Select Yes, change.
:::image type="content" source="../../../assets/images/Copilot/bot-based-me-tdp-type.png" alt-text="Screenshot shows API Message extension is already in use disclaimer when a user switches from API to bot message extension type.":::
-
If you have an existing bot, select Existing bot or if you have a bot ID, select Enter Bot ID.
- If you don't have an existing bot ID, select Create a bot, to create a new bot and enter the bot ID of the new bot that you created.
-
Select Save.
-
Select the required scopes.
-
Under Command, select + Add a command.
A command details page appears.
-
In the Command details page, select Search as the type of command and update the following fields:
- Command ID
- Command title
- Command description
- Context in which the command works
- Parameter name
- Parameter title
- Parameter description
- Select the type of input
-
Select Save. A search message extension is created.
-
At the upper-right corner, select Preview in Teams. The app opens in Teams desktop or web client.
Important
Agents for Microsoft 365 Copilot are in preview and only work in Microsoft 365 Copilot in Teams.
Microsoft 365 agents provide integration with various Microsoft 365 products, such as Teams and Outlook. The integration helps users to search or create content in external systems. Message extension agents allow Microsoft 365 Copilot to interact with APIs from other software and services through a bot. We recommend that you build or upgrade your existing message extensions to maximize their usefulness and usability in Microsoft 365 Copilot. For more information, see extend bot-based message extension as agent for Microsoft 365 Copilot.
The following code provides an example of search-based for message extensions:
teams.OnQuery(async (ctx) =>
{
var commandId = ctx.Activity.Value.CommandId;
var parameters = ctx.Activity.Value.Parameters;
var query = parameters?.FirstOrDefault()?.Value?.ToString() ?? "";
Console.WriteLine($"Query: command={commandId}, query={query}");
var attachments = new List<MsgExt.Attachment>();
// Route to appropriate search
if (commandId == "wikipediaSearch")
{
var results = await SearchWikipedia(query);
attachments = results.Select(r =>
{
var title = r["title"]?.ToString() ?? "No Title";
var snippet = Regex.Replace(r["snippet"]?.ToString() ?? "", "<[^>]+>", "");
return CreateAttachment(CreateWikipediaCard(r), title, snippet);
}).ToList();
}
if (attachments.Count == 0)
{
return new MsgExt.Response
{
ComposeExtension = new MsgExt.Result
{
Type = MsgExt.ResultType.Message,
Text = $"No results found for '{query}'"
}
};
}
return new MsgExt.Response
{
ComposeExtension = new MsgExt.Result
{
Type = MsgExt.ResultType.Result,
AttachmentLayout = Attachment.Layout.List,
Attachments = attachments
}
};
});app.on('message.ext.query', async ({ activity }) => {
const commandId = activity.value?.commandId
const params = activity.value?.parameters || []
const query = params[0]?.value || ''
console.log(`Query: command=${commandId}, query=${query}`)
let attachments: MessagingExtensionAttachment[] = []
if (commandId === 'wikipediaSearch') {
const results = await searchWikipedia(query)
attachments = results.map((r: any) =>
createAttachment(
createWikipediaCard(r),
r.title,
(r.snippet || '').replace(/<[^>]+>/g, '')
)
)
}
if (!attachments.length) {
return {
composeExtension: {
type: 'message',
text: `No results found for '${query}'`,
},
}
}
return {
composeExtension: {
type: 'result',
attachmentLayout: 'list',
attachments,
},
}
})@app.on_message_ext_query
async def handle_query(ctx: ActivityContext[MessageExtensionQueryInvokeActivity]):
command_id = ctx.activity.value.command_id
params = ctx.activity.value.parameters or []
query = params[0].value if params else ""
print(f"Query: command={command_id}, query={query}")
# Route to appropriate search
if command_id == "wikipediaSearch":
results = await search_wikipedia(query)
attachments = [
create_attachment(
create_wikipedia_card(r),
r["title"],
re.sub(r"<[^>]+>", "", r.get("snippet", "")),
)
for r in results
]
if not attachments:
return MessagingExtensionResponse(
compose_extension=MessagingExtensionResult(
type=MessagingExtensionResultType.MESSAGE,
text=f"No results found for '{query}'",
)
)
return MessagingExtensionResponse(
compose_extension=MessagingExtensionResult(
type=MessagingExtensionResultType.RESULT,
attachment_layout=MessagingExtensionAttachmentLayout.LIST,
attachments=attachments,
)
)| Sample name | Description | .NET | Node.js | Python |
|---|---|---|---|---|
| Bot Message Extensions | This sample demonstrates a search-based messaging extension in Microsoft Teams that allows users to search for Wikipedia articles. The extension supports search commands, item selection, and link unfurling. | View | View | View |
[!div class="nextstepaction"] Respond to search commands.