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:

  1. Declare an invocation contract.
  2. Mark the tool as invokable by agents.
  3. 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:

LayerPurpose
invocationDescribes the input payload the caller can send and the structured result the tool may return
agentsDeclares 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 termMCP runtime valueMeaning
Interactive tool runexecutionMode: "windowed"PPTB opens the tool UI and the assistant works through the normal interactive experience
Automated tool runexecutionMode: "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.invokable exposes the tool through MCP discovery.
  • agents.modes declares whether the tool supports fire-and-forget calls, result-returning calls, or both.
  • agents.defaultMode defines the fallback when the caller does not specify one.
  • agents.timeoutMS gives a timeout hint for result-returning calls.
  • agents.headless enables automated headless execution of the tool.
  • agents.executionModes lists the supported execution modes (e.g., windowed, headless).
  • agents.defaultExecutionMode sets the default execution mode when none is specified.
  • agents.headlessEntry points to the compiled automated runtime entry point.

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:

  1. agents.headlessEntry in pptb.config.json
  2. dist/headless.js
  3. headless.js
  4. package.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-way or two-way
  • executionMode: windowed or headless
  • timeoutMs: per-call timeout hint
  • authToken: optional token provided by the caller
  • connectionName: 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:

  1. Validate pptb.config.json locally with Local Validation.
  2. Check that the tool appears in MCP discovery only when agents.invokable is true.
  3. Test both one-way and two-way calls if you advertise both modes.
  4. Confirm that your automated runtime returns a payload matching returnTopic.
  5. 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.

Next Steps

Was this page helpful?