Hello Techies, Copilot Studio ships changes every wave. The architecture and decision logic below are stable, but verify UI paths, transport support and preview labels against the current documentation before you rely on them.

In the world of low-code agents, a "tool" is anything the agent can call to reach outside its own reasoning. In Copilot Studio you have two ways to register one: build a custom connector , or connect an MCP server .

Most teams pick one and standardise on it. That is the mistake.

The answer here is the same "No Free Lunch" answer we hit in retrieval. No single integration method is correct for every API in an enterprise. The two mechanisms have genuinely different failure modes:

By deliberately splitting your tool inventory across both, based on who owns the API and how fast it changes , you get a tool layer that is stable where it needs to be stable and fast-moving where it needs to be fast-moving.

In this end-to-end guide we will build that hybrid tool layer for a real-shaped enterprise scenario.

The Real-World Use Case: Meridian Power's "Field Ops Copilot"

Imagine you are building an agent for Meridian Power, a utility with 4,000 field engineers. The agent runs in Teams and on mobile. Engineers ask it things like "what is the health score on transformer TX-4471 and do I have leave approved for Thursday?"

To answer, the agent needs to reach three completely different backends:

BackendOwnerChange frequencyAlso needed in Power Automate?
SAP HR (leave, shifts, certifications)Central SAP team, change board approvalTwice a yearYes, three existing flows use it
Asset Intelligence PlatformInternal data science teamNew tools most sprintsNo, agent-only
ServiceNow ticketingVendor-managed, ships its own MCP serverVendor's cadenceNo

The Failure of Standardising on One Method

If you build custom connectors for everything: the Asset Intelligence team ships a new predict_failure_window tool in sprint 14. It sits unused for three weeks because you are the bottleneck, editing an OpenAPI definition for a system you do not own. By sprint 20 you are maintaining eleven connector versions and your backlog is entirely other teams' changes.

If you use MCP for everything: you write an MCP server in front of SAP HR. Now the same SAP logic exists twice, once in your MCP server and once in the connector that three Power Automate flows already depend on. Two codebases, two auth configurations, two places for the leave-balance calculation to drift. And you cannot use your MCP server from a Power App.

The Hybrid Solution

We assign each backend by ownership and reuse, not by preference:

  1. SAP HR → custom connector. Stable, needed outside the agent, and we need to reshape a forty-field response down to four.

  2. Asset Intelligence → MCP server. Owned by another team, changes weekly, agent-only. Dynamic discovery is a feature here, not a risk.

  3. ServiceNow → vendor MCP server. Never hand-roll a connector for something a vendor maintains.

Technology Stack

ComponentTechnologyRole in the hybrid tool layer
OrchestrationCopilot Studio (generative orchestration)Selects which tool to call per turn
Fast-moving toolsPython MCP SDK, streamable HTTPSelf-describing tool surface owned by the data science team
Stable toolsPower Platform custom connectorReshaped, versioned contract over SAP HR
Vendor toolsServiceNow MCP serverVendor-maintained, no build effort
IdentityMicrosoft Entra ID, OAuth 2.0User-delegated calls so row-level security actually applies
GovernancePower Platform DLP, connector classificationBoth paths land as connections an admin can see and block
ALMSolutions and connection referencesMoves the whole tool layer between environments

End-to-End Implementation

Step 0: Build the Tool Inventory First

Before you open Copilot Studio, fill this table in. Every column has a decision attached to it.

ToolOwnerCadenceReuse outside agentNeeds reshapingVerdict
get_leave_balanceSAP teamLowYesYes, 40 fields to 4Connector
get_shift_rosterSAP teamLowYesYesConnector
get_asset_healthData scienceHighNoNoMCP
predict_failure_windowData scienceHighNoNoMCP
create_incidentVendorVendorNoNoVendor MCP

If you skip this step, you will make the decision per tool, in a hurry, based on whichever blog post someone read last week.

Step 1: Build the MCP Server (Fast-Moving Tools)

The data science team owns this file. Copilot Studio talks to remote servers, so a local stdio server will not work. Use streamable HTTP.

python

  
    # asset_intelligence_server.pyfrom typing import Annotated
from pydantic import Field
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Meridian Asset Intelligence", stateless_http=True)@mcp.tool()def get_asset_health(    asset_id: Annotated[str, Field(description="Asset tag, for example TX-4471")]) -> dict:    """Returns the current health score and last inspection date for a single    grid asset. Use this when an engineer asks about the condition, health,    or reliability of a specific transformer, breaker or feeder."""    return {        "asset_id": asset_id,        "health_score": 62,        "band": "degraded",        "last_inspection": "2026-06-14",        "open_defects": 2,    }@mcp.tool()def predict_failure_window(    asset_id: Annotated[str, Field(description="Asset tag, for example TX-4471")],    horizon_days: Annotated[int, Field(description="Forecast horizon, 30 to 365")] = 90,) -> dict:    """Returns the predicted failure probability for an asset over a horizon.    Use this only for forward-looking risk questions, not for current condition."""    return {        "asset_id": asset_id,        "horizon_days": horizon_days,        "failure_probability": 0.18,        "primary_driver": "oil_temperature_trend",    }if __name__ == "__main__":    mcp.run(transport="streamable-http")
  

Two things in that code matter more than the logic:

The docstrings are production code. They are what the orchestrator reads when it decides whether to call this tool. Notice predict_failure_window explicitly says "not for current condition". Without that line, the model will confuse the two tools maybe one time in five.

The return payloads are small. Six fields, not sixty. The data science team's internal API returns far more. They trim it at the MCP boundary.

Step 2: Register the MCP Server in Copilot Studio

Copilot Studio registers MCP servers through the Power Platform connector infrastructure. That is why your admin can still govern them. The declaration is an OpenAPI file that declares an agentic protocol instead of listing REST actions:

yaml

  
    swagger: '2.0'info:  title: Meridian Asset Intelligence  description: >    Grid asset health scores and failure predictions for field engineers.    Covers transformers, breakers and feeders.  version: 1.0.0host: mcp.meridian-internal.combasePath: /schemes:  - httpspaths:  /mcp:    post:      summary: Meridian Asset Intelligence      operationId: InvokeMCP      x-ms-agentic-protocol: mcp-streamable-1.0      responses:        '200':          description: SuccesssecurityDefinitions:  oauth2-auth:    type: oauth2    flow: accessCode    authorizationUrl: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/authorize    tokenUrl: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token    scopes: {}security:  - oauth2-auth: []
  

Then in the maker experience:

  1. Open your agent in Copilot Studio .

  2. Go to Tools , then Add a tool , then Model Context Protocol .

  3. Enter the server name, description and HTTPS URL. Choose OAuth 2.0 and point it at your Entra app registration.

  4. Select Create , then create the connection . This connection is the object DLP will evaluate.

  5. Copilot Studio now lists the tools the server advertises. Disable every tool the agent does not need. Every enabled tool consumes context and gives the orchestrator another way to choose wrong.

  6. Add the agent and its connection references to a solution so the registration survives the move to production.

Step 3: Build the Custom Connector (Stable Tools)

For SAP HR you want the opposite properties: a frozen contract you control, and a response shaped for a model rather than for a developer.

yaml

  
    swagger: '2.0'info:  title: Meridian HR Services  description: Leave balances and shift rosters for field engineers.  version: 1.0.0host: hr-api.meridian.combasePath: /v2schemes:  - httpspaths:  /employees/{employeeId}/leave-balance:    get:      summary: Get leave balance      description: >        Returns remaining annual and sick leave days for an employee for the        current calendar year. Use when an engineer asks how much leave they        have left, or whether they can take a specific day off.      operationId: GetLeaveBalance      parameters:        - name: employeeId          in: path          required: true          type: string          description: Employee number, for example 100472      responses:        '200':          description: Success          schema:            type: object            properties:              annual_days_remaining:                type: number                description: Remaining annual leave days this year              sick_days_remaining:                type: number                description: Remaining paid sick leave days this year              pending_requests:                type: integer                description: Count of leave requests awaiting approval              policy_year_end:                type: string                description: Date the current leave year closes
  

Note that the underlying SAP response has around forty fields. We expose four. This is the highest-value hour of work in the whole build. An agent handed an untyped forty-field blob will hallucinate against it, and the failure looks like a model problem when it is a schema problem.

Then:

  1. Go to make.powerapps.com , select the environment, open Custom connectors .

  2. Choose New custom connector , then Import an OpenAPI file .

  3. On the Security tab configure OAuth 2.0 with Entra ID. Copy the generated redirect URL into your app registration.

  4. On the Definition tab confirm each operation has a clear summary, description and stable operation ID.

  5. Select Create connector , then test on the Test tab with a live connection before touching the agent.

  6. In Copilot Studio , go to Tools , Add a tool , pick your connector and select only the actions you need.

  7. For each action, review the tool description and every input parameter inside Copilot Studio. You can override the connector text here, and you usually should. Then set explicitly how each input is filled: inferred by the model, fixed value, or ask the user.

Step 4: Add the Vendor MCP Server

Same flow as Step 2, using ServiceNow's published endpoint and their auth guidance. Zero build effort. If a vendor ships an MCP server, use it.

Running the System

Query: "Transformer TX-4471 is running hot. What's its condition, and can I take Thursday off to deal with the follow-up?"

  
    🧭 [Orchestrator] Query requires 2 capabilities: asset condition, leave availability.

🔧 [MCP: Asset Intelligence] get_asset_health(asset_id="TX-4471")
   → health_score: 62, band: "degraded", open_defects: 2
   ✓ Selected over predict_failure_window (query is present-tense condition)

🔧 [Connector: Meridian HR] GetLeaveBalance(employeeId="100472")
   → annual_days_remaining: 11, pending_requests: 0
   ✓ employeeId resolved from authenticated user identity, not model inference

✍️ [Synthesizer] Grounding answer in 2 tool results.
  

Final response:

TX-4471 is currently in the degraded band with a health score of 62 and 2 open defects, last inspected 14 June 2026. You have 11 annual leave days remaining and no pending requests, so Thursday is available to request. Want me to raise an incident for the temperature reading?

Look at the second tool call. employeeId came from the authenticated identity, not from the model guessing. If you let the orchestrator infer that parameter, an engineer can ask about someone else's leave balance and the platform will happily comply. This is the most common security hole I see in Copilot Studio agents, and it has nothing to do with which integration method you picked.

hybrid-tool-layer-sketch-2x

Why This Specific Split?

SAP HR as a connector. Three Power Automate flows already consume this API. MCP is an agent-facing surface, so wrapping SAP in MCP would mean maintaining the same logic twice. The API also changes twice a year, which means a frozen design-time contract costs us almost nothing. And we needed heavy response reshaping, which is exactly what connectors are good at.

Asset Intelligence as MCP. A different team owns it and ships most sprints. With a connector, every one of their releases becomes a ticket in our backlog. With MCP, predict_failure_window appeared in the agent's tool list on the day they shipped it, and we only had to review and enable it. The trade-off is real: their Friday release can change our Monday behaviour. We handled that with process, not technology. They notify us in a shared channel before adding or renaming a tool, and we review descriptions in their PR.

ServiceNow as vendor MCP. Never rebuild what a vendor maintains for you.

Why both paths are governable. Because MCP registers through connector infrastructure, DLP policies, connector classification and environment strategy apply to it exactly as they apply to custom connectors. Raise this with your security team in the first design session. It converts "MCP is shadow IT" into a normal policy conversation, which is a much shorter meeting.

The Decision Rule, Compressed

Work down this list. First clear answer wins.

  1. Does a vendor already ship an MCP server for this system? Use it.

  2. Is this capability also needed in Power Apps or Power Automate? Custom connector.

  3. Does the API need heavy reshaping, or is it legacy and badly shaped? Custom connector.

  4. Is the backend on-premises with no public HTTPS endpoint? Custom connector, and involve networking early. This is where most MCP proofs of concept stall.

  5. Does another team own it and ship changes faster than monthly? MCP server.

  6. More than roughly fifteen or twenty candidate tools total? Neither method saves you. Split into multiple focused agents, because orchestration accuracy degrades with tool count regardless of how the tools got there.

Gotchas Worth Knowing Before You Start

Conclusion

In enterprise agent work, "we standardised on MCP" and "we standardised on connectors" are both architecture smells. They optimise for tidiness in a design document rather than for how the organisation actually ships software.

The useful question is never which protocol is better. It is: who owns this API, how fast does it change, and who else needs it? Answer that per tool, fill in the inventory table, and the split writes itself. For Meridian Power the result was a tool layer where the SAP contract has not moved in a year while the asset intelligence tools shipped eleven times, and the agent team was not the bottleneck for either.