feat: teach agents to author allowedActions for shared connections - #28
Conversation
PRs #1004 and #1077 in bic/PowerPlatform-Managed-Host added a connector runtime policy contract that the plugin knew nothing about. For a shared connection reference (sharedConnectionId set), ms.config.json must declare allowedActions -- per table for tabular sources, connector-level for action connectors -- and both `ms app pack` and `ms app deploy` hard-fail without it. sharedConnectionId is written automatically by the CLI whenever the connector and auth type are shareable, so agents hit this failure without ever opting in. Adds shared/allowed-actions.md as the single source of truth and wires it into the two moments that matter: immediately after a data source is added, and as a blocking preflight gate in /deploy. Because all eleven /add-* skills delegate to add-data-source, one step there covers every connector. - shared/allowed-actions.md: new canonical reference -- why the field exists (RP translates it into an executionRestrictions policy and APIHub runs every connector request through it; the managed apps host only addresses the policy, the app SDK is uninvolved, and app code never changes), how to detect a shared reference, both authoring shapes, least-privilege inference, the verbatim validation errors, and the deferral path when no app code exists yet - add-data-source: new Step 4 reads the config back after every add and runs infer -> propose -> confirm -> write; defers rather than guessing during /create-app, which adds data sources before any UI exists - deploy: new Step 4 preflight gate catches a missing policy before it can reach a failing deploy - list-connectors: documents the real list-actions output columns and that the id values feed connector-level allowedActions, filtered to behavior: Allow - schemas/ms.config.schema.json: adds allowedActions at both nodes, copied from the product repo's regenerated JsonSchema snapshot. The published schema was stale and closed (additionalProperties: false), so a correctly authored config drew a red squiggle in VS Code - version 2.0.2 -> 2.1.0 (new required agent behavior, not a fix) Stage overlays deliberately cannot carry allowedActions -- StageConnectionReferenceSchema is .strict() and does not whitelist it -- so stage-overlay.schema.json is intentionally unchanged. The deploy gate is a node -e script rather than jq: jq is not on PATH on Windows and the plugin treats PowerShell as a first-class host, whereas Node 22+ is already a hard prerequisite for every Microsoft App. Verified against fixtures covering missing per-table, whitespace-only, missing connector-level, clean, and no-reference configs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved validation, schema-generation, and deployment-overlay issues remain.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR teaches managed-app agents to author and validate least-privilege allowedActions policies for shared connections.
Changes:
- Adds canonical guidance and policy authoring flows.
- Adds add-data-source and deploy validation, including create-app deferral.
- Updates schemas, agent documentation, memory guidance, and plugin versions to 2.1.0.
File summaries
| File | Summary | Final review findings |
|---|---|---|
schemas/ms.config.schema.json |
Adds connector- and table-level allowedActions. |
Moderate (3 votes): update the source contract and regenerate the schema instead of hand-editing the generated snapshot. |
plugins/microsoft-managed-apps/skills/list-connectors/SKILL.md |
Documents action output and IDs. | No final review findings. |
plugins/microsoft-managed-apps/skills/deploy/SKILL.md |
Adds shared-policy preflight validation. | Moderate (3 votes): return a nonzero status on failure. Moderate (1 vote each): validate optional connector-level arrays, reject non-string entries, and account for deployment overlays. |
plugins/microsoft-managed-apps/skills/create-app/SKILL.md |
Documents deferred policy handling. | No final review findings. |
plugins/microsoft-managed-apps/skills/add-data-source/SKILL.md |
Adds post-add policy authoring. | Moderate (2 votes): return a nonzero status on failure. Moderate (1 vote each): validate optional connector-level arrays and reject non-string entries. |
plugins/microsoft-managed-apps/shared/shared-instructions.md |
Adds shared-policy rules. | Nit (3 votes): clarify that allowing every action removes the restriction. |
plugins/microsoft-managed-apps/shared/memory-bank.md |
Tracks policy state and actions. | No final review findings. |
plugins/microsoft-managed-apps/shared/connector-reference.md |
Documents shared connection requirements. | Nit (1 vote): qualify the guidance to preserve the create-app deferral path. |
plugins/microsoft-managed-apps/shared/allowed-actions.md |
Provides canonical policy guidance. | Moderate (2 votes): return a nonzero status on failure, including the corresponding check at line 194. Moderate (1 vote each): validate optional connector-level arrays and reject non-string entries. |
plugins/microsoft-managed-apps/agents/microsoft-apps-architect.md |
Adds architectural policy guidance. | No final review findings. |
plugins/microsoft-managed-apps/AGENTS.md |
Registers the new shared reference. | No final review findings. |
plugins/microsoft-managed-apps/.claude-plugin/plugin.json |
Bumps the plugin version. | No final review findings. |
.claude-plugin/marketplace.json |
Bumps the marketplace version. | No final review findings. |
Review details
Suppressed comments (9)
plugins/microsoft-managed-apps/shared/allowed-actions.md:195
- This repeats the misleading claim that declaring every action “grants exactly nothing.” It actually grants all operations available to the connection; the intended warning is that it narrows nothing. Please correct the wording so the least-privilege guidance is unambiguous.
Declaring every verb, or every action the connector exposes, passes validation and grants
exactly nothing — don't do it.
plugins/microsoft-managed-apps/shared/allowed-actions.md:93
- When a tabular reference also has an optional connector-level policy, this branch only checks the per-table arrays. A config with
r.allowedActions: [](or a whitespace entry) plus valid table policies therefore printsOK, even though the validation rules below require every present array to be non-empty and pack/deploy rejects it. Validate a present connector-level array in thet.lengthbranch.
if (t.length) {
for (const [p, v] of t) if (!ok(v.allowedActions)) { bad++; console.log("MISSING per-table allowedActions: " + name + " -> " + p); }
} else if (!ok(r.allowedActions)) { bad++; console.log("MISSING connector-level allowedActions: " + name); }
plugins/microsoft-managed-apps/shared/allowed-actions.md:85
String(x).trim()lets numeric, boolean, or object entries pass this preflight even thoughms.config.schema.jsonrequires eachallowedActionsitem to be a string. The check can reportOKand defer the real schema failure to pack/deploy; require a string type before trimming.
const ok = (a) => Array.isArray(a) && a.length > 0 && a.every((x) => String(x).trim());
plugins/microsoft-managed-apps/shared/connector-reference.md:53
- This wording conflicts with the documented
/create-appdeferral path: when the app has no code yet,add-data-sourceis instructed to leave the policy unset and record it as pending rather than declare actions immediately. Qualify this bullet so agents do not prompt or write guessed actions during app creation.
- `sharedConnectionId` set → declare the actions before moving on.
plugins/microsoft-managed-apps/skills/add-data-source/SKILL.md:174
- When a tabular reference also has an optional connector-level policy, this branch only checks the per-table arrays. A config with
r.allowedActions: [](or a whitespace entry) plus valid table policies therefore printsOK, even though the validation rules require every present array to be non-empty and pack/deploy rejects it. Validate a present connector-level array in thet.lengthbranch.
if (t.length) {
for (const [p, v] of t) if (!ok(v.allowedActions)) { bad++; console.log("MISSING per-table allowedActions: " + name + " -> " + p); }
} else if (!ok(r.allowedActions)) { bad++; console.log("MISSING connector-level allowedActions: " + name); }
plugins/microsoft-managed-apps/skills/add-data-source/SKILL.md:166
String(x).trim()lets numeric, boolean, or object entries pass this preflight even thoughms.config.schema.jsonrequires eachallowedActionsitem to be a string. The check can reportOKand defer the real schema failure to pack/deploy; require a string type before trimming.
const ok = (a) => Array.isArray(a) && a.length > 0 && a.every((x) => String(x).trim());
plugins/microsoft-managed-apps/skills/deploy/SKILL.md:67
- When a tabular reference also has an optional connector-level policy, this branch only checks the per-table arrays. A config with
r.allowedActions: [](or a whitespace entry) plus valid table policies therefore printsOK, even though the validation rules require every present array to be non-empty and pack/deploy rejects it. Validate a present connector-level array in thet.lengthbranch.
if (t.length) {
for (const [p, v] of t) if (!ok(v.allowedActions)) { bad++; console.log("MISSING per-table allowedActions: " + name + " -> " + p); }
} else if (!ok(r.allowedActions)) { bad++; console.log("MISSING connector-level allowedActions: " + name); }
plugins/microsoft-managed-apps/skills/deploy/SKILL.md:59
String(x).trim()lets numeric, boolean, or object entries pass this preflight even thoughms.config.schema.jsonrequires eachallowedActionsitem to be a string. The check can reportOKand defer the real schema failure to pack/deploy; require a string type before trimming.
const ok = (a) => Array.isArray(a) && a.length > 0 && a.every((x) => String(x).trim());
plugins/microsoft-managed-apps/skills/deploy/SKILL.md:58
- This preflight reads only the base
ms.config.json, so it skips a base reference whosesharedConnectionIdis supplied by a deployment overlay. The new overlay guidance says that case is still a shared effective reference whose base policy must satisfy validation; the gate currently printsOKand leaves the failure to pack/deploy. Inspect the selectedms.<deployment>.config.jsonoverlay or explicitly handle overlay-based sharing here.
const refs = JSON.parse(fs.readFileSync("ms.config.json", "utf8")).connectionReferences || {};
- Files reviewed: 13/13 changed files
- Comments generated: 5
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
The schema snapshot must be regenerated from the authoritative Zod contract rather than edited directly. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Generated from the authoritative Zod contract updated in bic/PowerPlatform-Managed-Host#1288 at 8e6f0591fa0fa6a20814b7dd38d643474d3ba3cd. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Require nonblank string entries and validate optional connector-level policies independently across the three documented preflight checks. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follows bic/PowerPlatform-Managed-Host#1004 (authoring surface + validation) and #1077 (runtime policy plumbing):
Those PRs added a connector runtime policy contract that the plugin knew nothing about. A shared connection reference (non-empty
sharedConnectionId) must declareallowedActionsinms.config.json-- per table for tabular sources, connector-level for action connectors -- and bothms app packandms app deployhard-fail without it.sharedConnectionIdis written automatically by the CLI whenever the connector and auth type are shareable, so agents hit this failure without ever opting in. That framed the whole design: the agent can't wait to be told a connection is shared, it has to read the config back after every add.Approach
One canonical reference wired into the two moments that matter. Because all eleven
/add-*skills delegate toadd-data-source, a single step there covers every connector.shared/allowed-actions.md(new) -- the source of truth. Why the field exists, how to detect a shared reference, both authoring shapes, least-privilege inference, verbatim validation errors, gotchas.add-data-sourceStep 4 -- readsms.config.jsonback after every add; infers from actual usage insrc/, proposes, confirms, writes.deployStep 4 -- blocking preflight gate, so a missing policy surfaces before it can reach a failing deploy.list-connectors-- documents the reallist-actionsoutput columns and thatidvalues feed connector-levelallowedActions, filtered tobehavior: Allow.shared-instructions.md,connector-reference.md, the architect agent,memory-bank.md, andAGENTS.md.schemas/ms.config.schema.json-- addsallowedActionsat both nodes. The published schema was stale and closed (additionalProperties: false), so a correctly authored config drew a red squiggle in VS Code.2.0.2->2.1.0(new required agent behavior, not a fix).Notes for review
Enforcement attribution. RP translates the declaration into an
executionRestrictionspolicy; APIHub runs every connector request through it. The managed apps host only addresses the policy (URL rewrite +x-ms-shared-connection-idheader). The app is not in the enforcement path -- the doc says this explicitly so agents stop trying to invent a client-side allow-list check.Deferral path.
/create-appStep 8 mandates no per-connector prompts, and at that pointsrc/doesn't exist so inference is impossible. Rather than let the two instructions contradict, a shared reference created during/create-appis recorded as policy pending and decided once app code exists. Safe becauseassertAppConfigSchemaValidis called only from the pack/deploy path --Dev.tsnever validates, so local iteration is unaffected, and/deploy's gate catches it before it matters.node -e, notjq, for the deploy gate.jqisn't on PATH on Windows and the plugin treats PowerShell as first-class; a blocking gate depending on it would silently break. Node 22+ is already a hard prerequisite. Verified against fixtures covering missing per-table, whitespace-only entries, missing connector-level, clean, and non-shared configs.Stage overlays intentionally untouched.
StageConnectionReferenceSchemais.strict()and doesn't whitelistallowedActions, sostage-overlay.schema.jsoncorrectly has no such field.Two follow-ups worth a look
docs/features/connector-runtime-policies.mdshows["GetItems", "PatchItem"]for a tabular example, which contradicts theget/post/patch/deletevocabulary. I followed the latter, corroborated by theSchema.tscomment(e.g. `["get", "delete"]`). That doc example may need a correction PR.schemas/README.mdsays the schemas are generated, not hand-edited. I copied the exact shape from the product repo's regeneratedJsonSchemasnapshot, but the official path would be a regeneration PR.