mcs-deploy — solution import, publish, and real verification
The contract: the deploy is not done until a bot in the target environment has answered a real question, or the exact blocker is reported with the raw error.
Stage 0 — Build (only if no zip was provided)
If Kody points at agent.py files instead of a zip, build the two-solution shape from them: a parent solution holding the orchestrating agent, plus a connected child solution per tier holding the child agents it delegates to. Any builder that holds that shape works. Validate the built solution (every child the parent references exists; connector references resolve) before importing anything.
Stage 1 — Fresh version naming (always)
Kody iterates by fresh installs. Unless he says otherwise, bump the visible name
with the next suffix (v2, r3, ...) so the new import is unambiguous next to the old
one: check pac copilot list / the solution list for the highest existing suffix
first. Never reuse a name and hope the overwrite took.
Stage 2 — Credentials (never ask)
App-registration creds are in the project's local.settings.json:
DYNAMICS_365_CLIENT_ID, DYNAMICS_365_CLIENT_SECRET, DYNAMICS_365_TENANT_ID,
DYNAMICS_365_RESOURCE. Use them for Dataverse Web API calls. For pac, use the
existing auth profile (pac auth list); only ask Kody to run ! pac auth create
if no profile exists. Do not ask him to paste secrets — that is how seven ended up
in prompt history.
Stage 3 — Import
pac solution import --path <zip> --async --force-overwrite- Or Dataverse Web API when pac auth is unavailable. Prefer an in-place botcomponent PATCH when only bot content changed — it preserves existing Direct Line secrets, so channel configs do not need rebinding.
- Connection references must be bound by the maker on first import of a new solution — say so in the handoff rather than silently leaving them unbound.
Stage 4 — Publish (order is load-bearing)
- Publish the SUB-AGENTS FIRST, the ORCHESTRATOR LAST. A connected-agent root must have its children published before it, else publish 409s (ExternalServiceException).
pac copilot publish --bot <GUID>per bot (GUIDs frompac copilot list), or PvaPublish via Web API, children first.- pac publish is the oracle. A solution that zips and imports fine can still
fail publish — most often literal
{braces}anywhere Power Fx evaluates (display names, messages). If publish fails, look for braces first.
Stage 5 — Verify (the part that gets skipped and then bites)
- Direct Line requires
authenticationmode = 1(no-auth) on EVERY bot in the chain. Flip via Dataverse Web API (PATCHbots), then republish children-first. - Known limit: agent-to-agent delegation throws AuthenticationNotConfigured over anonymous Direct Line. So: single-bot answers are verified over Direct Line; connected/orchestrator flows are verified in the Test pane — if you cannot drive the Test pane, give Kody the one specific question to type and what the correct answer looks like.
- A verification pass = at least one real question, real answer, quoted in the summary. "Import succeeded" is not verification.
Stage 6 — Handoff
Report: solution name + version, environment, bots published (in order), what was
verified and how, and the direct maker link
(https://make.powerapps.com/environments/<env-id>/bots). If connection
references need manual binding, that is the first line of the handoff, not a
footnote.
Run this — do not improvise
This capability's deterministic implementation is a RAPP single-file agent, linked beside this file as mcs_deploy_agent.py and embedded as the fenced Python below (sha256 ec716066d4c9be50…; a byte-exact copy is also vaulted in the capsule comment at the end of this file). On a host with sandbox execution, run the linked file directly — if it is missing, write the fence contents verbatim to mcs_deploy_agent.py first:
python3 mcs_deploy_agent.py '{"key": "value"}' # arguments as one JSON object
echo '{"key": "value"}' | python3 mcs_deploy_agent.py # or on stdin
python3 mcs_deploy_agent.py --tool # emit the JSON tool contract
Treat stdout as a tool result. If it reports missing or unresolved inputs, stop and collect them. If it returns steps, execute those steps in order exactly as returned; if it returns instructions, follow them with the supplied inputs. Otherwise use the result verbatim. Do not invent behavior beyond that output. On a host without code execution, treat the Parameters schema and the code below as the exact specification and never paraphrase a step. Never edit inside the generated markers; a converter-equipped host can instead restore the original file checksum-verified with the installed rapp-agent-converter/scripts/toast.py convert SKILL.md --to agent.
"""McsDeploy -- Take a built Copilot Studio solution (or build one from agent.py files first),
import it with a FRESH version name, publish in the correct order, verify it
actually answers, and hand back the maker-portal link. Never asks Kody for creds
that live in local.settings.json. USE THIS SKILL when Kody says: "deploy it to
copilot studio", "deploy the vN so I can test", "import the solution", "publish
the bots", "test it over direct line", "make sure this copilot solution actually
does what it says", or asks for a fresh install of a solution.
Generated by the rapp skill from mcs-deploy. The RCI capsule at the bottom of this file carries the full original; `toast.py convert` restores it byte-exact."""
import json
import re
import sys
try:
from agents.basic_agent import BasicAgent
except ImportError: # running OUTSIDE a brainstem -- stay executable anyway.
class BasicAgent: # noqa: D101 - minimal stand-in, same contract
def __init__(self, name=None, metadata=None):
if name:
self.name = name
if metadata:
self.metadata = metadata
def perform(self, **kwargs):
return "Not implemented."
def system_context(self):
return None
def to_tool(self):
return {"type": "function", "function": {
"name": self.name,
"description": self.metadata.get("description", ""),
"parameters": self.metadata.get("parameters", {})}}
# The procedural layer, verbatim from the source capability.
INSTRUCTIONS = '# mcs-deploy — solution import, publish, and real verification\n\nThe contract: the deploy is not done until a bot in the target environment has\nanswered a real question, or the exact blocker is reported with the raw error.\n\n## Stage 0 — Build (only if no zip was provided)\n\nIf Kody points at agent.py files instead of a zip, build the two-solution shape\nfrom them: a parent solution holding the orchestrating agent, plus a connected\nchild solution per tier holding the child agents it delegates to. Any builder\nthat holds that shape works. Validate the built solution (every child the parent\nreferences exists; connector references resolve) before importing anything.\n\n## Stage 1 — Fresh version naming (always)\n\nKody iterates by fresh installs. Unless he says otherwise, bump the visible name\nwith the next suffix (v2, r3, ...) so the new import is unambiguous next to the old\none: check `pac copilot list` / the solution list for the highest existing suffix\nfirst. Never reuse a name and hope the overwrite took.\n\n## Stage 2 — Credentials (never ask)\n\nApp-registration creds are in the project's `local.settings.json`:\n`DYNAMICS_365_CLIENT_ID`, `DYNAMICS_365_CLIENT_SECRET`, `DYNAMICS_365_TENANT_ID`,\n`DYNAMICS_365_RESOURCE`. Use them for Dataverse Web API calls. For `pac`, use the\nexisting auth profile (`pac auth list`); only ask Kody to run `! pac auth create`\nif no profile exists. Do not ask him to paste secrets — that is how seven ended up\nin prompt history.\n\n## Stage 3 — Import\n\n- `pac solution import --path <zip> --async --force-overwrite`\n- Or Dataverse Web API when pac auth is unavailable. Prefer an **in-place\n botcomponent PATCH** when only bot content changed — it preserves existing\n Direct Line secrets, so channel configs do not need rebinding.\n- Connection references must be bound by the maker on first import of a new\n solution — say so in the handoff rather than silently leaving them unbound.\n\n## Stage 4 — Publish (order is load-bearing)\n\n- Publish the SUB-AGENTS FIRST, the ORCHESTRATOR LAST. A connected-agent root\n must have its children published before it, else publish 409s\n (ExternalServiceException).\n- `pac copilot publish --bot <GUID>` per bot (GUIDs from `pac copilot list`),\n or PvaPublish via Web API, children first.\n- **pac publish is the oracle.** A solution that zips and imports fine can still\n fail publish — most often literal `{braces}` anywhere Power Fx evaluates\n (display names, messages). If publish fails, look for braces first.\n\n## Stage 5 — Verify (the part that gets skipped and then bites)\n\n- Direct Line requires `authenticationmode = 1` (no-auth) on EVERY bot in the\n chain. Flip via Dataverse Web API (PATCH `bots`), then republish children-first.\n- Known limit: **agent-to-agent delegation throws AuthenticationNotConfigured over\n anonymous Direct Line.** So: single-bot answers are verified over Direct Line;\n connected/orchestrator flows are verified in the **Test pane** — if you cannot\n drive the Test pane, give Kody the one specific question to type and what the\n correct answer looks like.\n- A verification pass = at least one real question, real answer, quoted in the\n summary. "Import succeeded" is not verification.\n\n## Stage 6 — Handoff\n\nReport: solution name + version, environment, bots published (in order), what was\nverified and how, and the direct maker link\n(`https://make.powerapps.com/environments/<env-id>/bots`). If connection\nreferences need manual binding, that is the first line of the handoff, not a\nfootnote.'
# Ordered commands lifted verbatim from the capability's own documentation.
STEPS = []
class McsDeployAgent(BasicAgent):
def __init__(self):
self.name = 'McsDeploy'
self.metadata = {
"name": "McsDeploy",
"description": "Take a built Copilot Studio solution (or build one from agent.py files first),\nimport it with a FRESH version name, publish in the correct order, verify it\nactually answers, and hand back the maker-portal link. Never asks Kody for creds\nthat live in local.settings.json. USE THIS SKILL when Kody says: \"deploy it to\ncopilot studio\", \"deploy the vN so I can test\", \"import the solution\", \"publish\nthe bots\", \"test it over direct line\", \"make sure this copilot solution actually\ndoes what it says\", or asks for a fresh install of a solution.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, **kwargs): # toaster:generated-perform
return json.dumps({"status": "ok", "instructions": INSTRUCTIONS,
"inputs": kwargs,
"note": "Prose-only capability: follow INSTRUCTIONS "
"with the given inputs."}, indent=2)
if __name__ == "__main__":
# echo '{"arg": "value"}' | python3 mcs_deploy_agent.py
# python3 mcs_deploy_agent.py '{"arg": "value"}'
# python3 mcs_deploy_agent.py --tool # emit the JSON tool contract
_a = sys.argv[1:]
if _a and _a[0] == "--tool":
print(json.dumps(McsDeployAgent().to_tool(), indent=2))
else:
_raw = _a[0] if _a else (sys.stdin.read().strip() or "{}")
print(McsDeployAgent().perform(**json.loads(_raw)))
# rci-capsule:v1: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