diff --git a/msteams-platform/messaging-extensions/how-to/search-commands/define-search-command.md b/msteams-platform/messaging-extensions/how-to/search-commands/define-search-command.md index 89ff80aa793..ad20b1e5659 100644 --- a/msteams-platform/messaging-extensions/how-to/search-commands/define-search-command.md +++ b/msteams-platform/messaging-extensions/how-to/search-commands/define-search-command.md @@ -1,9 +1,10 @@ --- 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: 09/16/2024 +ms.date: 04/21/2026 ms.localizationpriority: medium ms.owner: slamba --- @@ -16,7 +17,7 @@ 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 invoked from the command box, the user interacts with the resulting card, or copies it for use elsewhere. +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: @@ -26,7 +27,7 @@ The following image displays the invoke locations of the search command: To add the search command to your [app manifest](/microsoft-365/extensibility/schema/root-compose-extensions-commands) (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. -### Create search message extension using Bot Framework +### Create search message extension using Teams SDK You can create a search message extension using Microsoft 365 Agents Toolkit (previously known as Teams Toolkit) and Developer Portal for Teams. @@ -34,10 +35,10 @@ You can create a search message extension using Microsoft 365 Agents Toolkit (pr Before you get started, ensure that you meet the following requirements: -* [Node.js](https://nodejs.org/en). The supported versions are 16, 18. -* [Microsoft 365 account for development](../../../toolkit/tools-prerequisites.md#microsoft-365-developer-program) -* [Set up your dev environment for extending Teams apps across Microsoft 365.](../../../m365-apps/prerequisites.md) 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 Visual Studio Code Extension](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) version 5.2.0 and higher or Microsoft 365 Agents Toolkit CLI (previously known as Teams Toolkit CLI). +* A supported version of [Node.js](https://nodejs.org/en) (16 or 18). +* A [Microsoft 365 account for development](../../../toolkit/tools-prerequisites.md#microsoft-365-developer-program). +* A [dev environment set up for extending Teams apps across Microsoft 365.](../../../m365-apps/prerequisites.md) 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](https://marketplace.visualstudio.com/items?itemName=TeamsDevApp.ms-teams-vscode-extension) (version 5.2.0 or later) or Microsoft 365 Agents Toolkit CLI (previously known as Teams Toolkit CLI). # [Agents Toolkit](#tab/Teams-toolkit) @@ -45,12 +46,11 @@ To create a search-based message extension using Agents Toolkit, follow these st 1. Open **Visual Studio Code**. 1. From the left pane, select **Microsoft 365 Agents Toolkit**. -1. Select **Create a New Agents/App** > **Teams App**. +1. Select **Create a New Agents/App** > **Teams Agents and Apps** > **Other Teams Capabilities**. 1. Select **Message Extension**. -1. Select **Custom Search Results**. 1. Select a **programming language**. 1. Select **Default folder**. -1. Enter the name of your app and select **Enter**. +1. Enter the name of your app/agent and select **Enter**. Agents Toolkit scaffolds your project and creates a search message extension. @@ -106,7 +106,7 @@ To run the message extension in Teams, follow these steps: * Parameter description * Select the type of input -1. Select **Save**. A search message extension using bot framework created. +1. Select **Save**. A search message extension is created. 1. At the upper-right corner, select **Preview in Teams**. The app opens in Teams desktop or web client. --- @@ -124,136 +124,149 @@ The following code provides an example of search-based for message extensions: # [.NET](#tab/dotnet) -* [SDK reference](/dotnet/api/microsoft.bot.builder.teams.teamsactivityhandler.onteamsmessagingextensionqueryasync?view=botbuilder-dotnet-stable&preserve-view=true) -* [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/app-hello-world/csharp/Microsoft.Teams.Samples.HelloWorld.Web/Bots/MessageExtension.cs#L26-L59) +* [SDK reference](/microsoftteams/platform/teams-sdk/in-depth-guides/message-extensions/search-commands?pivots=csharp) +* [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/TeamsSDK/bot-message-extensions/dotnet/bot-message-extensions/Program.cs) ```csharp -protected override async Task OnTeamsMessagingExtensionQueryAsync(ITurnContext turnContext, MessagingExtensionQuery query, CancellationToken cancellationToken) - { - var text = query?.Parameters?[0]?.Value as string ?? string.Empty; - - var packages = new[] { - new { title = "A very extensive set of extension methods", value = "FluentAssertions" }, - new { title = "Fluent UI Library", value = "FluentUI" }}; - - // We take every row of the results and wrap them in cards wrapped in MessagingExtensionAttachment objects. - // The Preview is optional, if it includes a Tap, that will trigger the OnTeamsMessagingExtensionSelectItemAsync event back on this bot. - var attachments = packages.Select(package => - { - var previewCard = new ThumbnailCard { Title = package.title, Tap = new CardAction { Type = "invoke", Value = package } }; - if (!string.IsNullOrEmpty(package.title)) - { - previewCard.Images = new List() { new CardImage(package.title, "Icon") }; - } - - var attachment = new MessagingExtensionAttachment - { - ContentType = HeroCard.ContentType, - Content = new HeroCard { Title = package.title }, - Preview = previewCard.ToAttachment() - }; - - return attachment; - }).ToList(); - - // The list of MessagingExtensionAttachments must we wrapped in a MessagingExtensionResult wrapped in a MessagingExtensionResponse. - return new MessagingExtensionResponse - { - ComposeExtension = new MessagingExtensionResult - { - Type = "result", - AttachmentLayout = "list", - Attachments = attachments - } - }; - } +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(); + + // 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 + } + }; +}); ``` -# [Node.js](#tab/nodejs) - -[Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/msgext-search-quickstart/js/botActivityHandler.js#L30-L53) - -```javascript -async handleTeamsMessagingExtensionQuery(context, query) { - const searchQuery = query.parameters[0].value; - const attachments = []; - const response = await axios.get(`http://registry.npmjs.com/-/v1/search?${ querystring.stringify({ text: searchQuery, size: 8 }) }`); - - response.data.objects.forEach(obj => { - const heroCard = CardFactory.heroCard(obj.package.name); - const preview = CardFactory.heroCard(obj.package.name); - preview.content.tap = { type: 'invoke', value: { description: obj.package.description } }; - const attachment = { ...heroCard, preview }; - attachments.push(attachment); - }); - - return { - composeExtension: { - type: 'result', - attachmentLayout: 'list', - attachments: attachments - } - }; - } - } +# [TypeScript](#tab/typescript) + +* [SDK reference](/microsoftteams/platform/teams-sdk/in-depth-guides/message-extensions/search-commands?pivots=typescript) +* [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/TeamsSDK/bot-message-extensions/nodejs/bot-message-extensions/index.ts) + +```TypeScript +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, + }, + } +}) ``` # [Python](#tab/python) +* [SDK reference](/microsoftteams/platform/teams-sdk/in-depth-guides/message-extensions/search-commands?pivots=python) +* [Sample code reference](https://github.com/OfficeDev/Microsoft-Teams-Samples/blob/main/samples/TeamsSDK/bot-message-extensions/python/bot-message-extensions/main.py) + ```python -async def on_teams_messaging_extension_query(self, context, query): - """ - Handles Messaging Extension queries in Teams. - - This method generates a list of thumbnail cards containing random text and images when the "getRandomText" command is triggered. It creates preview cards with tap actions and returns them as a Messaging Extension response. - """ - faker = Faker() - title = query.command_id - random_image_url = "https://loremflickr.com/200/200" - - if query.command_id == "getRandomText": - attachments = [] - # Generate 5 results with fake text and fake images - for i in range(5): - text = faker.paragraph() - images = [f"{random_image_url}?random={i}"] - - # Create a thumbnail card using ThumbnailCard - thumbnail_card = self.create_thumbnail_card(title, text, images) - - # Create a preview card and add the tap action - preview_card = self.create_thumbnail_card(title, text, images) - tap_action = CardAction( - type="invoke", - value={"title": title, "text": text, "images": images}, +@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", "")), ) - preview_attachment = CardFactory.thumbnail_card(preview_card) - preview_attachment.content.tap = tap_action - - # Combine the thumbnail card and the preview - attachment = MessagingExtensionAttachment( - content = thumbnail_card, - content_type=CardFactory.content_types.thumbnail_card, - preview=preview_attachment, - ) - attachments.append(attachment) + for r in results + ] + if not attachments: return MessagingExtensionResponse( compose_extension=MessagingExtensionResult( - type="result", - attachment_layout="list", - attachments=attachments, + type=MessagingExtensionResultType.MESSAGE, + text=f"No results found for '{query}'", ) ) + + return MessagingExtensionResponse( + compose_extension=MessagingExtensionResult( + type=MessagingExtensionResultType.RESULT, + attachment_layout=MessagingExtensionAttachmentLayout.LIST, + attachments=attachments, + ) + ) ``` --- ## Code sample -| Sample name | Description | .NET | Node.js | Manifest| +| Sample name | Description | .NET | Node.js | Python| |:---------------------|:--------------|:---------|:--------|:--------------| -|Teams message extension search | This sample demonstrates how to create a Messaging Extension in Microsoft Teams that allows users to perform searches and retrieve results. |[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search/csharp)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search/nodejs)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/msgext-search/csharp/demo-manifest/msgext-search.zip) +|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](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-message-extensions/dotnet/bot-message-extensions)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-message-extensions/nodejs/bot-message-extensions)|[View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-message-extensions/python/bot-message-extensions) ## Next step