The original account below follows our August 12–13 experiments. The September update covers Microsoft’s subsequent changes and our later live GitHub connector tests.
Editor’s note, 14 September 2026: Microsoft’s product name is Microsoft 365 Copilot Cowork, which the rest of this site shortens to Copilot Cowork. This field note keeps the shorter name we used at the time, Microsoft 365 Cowork.
The AI community has collectively decided that MCP servers are the best way for agents to use remote tools. (...right?)
Once every agent client speaks MCP, the next problem is that every client still packages MCP differently. Agent Plugins is a new attempt to make that layer portable: one package for Agent Skills and MCP servers, backed by maintainers from Amazon, Cursor, Microsoft, OpenAI, and Vercel.
We wanted to use it with Microsoft 365 Copilot Cowork. Cowork uses its own plugin format which, at least in principle, mirrors an agent plugin. At the time, Microsoft’s docs pointed us to a PowerShell script that converts Claude Code plugins into Cowork packages, so we thought we’d start there.
But a specification is different than a standard, as we learned.
A converter should be boring
An Agent Plugin is a small directory:
plugin.json
skills/
mcp.json
Cowork asks for the same basic ingredients inside a strict Microsoft 365 app manifest. A converter should preserve the skills and MCP endpoints, write an acceptable zip, and disappear from the story.
And Microsoft had already written 95% of what we needed.
Microsoft’s converter broke three different ways
We downloaded Microsoft’s documented Convert-ClaudePluginToMOS3.ps1 on 12 August 2026 and gave it one skill, one HTTPS MCP server, and ordinary author metadata.
It failed before producing a zip. Supplying source icons got us a package. Microsoft’s validator then failed twice more:
| Attempt | What the official script did | Result |
|---|---|---|
| 1 | Tried to generate a missing outline.png with System.Drawing. | Crashed inside Windows.Win32.PInvokeGdiPlus on PowerShell 7/macOS. |
| 2 | Wrote manifest.json with a UTF-8 byte-order mark. | ATK passed the bytes to JSON.parse, which rejected its first character. |
| 3 | Added developer.contactInfo when the plugin author had an email. | ATK rejected the object shape during manifest conversion. |
It also warned that the generated packageName field is deprecated.
Three failures across three different layers: packaging, parsing, schema. We filed a reproducible issue with the exact script hash and output.
No worries, we’ll whip one up from scratch. Conceptually, all we’re doing is copying folders and writing a manifest. And, hey, maybe we can share it with the community if it adds value.
The plan
Agent Plugins v1.0.0 gave us a cleaner input contract. Most of it mapped without drama:
| Agent Plugin input | Cowork result |
|---|---|
| Name, version, description, author | ✅ Microsoft 365 manifest metadata. |
skills/<name>/SKILL.md | ✅ Cowork Agent Skills. |
Headerless HTTPS streamable-http server | 👌 Remote Cowork MCP connector. |
stdio or legacy HTTP+SSE server | 👍 No supported target. |
| Literal HTTP headers | 🤨 No faithful Cowork field. |
Our rule was simple: translate only what keeps the same meaning. Everything else gets flagged.
Two specs, one hour, we thought.
The package was portable. Authentication wasn’t.
Agent Plugins says literal headers are visible package data and must not contain secrets. It is equally explicit about the larger boundary:
Agent Plugins v1 defines no OAuth configuration or portable credential-reference fields. Authorization discovery, user interaction, and credential storage are client-managed.
At this point, I’m starting to wonder which MCP servers will actually fit all these criteria and/or how we suddenly need a lot of user input to faithfully map a single server.
Because on the one hand, leaving auth out of a public plugin spec is justifiable. On the other, it means a converter cannot tell whether a server is public, supports MCP OAuth discovery, needs an API key, or requires a pre-registered OAuth client, and it means there will necessarily be more specs to follow 😅
And Cowork had its own auth choices to understand: None, OAuthPluginVault, or Dynamic Client Registration. The manifest also lists ApiKeyPluginVault, but API-key authentication is not supported for MCP connectors in Cowork. For vault-backed OAuth, referenceId points to a registration in Microsoft’s Enterprise Token Store. Writing an ID into a manifest does not create that registration.
The PowerShell converter, it turns out, guessed here when converting from Claude plugins. It took any external HTTPS endpoint and turned it into an OAuthPluginVault, handing back a plausible-looking referenceId whether or not the server could be registered.
It compiled, but didn’t work.
So we started building our own converter, if it still deserves that name. It grew into a local CLI experiment for converting and creating plugins, generating skills, and helping fill in the auth blanks. We never finished or released it.
Then Microsoft replied
The next morning, an ATK maintainer politely pointed out that the toolkit already had a related command, atk import openplugin, and that their team did not maintain the PowerShell script. They also pointed us to PR #16540 for the open Agent Plugins v1 work.
Our bad. Microsoft’s docs sent us to one converter, and we’d filed its bugs against the team that owned another.
So we gave ATK 1.1.15 the exact same fixture. It generated cross-platform placeholder icons. It wrote BOM-free JSON. It omitted the invalid contactInfo. Then atk package produced a zip and atk validate reported 57 passes and one harmless description warning.
The maintained converter worked for packaging.
Then we opened the connector it generated. Its default Auto mode had turned Microsoft’s public Learn MCP endpoint into OAuthPluginVault and generated repro-plugin-public-docs-auth as the referenceId. The package was valid. ATK had not created that registration, and the endpoint did not need one.
Passing --default-auth-type None fixed this server. It would be the wrong answer for an authenticated one. And a single global default still cannot faithfully describe a plugin containing both.
The Agent Plugins v1 PR snapshot we checked on August 13 kept the same heuristic. So we thanked the maintainer, closed our original issue, and opened a focused follow-up for unresolved auth.
The first bug was in a converter owned somewhere else. The deeper problem had reproduced itself in both.
The missing contract
Agent Plugins does not need to standardize credential storage. MCP already defines OAuth discovery and Dynamic Client Registration. The missing piece we hit was a way for a converter to make non-secret authentication requirements visible when it cannot preserve or resolve them for the target host.
We opened an Agent Plugins v2 discussion for that missing contract, originally filed as issue #55. The answer could be a tiny requirements declaration or capability negotiation. The important part is making the unresolved requirement visible without pretending the credential itself is portable.
The package moved. The authority was guessed.
Oh well. Back to wiring it up by hand until either side can describe the decision without making it for us.
Update — September 10, 2026
Microsoft has since merged endpoint-aware auth resolution. That change, merged August 21, checks the MCP endpoint and OAuth metadata before choosing an auth mode; a confirmed public endpoint can resolve to None. The Agent Plugins v1 import/export work merged on August 31. Microsoft’s build guide now recommends the cross-platform ATK importer and labels the PowerShell converter as legacy. Those are useful improvements to the path we tested.
Our GitHub connector experiments later that August included a successful Cowork sideload and authenticated tools/list calls to the remote GitHub MCP server. OAuth still gave us plenty to untangle. Our local CLI’s vault-registration path assumed Dynamic Client Registration, but GitHub’s OAuth App flow did not expose the registration_endpoint it needed. A minimal ATK oauth/register project could create Microsoft’s auth configuration; our local tool-export script still needed its own GitHub bearer token.
We also prototyped an Entra-backed relay: OAuth for Cowork’s connection, with GitHub credentials handled upstream. Direct API-key authentication for MCP connectors remains unsupported in Cowork. “Supports OAuth” turned out to be the beginning of the conversation.
Sources
- 01Agent Plugins
Project overview and the portable package’s stated purpose.
- 02Agent Plugins v1.0.0 specification
Canonical package fields, visible-header rule, and client-managed authorization boundary.
- 03Agent Plugins launch announcement
OpenAI’s announcement of the shared package format and participating companies.
- 04Agent Plugins maintainers
Current project maintainers and affiliations.
- 05Build plugins for Copilot Cowork
Microsoft’s documented Cowork package path and link to its converter.
- 06Microsoft Claude-to-Cowork conversion script
The exact script exercised in this test.
- 07ATK Agent Plugins v1 import/export pull request
The Agent Plugins v1 import/export update, merged August 31.
- 08Microsoft Teams manifest v1.28 schema
The strict target manifest contract used by Cowork packages.
- 09MCP authorization specification
Protocol-level authorization discovery and flow.
- 10ATK endpoint-aware auth resolution
The later importer change that checks endpoint and OAuth metadata.
- 11Agent Plugins auth requirements discussion
The proposal originally filed as issue #55.
- 12GitHub OAuth app authorization
GitHub’s registered-client authorization and token flows.