Skip to content

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.

MemberValue
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.

MemberValueDescription
Idle1Session is idle — no turn is active.
Error1 << 1Session ended with an error.
InProgress1 << 3A turn is actively streaming.
InputNeeded(1 << 3) | (1 << 4)A turn is in progress but blocked waiting for user input or tool confirmation.
IsRead1 << 5The client has viewed this session since its last modification.
IsArchived1 << 6The 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.

FieldTypeRequiredDescription
providerstringYesAgent provider ID
titlestringYesSession title
statusSessionStatusYesCurrent session status
activitystringNoHuman-readable description of what the session is currently doing
projectProjectInfoNoServer-owned project for this session
workingDirectoryURINoThe 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.
annotationsAnnotationsSummaryNoLightweight summary of this session's inline annotations channel (ahp-session:/&lt;uuid&gt;/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.

FieldTypeRequiredDescription
lifecycleSessionLifecycleYesSession initialization state
creationErrorErrorInfoNoError details if creation failed
serverToolsToolDefinition[]NoTools provided by the server (agent host) for this session
activeClientsSessionActiveClient[]YesThe 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.
chatsChatSummary[]YesCatalog of chats in this session.
defaultChatURINoThe 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.
configSessionConfigStateNoSession configuration schema and current values
customizationsCustomization[]NoTop-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.
changesetsChangeset[]NoCatalogue 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.
inputNeededSessionInputRequest[]NoOutstanding 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.
_metaRecord<string, unknown>NoAdditional 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.

FieldTypeRequiredDescription
clientIdstringYesClient identifier (matches clientId from initialize)
displayNamestringNoHuman-readable client name (e.g. "VS Code")
toolsToolDefinition[]YesTools this client provides to the session
customizationsClientPluginCustomization[]NoPlugin 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.

MemberValueDescription
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}.

FieldTypeDescription
kindSessionInputRequestKind.ChatInput
requestChatInputRequestThe 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.

FieldTypeDescription
kindSessionInputRequestKind.ToolConfirmation
turnIdstringThe turn the tool call belongs to.
toolCallToolCallConfirmationStateThe 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.

FieldTypeDescription
kindSessionInputRequestKind.ToolClientExecution
turnIdstringThe turn the tool call belongs to.
clientIdstringThe clientId expected to execute the tool. Matches the clientId of the tool call's client {@link ToolCallContributor}.
toolCallToolCallStateThe 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.

FieldTypeDescription
uriURIProject URI
displayNamestringHuman-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. Promote InputNeeded whenever any chat in the session needs input, and promote Error whenever 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 raised InputNeeded).
  • 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.

FieldTypeRequiredDescription
resourceURIYesSession URI
createdAtstringYesCreation timestamp (ISO 8601, e.g. "2025-03-10T18:42:03.123Z")
modifiedAtstringYesLast modification timestamp (ISO 8601, e.g. "2025-03-10T18:42:03.123Z")
changesChangesSummaryNoAggregate 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.
_metaRecord<string, unknown>NoLightweight 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.

FieldTypeRequiredDescription
additionsnumberNoTotal number of inserted lines across all changed files.
deletionsnumberNoTotal number of deleted lines across all changed files.
filesnumberNoNumber 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.

FieldTypeDescription
uriURIStable agent URI (matches an {@link AgentCustomization.uri}).

SessionConfigPropertySchema

A session configuration property descriptor.

Extends the generic {@link ConfigPropertySchema} with session-specific display extensions.

FieldTypeRequiredDescription
enumDynamicbooleanNoDisplay 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.
sessionMutablebooleanNoWhen true, the user may change this property after session creation

SessionConfigSchema

A JSON Schema object describing available session configuration metadata.

FieldTypeRequiredDescription
type'object'YesJSON Schema: always 'object'
propertiesRecord<string, SessionConfigPropertySchema>YesJSON Schema: property descriptors keyed by property id
requiredstring[]NoJSON 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.

FieldTypeDescription
schemaSessionConfigSchemaJSON Schema describing available configuration properties
valuesRecord<string, unknown>Current configuration values

ToolDefinition

Describes a tool available in a session, provided by either the server or the active client.

FieldTypeRequiredDescription
namestringYesUnique tool identifier
titlestringNoHuman-readable display name
descriptionstringNoDescription of what the tool does
inputSchema
{
  type: 'object';
  properties?: Record<string,
  object>;
  required?: string[];
}
NoJSON Schema defining the expected input parameters.

Optional because client-provided tools may not have formal schemas. Mirrors MCP Tool.inputSchema.
outputSchema
{
  type: 'object';
  properties?: Record<string,
  object>;
  required?: string[];
}
NoJSON Schema defining the structure of the tool's output.

Mirrors MCP Tool.outputSchema.
annotationsToolAnnotationsNoBehavioral hints about the tool. All properties are advisory.
_metaRecord<string, unknown>NoAdditional 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.

FieldTypeRequiredDescription
titlestringNoAlternate human-readable title
readOnlyHintbooleanNoTool does not modify its environment (default: false)
destructiveHintbooleanNoTool may perform destructive updates (default: true)
idempotentHintbooleanNoRepeated calls with the same arguments have no additional effect (default: false)
openWorldHintbooleanNoTool 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.

MemberValue
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}.

MemberValue
Loading'loading'
Loaded'loaded'
Degraded'degraded'
Error'error'

CustomizationLoadingState

Container is being loaded by the host.

FieldTypeDescription
kindCustomizationLoadStatus.Loading

CustomizationLoadedState

Container loaded successfully.

FieldTypeDescription
kindCustomizationLoadStatus.Loaded

CustomizationDegradedState

Container partially loaded but has warnings.

FieldTypeDescription
kindCustomizationLoadStatus.Degraded
messagestringHuman-readable description of the warning.

CustomizationErrorState

Container failed to load.

FieldTypeDescription
kindCustomizationLoadStatus.Error
messagestringHuman-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.

FieldTypeRequiredDescription
typeCustomizationType.PluginYes
versionstringNoVersion 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}.

FieldTypeRequiredDescription
noncestringNoOpaque 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.

FieldTypeDescription
typeCustomizationType.Directory
contentsChildCustomizationTypeWhich child customization type this directory holds.
writablebooleanWhether 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.

FieldTypeRequiredDescription
typeCustomizationType.AgentYes
descriptionstringNoShort description of what the agent specializes in and when to invoke it. Sourced from the agent file's frontmatter description.
modelstringNoModel the agent is pinned to, sourced from the agent file's frontmatter model. Absent means the agent inherits the session's default model.
toolsstring[]NoAllowlist 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.
disableModelInvocationbooleanNoWhen 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.
disableUserInvocationbooleanNoWhen 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.

FieldTypeRequiredDescription
typeCustomizationType.SkillYes
descriptionstringNoShort description used for help text and auto-invocation matching. Sourced from the skill's frontmatter description.
disableModelInvocationbooleanNoWhen 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.
disableUserInvocationbooleanNoWhen 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.

FieldTypeRequiredDescription
typeCustomizationType.PromptYes
descriptionstringNoShort 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.

FieldTypeRequiredDescription
typeCustomizationType.RuleYes
descriptionstringNoDescription of what the rule enforces.
alwaysApplybooleanNoWhen 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.
globsstring[]NoGlob 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.

FieldTypeDescription
typeCustomizationType.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.

FieldTypeRequiredDescription
typeCustomizationType.McpServerYes
enabledbooleanYesWhether this MCP server is currently enabled.
stateMcpServerStateYesCurrent lifecycle state of the MCP server.
channelURINoAn 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.
mcpAppMcpServerCustomizationAppsNoMCP 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.

FieldTypeDescription
capabilitiesAhpMcpUiHostCapabilitiesThe 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/list and tools/call to the MCP server. When listChanged is true, the host also forwards notifications/tools/list_changed.
  • {@link serverResources}: host proxies resources/read, resources/list, and resources/templates/list to the MCP server. When listChanged is true, the host also forwards notifications/resources/list_changed.
  • {@link logging}: host accepts notifications/message log entries from the App and forwards them via mcpNotification (and forwards logging/setLevel calls to the server).
  • {@link sampling}: host serves sampling/createMessage via mcpMethodCall. When sampling.tools is present, the host also accepts SEP-1577 tools / toolChoice / tool_use content blocks inside CreateMessageRequest.
FieldTypeRequiredDescription
serverTools
{
  listChanged?: boolean;
}
NoProducer proxies the MCP tools/* methods to the upstream server.
serverResources
{
  listChanged?: boolean;
}
NoProducer proxies the MCP resources/* methods to the upstream server.
loggingRecord<string, never>NoProducer accepts notifications/message log entries from the App via mcpNotification.
sampling
{
  tools?: Record<string,
  never>;
}
NoProducer 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.

MemberValueDescription
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.

MemberValueDescription
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.

FieldTypeDescription
kindMcpServerStatus.Starting

McpServerReadyState

Server is running and serving requests.

FieldTypeDescription
kindMcpServerStatus.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.

FieldTypeRequiredDescription
kindMcpServerStatus.AuthRequiredYes
reasonMcpAuthRequiredReasonYesWhy authentication is required.
resourceProtectedResourceMetadataYesRFC 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.
requiredScopesstring[]NoScopes 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.
descriptionstringNoHuman-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.

FieldTypeDescription
kindMcpServerStatus.Error
errorErrorInfoError details.

McpServerStoppedState

Server has been shut down. The host MAY remove the server from the session entirely shortly after this state.

FieldTypeDescription
kindMcpServerStatus.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.

FieldTypeDescription
typeActionType.SessionReady

session/creationFailed

Session backend failed to initialize.

FieldTypeDescription
typeActionType.SessionCreationFailed
errorErrorInfoError 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.

FieldTypeDescription
typeActionType.SessionChatAdded
summaryChatSummaryThe 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.

FieldTypeDescription
typeActionType.SessionChatRemoved
chatURIThe 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.

FieldTypeDescription
typeActionType.SessionChatUpdated
chatURIThe URI of the chat whose summary changed.
changesPartial<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.

FieldTypeRequiredDescription
typeActionType.SessionDefaultChatChangedYes
defaultChatURINoNew 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.

FieldTypeDescription
typeActionType.SessionTitleChanged
titlestringNew 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).

FieldTypeDescription
typeActionType.SessionIsReadChanged
isReadbooleanWhether 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.

FieldTypeDescription
typeActionType.SessionIsArchivedChanged
isArchivedbooleanWhether 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.

FieldTypeDescription
typeActionType.SessionActivityChanged
activitystring | undefinedHuman-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.

FieldTypeDescription
typeActionType.SessionChangesetsChanged
changesetsChangeset[] | undefinedNew 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.

FieldTypeDescription
typeActionType.SessionServerToolsChanged
toolsToolDefinition[]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.

FieldTypeDescription
typeActionType.SessionActiveClientSet
activeClientSessionActiveClientThe 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.)

FieldTypeDescription
typeActionType.SessionActiveClientRemoved
clientIdstringThe 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}.

FieldTypeDescription
typeActionType.SessionInputNeededSet
requestSessionInputRequestThe 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).

FieldTypeDescription
typeActionType.SessionInputNeededRemoved
idstringThe 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.

FieldTypeDescription
typeActionType.SessionCustomizationsChanged
customizationsCustomization[]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.

FieldTypeDescription
typeActionType.SessionCustomizationToggled
idstringThe id of the container or child to toggle.
enabledbooleanWhether 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 its children array. To preserve existing children, the host must include them on the payload.
  • If not found, the entry is appended.
FieldTypeDescription
typeActionType.SessionCustomizationUpdated
customizationCustomizationThe 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.

FieldTypeDescription
typeActionType.SessionCustomizationRemoved
idstringThe 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 startingreadyauthRequired 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.

FieldTypeRequiredDescription
typeActionType.SessionMcpServerStateChangedYes
idstringYesThe id of the {@link McpServerCustomization} to update.
stateMcpServerStateYesThe new lifecycle state.
channelURINoUpdated 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.

FieldTypeDescription
typeActionType.SessionMcpServerStartRequested
idstringThe 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.

FieldTypeDescription
typeActionType.SessionMcpServerStopRequested
idstringThe 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.

FieldTypeRequiredDescription
typeActionType.SessionConfigChangedYes
configRecord<string, unknown>YesUpdated config values
replacebooleanNoWhen 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.

FieldTypeDescription
typeActionType.SessionMetaChanged
_metaRecord<string, unknown> | undefinedNew _meta payload, or undefined to clear it

Commands

JSON Schema: commands.schema.json

createSession

PropertyValue
DirectionClient → Server
TypeRequest

Parameters:

FieldTypeRequiredDescription
channelURIYesSession URI (client-chosen, e.g. ahp-session:/&lt;uuid&gt;)
providerstringNoAgent provider ID
workingDirectoryURINoWorking directory for the session
forkSessionForkSourceNoFork from an existing session. The new session is populated with content from the source session up to and including the specified turn's response.
configRecord<string, unknown>NoAgent-specific configuration values collected via resolveSessionConfig. Keys and values correspond to the schema returned by the server.
activeClientSessionActiveClientNoEagerly 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.
progressTokenstringNoOpt-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.

PropertyValue
DirectionClient → Server
TypeRequest

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.

PropertyValue
DirectionClient → Server
TypeRequest

Parameters:

FieldTypeRequiredDescription
channelURIYesChat URI
cursorstringNoOpaque 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:

jsonc
// 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.

PropertyValue
DirectionClient → Server
TypeRequest

Parameters:

FieldTypeDescription
kindCompletionItemKindWhat kind of completion is being requested.
channelURIThe chat URI the completion is being requested for.
textstringThe complete text of the input being completed (e.g. the full user message text typed so far).
offsetnumberThe character offset within text at which the completion is requested, measured in UTF-16 code units. MUST satisfy 0 &lt;= offset &lt;= text.length.

Result:

FieldTypeDescription
itemsCompletionItem[]The completion items, in the order the server suggests displaying them.

Example:

jsonc
// User has typed "look at

---

Released under the MIT License.