>_ The Manifest

One Protocol to Rule Them All

MCP (the Model Context Protocol) lets a tool server work with Microsoft 365 Copilot, Claude, ChatGPT, VS Code, and any future MCP-compatible client. No vendor lock-in, no rewriting endpoints for every platform.

In this post we’ll take an existing MCP server, register it as a plugin in a declarative agent with OAuth authentication, and let Copilot orchestrate it alongside knowledge sources.

What Is MCP?

MCP is an open standard that defines how AI applications connect to external tools and data sources. Think of it as a contract: your server says “here are the tools I offer, here are the parameters they accept, here’s what they return”, and any MCP-compatible client knows exactly how to call them.

An MCP server exposes three kinds of capabilities:

  • Tools: Actions the agent can invoke (create a ticket, send a message, run a query)
  • Resources: Data the agent can read (configuration, reference docs, live state)
  • Prompts: Reusable prompt templates the agent can leverage

For declarative agents in M365 Copilot, we mostly care about tools. They’re how your agent does things in the real world, and they come with typed parameters, validation, and structured results baked right in.

📝 Note

MCP was created by Anthropic and has since been adopted across the industry. The protocol spec and SDKs are open source, which means you’re not betting on any single vendor when you invest in MCP.

Why MCP Over API Plugins?

Both API plugins and MCP servers let your agent call external services, but they serve different purposes:

API Plugins are built on OpenAPI specs. They’re deeply integrated into the Microsoft 365 ecosystem with features like adaptive card rendering, built-in OAuth flows, and rich response semantics. If you already have a REST API with an OpenAPI spec, wrapping it as an API plugin is the path of least resistance.

MCP Servers give you portability. The same server you connect to Copilot also works with Claude, ChatGPT, VS Code, and every other MCP client. You get a standardized tool protocol that works across the entire AI ecosystem, and Copilot handles the integration through its plugin system.

CriteriaAPI PluginMCP Server
ProtocolREST + OpenAPIModel Context Protocol
PortabilityCopilot-specificWorks with any MCP client
Best forWell-defined REST APIsAction-heavy tool integrations
AuthOAuth, API key (built-in)OAuth (via plugin manifest)
RenderingAdaptive cards supportText responses
EcosystemMicrosoft 365 nativeCross-platform open standard

My rule of thumb: if you have an existing REST API, use an API plugin. If you have an MCP server (or want tooling that works beyond Copilot), integrate it as an MCP plugin. Most production agents use a mix of both, and that’s exactly what we’re doing with our Zava Insurance scenario.

💡 Tip

You don’t have to choose one or the other. Declarative agents can register both API plugins and MCP servers in the same manifest. Use each where it shines.

Integrating an MCP Server as a Plugin

Let’s integrate a helpdesk MCP server for Zava Insurance, a fictional company whose HR team wants agents to create and track support tickets. We’ll assume the MCP server is already built and deployed (it exposes tools like createTicket and getTicketStatus over Streamable HTTP). Our job is to wire it into a declarative agent.

Create the Plugin Manifest

Since this MCP server handles employee data, we need OAuth authentication. Create an mcpPlugin.json in your appPackage/ folder with the OAuth configuration:

{
  "schema_version": "v2.4",
  "name_for_human": "Helpdesk",
  "namespace": "ZavaHelpdesk",
  "description_for_human": "Create and track IT support tickets for Zava Insurance.",
  "runtimes": [
    {
      "type": "RemoteMCPServer",
      "auth": {
        "type": "OAuthPluginVault",
        "reference_id": "${{OAUTH2AUTHCODE_CONFIGURATION_ID}}"
      },
      "spec": {
        "url": "https://zava-helpdesk-mcp.azurewebsites.net/mcp",
        "mcp_tool_description": {
          "file": "mcp-tools.json"
        }
      }
    }
  ]
}

The runtime type RemoteMCPServer tells Copilot this action points to an MCP server rather than a REST API. The auth block uses OAuthPluginVault with a reference_id that points to the OAuth client registration, exactly the same pattern as API plugins. Copilot handles the full OAuth 2.1 flow, consent prompts, and token injection for you.

The mcp_tool_description property references an mcp-tools.json file in your app package. This file contains the tool definitions that match the format returned by the MCP server’s tools/list method. You can grab this by calling your MCP server’s tools/list endpoint and saving the result:

[
  {
    "name": "createTicket",
    "description": "Create a new support ticket for an employee",
    "inputSchema": {
      "type": "object",
      "properties": {
        "title": {
          "type": "string",
          "description": "Short summary of the issue"
        },
        "description": {
          "type": "string",
          "description": "Detailed description of the problem"
        },
        "priority": {
          "type": "string",
          "enum": ["low", "medium", "high"],
          "description": "Ticket priority level"
        }
      },
      "required": ["title", "description", "priority"]
    }
  },
  {
    "name": "getTicketStatus",
    "description": "Look up the current status of a support ticket",
    "inputSchema": {
      "type": "object",
      "properties": {
        "ticketId": {
          "type": "string",
          "description": "The ticket ID to look up (e.g., HR-4127)"
        }
      },
      "required": ["ticketId"]
    }
  }
]

Copilot uses these tool descriptions to decide when to invoke each tool, so make your description fields count.

Automate the OAuth Registration

You don’t need to register OAuth credentials manually in the Teams Developer Portal. The M365 Agents Toolkit automates this through your m365agents.yml configuration file:

provision:
  - uses: oauth/register
    with:
      name: oAuth2AuthCode
      flow: authorizationCode
      appId: ${{TEAMS_APP_ID}}
      clientId: ${{AAD_APP_CLIENT_ID}}
      clientSecret: ${{SECRET_AAD_APP_CLIENT_SECRET}}
    writeToEnvironmentFile:
      configurationId: OAUTH2AUTHCODE_CONFIGURATION_ID

During provisioning, the toolkit registers the OAuth client in the Developer Portal and writes the resulting configuration ID back as OAUTH2AUTHCODE_CONFIGURATION_ID. Any ${{OAUTH2AUTHCODE_CONFIGURATION_ID}} placeholder in your plugin manifest resolves automatically, so you never have to copy-paste registration IDs by hand.

Reference the Plugin in Your Agent

Add the plugin to your declarativeAgent.json actions array:

"actions": [
  {
    "id": "helpdeskMcpServer",
    "file": "mcpPlugin.json"
  }
]
💡 Tip

Place mcpPlugin.json in your appPackage/ folder alongside declarativeAgent.json. The file path is relative to the manifest location.

That’s it. No server code to write, no middleware to configure. You’ve connected an existing MCP server to your declarative agent with proper OAuth authentication.

The Zava Insurance Helpdesk in Action

Here’s how it plays out:

HR Onboarding Buddy

I need help with my badge access.

Sorry to hear that! Could you give me more details: is your badge not working at all, or is it missing access to a specific building?

It won’t scan at Building 25. I’ve tried multiple doors.

Ticket #HR-4127 created

  • Issue: Badge not scanning at Building 25
  • Priority: Medium
  • Status: Open

The facilities team typically responds within 4 hours. Would you like to check on this later?

Later that day, the employee asks:

HR Onboarding Buddy

What’s the status of my badge ticket?

Ticket #HR-4127 is currently In Progress. The facilities team picked it up this morning and estimates resolution by end of day. No action needed from you at this time.

No portal-hopping, no searching through email threads.

In the same conversation, the employee can ask “What does the remote work policy say about working from home while my badge is being fixed?”, and the agent switches from the MCP server to the embedded knowledge sources, answering with a citation from the Employee Handbook. Zero routing logic on our part. The declarative agent orchestrates between tools and knowledge automatically.

The Value of MCP Plugins

Integrating MCP servers as plugins gives you concrete advantages:

  • Portability: Your Zava Insurance helpdesk server works with Copilot today, Claude tomorrow, and whatever comes next. Build once, connect everywhere.
  • Ecosystem access: The MCP ecosystem is growing fast. Hundreds of servers already exist for databases, APIs, developer tools, and enterprise systems. You can integrate them into your agents without building anything from scratch.
  • Composability: Agents can mix MCP tools, API plugins, and knowledge sources in a single conversation. The declarative agent orchestrates across all of them automatically.
  • Standardized auth: Copilot handles the full OAuth 2.1 flow with PKCE. Your MCP server receives a validated bearer token on every request, just like any other protected API.

Resources

Have questions or want to share what you're building? Connect with me on LinkedIn or check out more on The Manifest.