Microsoft 365 Copilot Plugins Explained

Example plugin Example plugin in use


In this article I’m going to go over how you can bring in (and modify) external data in to M365 Copilot (or just Copilot from here on in) by the use of plugins.

Copilot has had many names since launch (e.g. “M365 Chat” in Teams), but it is currently called just “Copilot” within Teams and M365.

There are two current methods to bring in data in to Copilot:

  • Graph connectors - pull in external data using Microsoft Graph on an import cycle. This is more for static data, or data that doesn’t need to be “live” - some might call it “grounding data”
  • Plugins - pull in external data from APIs or wherever at the time the user requested it

Today, we are talking about plugins.

What is a plugin?

Plugins are a recent addition that allows external data to be fetched (and updated) from within a Copilot conversation. Once enabled, there is no need to use a dedicated app or conversation, you can just “simply” ask Copilot and it will silently call your plugin in the background and return the information to the current conversation.

Who can make a plugin?

Anyone can make one. But it will require familiarisation with coding/development. Plugins are based on Teams message extensions underneath, so if you have experience in that, it will go a long way.

You can/will be able to create plugins in Copilot Studio, but at the time of writing this is in preview. This article will focus on creating plugins using the existing method.

How does a plugin work?

At a glance it is a pretty straightforward solution. You have an (externally hosted) app that responds to requests from Copilot - of course, its never that simple.

As mentioned, plugins use a message extension, and message extensions actually use a bot as a way of communication. This is the same for plugins. The bot is registered in Azure and is used to send and receive messages from Copilot.

You can adapt/enable an existing message extension to also work as a plugin for Copilot. Depending on how your message extension is built/configured this can be an easy or tricky task.

Copilot works on the basis of functions e.g. “What is the weather in Seattle, today?” might match a function to get the weather of a location. With plugins we have something similar in the way of “commands”. For example, you have a plugin that has a command to “get the weather of a location”, you want this being triggered when Copilot is asked about the weather.

You may see the terms “skill”, “function”, “task”, “command” used and these seem to be used interchangeably based on different products and documentation. It is essentially the act of doing “something” that has been defined.

The general workflow of a plugin is as follows:

Plugin workflow

  1. A M365 user has plugin enabled in Copilot and asks/requests something
  2. Copilot will take what has been asked and determine if it matches one of the functions of the plugin
  3. If it does, then Copilot will (in the background) call the function like a user would using a message extension
  4. And just like a message extension the request is sent via a bot registration in Azure (via the M365 channel)
  5. The bot registrations directs the request to its endpoint, which is your application
  6. Your application returns the data back to Copilot via the bot registration
  7. Copilot processes the response and responds to the M365 user. The response is typically a card with the information requested and a message to the user (generated by Copilot)

As long as we can convince Copilot to use our functions when it should, the above is a pretty straightforward process. Ask a question, send an answer back. Of course it’s not always that simple in practice.

What you will need

To use plugins, or more accurately create and use plugins, you will need the following:

  • A Microsoft tenant
  • Permission in tenant to:
    • Create/update a bot service in Azure
    • Create/update an app registration associated to bot
    • Upload/approve/assign the message extension/plugin
  • M365 Copilot license for each user - this is not part of any existing M365 plan, it is an add on
  • Somewhere to host your code (web app) - this can be run locally through a tunnel during development

Example plugin (code)

To demonstrate how a plugin may be useful, I’ve made a working example to demonstrate a plugin in action. The example code can be found on GitHub here.

The idea behind this application is to highlight that current information can be retrieved from a public API - in this case the Transport for London (TfL) - from within a Copilot conversation.

Sample usage

Sample usage of the TfL plugin

You can take the example provided, follow the instructions and have a working plugin in your Copilot in no time. Or, take the code and adapt it to your own needs.

Alternatively, you can use something like Teams Toolkit to create a new message extension and adapt it to work as a plugin.

Top tips

In building out Copilot plugins, here are some tips in troubleshooting/improving yours:

Fine tuning

Copilot requires you to be specific on what queries/questions your plugin can help with. It is not going to assume anything based on the plugin name alone - you need to tell it. This is done by being very verbose with what the plugin is, what it can do and also providing examples in the Teams app manifest. If you provide the following it should go a long way to being used by Copilot:

  • description section - Explain in detail what the app is, what it is used for and the sort of information it provides. If you can provide example user inputs that should trigger the plugin being used
  • composeExtensions.commands - Each command (skill/task/function) has a title, description and parameters. The title should be clear and concise, and the description and parameters should be clear on what is expected and what the user should provide. The more detail you provide here the better

Example manifest

    "$schema": "",
    "manifestVersion": "1.16",
    "version": "1.0.0",
    "id": "<YOUR-MICROSOFT-APP-ID>",
    "packageName": "",
    "name": {
        "short": "Transport for London",
        "full": "Transport for London Bot"
    "description": {
        "short": "App allows you to search and find the status of TfL services",
        "full": "Transport for London (Bot) is a very useful tool for finding the status of a Transport for London (TfL) service. By just entering the name of the service(s) you'll be able to easily see the current status of the service. \n\n **Features and Benefits:** \n\n - Easy search using Microsoft 365 Copilot. Simply ask, 'Find the status of the Jubilee line in London?' or 'What is the status of the Bakerloo line?' or 'Is the N98 bus running?'"
    "composeExtensions": [
            "botId": "<YOUR-MICROSOFT-APP-ID>",
            "commands": [
                    "id": "lineStatus",
                    "context": [
                    "description": "Search for the status of a Transport for London (TfL) services by their line name. For example, 'Jubilee', 'Bakerloo', 'N98'",
                    "title": "Line status",
                    "type": "query",
                    "initialRun": true,
                    "parameters": [
                            "name": "lineName",
                            "title": "Line name",
                            "description": "Enter a line name to find the status of the service. For example, 'Jubilee', 'Bakerloo', 'N98'",
                            "inputType": "text"

Developer mode

If you type -developer on in to a Copilot chat, it will enabled developer mode. This will give you additional details with each response from Copilot:

  • It will list the plugins it has available to use
  • The functions (skills/tasks) that match the against what the user entered (ordered list, can be multiple)
  • The chosen functions that were selected to be used (ordered list, can be multiple)
  • The result of the execution (success/failure, error code etc.)

This is invaluable in seeing if your plugin is being called on user inputs (see fine tuning section).

Developer mode Developer mode in Copilot

Bot channels

This one caused me a headache. I was calling my app as a Teams message extension fine, but it was failing as a Copilot plugin. In the end the issue was the M365 bot channel must be enabled to work as a plugin. I had enabled the Teams channel for use as a message extension, but this was not correct (note: it doesn’t mention Copilot anywhere).

Bot channels

If you don’t want to use it as a message extension I believe the Teams channel is not required for a Copilot plugin


So there it is. A whistle-stop tour of plugins for M365 Copilot. We’ve gone over what they are, how they work, how to make your own and finally, tips on how to ensure they work the best they can. Thanks for reading!