MCP, Part 5: Connect your MCP server to Copilot Cowork

Flow diagram in eight steps. 1: the member types "Reserve Dopamine Detox for me" in Copilot Cowork. 2: Cowork picks the reserve-a-book skill and the YEG & Tell Library connector from the plugin. 3: Cowork gets a token from Microsoft's token store, which holds the OAuth registration made in the Teams developer portal. 4: the first time only, the member signs in with Microsoft Entra ID and the token store swaps the code for a token for .../mcp/library.use. 5: Cowork calls search_books and reserve_book on the MCP server in Azure Container Apps with the token. 6: the server checks the token. 7: the server reserves the book in Azure SQL with its managed identity. 8: the result goes back to Cowork, which replies to the member.

Written by

in

,

This series started with a Copilot Cowork plugin. I was building one and found that, to reach the data it needed, Cowork expected an MCP server. The YEG & Tell Library, a fictional library, now has one: it runs in Azure and signs people in with Microsoft Entra ID (Part 4).

In this post, I turn that server into a Copilot Cowork plugin: the skills, the sign-in, the package, and what happened when I tested it. Claude and ChatGPT come in Parts 6 and 7.

The full code is in a private GitHub repo; if you’d like access, reach out to me on LinkedIn.

TL;DR

  • A Copilot Cowork plugin is a .zip with a manifest, skills and a connector that points at your MCP server. Cowork finds the tools itself.
  • With Entra ID sign-in, you register the plugin twice: as an app in Entra ID, and as an OAuth client in the Teams developer portal. The registration ID goes in the manifest.
  • Skills tell the model how to use the tools. Cowork’s docs don’t list MCP prompts or resources, so I covered those with skills too.
  • Gotcha: a cold start (the server and a paused database) was too slow for Cowork’s first call.
In this post

What’s in a Cowork plugin

Microsoft’s Build plugins for Copilot Cowork guide describes a plugin as a .zip with three parts:

  • manifest.json, which lists the skills and the connector
  • skills: one folder each, with a SKILL.md file of instructions for the model
  • a connector: your MCP server’s URL and how to sign in to it

You don’t describe the tools. The Manage plugins page says Cowork “calls initialize and tools/list at runtime to discover available tools automatically”. The same page lists what Cowork supports from MCP: the tool calls and authentication. Prompts and resources aren’t on the list, so I didn’t count on the server’s recommend_books prompt or its two resources reaching Cowork.

Flow diagram in eight steps. 1: the member types "Reserve Dopamine Detox for me" in Copilot Cowork. 2: Cowork picks the reserve-a-book skill and the YEG & Tell Library connector from the plugin. 3: Cowork gets a token from Microsoft's token store, which holds the OAuth registration made in the Teams developer portal. 4: the first time only, the member signs in with Microsoft Entra ID and the token store swaps the code for a token for .../mcp/library.use. 5: Cowork calls search_books and reserve_book on the MCP server in Azure Container Apps with the token. 6: the server checks the token. 7: the server reserves the book in Azure SQL with its managed identity. 8: the result goes back to Cowork, which replies to the member.
Figure 1: What happens when a member asks Copilot Cowork to reserve a book.

Step 1: Write the skills

Cowork can call the four tools without skills. The skills are where I tell the model how the library works. I wrote one per job a member would ask for:

SkillWhat it coversTools it uses
find-a-bookSearching the catalogue, one book’s detailssearch_books, get_book_details
reserve-a-bookFinding the right book and reserving itsearch_books, get_book_details, reserve_book
check-my-loansLoans, due dates, late booksget_my_loans
recommend-booksFive available books for a genre and reading levelsearch_books, get_book_details
library-infoLoan period, limits, late fees, programsNone

The model reads each skill’s description to decide when to use it, so I put the phrases a member would type in it. The name must match the folder name. This is the top of reserve-a-book:

---
name: reserve-a-book
description: |
  Reserves a book for the signed-in library member.
  Use when the member says "reserve Dopamine Detox for me", "put a hold on this
  book", "I want to borrow this one", or "book it for me", including after a
  search or a recommendation.
---

Two of the skills cover what Cowork may not read from the server. recommend-books does the job of the recommend_books prompt from Part 3, and library-info carries a copy of the borrowing policy and programs, which the server publishes as resources. The catch with the copy is that it can go out of date.

Writing the skills also showed me a gap: no tool lists or cancels a reservation. A skill can’t add a tool, so the skills tell the model to say so instead of promising it.

Step 2: Set up sign-in

The connector doesn’t hold any credentials. It holds the ID of an “auth config” that Microsoft keeps in its token store. Microsoft’s OAuth 2.0 page is clear about Entra ID: “Dynamic client registration isn’t available”, and “The agentConnectors node has no Microsoft Entra authorization type.” So, as in Part 4, the client has to be registered up front, in two places.

In Entra ID

  • A new app registration for Cowork, single tenant, with a Web redirect URI: https://teams.microsoft.com/api/platform/v1.0/oAuthRedirect
  • The delegated library.use permission of the server’s app registration
  • On the server’s app registration, the new app added as an authorized client, as I did for VS Code in Part 4, so members aren’t asked to consent
  • A client secret

In the Teams developer portal

Under Tools > OAuth client registration:

FieldValue
Base URLhttps://mcp.consultantdiary.tech/mcp
Restrict usage by Teams appAny Teams app
Client ID and secretFrom the new app registration
Authorization endpointhttps://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
Token and refresh endpointshttps://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
Scopehttps://mcp.consultantdiary.tech/mcp/library.use, offline_access
PKCEOn
  • Any Teams app: Microsoft warns that if you bind the registration to one app, “every tool call returns a 404 error”.
  • PKCE: Microsoft’s page says “It’s on by default”. On my form it was off, so check it.
  • The registration ID you get after saving goes in the manifest. When I decoded mine, it held my tenant ID, so I keep the built package out of the repo.

Step 3: Build the package and upload it

I wrote the skills in a folder laid out as a Claude Code plugin, because I’ll need that in Part 6. The Microsoft 365 Agents Toolkit CLI (version 1.1.18 in my case) turns it into a Cowork package:

atk import openplugin --path ./plugin --output ./cowork-build `
    --privacy-url <privacy-url> --terms-url <terms-url> `
    --default-auth-type OAuthPluginVault

The import left a placeholder referenceId on the connector, which you replace with the registration ID:

"remoteMcpServer": {
    "mcpServerUrl": "https://mcp.consultantdiary.tech/mcp",
    "authorization": {
        "type": "OAuthPluginVault",
        "referenceId": "<oauth-client-registration-id>"
    }
}

Then atk package builds the .zip. To upload it, Microsoft’s docs point to a Plugins tab with Add plugin. My Cowork had Customize > Add > Upload instead. After the upload, I selected the connector, signed in, and it showed Connected.

The YEG & Tell Library plugin page in Copilot Cowork. Under Connections, the YEG & Tell Library connector shows Connected with a Disconnect button. Under Skills, check-my-loans, find-a-book and library-info are listed.
Figure 2: The plugin in Copilot Cowork, connected, with its skills.

What testing showed

The first call failed on a cold start

My first question, “list my loans”, picked the right skill, but Cowork said the plugin “couldn’t be reached”. The server’s log in the Azure portal showed why. The container was starting from zero, Cowork’s token was valid, and then the first database call failed with TaskCanceledException: A task was canceled. while the database woke up from auto-pause.

The Azure portal Log stream for the yeg-and-tell-mcp container app. The log shows the server starting, the token validated, and then System.Threading.Tasks.TaskCanceledException: A task was canceled, in LibraryDb.EnsureMemberAsync.
Figure 3: The server’s log: a cold start, a valid token, then a cancelled database call (IDs hidden).

As I wrote in Part 4, the server checks the member on every request, so the first request of any kind wakes the database. Part 4 measured about 27 seconds for the container alone, and Microsoft asks for “Less than 30 seconds per tool call”. Three minutes later, with both awake, the same question worked in 310 ms. The same thing happened when I came back later to test a reservation: the first try failed, and the second one, a minute later, worked.

The test questions

I asked each question in a new chat, signed in as myself. Cowork shows which skill and tools it used, but not the arguments, and my server doesn’t log them.

QuestionWhat Cowork usedReply (quoted)Result
“What do I have on loan?”check-my-loans; Get my loans“you don’t have any books on loan”Pass
“Any self-help books I can borrow now?”find-a-book; Search books“There are 10 self-help books you can borrow right now”Pass
“Show me self-hlep books”find-a-book; one Search books call“Showing 10 of 16”. It fixed the typo itself, as the skill says.Pass
“What’s the late fee?”library-info; no tool“$0.25 a day for each book, up to a maximum of $5 per book”Pass
“recommend kids books”recommend-books; Search books; Get book details“The library has only four children’s books available”Partly
“Reserve Outliers by Malcolm Gladwell for me.”reserve-a-book; Search books; Reserve a book (after my approval)“I’ve reserved Outliers by Malcolm Gladwell (book 19) for you.”Pass
“What books do I have reserved?”Two steps; the reply says it checked my loans“I can’t see your reservations” … “check your account on the library’s website”Partly

For the reservation, Cowork asked me once, with its own approval card. The card shows the tool’s title from Part 2, “Reserve a book”, and the book ID. Microsoft says this confirmation comes from the tool’s annotations, and that for non-Microsoft MCP servers it “is being rolled out progressively”; in my tenant, it was already there. The model didn’t ask me first, which is what the skill says to do when the member names one book. It also didn’t make up pickup details: “The library system didn’t give any pickup details, so check with library staff”.

A Cowork chat. The member asks "Reserve Outliers by Malcolm Gladwell for me." Cowork shows the reserve-a-book skill and the Search books tool, then an approval card titled "Reserve a book" from the YEG & Tell Library, with Book id 19 and Cancel and Approve buttons.
Figure 4: Cowork asks once before running reserve_book.

The late fee is a good example of a skill helping. In Part 3, Claude couldn’t answer it unless I attached the policy resource. Here, library-info answered from its copy with no tool call.

The two misses are worth knowing. “recommend kids books” returned only the four books in the children genre. The catalogue also has six children’s fiction books (age group Children), and none came up. I can’t see the arguments, but those four books point to a genre search. And for reservations, the model said it can’t see them, as the skill asks, then suggested a library website that doesn’t exist.

A Cowork chat. The member asks "recommend kids books". Cowork says the library has only four children's books available and lists Charlotte's Web, Matilda, The Gruffalo and Where the Wild Things Are, then offers to reserve one.
Figure 5: “recommend kids books” found the four books in the children genre and missed six children’s fiction books.

What’s next

In Part 6, we connect the same server to Claude. The plugin folder from this post is already a Claude Code plugin, so the main work is another client registration in Entra ID. You can follow the series on the MCP series page.

Sources


A note on how this post was written: the experience, examples and voice are mine. The writing was enhanced with the help of AI.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

Get new posts by email

New posts on AI, agents and Power Platform, straight to your inbox. No spam, unsubscribe anytime.