Building an ACA Code Interpreter connector for Power Platform and Copilot Studio
July 06, 2026
Code execution in automation workflows has always been constrained. Power Automate gives you expressions and inline scripting, but what if your flow needs to run a full pandas analysis on a CSV, convert FetchXML to OData, evaluate nested Power Fx formulas, lint an OpenAPI spec with Spectral, render Jinja2 prompt templates, or test regex patterns with named capture groups — all in an isolated, ephemeral microVM? That’s the problem ACA Sandboxes solves, and this connector puts 16 languages and modes directly into Power Platform and Copilot Studio.
What this connector does
The ACA Code Interpreter is a dual-mode custom connector — typed operations with full schemas for Power Automate, plus an MCP endpoint for Copilot Studio agents. Each code execution runs inside a fresh microVM with deny-default egress networking. The sandbox boots in seconds, executes your code, and auto-suspends after 5 minutes idle.
It supports 16 languages and modes:
| Language | Identifier | Runtime |
|---|---|---|
| Python | python | Python 3.14+ with pandas, numpy, matplotlib, scikit-learn |
| JavaScript | javascript | Node.js 20+ |
| TypeScript | typescript | tsx (via npx) |
| Power Fx | powerfx | Custom CLI (Microsoft Power Fx engine) |
| Bash | bash | GNU Bash |
| PowerShell | powershell | pwsh 7.6+ |
| Ruby | ruby | Ruby 3.2+ |
| Perl | perl | Perl 5 |
| PHP | php | PHP CLI |
| SQL | sql | SQLite 3.45+ (in-memory) |
| Adaptive Card | adaptivecard | JSON validator |
| FetchXML | fetchxml | Parser + OData converter |
| OpenAPI Lint | openapi-lint | Spectral-based linter |
| Prompt | prompt | Jinja2 template renderer |
| Expression | expression | Power Automate expression evaluator |
| Regex | regex | Pattern tester with match output |
The first ten are traditional runtimes. The last six are Power Platform-specific modes — tools you’d normally run outside of your workflow that now live inside it.
Architecture
The connector talks directly to the ACA Sandboxes data plane at https://management.{region}.azuredevcompute.io. Each sandbox is a CloudHypervisor microVM with configurable CPU (500m–4000m) and memory (1–8 GiB). The custom disk image bakes in all 16 runtimes plus the specialized tooling (Power Fx CLI, expression evaluator, FetchXML parser, etc.).
graph LR
A[Power Automate / Copilot Studio] --> B[Custom Connector]
B --> C[script.csx]
C --> D[ACA Sandboxes Data Plane]
D --> E[microVM]
E --> F[Code Execution]
F --> G[stdout / stderr / files]
The script.csx handles:
- Routing — dispatches typed operations and MCP tool calls to the same underlying sandbox client
- Multi-line code — Base64 encodes code into a temp file to avoid shell escaping issues
- Output truncation — caps stdout/stderr at 4 KB to stay within connector response limits
- Auto-session creation — the MCP
execute_codetool creates a session on the fly if nosession_idis provided - Telemetry — fires events to Application Insights for execution tracking
Operations
The connector exposes eight operations:
| Operation | Purpose |
|---|---|
| CreateSession | Boot an isolated sandbox microVM |
| ExecuteCode | Run code in any of the 16 languages |
| UploadFile | Stage data files in the sandbox |
| DownloadFile | Retrieve generated artifacts |
| ListFiles | Browse the sandbox filesystem |
| GetSession | Check sandbox state |
| DestroySession | Clean up resources |
| InvokeMCP | Copilot Studio MCP endpoint |
The MCP endpoint exposes the same capabilities as tools — execute_code, upload_file, download_file, list_files, create_session, and destroy_session — so Copilot Studio agents can call them conversationally.
Security model
Every sandbox starts with deny-default egress. Only pypi.org and npmjs.org are allowed by default (for package installation). You can allowlist additional hosts at session creation time via the egressAllowHosts parameter, but nothing goes out unless explicitly permitted.
The connector authenticates to the ACA data plane using OAuth 2.0 with the https://dynamicsessions.io/.default token audience. The Entra app requires the Sessions.ReadWrite.All permission from Azure ContainerApps Sessions.
Deployment
Infrastructure is defined in Bicep (subscription-scoped). The deployment creates a resource group, sandbox group, and RBAC assignments:
az deployment sub create --name aca-code-interpreter `
--location westus2 `
--template-file infra/main.bicep `
--parameters principalId=$(az ad signed-in-user show --query id -o tsv)
The disk image builds via Docker. A full image includes all 16 runtimes; a lite image covers just Python and Bash:
cd infra
.\build-image.ps1 # Full (all 16 runtimes)
.\build-image.ps1 -Lite # Lite (Python + Bash only)
After deploying infrastructure and building the image, deploy the connector with PAC CLI:
pac connector create `
-df apiDefinition.swagger.json `
-sf script.csx `
-env <environment-id>
Then configure OAuth in the portal Security tab with the https://dynamicsessions.io/.default scope.
Language examples
Beyond straightforward Python or Bash scripts, the specialized modes handle tasks that normally require separate tooling.
Power Fx evaluation
Test Power Fx formulas without deploying a canvas app:
{
"sessionId": "abc123",
"language": "powerfx",
"code": "With({items: [1,2,3,4,5]}, Sum(items, Value) & \" total, \" & CountRows(Filter(items, Value > 3)) & \" above threshold\")"
}
Returns: 15 total, 2 above threshold
FetchXML to OData conversion
Convert a FetchXML query into its equivalent OData URL — useful when migrating Dynamics integrations from classic to modern endpoints:
{
"sessionId": "abc123",
"language": "fetchxml",
"code": "<fetch top='50'><entity name='account'><attribute name='name'/><attribute name='revenue'/><filter><condition attribute='statecode' operator='eq' value='0'/><condition attribute='revenue' operator='gt' value='1000000'/></filter><order attribute='revenue' descending='true'/></entity></fetch>"
}
Returns the OData equivalent:
/api/data/v9.2/accounts?$select=name,revenue&$filter=statecode eq 0 and revenue gt 1000000&$orderby=revenue desc&$top=50
Power Automate expression evaluation
Validate complex expressions outside of a running flow:
{
"sessionId": "abc123",
"language": "expression",
"code": "formatDateTime(addDays(utcNow(), -7), 'yyyy-MM-dd')"
}
Returns the evaluated result (e.g., 2026-06-29). Particularly useful for building expression libraries or debugging nested if/coalesce chains.
OpenAPI linting
Run Spectral against an API spec inline during a deployment approval flow:
{
"sessionId": "abc123",
"language": "openapi-lint",
"code": "{\n \"openapi\": \"3.0.0\",\n \"info\": { \"title\": \"My API\" },\n \"paths\": { \"/users\": { \"get\": { \"responses\": {} } } }\n}"
}
Returns structured violations:
[warning] info-description: OpenAPI object info description must be present.
[error] operation-operationId: Operation must have operationId.
[warning] oas3-api-servers: OpenAPI servers must be present and non-empty.
Adaptive Card validation
Validate card JSON before sending it through a Teams bot or connector:
{
"sessionId": "abc123",
"language": "adaptivecard",
"code": "{ \"type\": \"AdaptiveCard\", \"version\": \"1.5\", \"body\": [{ \"type\": \"TextBlock\", \"text\": \"Hello\", \"invalidProp\": true }] }"
}
Returns any schema violations, unknown properties, or version compatibility warnings.
Regex pattern testing
Test regex patterns with match output and capture groups:
{
"sessionId": "abc123",
"language": "regex",
"code": "(?P<year>\\d{4})-(?P<month>\\d{2})-(?P<day>\\d{2})",
"variables": {
"input": "Created on 2026-07-06 and updated on 2026-07-05"
}
}
Returns all matches with named groups:
[
{"match": "2026-07-06", "groups": {"year": "2026", "month": "07", "day": "06"}},
{"match": "2026-07-05", "groups": {"year": "2026", "month": "07", "day": "05"}}
]
Prompt template rendering
Render Jinja2 prompt templates with variable substitution:
{
"sessionId": "abc123",
"language": "prompt",
"code": "You are a assistant. The user's name is and they work in .\n\n",
"variables": {
"role": "technical support",
"name": "Dana",
"department": "Engineering",
"priority": "high"
}
}
Returns the fully rendered prompt string — useful for testing templates before committing them to a prompt library.
Use cases
Data analysis in flows — Upload a CSV, run a pandas script, download the transformed output. No Azure Functions, no Logic Apps connectors, no external dependencies.
Copilot Studio code agent — Give your agent the ability to write and run code. The MCP tools auto-create sessions, so the agent just says “run this Python” and gets results back.
OpenAPI validation — Lint an API spec inline during a deployment approval flow. The Spectral-based linter runs in the sandbox and returns any violations.
Power Automate expression testing — Evaluate complex expressions outside of a running flow. Useful for building expression libraries or validating user-submitted formulas.
FetchXML to OData — Convert FetchXML queries to OData URLs programmatically. Helpful when migrating from classic Dynamics APIs to modern endpoints.
Project structure
ACA Code Interpreter/
├── apiDefinition.swagger.json # OpenAPI 2.0 (8 operations + MCP)
├── apiProperties.json # OAuth config, scriptOperations
├── script.csx # C# connector logic
├── infra/
│ ├── Dockerfile # Full image (all 16 runtimes)
│ ├── Dockerfile.lite # Lite image (Python + Bash)
│ ├── build-image.ps1 # Build + import script
│ ├── deploy.ps1 # End-to-end deployment
│ ├── main.bicep # Subscription-scoped infra
│ ├── main.bicepparam # Parameters
│ ├── modules/
│ │ └── sandbox-group.bicep # Sandbox group + RBAC
│ └── tools/
│ ├── pfx/ # Power Fx CLI (.NET)
│ ├── expr-eval/ # Expression evaluator (.NET)
│ ├── validate-card.js # Adaptive Card validator
│ ├── fetchxml_eval.py # FetchXML → OData converter
│ ├── lint-openapi.js # OpenAPI linter (Spectral)
│ ├── render_prompt.py # Jinja2 prompt renderer
│ └── regex_test.py # Regex pattern tester
└── readme.md
What’s next
The sandbox architecture opens up interesting possibilities. Session persistence between flow runs (resume a suspended VM), GPU-enabled sandboxes for ML inference, and multi-file project scaffolding are all on the roadmap.
The full source is available in the SharingIsCaring repository.