Session Channel
Reference for the ahp-session:/<uuid> channel — per-session state, the turn lifecycle, tool-call state machine, attachments, pending messages, input requests, and per-session customizations. See Session Channel specification for the wire-level overview.
JSON Schema: state.schema.json
State Types
SessionLifecycle
Session initialization state.
| Member | Value |
|---|---|
Creating | 'creating' |
Ready | 'ready' |
CreationFailed | 'creationFailed' |
SessionStatus
Bitset of summary-level session status flags.
Use bitwise checks instead of equality for non-terminal activity. For example, status & SessionStatus.InProgress matches both ordinary in-progress turns and turns that are paused waiting for input.
| Member | Value | Description |
|---|---|---|
Idle | 1 | Session is idle — no turn is active. |
Error | 1 << 1 | Session ended with an error. |
InProgress | 1 << 3 | A turn is actively streaming. |
InputNeeded | (1 << 3) | (1 << 4) | A turn is in progress but blocked waiting for user input or tool confirmation. |
IsRead | 1 << 5 | The client has viewed this session since its last modification. |
IsArchived | 1 << 6 | The session has been archived by the client. |
SessionMetadata
Metadata shared between the full {@link SessionState} (delivered when a client subscribes to a session's URI) and the lightweight {@link SessionSummary} (carried in the root-channel session catalog).
These fields describe the session at a glance and appear in both places. SessionState owns the authoritative values for a subscribed session; SessionSummary mirrors them into the catalog so clients that only render a session list don't have to subscribe to every session URI. The host keeps the catalog in sync via root/sessionSummaryChanged.
| Field | Type | Required | Description |
|---|---|---|---|
provider | string | Yes | Agent provider ID |
title | string | Yes | Session title |
status | SessionStatus | Yes | Current session status |
activity | string | No | Human-readable description of what the session is currently doing |
project | ProjectInfo | No | Server-owned project for this session |
workingDirectory | URI | No | The default working directory URI for this session. Individual chats MAY override via {@link ChatSummary.workingDirectory | their own workingDirectory}; this field acts as the fallback for any chat that does not. |
annotations | AnnotationsSummary | No | Lightweight summary of this session's inline annotations channel (ahp-session:/<uuid>/annotations). Surfaced so badge UI can render annotation / entry counts without subscribing. Absent when the session does not expose an annotations channel. |
SessionState
Full state for a single session, loaded when a client subscribes to the session's URI.
Inlines (denormalizes) every {@link SessionMetadata} field directly onto itself so subscribers receive one flat object instead of a nested summary. The lightweight catalog representation is {@link SessionSummary}, surfaced on the root channel; the host keeps the two in sync via root/sessionSummaryChanged.
| Field | Type | Required | Description |
|---|---|---|---|
lifecycle | SessionLifecycle | Yes | Session initialization state |
creationError | ErrorInfo | No | Error details if creation failed |
serverTools | ToolDefinition[] | No | Tools provided by the server (agent host) for this session |
activeClients | SessionActiveClient[] | Yes | The clients currently providing tools and interactive capabilities to this session. If multiple tools or customizations are provided by the same active client, an agent host MAY deduplicate them when exposed to a model, with a preference given to the client that started the turn. Membership is host-managed: clients add (or refresh) themselves with session/activeClientSet, and the host removes them with session/activeClientRemoved when they unsubscribe, disconnect without reconnecting in time, or reconnect without resubscribing to the session. |
chats | ChatSummary[] | Yes | Catalog of chats in this session. |
defaultChat | URI | No | The chat that receives input when the user addresses the session without selecting a specific chat. This is a UI routing hint, not a hierarchy marker — chats remain equal peers at the protocol level. Hosts MAY change this over the session's lifetime. |
config | SessionConfigState | No | Session configuration schema and current values |
customizations | Customization[] | No | Top-level customizations active in this session. Always one of the {@link Customization} variants: - Container customizations ({@link PluginCustomization}, {@link DirectoryCustomization}) whose children — agents, skills, prompts, rules, hooks, MCP servers — live in each container's {@link ContainerCustomizationBase.children | children} array. - Top-level {@link McpServerCustomization} entries the host surfaces directly (for example a globally-configured MCP server that isn't bundled in a plugin or directory). MCP servers may also appear as children of a container.Client-published plugins arrive via {@link SessionActiveClient.customizations | activeClients[].customizations} and the host propagates them into this list (typically with the container's clientId set and children populated). Clients publish in container shape only; bare MCP servers at the top level are server-originated. |
changesets | Changeset[] | No | Catalogue of changesets the server can produce for this session. Each entry advertises a subscribable view of file changes (uncommitted, session-wide, per-turn, etc.) and the URI template the client expands before subscribing. See {@link Changeset} for the full shape and {@link /guide/changesets | Changesets} for an overview of the model. |
inputNeeded | SessionInputRequest[] | No | Outstanding input the session is blocked on, aggregated across every chat so a client can discover and answer it from the session channel alone, without subscribing to individual chats. Each entry is self-sufficient: it carries the owning chat's URI plus every identifier the client needs to respond. A client answers by dispatching the ordinary chat/* action to that chat's channel — see {@link SessionInputRequest} for the per-variant response path. A present, non-empty list implies {@link SessionStatus.InputNeeded} on {@link SessionSummary.status}.Host-managed: the host upserts entries with session/inputNeededSet as chats raise requests and removes them with session/inputNeededRemoved once the underlying request resolves. |
_meta | Record<string, unknown> | No | Additional provider-specific metadata for this session. Clients MAY look for well-known keys here to provide enhanced UI. For example, a git key may provide extra git metadata about the session's workingDirectory. |
SessionActiveClient
A client currently providing tools and interactive capabilities to a session.
A session MAY have several active clients at once; entries in {@link SessionState.activeClients} are keyed by clientId. The server SHOULD automatically remove an active client when that client disconnects.
| Field | Type | Required | Description |
|---|---|---|---|
clientId | string | Yes | Client identifier (matches clientId from initialize) |
displayName | string | No | Human-readable client name (e.g. "VS Code") |
tools | ToolDefinition[] | Yes | Tools this client provides to the session |
customizations | ClientPluginCustomization[] | No | Plugin customizations this client contributes to the session. Clients publish in Open Plugins format — i.e. always container-shaped plugins. They MAY synthesize virtual plugins in memory and rely on the host to expand them into concrete children inside {@link SessionState.customizations}. |
SessionInputRequestKind
Discriminant for the kinds of outstanding input a session can surface in {@link SessionState.inputNeeded}.
This is a general/typological union (not a lifecycle), so the discriminant is a *Kind.
| Member | Value | Description |
|---|---|---|
ChatInput | 'chatInput' | A user-facing elicitation mirrored from a chat's inputRequests. |
ToolConfirmation | 'toolConfirmation' | A tool call awaiting parameter- or result-confirmation. |
ToolClientExecution | 'toolClientExecution' | A running tool the session wants an active client to execute. |
SessionChatInputRequest
A user-input elicitation surfaced at the session level, mirroring one entry of the owning chat's {@link ChatState.inputRequests}.
Respond by dispatching chat/inputCompleted (or syncing drafts with chat/inputAnswerChanged) to {@link SessionInputRequestBase.chat | chat}, keyed by {@link ChatInputRequest.id | request.id}.
| Field | Type | Description |
|---|---|---|
kind | SessionInputRequestKind.ChatInput | |
request | ChatInputRequest | The mirrored chat input request. |
SessionToolConfirmationRequest
A tool call blocked on confirmation — either parameter confirmation before execution or result confirmation after — surfaced at the session level.
Respond by dispatching chat/toolCallConfirmed (for {@link ToolCallPendingConfirmationState}) or chat/toolCallResultConfirmed (for {@link ToolCallPendingResultConfirmationState}) to {@link SessionInputRequestBase.chat | chat}, keyed by turnId and toolCall.toolCallId.
| Field | Type | Description |
|---|---|---|
kind | SessionInputRequestKind.ToolConfirmation | |
turnId | string | The turn the tool call belongs to. |
toolCall | ToolCallConfirmationState | The tool call awaiting confirmation. |
SessionToolClientExecutionRequest
A running tool whose execution is delegated to an active client. Surfaced so a client that provides the tool can pick up the work without subscribing to the owning chat.
The {@link toolCall} is always a {@link ToolCallRunningState} (a {@link ToolCallState} in running status) whose {@link ToolCallRunningState.contributor | contributor} is a client {@link ToolCallClientContributor} whose clientId matches the denormalized {@link clientId} here. Execute and report the result by dispatching chat/toolCallComplete (and optionally streaming with chat/toolCallContentChanged) to {@link SessionInputRequestBase.chat | chat}, keyed by turnId and toolCall.toolCallId.
| Field | Type | Description |
|---|---|---|
kind | SessionInputRequestKind.ToolClientExecution | |
turnId | string | The turn the tool call belongs to. |
clientId | string | The clientId expected to execute the tool. Matches the clientId of the tool call's client {@link ToolCallContributor}. |
toolCall | ToolCallState | The running tool call the session wants the owning client to execute. The host only ever populates this with a {@link ToolCallRunningState} (i.e. a {@link ToolCallState} in running status). |
SessionInputRequest
One outstanding piece of input a session is blocked on, aggregated across all chats in {@link SessionState.inputNeeded}.
Each entry is self-sufficient: it carries the owning {@link SessionInputRequestBase.chat | chat} URI plus every identifier needed to construct the response, so a client can answer by dispatching the ordinary chat/* action (chat/inputCompleted, chat/toolCallConfirmed, chat/toolCallComplete, …) to that chat's channel without having subscribed to the chat. The host removes the entry with session/inputNeededRemoved once the underlying request resolves.
SessionChatInputRequest | SessionToolConfirmationRequest | SessionToolClientExecutionRequest
ProjectInfo
Server-owned project metadata for a session.
| Field | Type | Description |
|---|---|---|
uri | URI | Project URI |
displayName | string | Human-readable project name |
SessionSummary
Lightweight catalog entry summarizing one session. Surfaced via {@link RootChannelCommands.listSessions | root/listSessions} and root/sessionAdded/root/sessionSummaryChanged notifications.
Aggregation across chats. Once a session contains more than one chat, several SessionSummary fields are derived from the underlying {@link SessionState.chats | chat catalog}. Producers SHOULD follow these rules so clients that only consume the session summary (e.g. a session list) still see meaningful state:
status: take the activity bits (Idle/InProgress/InputNeeded/Error— bits 0–4) from the {@link SessionState.defaultChat | default chat} when present, else from the most recently modified chat. PromoteInputNeededwhenever any chat in the session needs input, and promoteErrorwhenever any chat is in an error state — both override the default-chat bits. The orthogonal flag bits (IsRead,IsArchived) remain session-scoped.activity: mirror the activity string of the default chat, or of the chat currently driving the promoted status bits when a non-default chat wins (e.g. the chat that raisedInputNeeded).modifiedAt: the max of all chats'modifiedAt.workingDirectory: the session-level default. Individual chats MAY override via {@link ChatSummary.workingDirectory}; aggregating these up is meaningless and SHOULD NOT be attempted.changes: optional roll-up across all chats. Producers MAY sum the per-chat changeset stats or report the most expensive chat's stats — whichever is cheaper for the host to compute.
Sessions with a single chat trivially satisfy all of the above (the chat's values pass through unchanged). The rules only matter once a session carries multiple chats.
| Field | Type | Required | Description |
|---|---|---|---|
resource | URI | Yes | Session URI |
createdAt | string | Yes | Creation timestamp (ISO 8601, e.g. "2025-03-10T18:42:03.123Z") |
modifiedAt | string | Yes | Last modification timestamp (ISO 8601, e.g. "2025-03-10T18:42:03.123Z") |
changes | ChangesSummary | No | Aggregate summary of file changes associated with this session. Servers may populate this to give clients a quick at-a-glance view of the session's footprint (e.g., for list rendering) without requiring the client to subscribe to a changeset. |
_meta | Record<string, unknown> | No | Lightweight server-defined metadata clients may use for the session presentation. The protocol does not interpret these values; producers SHOULD keep the payload small because summaries appear in session lists and session notifications. |
ChangesSummary
Aggregate counts describing the file changes associated with a session.
All fields are optional so servers can populate only the metrics they cheaply have available.
| Field | Type | Required | Description |
|---|---|---|---|
additions | number | No | Total number of inserted lines across all changed files. |
deletions | number | No | Total number of deleted lines across all changed files. |
files | number | No | Number of files that have changes. |
AgentSelection
A selected custom agent for a session.
The uri identifies a specific custom agent (matching an {@link AgentCustomization.uri | AgentCustomization.uri} exposed via the session's effective customizations). Consumers resolve the agent's display name by looking up uri in the session's customization tree.
A message with no agent selected uses the provider's default behavior.
| Field | Type | Description |
|---|---|---|
uri | URI | Stable agent URI (matches an {@link AgentCustomization.uri}). |
SessionConfigPropertySchema
A session configuration property descriptor.
Extends the generic {@link ConfigPropertySchema} with session-specific display extensions.
| Field | Type | Required | Description |
|---|---|---|---|
enumDynamic | boolean | No | Display extension: when true, the full set of allowed values is too large to enumerate statically. The client SHOULD use sessionConfigCompletions to fetch matching values based on user input. Any values in enum are seed/recent values for initial display. |
sessionMutable | boolean | No | When true, the user may change this property after session creation |
SessionConfigSchema
A JSON Schema object describing available session configuration metadata.
| Field | Type | Required | Description |
|---|---|---|---|
type | 'object' | Yes | JSON Schema: always 'object' |
properties | Record<string, SessionConfigPropertySchema> | Yes | JSON Schema: property descriptors keyed by property id |
required | string[] | No | JSON Schema: list of required property ids |
SessionConfigState
Live session configuration metadata.
The schema describes the available configuration properties and the values contain the current value for each resolved property.
| Field | Type | Description |
|---|---|---|
schema | SessionConfigSchema | JSON Schema describing available configuration properties |
values | Record<string, unknown> | Current configuration values |
ToolDefinition
Describes a tool available in a session, provided by either the server or the active client.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Unique tool identifier |
title | string | No | Human-readable display name |
description | string | No | Description of what the tool does |
inputSchema | | No | JSON Schema defining the expected input parameters. Optional because client-provided tools may not have formal schemas. Mirrors MCP Tool.inputSchema. |
outputSchema | | No | JSON Schema defining the structure of the tool's output. Mirrors MCP Tool.outputSchema. |
annotations | ToolAnnotations | No | Behavioral hints about the tool. All properties are advisory. |
_meta | Record<string, unknown> | No | Additional provider-specific metadata. Mirrors the MCP _meta convention. |
ToolAnnotations
Behavioral hints about a tool. All properties are advisory and not guaranteed to faithfully describe tool behavior.
Mirrors MCP ToolAnnotations from the Model Context Protocol specification.
| Field | Type | Required | Description |
|---|---|---|---|
title | string | No | Alternate human-readable title |
readOnlyHint | boolean | No | Tool does not modify its environment (default: false) |
destructiveHint | boolean | No | Tool may perform destructive updates (default: true) |
idempotentHint | boolean | No | Repeated calls with the same arguments have no additional effect (default: false) |
openWorldHint | boolean | No | Tool may interact with external entities (default: true) |
CustomizationType
Discriminant for the kind of customization.
Top-level entries in {@link SessionState.customizations} and {@link AgentInfo.customizations} are either container customizations ({@link CustomizationType.Plugin | Plugin} or {@link CustomizationType.Directory | Directory}) or {@link CustomizationType.McpServer | McpServer} entries surfaced directly by the host. The remaining types appear only as children of a container.
| Member | Value |
|---|---|
Plugin | 'plugin' |
Directory | 'directory' |
Agent | 'agent' |
Skill | 'skill' |
Prompt | 'prompt' |
Rule | 'rule' |
Hook | 'hook' |
McpServer | 'mcpServer' |
ChildCustomizationType
Customization types that appear as children of a {@link PluginCustomization} or {@link DirectoryCustomization}.
CustomizationType.Agent | CustomizationType.Skill | CustomizationType.Prompt | CustomizationType.Rule | CustomizationType.Hook | CustomizationType.McpServer
CustomizationLoadStatus
Discriminant values for {@link CustomizationLoadState}.
| Member | Value |
|---|---|
Loading | 'loading' |
Loaded | 'loaded' |
Degraded | 'degraded' |
Error | 'error' |
CustomizationLoadingState
Container is being loaded by the host.
| Field | Type | Description |
|---|---|---|
kind | CustomizationLoadStatus.Loading |
CustomizationLoadedState
Container loaded successfully.
| Field | Type | Description |
|---|---|---|
kind | CustomizationLoadStatus.Loaded |
CustomizationDegradedState
Container partially loaded but has warnings.
| Field | Type | Description |
|---|---|---|
kind | CustomizationLoadStatus.Degraded | |
message | string | Human-readable description of the warning. |
CustomizationErrorState
Container failed to load.
| Field | Type | Description |
|---|---|---|
kind | CustomizationLoadStatus.Error | |
message | string | Human-readable error message. |
CustomizationLoadState
Discriminated load state for a container customization ({@link PluginCustomization} or {@link DirectoryCustomization}).
CustomizationLoadingState | CustomizationLoadedState | CustomizationDegradedState | CustomizationErrorState
PluginCustomization
An Open Plugins plugin.
| Field | Type | Required | Description |
|---|---|---|---|
type | CustomizationType.Plugin | Yes | |
version | string | No | Version of the plugin, sourced from the Open Plugins manifest's optional version field (semver, e.g. "1.2.0"). Absent when the manifest declares no version — the field is optional there — or the source has no version concept. Provenance / display only: the host neither parses nor enforces it. |
ClientPluginCustomization
A {@link PluginCustomization} as published by a client. Extends the server-facing shape with an opaque nonce so the host can detect when the client's view of a plugin has changed and re-parse only as needed.
Clients SHOULD include a nonce. Server-side fields like {@link ContainerCustomizationBase.children | children} and {@link ContainerCustomizationBase.load | load} are typically left absent on publication and populated by the host when the resolved plugin appears in {@link SessionState.customizations}.
| Field | Type | Required | Description |
|---|---|---|---|
nonce | string | No | Opaque version token used by the host to detect changes. |
DirectoryCustomization
A directory the host watches for this session.
Presence in the customization list signals that the host may discover customizations from this directory. When writable is true, clients MAY persist new customizations into the directory using resourceWrite; the host will then surface the resulting child via the customization actions.
The directory may not yet exist on disk.
| Field | Type | Description |
|---|---|---|
type | CustomizationType.Directory | |
contents | ChildCustomizationType | Which child customization type this directory holds. |
writable | boolean | Whether clients may write into this directory. |
AgentCustomization
A custom agent contributed by a plugin or directory.
Mirrors the Open Plugins agent format: a markdown file with YAML frontmatter, where the body is the agent's system prompt.
| Field | Type | Required | Description |
|---|---|---|---|
type | CustomizationType.Agent | Yes | |
description | string | No | Short description of what the agent specializes in and when to invoke it. Sourced from the agent file's frontmatter description. |
model | string | No | Model the agent is pinned to, sourced from the agent file's frontmatter model. Absent means the agent inherits the session's default model. |
tools | string[] | No | Allowlist of tool names the agent is scoped to, sourced from the agent file's frontmatter tools. A non-empty list restricts the agent to exactly those tools. Absent — or an empty list — imposes no restriction beyond the session default: the agent may use any available tool. Producers express "no restriction" by omitting the field rather than sending an empty array, so an empty list carries no meaning distinct from absence. |
disableModelInvocation | boolean | No | When true, the agent will not auto-delegate to this custom agent as a sub-agent; it can only be selected by the user. Absent or false means the agent may delegate to it. |
disableUserInvocation | boolean | No | When true, the user cannot select this custom agent (for example, in a picker); it remains available for the agent to auto-delegate to. Absent or false means the user may select it. |
SkillCustomization
A skill contributed by a plugin or directory.
Covers both Open Plugins skill formats — the skills/ directory layout (one subdirectory per skill, each with a SKILL.md) and the flatter commands/ directory of slash-command skills.
| Field | Type | Required | Description |
|---|---|---|---|
type | CustomizationType.Skill | Yes | |
description | string | No | Short description used for help text and auto-invocation matching. Sourced from the skill's frontmatter description. |
disableModelInvocation | boolean | No | When true, only the user can invoke this skill — the agent will not auto-invoke it. Sourced from the command skill's frontmatter disable-model-invocation flag. |
disableUserInvocation | boolean | No | When true, the user cannot directly invoke this skill (for example, as a slash command); it remains available for the agent to auto-invoke. Absent or false means the user may invoke it. |
PromptCustomization
A prompt contributed by a plugin or directory.
| Field | Type | Required | Description |
|---|---|---|---|
type | CustomizationType.Prompt | Yes | |
description | string | No | Short description of what the prompt does. |
RuleCustomization
A rule contributed by a plugin or directory.
Mirrors the Open Plugins rule format: a markdown file (e.g. .mdc) whose body is injected into context while the rule is active. This type also covers tool-specific "instruction" formats (e.g. VS Code Copilot's .github/instructions/*.md), which differ only in naming — they share the same semantics of description, optional always-on activation, and optional glob scoping.
| Field | Type | Required | Description |
|---|---|---|---|
type | CustomizationType.Rule | Yes | |
description | string | No | Description of what the rule enforces. |
alwaysApply | boolean | No | When true, the rule is always active (subject to globs if any). When false or absent, the agent or user decides whether to apply the rule. |
globs | string[] | No | Glob patterns the rule applies to. When present, the rule is only active for matching files. |
HookCustomization
A hook manifest contributed by a plugin or directory.
| Field | Type | Description |
|---|---|---|
type | CustomizationType.Hook |
McpServerCustomization
An MCP server contributed by a plugin or directory.
When the server is declared inline in the containing plugin manifest, uri points at the manifest file and {@link CustomizationBase.range | range} narrows it to the declaration's span.
The MCP server customization also reflects its current status.
| Field | Type | Required | Description |
|---|---|---|---|
type | CustomizationType.McpServer | Yes | |
enabled | boolean | Yes | Whether this MCP server is currently enabled. |
state | McpServerState | Yes | Current lifecycle state of the MCP server. |
channel | URI | No | An mcp://-protocol channel the client uses to side-channel traffic into the upstream MCP server itself. The channel is NOT a fresh raw MCP connection: it piggybacks on the AHP transport and skips the MCP initialize sequence.The agent host MAY only serve a subset of MCP on this channel; the served subset is described by domain-specific capabilities such as those in {@link McpServerCustomizationApps.capabilities}. The channel URI SHOULD be stable across the server's lifetime, but the agent host MAY change it (for example across a restart) and MAY only expose it while the server is in {@link McpServerStatus.Ready | Ready}. Absence means no side-channel is currently available. |
mcpApp | McpServerCustomizationApps | No | MCP App support. This property SHOULD be advertised for MCP servers which support apps. |
McpServerCustomizationApps
Information from the agent host needed to render MCP Apps served by this MCP server.
| Field | Type | Description |
|---|---|---|
capabilities | AhpMcpUiHostCapabilities | The subset of MCP App HostCapabilities the AHP host can satisfy for Views backed by this server. The client feeds these straight through into the hostCapabilities of the ui/initialize response delivered to the View. |
AhpMcpUiHostCapabilities
The subset of MCP App HostCapabilities an AHP host can derive from the upstream MCP server (and from AHP's own forwarding plumbing). Advertised on {@link McpServerCustomizationApps.capabilities} so clients can pass it through into the hostCapabilities of the ui/initialize response delivered to an MCP App View.
Field names mirror the MCP Apps spec exactly, so the AHP-side producer can pass them straight through into the hostCapabilities of the ui/initialize response delivered to the View.
Capabilities outside this set (openLinks, downloadFile, sandbox, experimental) are decided locally by whichever AHP client renders the View and are NOT part of this AHP-level advertisement — only the server-derived subset is.
An agent host MUST only advertise a capability when it actually accepts the corresponding methods/notifications on the mcp:// channel:
- {@link serverTools}: host proxies
tools/listandtools/callto the MCP server. WhenlistChangedistrue, the host also forwardsnotifications/tools/list_changed. - {@link serverResources}: host proxies
resources/read,resources/list, andresources/templates/listto the MCP server. WhenlistChangedistrue, the host also forwardsnotifications/resources/list_changed. - {@link logging}: host accepts
notifications/messagelog entries from the App and forwards them viamcpNotification(and forwardslogging/setLevelcalls to the server). - {@link sampling}: host serves
sampling/createMessageviamcpMethodCall. Whensampling.toolsis present, the host also accepts SEP-1577tools/toolChoice/tool_usecontent blocks insideCreateMessageRequest.
| Field | Type | Required | Description |
|---|---|---|---|
serverTools | | No | Producer proxies the MCP tools/* methods to the upstream server. |
serverResources | | No | Producer proxies the MCP resources/* methods to the upstream server. |
logging | Record<string, never> | No | Producer accepts notifications/message log entries from the App via mcpNotification. |
sampling | | No | Producer serves sampling/createMessage via mcpMethodCall. |
ChildCustomization
Child customizations that live inside a {@link PluginCustomization} or {@link DirectoryCustomization}.
AgentCustomization | SkillCustomization | PromptCustomization | RuleCustomization | HookCustomization | McpServerCustomization
Customization
A top-level customization active in a session. Either a container ({@link PluginCustomization} or {@link DirectoryCustomization}) whose leaf customizations live in its {@link ContainerCustomizationBase.children | children} array, or a bare {@link McpServerCustomization} surfaced directly by the host.
PluginCustomization | DirectoryCustomization | McpServerCustomization
McpServerStatus
Discriminant for the {@link McpServerState} union.
| Member | Value | Description |
|---|---|---|
Starting | 'starting' | Server has been registered but is not yet running. |
Ready | 'ready' | Server is running and serving requests. |
AuthRequired | 'authRequired' | Server is reachable but requires additional authentication before it can start, or before it can serve a particular request. Carries the RFC 9728 Protected Resource Metadata the client needs to obtain a token; the client then pushes the token via the existing authenticate command. |
Error | 'error' | Server failed to start, crashed, or otherwise transitioned to a fatal error. |
Stopped | 'stopped' | Server has been shut down. |
McpAuthRequiredReason
Why an MCP server is currently in the {@link McpServerStatus.AuthRequired} state. Mirrors the three failure modes defined by the MCP authorization spec.
| Member | Value | Description |
|---|---|---|
Required | 'required' | No token has been provided yet (HTTP 401, no prior token). |
Expired | 'expired' | A previously valid token expired or was revoked (HTTP 401). |
InsufficientScope | 'insufficientScope' | Step-up auth: a token is present but its scopes are insufficient for the requested operation (HTTP 403 with WWW-Authenticate: Bearer error="insufficient_scope").Unlike {@link Required} and {@link Expired} — which typically surface before any tool work is in flight — InsufficientScope is almost always triggered by an MCP request issued mid-turn (a tools/call, resources/read, etc.). The host SHOULD pair the {@link McpServerAuthRequiredState} transition with {@link SessionStatus.InputNeeded} on {@link SessionSummary.status | the session} so the activity becomes visible at the session-summary level, and clients SHOULD watch for this kind on any {@link McpServerCustomization | MCP server} backing a running tool call so they can present an explicit "grant more access" affordance tied to the blocked tool call. |
McpServerStartingState
Server is registered with the host but has not yet started.
| Field | Type | Description |
|---|---|---|
kind | McpServerStatus.Starting |
McpServerReadyState
Server is running and serving requests.
| Field | Type | Description |
|---|---|---|
kind | McpServerStatus.Ready |
McpServerAuthRequiredState
Server is reachable but cannot serve requests until the client authenticates. Mirrors the discovery flow defined by RFC 9728 (Protected Resource Metadata) and the OAuth 2.1 / RFC 6750 challenge semantics required by the MCP authorization spec.
Clients react to this state by calling the existing authenticate command with the {@link ProtectedResourceMetadata.resource | resource} carried here. There is no notify/authRequired notification for MCP servers — the action stream is the single source of truth.
When the transition is triggered by a request issued during a turn — most commonly {@link McpAuthRequiredReason.InsufficientScope | InsufficientScope} surfacing mid-tool-call — the host SHOULD also raise {@link SessionStatus.InputNeeded} on the session so the block is visible at the summary level. Clients SHOULD watch this status on any MCP server backing a running tool call and surface an explicit affordance (e.g. a "grant additional access" prompt) tied to that tool call, rather than relying on the user to notice the customization’s status badge.
| Field | Type | Required | Description |
|---|---|---|---|
kind | McpServerStatus.AuthRequired | Yes | |
reason | McpAuthRequiredReason | Yes | Why authentication is required. |
resource | ProtectedResourceMetadata | Yes | RFC 9728 Protected Resource Metadata. The resource field is the canonical MCP server URI per RFC 8707, used as the OAuth resource indicator. authorization_servers is REQUIRED by the MCP authorization spec. |
requiredScopes | string[] | No | Scopes required for the current challenge, parsed from the WWW-Authenticate: Bearer scope="…" header (or scopes_supported fallback). Authoritative for the next authorization request — clients MUST NOT assume any subset/superset relationship to resource.scopes_supported. |
description | string | No | Human-readable hint, typically from the OAuth error_description. |
McpServerErrorState
Server failed to start, crashed, or otherwise transitioned to a non-recoverable error. Use {@link McpServerStatus.AuthRequired} for authentication failures.
| Field | Type | Description |
|---|---|---|
kind | McpServerStatus.Error | |
error | ErrorInfo | Error details. |
McpServerStoppedState
Server has been shut down. The host MAY remove the server from the session entirely shortly after this state.
| Field | Type | Description |
|---|---|---|
kind | McpServerStatus.Stopped |
McpServerState
Discriminated union of all MCP server lifecycle states. Discriminated by kind (a {@link McpServerStatus} value).
McpServerStartingState | McpServerReadyState | McpServerAuthRequiredState | McpServerErrorState | McpServerStoppedState
Actions
Mutate SessionState. Scoped to a session URI via the enclosing ActionEnvelope.channel.
JSON Schema: actions.schema.json
session/ready
Session backend initialized successfully.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionReady |
session/creationFailed
Session backend failed to initialize.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionCreationFailed | |
error | ErrorInfo | Error details |
session/chatAdded
A chat was added to this session's catalog. Upsert semantics: if a chat with the same summary.resource already exists, the existing entry is replaced.
Mirrors the root-channel root/sessionAdded notification.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionChatAdded | |
summary | ChatSummary | The full summary of the newly added (or upserted) chat. |
session/chatRemoved
A chat was removed from this session's catalog. No-op when no entry matches.
Mirrors the root-channel root/sessionRemoved notification.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionChatRemoved | |
chat | URI | The URI of the chat to remove. |
session/chatUpdated
One existing chat's summary fields changed.
Partial-update semantics: only fields present in changes are written; omitted fields are preserved. Identity fields (resource) MUST NOT be carried in changes. No-op when no entry with chat exists — clients SHOULD then wait for a {@link SessionChatAddedAction | session/chatAdded}.
Mirrors the root-channel root/sessionSummaryChanged notification.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionChatUpdated | |
chat | URI | The URI of the chat whose summary changed. |
changes | Partial<ChatSummary> | Mutable summary fields that changed; omitted fields are unchanged. Identity fields ( resource) never change and MUST be omitted by senders; receivers SHOULD ignore them if present. |
session/defaultChatChanged
The default chat input-routing hint for this session changed.
| Field | Type | Required | Description |
|---|---|---|---|
type | ActionType.SessionDefaultChatChanged | Yes | |
defaultChat | URI | No | New default chat URI, or undefined to clear the hint. |
session/titleChanged
Session title updated. Fired by the server when the title is auto-generated from conversation, or dispatched by a client to rename a session.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionTitleChanged | |
title | string | New title |
session/isReadChanged
The read state of the session changed.
Dispatched by a client to mark a session as read (e.g. after viewing it) or unread (e.g. after new activity since the client last looked at it).
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionIsReadChanged | |
isRead | boolean | Whether the session has been read |
session/isArchivedChanged
The archived state of the session changed.
Dispatched by a client to archive a session (e.g. the task is complete) or to unarchive it.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionIsArchivedChanged | |
isArchived | boolean | Whether the session is archived |
session/activityChanged
The activity description of the session changed.
Dispatched by the server to indicate what the session is currently doing (e.g. running a tool, thinking). Clear activity by setting it to undefined.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionActivityChanged | |
activity | string | undefined | Human-readable description of current activity, or undefined to clear |
session/changesetsChanged
The {@link Changeset | catalogue of changesets} the agent host advertises for this session changed. Replaces {@link SessionState.changesets | state.changesets} entirely (full-replacement semantics) — set to undefined to clear the catalogue.
Producers dispatch this whenever entries are added or removed. The fan-out happens through this action so observers see catalogue mutations in the same {@link ChangesetAction | per-changeset} action stream they already follow for file-level updates.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionChangesetsChanged | |
changesets | Changeset[] | undefined | New catalogue, or undefined to clear it |
session/serverToolsChanged
Server tools for this session have changed.
Full-replacement semantics: the tools array replaces the previous serverTools entirely.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionServerToolsChanged | |
tools | ToolDefinition[] | Updated server tools list (full replacement) |
session/activeClientSet
An active client for this session was added or updated.
Upsert semantics keyed by {@link SessionActiveClient.clientId | clientId}: a client dispatches this action with its own SessionActiveClient to join the session's active clients or refresh its entry, replacing any existing entry that has the same clientId. Multiple clients may be active at once. This is also how a client updates its published tools or customizations — re-dispatch with the full, updated entry. Use {@link SessionActiveClientRemovedAction | session/activeClientRemoved} to leave. The server SHOULD automatically dispatch that removal when an active client disconnects.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionActiveClientSet | |
activeClient | SessionActiveClient | The active client to add or update, matched by clientId. |
session/activeClientRemoved
An active client was removed from this session.
Removes the entry for the client identified by clientId from {@link SessionState.activeClients}; a no-op when no entry matches.
The host SHOULD dispatch this automatically when a client stops participating in the session — for example when it unsubscribes from the session channel, when it disconnects and does not reconnect within a host-defined grace period, or when a reconnect command's subscriptions omit a session the client was still active in. When removing a client, the host SHOULD also cancel that client's in-flight tool calls — those whose tool call state carries a client ToolCallContributor with the matching clientId — by dispatching chat/toolCallComplete with result.success = false. (There is no per-tool-call server cancel; a failed completion is the cancellation mechanism, and the call ends in completed status with a failed result.)
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionActiveClientRemoved | |
clientId | string | The clientId of the active client to remove. |
session/inputNeededSet
A session-level input request was added or updated.
Upsert semantics keyed by {@link SessionInputRequest.id | request.id}: the host dispatches this with the full {@link SessionInputRequest} to append a new entry to {@link SessionState.inputNeeded} or replace the existing entry with the same id.
Server-originated: the host mirrors chat-level requests (elicitations, tool confirmations, client-tool executions) into the session aggregate so clients subscribed only to the session channel can discover them. Clients respond by dispatching the ordinary chat/* action to the entry's chat channel — see {@link SessionInputRequest}.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionInputNeededSet | |
request | SessionInputRequest | The input request to add or update, matched by id. |
session/inputNeededRemoved
A session-level input request was removed.
Removes the entry identified by id from {@link SessionState.inputNeeded}; a no-op when no entry matches.
Server-originated: the host dispatches this once the underlying request resolves (the user answers, the tool call is confirmed, or the client reports its result).
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionInputNeededRemoved | |
id | string | The id of the input request to remove. |
session/customizationsChanged
The session's customizations have changed.
Full-replacement semantics: the customizations array replaces the previous customizations entirely.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionCustomizationsChanged | |
customizations | Customization[] | Updated customization list (full replacement). |
session/customizationToggled
A client toggled a customization on or off.
Matches id against every top-level customization first — a plugin or directory container, or a bare top-level MCP server — then against the children inside each container (a skill, agent, or other entry), and sets the matched entry's enabled flag. Disabling a container still disables all of its children — the effective state of a child is container.enabled && (child.enabled ?? true) — so toggling a child only matters while its container is enabled. Is a no-op when no customization has the given id.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionCustomizationToggled | |
id | string | The id of the container or child to toggle. |
enabled | boolean | Whether to enable or disable the targeted customization. |
session/customizationUpdated
Upserts a top-level customization (plugin or directory).
The reducer locates the existing entry by customization.id:
- If found, the entry is replaced entirely with
customization, including itschildrenarray. To preserve existing children, the host must include them on the payload. - If not found, the entry is appended.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionCustomizationUpdated | |
customization | Customization | The customization to upsert (matched by customization.id). |
session/customizationRemoved
Removes a customization by id.
Searches every container and its children for the entry. If the entry is a container, its children are removed with it. Is a no-op when no matching id is found.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionCustomizationRemoved | |
id | string | The id of the customization to remove. |
session/mcpServerStateChanged
Updates the runtime fields of an existing {@link McpServerCustomization} — narrow alternative to {@link SessionCustomizationUpdatedAction} for the high-frequency starting ↔ ready ↔ authRequired transitions.
Locates the target entry by id, searching both the top-level customization list and the children array of every container. Replaces the entry's {@link McpServerCustomization.state | state} and {@link McpServerCustomization.channel | channel} (full-replacement semantics: omit channel to clear an existing channel URI). Other fields of the customization are preserved.
Is a no-op when no matching McpServerCustomization is found. To update any other field (name, icons, mcpApp capabilities, etc.) use {@link SessionCustomizationUpdatedAction} instead.
When the transition is to {@link McpServerStatus.AuthRequired} because of a request issued mid-turn, the host SHOULD also raise {@link SessionStatus.InputNeeded} on the session — see {@link McpServerAuthRequiredState} for the rationale.
| Field | Type | Required | Description |
|---|---|---|---|
type | ActionType.SessionMcpServerStateChanged | Yes | |
id | string | Yes | The id of the {@link McpServerCustomization} to update. |
state | McpServerState | Yes | The new lifecycle state. |
channel | URI | No | Updated mcp:// side-channel URI. Full-replacement: omit to clear an existing channel (typical when leaving {@link McpServerStatus.Ready | Ready}). |
session/mcpServerStartRequested
Requests that the host start or restart an existing {@link McpServerCustomization}.
Locates the target entry by id, searching both the top-level customization list and the children array of every container. The reducer optimistically moves the server to {@link McpServerStatus.Starting | starting} and clears any previous {@link McpServerCustomization.channel | channel}; the host remains authoritative and SHOULD follow with {@link SessionMcpServerStateChangedAction | session/mcpServerStateChanged} once the server becomes ready, needs authentication, fails, or is rejected. Is a no-op when no matching McpServerCustomization is found.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionMcpServerStartRequested | |
id | string | The id of the {@link McpServerCustomization} to start. |
session/mcpServerStopRequested
Requests that the host stop an existing {@link McpServerCustomization}.
Locates the target entry by id, searching both the top-level customization list and the children array of every container. The reducer optimistically moves the server to {@link McpServerStatus.Stopped | stopped} and clears any previous {@link McpServerCustomization.channel | channel}. Replacing an {@link McpServerStatus.AuthRequired | authRequired} lifecycle state with stopped unblocks the server from waiting on authentication. If the host also raised session-level input-needed state solely for that MCP server, it SHOULD remove that input-needed entry when accepting the stop.
The host remains authoritative and MAY reject the action or follow with {@link SessionMcpServerStateChangedAction | session/mcpServerStateChanged} if the final lifecycle state differs. Is a no-op when no matching McpServerCustomization is found.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionMcpServerStopRequested | |
id | string | The id of the {@link McpServerCustomization} to stop. |
session/configChanged
Client changed a mutable config value mid-session.
Only properties with sessionMutable: true in the config schema may be changed. The server validates and broadcasts the action; the reducer merges the new values into state.config.values.
| Field | Type | Required | Description |
|---|---|---|---|
type | ActionType.SessionConfigChanged | Yes | |
config | Record<string, unknown> | Yes | Updated config values |
replace | boolean | No | When true, replaces all config values instead of merging |
session/metaChanged
The session's _meta side-channel changed. Replaces state._meta entirely (full-replacement semantics). Producers SHOULD merge any keys they wish to preserve into the new value before dispatching.
| Field | Type | Description |
|---|---|---|
type | ActionType.SessionMetaChanged | |
_meta | Record<string, unknown> | undefined | New _meta payload, or undefined to clear it |
Commands
JSON Schema: commands.schema.json
createSession
| Property | Value |
|---|---|
| Direction | Client → Server |
| Type | Request |
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
channel | URI | Yes | Session URI (client-chosen, e.g. ahp-session:/<uuid>) |
provider | string | No | Agent provider ID |
workingDirectory | URI | No | Working directory for the session |
fork | SessionForkSource | No | Fork from an existing session. The new session is populated with content from the source session up to and including the specified turn's response. |
config | Record<string, unknown> | No | Agent-specific configuration values collected via resolveSessionConfig. Keys and values correspond to the schema returned by the server. |
activeClient | SessionActiveClient | No | Eagerly claim an active client role for the new session. When provided, the server initializes the session with this client as an active client, equivalent to dispatching a session/activeClientSet action immediately after creation. The clientId MUST match the clientId the creating client supplied in initialize. |
progressToken | string | No | Opt-in progress token. When set, the client is offering to receive progress notifications (see ProgressParams) for any long-running work the server does to bring this session up — most notably the lazy, first-use download of the provider's native SDK. The server echoes this exact token on every progress frame so the client can correlate it to this createSession call (and the UI awaiting it).The token MUST be unique across the client's active requests. The server MAY ignore it (e.g. when nothing long-running is needed), in which case no progress notifications are emitted. |
Result: null on success.
disposeSession
Disposes a session and cleans up server-side resources.
The server broadcasts a root/sessionRemoved notification to all clients.
| Property | Value |
|---|---|
| Direction | Client → Server |
| Type | Request |
Parameters:
No parameters.
Result: null on success.
fetchTurns
Requests that the host load older historical turns into a chat state.
The command result does not carry turns. Instead, before responding, the host MUST dispatch chat/turnsLoaded to insert any loaded turns into the chat channel's turns state, ahead of the already-loaded window, and update or clear turnsNextCursor.
Before applying any operation that references a turn outside the currently loaded window, the host MUST eagerly load enough older turns into state for that operation to reduce against valid state.
| Property | Value |
|---|---|
| Direction | Client → Server |
| Type | Request |
Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
channel | URI | Yes | Chat URI |
cursor | string | No | Opaque cursor from ChatState.turnsNextCursor.The host MUST reject unrecognised cursors with InvalidParams. Omit only when asking the host to opportunistically load its next older page for the chat, if any. |
Result:
(empty object)
Example:
// Client → Server (load the next page indicated by ChatState.turnsNextCursor)
{ "jsonrpc": "2.0", "id": 8, "method": "fetchTurns",
"params": { "channel": "ahp-chat:/<uuid>", "cursor": "opaque-cursor" } }
// Server updates chat state, then responds
{ "jsonrpc": "2.0", "id": 8, "result": {} }completions
Requests completion items for a partially-typed input (e.g. a user message the user is currently composing). Used to power @-mention pickers, file/symbol references, and similar inline-completion experiences.
Servers SHOULD treat this command as best-effort and return promptly. The client SHOULD debounce calls to avoid flooding the server with requests on every keystroke.
| Property | Value |
|---|---|
| Direction | Client → Server |
| Type | Request |
Parameters:
| Field | Type | Description |
|---|---|---|
kind | CompletionItemKind | What kind of completion is being requested. |
channel | URI | The chat URI the completion is being requested for. |
text | string | The complete text of the input being completed (e.g. the full user message text typed so far). |
offset | number | The character offset within text at which the completion is requested, measured in UTF-16 code units. MUST satisfy 0 <= offset <= text.length. |
Result:
| Field | Type | Description |
|---|---|---|
items | CompletionItem[] | The completion items, in the order the server suggests displaying them. |
Example:
// User has typed "look at
---