This article is the practical companion to Cowork plugins explained for SharePoint AI Skills practitioners. That article covers what Cowork plugins are. This article covers how to build one.
The starting assumption: you already have a working SharePoint AI Skill in a SKILL.md file. If you do not, start with how to write your first SharePoint AI Skill, then come back here. The packaging steps below assume the content is already written.
What you will build
A .zip archive containing:
my-plugin.zip
├── manifest.json # Plugin metadata: name, version, declared skills
├── color.png # 192x192 colour icon for the M365 App Store
├── outline.png # 32x32 outline icon for inline UI
└── skills/
└── my-skill/
└── SKILL.md # Your existing SharePoint AI Skill, copied verbatim
That is the minimum. The result is a sideloadable Cowork plugin you can upload to your tenant in minutes.
The build steps below take ~30 minutes the first time. Subsequent plugins take 5 to 10 minutes because the manifest pattern is reusable.
Step 1: Set up the folder structure
Create a working folder for the plugin. Inside it, create a skills/ subfolder, then a folder for your specific skill (named in kebab-case). Drop your existing SKILL.md inside.
my-plugin/
└── skills/
└── meeting-prep/
└── SKILL.md
The folder name (meeting-prep) must match the name field in the SKILL.md frontmatter exactly. This is the most common cause of plugin failures. If your SharePoint AI Skill is called MeetingPrep or meeting_prep, rename both the folder and the frontmatter to kebab-case (meeting-prep).
Naming rules:
- Lowercase letters, numbers, hyphens only
- No leading or trailing hyphens
- No consecutive hyphens
- 1 to 64 characters
Examples: meeting-prep, contract-review, email, quarterly-report. Invalid: MeetingPrep, meeting_prep, --meeting-prep--, meeting--prep.
Step 2: Verify the SKILL.md frontmatter
Open the SKILL.md and confirm the YAML frontmatter has the required fields.
---
name: meeting-prep
description: Use when user asks to prepare for an upcoming meeting. Pulls calendar context, recent email threads, and relevant documents into a briefing document.
---
Required:
name: must match the folder name (1-64 chars, kebab-case)description: when to use the skill, including trigger phrases (1-1024 chars)
Optional but useful:
cowork.category: display label like "Productivity" or "Sales"cowork.icon: Fluent UI icon name in PascalCase, likeBriefcaseorCalendarmetadata: author, version, any custom key-value pairs
The description is what Cowork uses to decide when to load the skill. Write it as a trigger description, not a marketing blurb. "Use when user asks to..." is the pattern that works.
Step 3: Add reference materials (optional)
For longer skills, keep the main SKILL.md focused on the workflow and move detailed reference material to a references/ subfolder inside the skill folder.
skills/
└── meeting-prep/
├── SKILL.md # ~1,500-2,000 words, the core workflow
├── references/
│ ├── briefing-template.md
│ └── meeting-types.md
└── scripts/
└── extract-attendees.py
Limits per skill:
- Up to 20 companion files (anything that is not SKILL.md)
- Up to 5 MB per file
- Up to 10 MB total companion size
- 15 seconds total download time for all companions
Skip this step for your first plugin. Add references later when you find the SKILL.md getting too long to read in one pass.
Step 4: Add the icons
Two PNG files at the root of the plugin folder.
color.png: 192x192 pixels, full-colour, shown in the M365 App Storeoutline.png: 32x32 pixels, monochrome with transparency, shown inline in the UI
You can use placeholder squares for testing. The official documentation accepts placeholders during sideload but the App Store submission requires real icons.
Step 5: Write the manifest
Create manifest.json at the root of the plugin folder. This is the M365 App Package descriptor.
{
"$schema": "https://developer.microsoft.com/json-schemas/teams/v1.19/MicrosoftTeams.schema.json",
"manifestVersion": "1.19",
"version": "1.0.0",
"id": "00000000-0000-0000-0000-000000000000",
"packageName": "com.yourorg.meeting-prep-plugin",
"developer": {
"name": "Your Organisation",
"websiteUrl": "https://yourorg.com",
"privacyUrl": "https://yourorg.com/privacy",
"termsOfUseUrl": "https://yourorg.com/terms"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"name": {
"short": "Meeting Prep",
"full": "Meeting Prep Plugin for Cowork"
},
"description": {
"short": "Prepares briefing documents for upcoming meetings.",
"full": "Pulls calendar context, recent email threads, and relevant documents into a briefing document for any meeting on your schedule."
},
"accentColor": "#0066CC",
"agentSkills": [
{
"id": "meeting-prep",
"skillFile": "skills/meeting-prep/SKILL.md"
}
]
}
Generate the id GUID using PowerShell ([guid]::NewGuid()) or any UUID generator. The id should be unique per plugin and stable across versions.
The agentSkills array points at each SKILL.md inside the plugin. For multiple skills, add multiple entries. The id in each entry should match the skill's folder name and SKILL.md frontmatter name.
Step 6: Package as .zip
From the plugin folder root, create a .zip with all files at the root level (not inside a parent folder).
PowerShell:
Compress-Archive -Path manifest.json,color.png,outline.png,skills -DestinationPath meeting-prep-plugin.zip
macOS or Linux:
zip -r meeting-prep-plugin.zip manifest.json color.png outline.png skills/
Open the .zip and confirm the structure. manifest.json should be at the root, not inside a folder. This is the second most common cause of plugin install failures.
Step 7: Sideload to test
Sign in to Microsoft 365 Admin Centre.
- Manage Apps → Upload custom app
- Upload your
meeting-prep-plugin.zip - Wait for the upload to complete (usually under a minute)
- Open Cowork in
m365.cloud.microsoft - Sources & Skills → your plugin's skills should appear
- Start a new conversation that triggers the skill (use a prompt that matches the description)
If the skill does not appear, common causes:
- Folder name does not match the SKILL.md frontmatter
name agentSkillsentry inmanifest.jsondoes not match the skill folder- Plugin .zip has files inside a parent folder rather than at the root
- The Frontier program is not enabled for your tenant
- Your admin account is not enrolled in Frontier (Copilot → Settings → Frontier in the Admin Centre)
For automated deployment via API:
POST /users/titles
Content-Type: application/zip
Body: <your-package.zip>
Test the skill end to end with a real Cowork conversation before publishing or distributing internally. The skill description (in SKILL.md frontmatter) determines when Cowork loads the skill. If Cowork is not picking up your skill on a relevant prompt, the description is usually too vague. Add specific trigger phrases like "Use when the user asks for a meeting briefing".
The shortcut: convert an existing Claude Code plugin
If you already have a Claude Code plugin (with skills and optional MCP servers), Microsoft ships a PowerShell script that converts it to an M365 package automatically.
.\Convert-ClaudePluginToMOS3.ps1 -PluginPath .\my-claude-plugin -OutputPath .\output
The script reads:
.claude-plugin/plugin.json→ maps tomanifest.jsonskills/*/SKILL.md→ copied verbatim intoagentSkills.mcp.json→ maps MCP servers toagentConnectorscolor.pngandoutline.png→ used if present, generated as placeholders if not
Conversion takes about 5 minutes. The output is a valid M365 .zip ready to sideload or publish.
What does not convert (yet):
commands/: Claude Code slash commands are not yet supported in Coworkagents/: sub-agents are not yet supportedhooks/: event handlers are not yet supported
The script is at aka.ms/copilot-cowork-plugin-conversion-script.
Publish to the Microsoft 365 App Store
For public distribution, submit through Partner Center.
The submission flow is the same as for Teams apps and Office add-ins. You upload the .zip, complete the listing metadata (description, screenshots, support URLs, privacy policy, terms of use), and submit for Microsoft review. Approval typically takes 5 to 10 business days.
For internal use only, skip the App Store and stick with sideload via the Admin Centre. The .zip is the same; the distribution channel is different.
What good looks like for your first plugin
Three patterns I have seen work and three I have seen fail.
Works: A single SharePoint AI Skill, packaged as Skills-only, sideloaded for one team to use, iterated on for two weeks based on real usage. By week three the plugin is stable and ready for tenant-wide deployment.
Works: A Claude Code plugin that the team is already using locally, converted with the script, sideloaded into Cowork, available to non-developers in the team for the first time.
Works: Three small, focused plugins each doing one workflow well, distributed separately, rather than one large plugin doing everything. Smaller plugins are easier to update, test, and explain.
Fails: A plugin built before the underlying skill was tested in a real workflow. The packaging works but the skill produces vague output because the SKILL.md was written for the manifest, not for the user.
Fails: A plugin with connector AND custom skills AND new authentication, all built simultaneously. Too many moving parts. Build the connector separately first, then add skills on top.
Fails: Skipping the sideload test loop. Some teams package, submit to the App Store, then find out the skill description does not match how users actually phrase their requests. Sideload first, gather usage signals, then publish.
The companion explainer is Cowork plugins explained for the conceptual context. The strategic landscape is in the Agent Skills standard. For the broader Skills surface comparison, Cowork Skills and SharePoint AI Skills is the foundational read.