Render the template's prompt when launching an agent via MCP - #941
Merged
selfcontained merged 5 commits intoAug 12, 2026
Merged
Conversation
dispatch_launch_agent accepted a templateId but only ever used it for worktree settings and record-keeping — template.prompt was never read, so an MCP-launched agent's entire prompt was the caller's short string and every one of the template's own instructions was silently dropped. The web UI's launch path (TemplateService.launchTemplate) has always rendered it. Launching a template is a request for the template's instructions, so the template prompt is now always used. Its placeholders fill from a new templateArgs param; a single remaining placeholder absorbs the caller's free-text prompt, which is the unambiguous case. Anything the caller said that did not become a placeholder value is appended under a heading rather than dropped, and placeholders nobody supplied render empty with a note back to the caller instead of failing the launch. Templates with selfImprove set get the same footer the UI path adds. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A placeholder named after an Object member ({{D:toString}},
{{D:constructor}}) read as already supplied off the prototype chain, so it
rendered a built-in into the prompt instead of the caller's value.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>A template whose prompt has {{D:...}} variables cannot render from a single
free-text prompt alone, so dispatch_launch_agent takes an optional
templateArgs map keyed by variable name. A single variable left unset after
templateArgs takes the caller's free text — the unambiguous case, and the
common one; anything not consumed follows the rendered template.
Callers had no way to learn a template's variable names short of
recognising the placeholder syntax inside its prompt text, so get_template
and list_templates now report a parsed `args` list alongside the record.
Variables nobody supplies still render empty rather than failing the launch,
and the launch result names them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Both launch surfaces independently encoded "substitute args, then append the
self-improvement footer" — the same class of divergence this branch exists to
fix. renderTemplatePrompt is now the one place a template's prompt becomes an
agent's startup prompt, and TemplateService.launchTemplate sits on it too.
The MCP-only job of resolving free text into args keeps its own entry point,
renderTemplatePromptFromFreeText.
Two ways a caller's args were silently mishandled:
A templateArgs key matching no arg was dropped, and the arg it was meant for
then counted as unset — so the free-text prompt took that slot and nothing
warned. Unmatched keys are now reported, and their presence stops free text
from filling a slot the caller was clearly aiming at.
An arg written in two cases ({{D:Repo}} and {{D:repo}}) half-rendered: values
resolve per-arg but substituteArgs resolves per-occurrence, so a name-keyed
value only reached the occurrence whose spelling matched. Args now collapse
into key space before rendering.
Also: real types on the template crud callbacks instead of unknown, promptArgs
(not args) as the reported field so it can't be shadowed by a future column,
one term for the concept in every description, and coverage for the discovery
half.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>…umer The prompt/templateArgs descriptions stated the fill rule unconditionally, so a caller who typos a key on a single-arg template would predict the opposite of what happens. Both now say an unrecognized key suppresses the shortcut. RenderedTemplatePrompt.unknown becomes unknownArgs, matching what the handler and the caller-facing note already call it — and reading less like the TS keyword. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
selfcontained
deleted the
agt_75b8f7551dcf/build-fix-mcp-launch-template-prompt-bug
branch
August 12, 2026 04:02
selfcontained added a commit
that referenced
this pull request
Aug 13, 2026
The #945/#946 wave added five tools and changed how three others are addressed. #945 removed get_agent_history and get_parent_context from every doc surface in the same commit that introduced dispatch_review_get_feedback, brain_get_event, brain_get_list_item and whiteboard_howto — so the lists looked freshly maintained while being short by five entries. - docs-pane Repo Tools "Built-in tools": the five new tools, plus pin update-by-id, dispatch_pins batch writes with merge/replace, delete by id/ids/group, and reading one pin back whole by id. A closing paragraph states the list/detail policy response.ts now encodes, so an agent reading the docs knows a truncated listing has a matching full read. - dispatch_launch_agent: with a templateId the template's own prompt is now rendered and filled from templateArgs (#941) — it was previously used only for worktree settings, and the bullet still described it as just "or template". - media.tsx Pins tab: pin groups collapse under a heading with a member count, groups over eight start collapsed, and the choice persists per agent and group (#946). - automations.tsx job-agent lists: the new tools, plus persona_templates / persona_upsert / persona_validate and dispatch_archive_agent, which are in JOB_TOOLS but were never enumerated there. - README interactive-agents table and the persona-agents list. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
dispatch_launch_agentaccepted atemplateIdbut only ever used it for worktree settings and record-keeping —template.promptwas never read. An MCP caller passingtemplateIdplus a shortpromptgot an agent whose entire prompt was that short string: every one of the template's own instructions was silently dropped. The web UI's launch path (TemplateService.launchTemplate) has always rendered it, so the two launch surfaces disagreed.Impact was broad — every MCP caller using
templateId, not just one workflow — and invisible, since the launch succeeds and the agent just behaves as if the template said nothing.The fix
One assembly point.
renderTemplatePrompt(template, args, appendix?)in the newapps/server/src/templates/launch-prompt.tsis now the only place a template's prompt becomes an agent's startup prompt — substitute args, then the caller's own text, then the self-improvement footer.TemplateService.launchTemplatesits on it too. Both paths previously encoded that rule independently, which is the same class of divergence this PR exists to fix.Free text → args. The UI collects one value per
{{D:...}}arg from a form; an MCP caller has only free text, sorenderTemplatePromptFromFreeTextresolves it:dispatch_launch_agenttakes an optionaltemplateArgsmap keyed by arg name.templateArgstakes the caller'sprompt. Anything not consumed follows the rendered template.{{D:...}}; the launch result names them.Discovery.
get_templateandlist_templatesnow report a parsedpromptArgslist — the same parse the UI's launch form uses to build its fields. Without it a caller would have to recognise placeholder syntax inside the prompt text to know whattemplateArgswants.model,fullAccess,agentType, andcwdstill inherit from the launching agent as the tool's docs promise — full UI parity there would change behavior for every existing MCP caller and is out of scope for this bug.Two ways caller args were silently mishandled
Both found by review, both fixed with tests and confirmed live:
templateArgskey matching no arg was dropped — and the arg it was meant for then counted as unset, so the free-text prompt took that slot and nothing warned.{prUrl: "..."}against{{D:PR URL|required}}producedReview review it please.with an empty note. Unmatched keys are now reported, and their presence suppresses the one-arg fill shortcut, since an unmatched key is evidence the caller was aiming a different value at that slot.substituteArgsresolves per-occurrence, so a name-keyed value only reached the occurrence whose spelling matched:{{D:Repo}}/{{D:repo}}with{Repo: "dispatch"}renderedRepo: dispatch / again:. Args now collapse into key space before rendering.Verification
Unit tests cover both renderers and the handler path — lone arg, no args,
templateArgs, unset args, unrecognized keys, case-collision, repeated args,Object-member arg names, caller values containing{{D:...}}syntax, appendix-before-footer ordering, and thepromptArgsdiscovery half through bothget_templateandlist_templates.Exercised live over the real MCP endpoint against an isolated dev stack, reading each launched
claudeprocess's argv to confirm what it actually received:templateArgsReview the diff for security.then the caller's promptReview for .then the caller's prompt; result named both unset argsReview .then the caller's prompt; result named the unrecognized key and the unfilled argRepo: dispatch / again: dispatchpnpm run check,pnpm run test, andpnpm run test:e2eall pass.Reviews
Architecture and backend-security personas both reviewed; nine findings between them, all fixed and verified by the reviewers.
Follow-up (not in this diff)
The "Idea Inbox" template works around this bug by fetching the Build Idea template and substituting the arg itself, then passing both the substituted text and
templateId. Once this ships, that stopgap would nest the rendered prompt inside the arg. Its instructions need updating after the fix is live on the server — merged is not deployed. Feature-detect: the new server'sdispatch_launch_agenthastemplateArgsandget_templatereturnspromptArgs.