Agent Integration
Power Platform ToolBox can expose selected tools to AI assistants through its built-in MCP server. As a tool developer, you can decide whether your tool is discoverable by assistants, what input it accepts, what result it returns, and whether it supports only interactive runs or both interactive and automated runs.
Overview
Agent integration builds on the same invocation model used for inter-tool workflows, but the caller is an external assistant instead of another PPTB tool.
At a high level, you need to do three things:
- Declare an invocation contract.
- Mark the tool as invokable by agents.
- Add an automated runtime if you want the tool to work without opening its UI.
MCP Contract
There are two distinct layers in the contract:
| Layer | Purpose |
|---|---|
invocation | Describes the input payload the caller can send and the structured result the tool may return |
agents | Declares that the tool is available to assistants and how it should behave when called through MCP |
The same prefill and returnTopic shapes used by PPTB invocation are also what assistants rely on for predictable tool calls.
Interactive and Automated Runs
These docs use end-user language for the two execution styles:
| User-facing term | MCP runtime value | Meaning |
|---|---|---|
| Interactive tool run | executionMode: "windowed" | PPTB opens the tool UI and the assistant works through the normal interactive experience |
| Automated tool run | executionMode: "headless" | PPTB runs the tool without opening the UI and returns a structured result when available |
Interactive runs are a good fit for existing UI-driven tools. Automated runs are a good fit for repeatable tasks, direct data returns, and unattended workflows.
Declare Tool Metadata
Add both invocation and agents to pptb.config.json.
{
"invocation": {
"version": "1.0.0",
"capabilities": ["fetchxml-builder"],
"prefill": {
"properties": {
"entityName": { "type": "string" }
}
},
"returnTopic": {
"properties": {
"fetchXml": { "type": "string" }
}
}
},
"agents": {
"version": "1.0.0",
"invokable": true,
"modes": ["one-way", "two-way"],
"defaultMode": "two-way",
"timeoutMS": 12000,
"headless": true,
"executionModes": [
"windowed",
"headless"
],
"defaultExecutionMode": "headless",
"headlessEntry": "dist/headless.js"
}
}
Field summary:
agents.invokableexposes the tool through MCP discovery.agents.modesdeclares whether the tool supports fire-and-forget calls, result-returning calls, or both.agents.defaultModedefines the fallback when the caller does not specify one.agents.timeoutMSgives a timeout hint for result-returning calls.agents.headlessenables automated headless execution of the tool.agents.executionModeslists the supported execution modes (e.g.,windowed,headless).agents.defaultExecutionModesets the default execution mode when none is specified.agents.headlessEntrypoints to the compiled automated runtime entry point.
In prose we refer to automated runs, but the runtime field value remains
executionMode: "headless". Use the real field names and values in your
code and configuration.
Add an Automated Runtime
If you want your tool to support automated runs, export an invokeHeadless(input, context) function from a file that PPTB can discover.
PPTB checks for an automated entry in this order:
agents.headlessEntryinpptb.config.jsondist/headless.jsheadless.jspackage.json.main
This sample is based on the HTML sample tool and returns a simple FetchXML payload without opening the UI:
/// <reference types="@pptb/types" />
async function invokeHeadless(input, context) {
const { toolId, toolName, invocationMode, authToken, updateProgress, logger } =
context
logger.info(
`Starting headless run for ${toolName} (${toolId}) in mode ${invocationMode}`,
)
updateProgress(10, 'validating input')
const entityName =
typeof input.entityName === 'string' && input.entityName.trim() !== ''
? input.entityName.trim()
: 'account'
if (authToken) {
updateProgress(40, 'auth token received')
} else {
updateProgress(40, 'running without auth token')
}
updateProgress(80, 'building FetchXML')
const fetchXml = `<fetch top="10">
<entity name="${entityName}">
<attribute name="name" />
<attribute name="${entityName}id" />
<order attribute="name" />
</entity>
</fetch>`
updateProgress(100, 'done')
logger.info(`Headless run complete for entity: ${entityName}`)
return { fetchXml }
}
module.exports = {
invokeHeadless,
}
For two-way calls, return a JSON object that matches invocation.returnTopic.
Invocation Metadata
Assistants can pass PPTB-specific metadata under arguments.__pptb.
{
"entityName": "account",
"__pptb": {
"mode": "two-way",
"executionMode": "headless",
"timeoutMs": 60000,
"authToken": "optional-caller-token",
"connectionName": "optional-saved-connection"
}
}
Key fields:
mode:one-wayortwo-wayexecutionMode:windowedorheadlesstimeoutMs: per-call timeout hintauthToken: optional token provided by the callerconnectionName: optional saved PPTB connection name
When your UI-based tool is launched through MCP, toolboxAPI.invocation.getLaunchContext() includes the original prefill data and a __pptb metadata object such as:
{
"entityName": "account",
"__pptb": {
"source": "mcp",
"mode": "two-way",
"correlationId": "mcp-...",
"timeoutMs": 60000,
"expectsResponse": true
}
}
Design for Both Run Styles
If your tool supports both interactive and automated runs, keep the contract aligned across both paths.
- Accept the same core input shape in the UI and automated runtime.
- Return the same result shape regardless of how the tool was executed.
- Treat automated runs as task-oriented operations, not hidden UI automation.
- Use progress updates and structured logging so callers can reason about long-running work.
- Keep secrets out of logs and return payloads.
The cleanest pattern is to move business logic into shared functions and have both the UI path and invokeHeadless call the same domain layer.
Validation and Testing
Before publishing or sharing your tool with assistant users:
- Validate
pptb.config.jsonlocally with Local Validation. - Check that the tool appears in MCP discovery only when
agents.invokableistrue. - Test both
one-wayandtwo-waycalls if you advertise both modes. - Confirm that your automated runtime returns a payload matching
returnTopic. - Use MCP Inspector as the manual test harness for tool discovery and tool calls.
If a call fails, compare the request and response payloads against prefill and returnTopic, then verify that the automated entry file can be discovered from your built output.