- Author(s): @wpfleger96
- Champion: @benbrandt
Elevator pitch
What are you proposing to change?Allow clients to provide system-level instructions for a session in ACP v1 and v2. Today, ACP has no mechanism for a client to deliver system-level instructions to an agent — the only content a client can send is user-role messages via
session/prompt. This means behavioral instructions, persona definitions, and platform context all compete with user content in the same message role, instead of reaching the instructions the agent gives the model at system level.
We propose:
- A
systemPromptsession capability, advertised by agents atinitialize, so clients know before creating a session whether system prompt delivery is supported. - An optional
systemPromptobject onsession/newwith amodeandcontent.contentis aContentBlock[]for consistency withsession/prompt, with text as the baseline. Clients re-send the object on the other session lifecycle requests, as they already do forcwdandmcpServers. - Two modes: append (the baseline: client instructions are added to the agent’s own instructions) and override (client content replaces the agent’s built-in default instructions), the latter gated behind an
overridesub-capability.
Status quo
How do things work today and what problems does this cause? Why would we change things?Aside from the extensibility
_meta field present on every request, ACP’s session/new accepts only cwd, mcpServers, and additionalDirectories, and session/prompt accepts only sessionId and prompt (a ContentBlock[] of user content). There is no systemPrompt, instructions, or equivalent field on any ACP method in v1 or v2.
This creates several problems for clients that need to provide behavioral instructions to agents:
Workarounds are fragmented and non-standard. Every implementation works around the gap differently:
claude-agent-acpadded a_meta.systemPromptextension (issue #90, PR #91). A string replaces the Claude Code preset and an object withappendadds to it — the same two modes this RFD proposes. But the spec states implementations “MUST NOT make assumptions about values at these keys,” making_metaunsuitable for instruction delivery that agents are expected to act on. The implementation silently droppedexcludeDynamicSectionsfor months (issue #581), demonstrating the fragility of non-standard extensions.- Buzz is a Nostr-based team collaboration and messaging platform where AI agents are first-class workspace members. Each managed agent has a persona (behavioral instructions, role definition, platform context) that the ACP harness needs to deliver as system-level instructions. Today, Buzz injects this as
[System]\n{content}prepended to the user message on every turn — content that never reaches the LLM’s system slot. Buzz’s own Persona Pack Spec lists “true system prompt injection via ACP” as a planned feature, explicitly acknowledging the current approach is a workaround. - Goose builds its system prompt internally via
PromptManagerand stores per-session overrides and additions, but the only way for an ACP client to set them is a Goose-specific extension method,_goose/unstable/session/system-prompt/set, withset(replace) andappendmodes. Goose has an open issue documenting the same gap in terms of its “recipe” system — recipes are Goose’s mechanism for injecting behavioral instructions into a session, and the issue notes “there is no recipe field onNewSessionRequestand no logic to load/apply a recipe to the new session’s agent.” Goose’s_goose/session/update_projectmethod works around this by sending a project ID that the agent resolves to a file on disk — a Goose-specific indirection, not a protocol-level solution. - Codex supports both modes through configuration —
developer_instructionsadds a developer message andmodel_instructions_filereplaces its base instructions — butcodex-acpexposes neither to ACP clients.
Agent frameworks follow the same pattern: AutoGen has
system_message, CrewAI has role/goal/backstory, LangGraph has prompt. Only Google’s A2A protocol is similarly silent — but A2A is agent-to-agent transport, a fundamentally different abstraction layer than ACP’s client-to-agent communication.
Harnesses also already distinguish delivery modes: Claude Code exposes --system-prompt (replace the built-in prompt) and --append-system-prompt (add to it), Codex has model_instructions_file and developer_instructions, and Goose has set and append. A protocol-level design needs to be able to express both.
What we propose to do about it
What are you proposing to improve the situation?
Capability advertisement
Agents that support client-provided system prompts advertise asystemPrompt session capability in the initialize response, following the established convention: omitted or null means unsupported, {} means supported. In v1 it lives under agentCapabilities.sessionCapabilities:
capabilities.session.systemPrompt.
Clients MUST NOT include systemPrompt in any request unless the agent advertised the capability.
Setting a system prompt
Clients provide the system prompt onsession/new as an object with a mode and content. content is a ContentBlock[], matching the content model used by session/prompt:
session/new is the natural place for this — it already serves as the configuration point for cwd and mcpServers, and system prompts are fundamentally session-scoped (they define the agent’s behavioral context for the entire interaction).
mode is required, so every request states which behavior it asks for. The object can also carry _meta for agent-specific options that only make sense alongside the prompt.
Text content blocks are the baseline: any agent advertising the capability MUST accept ContentBlock::Text. Other content block types are not part of the initial baseline and may be introduced later as sub-capabilities (see FAQ).
Delivery modes: append and override
"append": the client content is added to the agent’s own instructions rather than replacing them. Agents SHOULD give it system-level precedence — for example, in the model’s system prompt or developer instructions rather than in user-turn content — but where it is placed, in what order, and how it is formatted is implementation-defined. Every agent that advertises the capability MUST support append mode. Existing harnesses already behave this way: Goose adds appended text under an# Additional Instructionsheading, Claude Code’sappendadds text after its preset, and Codex deliversdeveloper_instructionsas a developer message alongside its base instructions."override": the client content replaces the agent’s built-in default instructions. This mode only changes the instructions the agent itself authors: it does not change tool availability, MCP servers, permissions, or session configuration. Context the agent supplies from the workspace or environment — project instruction files such asAGENTS.md, environment details, skill listings — MAY still be included; every harness surveyed keeps some of it (see FAQ), and agents SHOULD document what they keep. Clients that use override take responsibility for any tool guidance or safety instructions the agent’s default prompt provided. This mode is opt-in on both sides: agents advertise it via themode.overridesub-capability, and clients MUST NOT request it unless advertised.
mode, mirroring the request’s mode field. Append needs no entry because every agent advertising the capability supports it. An agent supporting both modes advertises:
Session lifecycle
The samesystemPrompt object is accepted on the other session lifecycle requests: session/load, session/resume, and session/fork in v1, and session/resume and session/fork in v2. This follows the existing pattern for client-supplied session settings, which clients already re-send on these requests (cwd, mcpServers, additionalDirectories).
- Clients SHOULD send the same
systemPrompton these requests that they used when the session was created. Agents that rebuild their prompt from request parameters depend on it. - When a lifecycle request omits
systemPrompt, the resulting prompt is implementation-defined: the agent MAY keep using a prompt it stored with the session, or fall back to its default. Existing harnesses already persist the prompt in different ways (see FAQ), so this RFD requires agents neither to store it nor to discard a stored one. - When a lifecycle request supplies a
systemPromptthat differs from the one the session was using, the agent SHOULD apply it. An agent MAY defer the change until it next rebuilds its prompt (for example, after compaction) when applying it immediately would discard prompt caches or preserved reasoning.
Errors
If an agent that advertises the capability cannot apply a suppliedsystemPrompt — for example, an unadvertised mode, a content block type it does not support, or content that exceeds its limits — it MUST return an error and MUST NOT create, load, resume, or fork the session. invalid_params is the RECOMMENDED error for malformed or unsupported values. This gives clients an explicit failure rather than a session running without the instructions they intended.
Design choices
- Capability-gated opt-in. Without a capability, an “optional” field is a de facto breaking change: existing agents would silently ignore it, and the requirements below would retroactively put every existing agent in violation. The capability lets agents opt in and then be held to the behavior, and gives clients reliable feature detection —
protocolVersioncannot serve that role since it only tracks major versions. - A
systemPromptobject withmodeandcontent. Keeping the mode next to the content means there is no mode without a prompt to rule out, and leaves room for prompt-scoped_meta(see FAQ). - Content as
ContentBlock[]. Consistent withsession/promptand the rest of the protocol’s content model. Text is the required baseline; richer types are future sub-capabilities (see FAQ for why they are excluded from the initial version). - Both modes, append as baseline. Append is what Claude Code, Codex, and Goose already support and is the mode every advertising agent must support. Override has real demand (Claude Code, Codex, and Goose ship it) but changes the safety posture — so it is double opt-in: agent advertises, client explicitly requests.
- Re-sent on lifecycle requests. This follows the pattern for
cwd,mcpServers, andadditionalDirectories, while leaving room for harnesses that already persist the prompt (see FAQ).
Shiny future
How will things will play out once this feature exists?Clients provide behavioral instructions through a standard protocol field, and agents deliver them as system-level instructions. The current landscape of fragmented workarounds —
_meta extensions, agent-specific extension methods, user-message injection, pre-session environment configuration — is replaced by a single interoperable mechanism with explicit feature detection.
Buzz’s managed agent personas — role definitions, platform context, behavioral guidelines — reach the model as system-level instructions across every agent that advertises the capability (Goose, Claude Code adapters, or others). The same persona works across those agents without agent-specific plumbing. Goose’s recipe system can deliver instructions through the protocol instead of relying on filesystem indirection. An IDE can provide project-specific coding guidelines as system-level instructions. A CI/CD pipeline can configure an agent’s behavioral constraints for automated workflows. Adapters for harnesses that support prompt replacement (e.g., claude-agent-acp) can additionally expose override mode.
Agents that don’t support client system prompts continue working exactly as they do today — they simply don’t advertise the capability, and clients never send the field.
Implementation details and plan
Tell me more about your implementation. What is your detailed implementation plan?
Schema changes
Add asystemPrompt session capability (advertised in the initialize response):
SystemPrompt type and an optional systemPrompt field to the session lifecycle requests:
content is deserialized strictly, like session/prompt’s prompt: content blocks the agent cannot parse are not silently dropped, so the agent can return the error described above.
This is additive — no existing fields change, no breaking modifications.
ACP v1 and v2
The feature has the same shape in both protocol versions:mode decides how the agent fulfills the request, so it stays a closed enum in v2. The enum variant extension fallback does not apply: an agent that receives a mode it does not understand returns an error rather than guessing.
The schema implementation would be gated behind an unstable_client_system_prompt feature until the RFD reaches Preview, and the initialization and session setup docs for both versions would document the capability and the field.
Agent behavior
Agents that advertise thesystemPrompt capability:
- MUST accept
ContentBlock::Textcontent - MUST support append mode, and MUST support override mode if they advertise the
mode.overridesub-capability - MUST apply a
systemPromptsupplied onsession/newin the requested mode, or return an error as described above
systemPrompt is absent from session/new, the agent uses its own default system prompt (current behavior, unchanged). Agents that do not advertise the capability are unaffected — clients never send them the field.
Client behavior
- Clients MUST NOT include
systemPromptin any request unless the agent advertised thesystemPromptcapability - Clients MUST NOT use
"mode": "override"unless the agent advertised themode.overridesub-capability - Clients MUST NOT include content block types other than text unless a future sub-capability allows them
- Clients SHOULD re-send the session’s
systemPromptonsession/load,session/resume, andsession/fork
Security considerations
Content delivered at system level carries more weight with the model than user turns or tool output. Clients SHOULD only send content they trust at that level; for example, a persona authored by another workspace member is a trust decision for the client, not just a formatting choice. Override mode removes the agent’s default instructions, which can include tool guidance and safety rules. Agents that do not want to delegate their default instructions simply do not advertiseoverride. Agents MAY reject a system prompt that violates their own policy or limits, using the error behavior above.
Backward compatibility
The field is optional and capability-gated, so there is no default behavior change anywhere:- Existing clients that don’t send
systemPromptsee no difference. - Existing agents never receive the field, because they don’t advertise the capability and clients gate on it. The unknown-param hazard — an old agent may silently ignore the field (no error, no incorporation) or, if it validates strictly, reject the request — is closed by construction, matching the approach taken by
additionalDirectories. - Capability detection follows the established rule from initialization: all capabilities omitted in the
initializeresponse are treated as unsupported, and introducing a new capability is not a breaking change.
Frequently asked questions
What questions have arisen over the course of authoring this document or during subsequent discussions?
Why ContentBlock[] and not string?
An earlier draft proposed a plain string, matching MCP’s sampling/createMessage. Review feedback pushed toward consistency with the rest of ACP, and it’s the right call: session/prompt already uses ContentBlock[], the capability object gives us a natural place to gate richer content types later, and the marginal implementation cost over a string is near zero once the capability exists. Clients that only need plain text send a single text block.
Why is text the only baseline content type?
Thesession/prompt baseline requires text and resource links, so deviating for systemPrompt deserves justification. Each of the other ContentBlock variants is either hard to apply at session creation or has no known use case:
resource_linkis a pointer, not content. The agent would have to fetch and resolve the URI to text before session creation, with unspecified failure and lifecycle semantics (missing or oversized resources, snapshot-once vs. re-read, unreachable URIs). In a user prompt the agent can address a link with its own tools mid-turn; at session creation there is no turn to do it in.image/audiohave no known use case. Model system slots are typically text-only (Anthropic’ssystemaccepts text blocks only, and OpenAI’sinstructionsis a plain string), so an agent would have to deliver media elsewhere, such as a developer or user message at the start of the conversation, the way Claude Code delivers CLAUDE.md. The append rules allow that, but no client needs it yet.resource(embedded content) is the borderline case — inline delivery, can carry text mime-types — and is the plausible first future sub-capability if a real use case emerges. It’s excluded from the baseline because it adds URI/mime handling with no known consumer.
Why a systemPrompt object instead of a separate mode field?
An earlier draft used a bare ContentBlock[] plus a sibling systemPromptMode field. That allowed a mode without a prompt and needed extra client and agent rules to rule it out. An object keeps mode and content together, gives agent-specific options (such as Claude’s excludeDynamicSections) a _meta slot scoped to the prompt, and is a single field to repeat on lifecycle requests.
It also matches existing shapes. Elicitation selects its behavior with a mode field and gates each mode with a sub-capability, and both claude-agent-acp’s _meta.systemPrompt and Goose’s _goose/unstable/session/system-prompt/set carry the mode alongside the text.
What does override replace in existing harnesses?
Every surveyed harness replaces only its own default instructions. Tools stay available, and workspace context such as project instruction files is still supplied, either elsewhere in the conversation or appended after the replaced text:
What each harness keeps differs in the details, so this RFD defines override by what it replaces (the agent’s built-in default instructions) and what it must not change (tools, MCP servers, permissions, session configuration), and leaves the rest to the agent to document.
Why re-send the prompt on load, resume, and fork instead of having the agent restore it?
An earlier draft required agents to store the prompt and restore it on load and resume, and required forks to inherit it. Existing agents don’t agree on where the prompt lives:
No single storage rule fits all of them. Requiring agents to restore the prompt would force adapters like
claude-agent-acp to add their own storage, because Claude Code only keeps its record until compaction. Requiring “omitted means no prompt”, the rule additionalDirectories uses, would force Codex, Goose, and Claude Code to discard a prompt their harness already persists, which Claude Code cannot do before compaction. Asking clients to re-send the prompt, and leaving the omitted case implementation-defined, works with all of them and matches how clients already treat cwd and mcpServers.
How does this interact with session config options and modes?
Session config options (and v1 session modes) are agent-defined settings the client selects from;systemPrompt is client-authored content. They are independent mechanisms. Selecting a mode or config option does not remove the client-provided prompt, but how an agent combines its own mode- or config-driven instructions with it is implementation-defined — for example, an agent may add plan-mode instructions alongside an appended client prompt.
Why include both append and override in the first version?
Append alone looked sufficient until we accounted for existing harness behavior: Claude Code, Codex, and Goose all ship both a replace option and an append option, so ACP adapters over them could expose override on day one — deferring override wouldn’t buy simplicity, just a guaranteed second design round. The two modes have different safety postures, which the design reflects: append is the baseline (agents keep their own instructions), while override requires the agent to advertise theoverride sub-capability and the client to explicitly request the mode. An agent that never wants to give up its default instructions simply advertises systemPrompt: {} and never sees an override request.
What alternative approaches did you consider, and why did you settle on this one?
systemPromptonsession/prompt(per-turn): Would allow mid-session updates, matching how most LLM APIs send system content per-request. But it introduces ambiguous semantics whensystemPromptis omitted on a turn (inherit last value? revert to default?) and moves system prompt delivery from a one-time configuration to a repeated per-turn concern. For the common case — set instructions once at session start —session/newis simpler and sufficient.- Dedicated
session/set_system_promptmethod: Changing the prompt of a live session is a separate feature; Goose already ships one as_goose/unstable/session/system-prompt/set. This RFD covers session creation and the lifecycle requests, and a dedicated method can be added later without conflicting with it. - Standardize
_meta.systemPrompt: Violates_metadesign intent (see Status quo). - CWD file conventions (e.g., placing instruction files in the session
cwd): Agent-specific, not portable, and relies on the agent voluntarily reading the file. Not a protocol-level solution. - Environment variables or CLI flags: Pre-session only, not portable across agent implementations, can’t be session-specific without restarting the agent.
Revision history
- 2026-05-18: Initial draft
- 2026-05-19: Upgraded to MUST with response acknowledgment per PR review feedback
- 2026-05-21: Replaced the response acknowledgment (
systemPromptStatus) with a JSON-RPC error when the agent cannot apply the prompt - 2026-07-21: Capability-gated opt-in under
sessionCapabilities, type changed toContentBlock[]with text-only baseline, explicit append/override delivery modes withoverridesub-capability, invalid-combination and load/resume/fork semantics — per maintainer review - 2026-10-09: Moved to Draft. Replaced the bare array and
systemPromptModewith asystemPromptobject, grouped theoverridecapability undermode, accepted it on load/resume/fork with re-send semantics based on a survey of existing agents, covered ACP v1 and v2, defined what override replaces, and relaxed requirements that agents could not observe or implement