Get started building a plugin for Microsoft Copilot

Plugins that use OpenAI schemas can extend the functionality of Microsoft Copilot with the ability to call remote APIs that can retrieve real-time data or handle user actions.

This tutorial shows how to build your own plugin that uses an OpenAI schema for Microsoft Copilot. At the end of the tutorial, you'll have a working plugin. The plugin you'll build is a real estate plugin that can find properties for sale in a given city, with a certain number of bedrooms, a certain number of bathrooms, and specified amenities.

You can view the source code for the plugin you'll build in this tutorial at the MicrosoftDocs/mslearn-copilot-plugins > contoso-real-estate repo folder.

There are three steps needed to build your own plugin:

  1. Create an API.
  2. Document the API using the OpenAPI specification.
  3. Create a manifest file for your plugin.

These steps are documented in the sections below. If you already have a working API that you want to expose as a plugin for Microsoft Copilot, you can skip the first step and continue with Document your API, below.

Files in this tutorial

A plugin consists of an API implementation, an API specification, and a plugin manifest. In this tutorial, the plugin's files are:

Filename Role
server.js The API code, defining the /get-listings path.
openapi.yaml The API specification; API documentation using the OpenAPI specification.
ai-plugin.json The plugin manifest.

This tutorial also uses these supporting files:

Filename Role
package.json Package file for Node Package Manager (NPM).
listings.json Data (a downloaded, pre-populated file).
get-listings.js Server-side code that finds real estate listings.

Prerequisites

To complete this tutorial, you'll need the following:

  • Some experience writing JavaScript. In this tutorial, you create your API with JavaScript code.
  • Install Visual Studio Code, to write the code for your plugin.
  • Install Node.js, to run your API on a local web server.

Create your API

Your plugin will be installed in a Microsoft Copilot experience. Your API is the programmatic interface that Microsoft Copilot calls. The role of your API is to receive HTTP requests from Microsoft Copilot, handle the requests in some way, and then send HTTP responses in return.

Write the API request handling code

The first step is to write the server code that listens to HTTP requests, calls a function to find real estate listings, and then responds with HTTP responses.

Here you learn to run your API on your local device only. When you're ready to publish your plugin, you'll need to host your API on a server that's accessible to the internet. There are many ways to deploy your API to the internet, and doing so is not covered by this tutorial. For example, you can host your API on Azure Functions. Learn more in the Azure Functions documentation.

Create the project

To write the server code, create a new project on your computer:

  1. Create a new directory on your computer, named contoso-real-estate-plugin.
  2. Open this directory in Visual Studio Code.

You'll use JavaScript to write the API, and the Express NPM package (for Node Package Manager) to run the local server.

To download the Express package:

  1. Create a new file at the root of your project directory, named package.json.

  2. Paste the following content into the package.json file:

    {
      "name": "contoso-real-estate-plugin",
      "type": "module"
    }
    
  3. Save the file.

  4. In Visual Studio Code, click View > Terminal, or press CTRL + ` to open the Terminal panel.

  5. Type npm install express and press Enter. The Express package is now installed in your JavaScript project, ready to be used.

Write the API code

We'll name the API code file server.js.

To write the API code:

  1. Create a new JavaScript file at the root of your project directory, named server.js.

  2. Paste the following JavaScript code into the file:

    import express from "express";
    
    const app = express();
    app.get("/get-listings", (req, res) => {
       res.send({});
    });
    
    app.listen(8080);
    

    This code snippet creates a new Express server that listens to requests on port 8080. This server only accepts GET HTTP requests sent to the /get-listings path.

  3. Save the file.

Test the server

To test your new server:

  1. Go to the Terminal in Visual Studio Code, type node server.js, and then press Enter.

  2. In a web browser, open a new tab and go to http://localhost:8080/get-listings. An empty JSON object ({}) is displayed, which means that the server is running correctly and is responding to requests that are sent to the /get-listings path.

Write the code to get real estate listings

You now need to write the server-side code that finds real estate listings. We'll name this file get-listings.js.

  1. To get some data in your example project, download the following JSON file and save it in your project directory as listings.json: listings JSON file.

  2. Create a new file at the root of your project directory, named get-listings.js.

  3. Paste the following code snippet into the file:

    import listings from "./listings.json" assert { type: "json" };
    
    // Return only the first 5 results.
    const RESULT_LIMIT = 5;
    
    export default function getListings(city, bedrooms, bathrooms, amenities) {
      return listings.filter(listing => {
        const cityMatch = city
          ? listing.city.toLowerCase() === city.toLowerCase()
          : true;
    
        const bedroomsMatch = bedrooms
          ? listing.bedrooms === bedrooms
          : true;
    
        const bathroomsMatch = bathrooms
          ? listing.bathrooms === bathrooms
          : true;
    
        const amenitiesMatch = amenities && amenities.length
          ? amenities.every(amenity => listing.amenities.includes(amenity))
          : true;
    
        return cityMatch && bedroomsMatch && bathroomsMatch && amenitiesMatch;
      }).slice(0, RESULT_LIMIT);
    }
    
  4. Save the file.

This code snippet contains a JavaScript module that exports a single function. This function, named getListings, takes four optional parameters, and returns matching entries from the listings.json file.

The export default syntax makes it possible for other JavaScript modules in the same project to use the getListings function.

Use the get-listings module in the API code

You're now ready to call the getListings function from your API request handling code and run your API locally.

  1. Open server.js in Visual Studio Code.

  2. On line 2, add the following code to import the getListings function into the module to have access to it:

    import getListings from "./get-listings.js"
    
  3. Modify the /get-listings request handler to extract request parameters and find matching listings:

    app.get("/get-listings", (req, res) => {
      const city = req.query.city;
      const bedrooms = parseInt(req.query.bedrooms);
      const bathrooms = parseInt(req.query.bathrooms);
      const amenities = req.query.amenities;
    
      try {
        const listings = getListings(city, bedrooms, bathrooms, amenities);
        res.send(listings);
      } catch (e) {
        res.status(400).send({ error: e.message });
      }
    });
    
  4. Restart the Express server for the changes to be visible. To do this, in the Terminal panel, press Ctrl+C to stop the NodeJS process you started in the previous step, type node server.js, and then press Enter.

  5. To test that your API is running correctly, go to http://localhost:8080/get-listings?city=Paris&bedrooms=4 in a new window or tab. The following response is displayed:

    [
      {
        "title": "Luxurious house in the suburbs",
        "city": "Paris",
        "bedrooms": 4,
        "bathrooms": 2,
        "amenities": ["no furniture", "furniture", "balcony"],
        "description": "Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus."
      }
    ]
    

Your server-side code is now ready, and your API is running on your local device.

Document your API

For Microsoft Copilot to know how to call your API, document your API in an API specification file, which describes the API requests, responses, parameters, and return values. We'll name this file openapi.yaml.

You must document your API by using the OpenAPI specification. OpenAPI is a standard for documenting HTTP APIs in a way that's human- and machine-readable. To learn more, see the OpenAPI Initiative website.

Your API can include more than one endpoint for handling requests. Microsoft Copilot will only use the endpoints that are documented in your OpenAPI specification. For example, if your API defines three endpoints such as/get-listings, /get-listing-by-id, and /get-reservations, you may choose to only document /get-listings in your OpenAPI specification. Microsoft Copilot will only use the endpoints that are documented in your OpenAPI specification.

There are many tools that can help you generate the OpenAPI specification for your API, depending on the server-side code you use. In this tutorial, because the API is very simple, you write the OpenAPI specification manually, by following the steps below:

  1. In Visual Studio Code, create a new file at the root of your project directory, named openapi.yaml.

    This file can be anywhere you want, as long as it's available on your server. Later, you'll define the URL for this file in your plugin manifest.

  2. Paste the following code snippet into the new file:

    openapi: 3.0.1
    info:
      title: Contoso Real Estate API
      description: Finds up to date and detailed real estate properties for sale on the market
      version: 0.1.0
    servers:
      - url: http://localhost:8080
    paths:
      /get-listings:
        get:
          operationId: getListings
          summary: Get a list of properties matching the specified criteria
          description: Returns a list of properties, optionally filtered by providing the city, number of bedrooms, number of bathrooms and a list of amenities as query parameters
          parameters:
            - name: city
              in: query
              description: The city to search in
              required: false
              schema:
                type: string
            - name: bedrooms
              in: query
              description: The number of bedrooms
              required: false
              schema:
                type: integer
            - name: bathrooms
              in: query
              description: The number of bathrooms
              required: false
              schema:
                type: integer
            - name: amenities
              in: query
              description: The list of amenities
              required: false
              schema:
                $ref: '#/components/schemas/AmenitiesEnum'
          responses:
            '200':
              description: OK - Returns a list of properties
              content:
                application/json:
                  schema:
                    type: array
                    items:
                      $ref: '#/components/schemas/Property'
    components:
      schemas:
        AmenitiesEnum:
          type: string
          enum:
            - air conditioning
            - balcony
            - dishwasher
            - elevator
            - fireplace
            - furniture
            - garden
            - gym
            - heating
            - jacuzzi
            - laundry room
            - microwave
            - no furniture
            - parking
            - patio
            - sauna
            - swimming pool
            - terrace
            - wi-fi
        Property:
          type: object
          properties:
            title:
              type: string
              description: The title of the property listing
            city:
              type: string
              description: The city of the property
            bedrooms:
              type: integer
              description: The number of bedrooms
            bathrooms:
              type: integer
              description: The number of bathrooms
            amenities:
              type: array
              description: The list of amenities
              items:
                $ref: '#/components/schemas/AmenitiesEnum'
            description:
              type: string
              description: The description of the property
    
  3. Save the file.

    This code snippet contains:

    • A description of the API.

    • A description of the /get-listings request.

    • The names, types, and descriptions of the request and response parameters.

      The following types are supported, only: string, array, boolean, integer, and number.

  4. To make the new YAML file available on your server, open the server.js file, and then add the following lines of code. First, add the following module import on line 3:

    import path from "path";
    
  5. Add the following Express request handler:

    app.get("/openapi.yaml", (req, res) => {
      res.sendFile(path.resolve() + "/openapi.yaml");
    });
    

Your API specification is now ready, as the file openapi.yaml. To test the availability of your API specification, restart your server:

  1. In the Terminal panel of Visual Studio Code, press Ctrl+C to stop the current NodeJS process.

  2. Type node server.js, and then press Enter.

  3. In a new browser tab, go to http://localhost:8080/openapi.yaml. The browser either displays the content of the YAML file, or downloads the YAML file.

API specification fields

The following are the most important fields that your API specification must contain:

  • openapi
  • info
  • servers
  • paths

For descriptions, see API specification fields in Overview of Plugins for Microsoft Copilot.

Create your plugin's manifest file

You now have an API (server.js) and documentation for this API (the API specification, openapi.yaml). It's now time to create a plugin manifest - we'll name it ai-plugin.json.

The plugin manifest plays an important role for Microsoft Copilot to be able to use your plugin. The plugin manifest contains a natural-language description of your plugin. The large language model that powers Microsoft Copilot can decide when and how to use your plugin, based only on the descriptions provided in your plugin manifest and API specification.

For a list of the manifest fields, see Plugin manifest fields in Overview of Plugins for Microsoft Copilot.

Write your plugin manifest

To finalize your plugin, write a plugin manifest:

  1. In Visual Studio Code, create a new file at the root of your project directory, named ai-plugin.json.

  2. Paste the following code snippet into the file:

    {
      "schema_version": "v1",
      "name_for_model": "contosorealestate",
      "description_for_model": "Plugin for finding properties for sale. Use it whenever a user asks about real estate properties for sale on the market. This plugin can be used to search for properties in a particular city, and with a given number of bedrooms, bathrooms, and amenities.",
      "name_for_human": "Contoso Real Estate",
      "description_for_human": "Find up-to-date, detailed real estate properties for sale on the market",
      "api": {
        "type": "openapi",
        "url": "http://localhost:8080/openapi.yaml",
        "is_user_authenticated": false
      },
      "auth": {
        "type": "none"
      },
      "logo_url": "http://localhost:8080/logo.png",
      "contact_email": "contact@contoso.com",
      "legal_info_url": "https://contoso.com/legal/",
      "privacy_policy_url": "https://contoso.com/privacy-policy"
    }
    
  3. Save the file.

  4. Download the following image and save it in your project directory as logo.png: The plugin logo. This image is the logo that will be displayed to users in Microsoft Copilot. It must be 512x512 pixels and must not be a GIF.

Make the plugin manifest and image available on your server

Your final task is to make this new plugin manifest and image available on your server. We recommend placing your plugin manifest file in the standard path /.well-known/ai-plugin.json under your verified domain, though the file can be at any other location within your verified domain.

To make the plugin manifest and image available on your server:

  1. In Visual Studio Code, open the server.js file.

  2. Add the following Express request handlers:

    app.get("/.well-known/ai-plugin.json", (req, res) => {
      res.sendFile(path.resolve() + "/ai-plugin.json");
    });
    
    app.get("/logo.png", (req, res) => {
      res.sendFile(path.resolve() + "/logo.png");
    });
    
  3. Save the file.

  4. In the Terminal panel, press Ctrl+C. The server stops.

  5. In the Terminal panel, type node server.js, and then press Enter.

You now have a fully functional local plugin. Before deploying your plugin to a production server, test your plugin. To learn more about testing your local plugin, see Test and debug a plugin (Private Preview).

See also