Call a Copilot Studio agent from Power Automate, including GitHub Copilot harness agents
September 14, 2026
Power Automate can’t consume a stream. The Copilot Studio agentic runtime only speaks one, and the supported Copilot Studio client library doesn’t cover agents built on the GitHub Copilot harness at all.
Copilot Studio Agent Runtime closes that gap. The connector opens a conversation with a published agent, reads the entire server-sent event stream, consolidates it, and returns one completed JSON answer. A flow gets a finished reply. A Copilot Studio agent gets a Model Context Protocol (MCP) tool it can call to delegate work to another agent.
A data plane connector, not another control plane one
The other Copilot Studio connectors in the repository manage agents. This one runs them.
| Connector | Plane | What it does |
|---|---|---|
| Copilot Studio Bots | Control | Evaluate, quarantine, inventory, migrate identity |
| Copilot Studio Analytics | Reporting | Read transcripts and session records after the fact |
| Copilot Studio Agent Runtime | Data | Open a conversation and get an answer |
The two pair well. List Agents in Copilot Studio Bots returns schemaName and isCLIAgent, which tell you which agent to call here and whether it runs on the GitHub Copilot harness.
The endpoint is undocumented
POST https://{environment-host}/copilotstudio/agenticruntime/3p/dataverse-backed/
authenticated/bots/{schemaName}/conversations?api-version=1
POST https://{environment-host}/copilotstudio/agenticruntime/3p/dataverse-backed/
authenticated/bots/{schemaName}/conversations/{conversationId}?api-version=1
This route doesn’t appear on Microsoft Learn and carries no compatibility guarantee. Microsoft’s position today is that the Copilot Studio client library “can only be used with agents created by using the standard harness,” and that “agents using the GitHub Copilot harness aren’t yet officially supported.” The /3p agentic runtime route is how Microsoft’s own experimental Copilot Studio plugin reaches those agents.
Treat it as experimental and keep a fallback.
The agenticruntime prefix and the 3p third-party segment separate this from the classic Direct-to-Engine route at /copilotstudio/dataverse-backed/ with api-version=2022-03-01-preview, which Power Agent Desktop and Power Agent Tray use.
What the agent and app registration need
The agent must be published, because draft agents don’t resolve. It must use Authenticate with Microsoft, which is what the /authenticated/ path segment means. And it must be shared with the signed-in user, or the call returns 403.
The app registration needs the Power Platform API delegated permission CopilotStudio.Copilots.Invoke with admin consent granted, plus a web redirect URI matching the connector’s generated consent URL.
Sign-in is delegated only. The /authenticated/ route rejects app-only client credential tokens and answers with S2SDirectEngineRequiresNoAuthentication. Service-to-service Direct-to-Engine exists, but it’s a private preview and applies only to No Authentication agents. Every connection here is a user sign-in.
Derive the environment host
The connector reads the host from the connection rather than hardcoding it. Take your environment GUID, strip the hyphens, and split after the 30th character:
$environmentId = "0a1b2c3d-4e5f-6071-8293-a4b5c6d7e809"
$id = $environmentId.Replace("-", "").ToLower()
"$($id.Substring(0, 30)).$($id.Substring(30)).environment.api.powerplatform.com"
# 0a1b2c3d4e5f60718293a4b5c6d7e8.09.environment.api.powerplatform.com
Enter the hostname only, with no https:// and no trailing slash. Non-production clouds use api.test., api.preprod., or api.dev. suffixes and work the same way. Sovereign clouds aren’t verified — the agentic runtime may not be deployed there.
The schema name looks like cr1a2_myAgent. Read it from the agent’s Settings page, from the direct connection URL in channel settings, or from List Agents in Copilot Studio Bots.
Four REST actions, three MCP tools
| Operation | Description |
|---|---|
| Ask Agent | Opens a conversation, sends a message, returns the finished answer |
| Start Conversation | Opens a conversation and returns its ID plus any greeting |
| Send Message | Sends a turn to an existing conversation and returns the finished reply |
| Invoke Copilot Studio Agent Runtime MCP | The JSON-RPC endpoint — it shows in the action list, but skip it in a flow |
Use Ask Agent for stateless work. Use Start Conversation followed by Send Message when the agent needs to remember earlier turns. Both message operations accept an optional BCP 47 locale such as en-GB.
The MCP tools mirror the same three operations:
| Tool | Arguments |
|---|---|
ask_agent |
schemaName*, text*, locale |
start_conversation |
schemaName*, emitStartConversationEvent |
send_message |
schemaName*, conversationId*, text*, locale |
* required
emitStartConversationEvent defaults to true. Set it to false to suppress the greeting.
This is what makes agent-to-agent invocation work. A Copilot Studio agent can call a GitHub Copilot harness agent as a tool, which no supported channel currently offers.
Consolidating the stream takes two rules
The runtime replies with text/event-stream. Power Platform can’t surface a stream to a flow, so the connector reads the whole thing and consolidates it.
Naive concatenation breaks, because the runtime streams in two shapes depending on the agent and client:
| Shape | What arrives | How it’s handled |
|---|---|---|
| Cumulative | Each typing activity repeats everything so far |
Keep only the last one |
| Delta | Each typing activity carries a new fragment |
Concatenate in order |
| Final | A message activity with streamType: "final" |
Authoritative — wins over both |
The connector detects the shape by testing whether each chunk begins with the previous one. Guessing wrong is visible in the output either way: treating cumulative text as deltas repeats the answer several times over, and treating deltas as cumulative truncates it to the last fragment.
The response keeps both the consolidated text and the raw activities:
{
"conversationId": "abc123",
"text": "Paris is the capital of France.",
"isComplete": true,
"activityCount": 4,
"attachments": [],
"suggestedActions": [ { "type": "imBack", "title": "Tell me more", "value": "more" } ],
"citations": [ { "@type": "Claim", "position": 1, "appearance": { "name": "France", "url": "https://example.com" } } ],
"activities": [ "..." ]
}
Read text in almost every flow. Check isComplete when correctness matters — false means the runtime never signaled turn completion, so text is partial. activityCount covers this turn only, not the whole conversation. Ask Agent also returns greetingActivities separately, so the agent’s opening line never pollutes text.
Send a raw activity to trigger a topic
Send Message exposes a Raw Activity field for anything that isn’t a plain user message. An event or invoke activity can trigger a topic directly:
{
"activity": {
"type": "event",
"name": "StartOrderLookup",
"value": { "orderId": "SO-4417" }
}
}
Message is ignored when Raw Activity is present. The connector always overwrites the conversation property with the conversation in the request path, so a caller can’t post into a conversation it doesn’t own.
Errors name the thing to check
The runtime returns bare status codes for common misconfigurations, so the connector translates each one:
{
"status": 403,
"message": "Forbidden. Share the agent with the signed-in user, and confirm admin consent was granted for CopilotStudio.Copilots.Invoke. This endpoint rejects app-only (service principal) tokens: the connection must be a delegated user sign-in.",
"details": "<raw response body from the runtime>"
}
| Status | Meaning |
|---|---|
400 |
Missing schemaName, conversationId, or message text — raised before any call is made |
401 |
Wrong token audience, or the app registration is missing CopilotStudio.Copilots.Invoke |
403 |
Agent not shared, admin consent not granted, or an app-only token was used |
404 |
Wrong host or schema name, agent not published, or not served by the agentic runtime |
408 / 504 |
The turn didn’t finish in time |
429 |
Throttled — retry after a delay |
MCP callers get the same payload as the tool result with isError: true, so the calling agent reads the explanation instead of seeing an opaque failure.
When a call fails, confirm each layer in order. It isolates the problem faster than reading the error alone:
- Host — does it match the
{30}.{2}split of your environment GUID? A wrong host gives404, not a connection error. - Schema name — copy it from
List Agentsexactly. It’s case-sensitive. - Published — republish if in doubt. A draft-only agent gives
404. - Sharing — confirm the signed-in user is on the agent’s share list.
- Permission — check
CopilotStudio.Copilots.Invokehas admin consent.
A 401 is almost always the app registration rather than the agent.
Know the limits before you build on it
Turn length is the hard one. Power Platform cuts a connector request off at roughly 120 seconds. Agentic loop turns routinely run longer, and this endpoint has no polling or resume route, so a long turn is simply lost. Keep prompts tightly scoped and don’t build deep research tasks on it.
The rest:
- No streaming to the caller. The answer arrives all at once when the turn completes. Progressive rendering isn’t possible through a custom connector.
- Delegated identity only. Every call runs as the signed-in user. There’s no unattended option.
- Unversioned in practice.
api-version=1is pinned because it’s what the route accepts today. - Cost. Agents on the GitHub Copilot harness bill 100–500+ Copilot Credits per task regardless of Microsoft 365 Copilot licensing. Calling one in a loop from a flow gets expensive fast. Check the harness with
List Agentsbefore wiring an agent into automation.
Deploy the connector
pac auth create --environment <environment-id>
pac connector create `
--api-definition-file apiDefinition.swagger.json `
--api-properties-file apiProperties.json `
--script-file script.csx
Replace [YOUR_CLIENT_ID] in apiProperties.json with your app registration’s client ID and set the client secret in the portal Security tab. Then read back the generated redirect URL and register it:
pac connector download --connector-id <id> --outputDirectory ./verify
# properties.connectionParameters.token.oAuthSettings.redirectUrl
az ad app update --id <appId> --web-redirect-uris `
"https://global.consent.azure-apim.net/redirect" "<per-connector-url>"
Each environment generates a different redirect URL. Consent fails if only the generic one is registered.
Validate before deploying:
npx ppcv "./Copilot Studio Agent Runtime"
Finally, create a connection and supply the environment host derived above.
Three ways to use it
Ask an agent from a flow
Use Ask Agent when each call is independent.
| Field | Value |
|---|---|
| Agent Schema Name | cr1a2_contractReviewer |
| Message | Summarize the risks in contract @{triggerBody()?['contractId']} |
Read text from the output. You don’t need to touch activities.
Hold a multi-turn conversation
When later turns depend on earlier ones, keep the conversation open:
- Start Conversation, then store
conversationIdin a variable. - Send Message with that
conversationId, then readtext. - Repeat step 2 for each follow-up. The agent retains context across turns.
Don’t call Ask Agent in a loop for this. Each call opens a fresh conversation and the agent forgets everything.
Let one agent call another
This is the scenario the MCP endpoint exists for, and the only way to reach a GitHub Copilot harness agent as a tool:
- In Copilot Studio, open the calling agent and go to Tools > Add a tool > Model Context Protocol.
- Select this connector and create or pick a connection.
- Add
ask_agentfor one-shot delegation. -
In the calling agent’s instructions, say when to delegate and which
schemaNameto pass. The model won’t know the schema name otherwise:When the user asks a contract question, call
ask_agentwithschemaNameset tocr1a2_contractReviewerand the user’s question astext.
Add start_conversation and send_message only when the calling agent genuinely needs a multi-turn side conversation. For most delegation, ask_agent alone produces better behavior.