1
>
/*---------------------------------------------------------------------------------------------
state.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
// allow-any-unicode-comment-file
7
>
// DO NOT EDIT -- auto-generated by scripts/sync-agent-host-protocol.ts
8
>
9
>
import type { Changeset } from '../channels-changeset/state.js';
10
>
import type { AnnotationsSummary } from '../channels-annotations/state.js';
11
>
import type { ChatSummary, ChatInputRequest, ToolCallConfirmationState, ToolCallState, ToolCallAuthRequiredState } from '../channels-chat/state.js';
12
>
import type { ConfigPropertySchema, ErrorInfo, Icon, ProtectedResourceMetadata, TextRange, URI } from '../common/state.js';
13
>
14
>
// ─── Session State ───────────────────────────────────────────────────────────
15
>
16
>
/**
17
>
* Session initialization state.
18
>
*
19
>
* @category Session State
20
>
*/
21
>
export const enum SessionLifecycle {
22
>
Creating = 'creating',
23
>
Ready = 'ready',
24
>
CreationFailed = 'creationFailed',
25
>
}
26
>
27
>
/**
28
>
* Bitset of summary-level session status flags.
29
>
*
30
>
* Use bitwise checks instead of equality for non-terminal activity. For example,
31
>
* `status & SessionStatus.InProgress` matches both ordinary in-progress turns
32
>
* and turns that are paused waiting for input.
33
>
*
34
>
* @category Session State
35
>
*/
36
>
export const enum SessionStatus {
37
>
/** Session is idle — no turn is active. */
38
>
Idle = 1,
39
>
/** Session ended with an error. */
40
>
Error = 1 << 1,
41
>
/** A turn is actively streaming. */
42
>
InProgress = 1 << 3,
43
>
/** A turn is in progress but blocked waiting for user input or tool confirmation. */
44
>
InputNeeded = (1 << 3) | (1 << 4),
45
>
/** The client has viewed this session since its last modification. */
46
>
IsRead = 1 << 5,
47
>
/** The session has been archived by the client. */
48
>
IsArchived = 1 << 6,
49
>
}
50
>
51
>
/**
52
>
* Metadata shared between the full {@link SessionState} (delivered when a
53
>
* client subscribes to a session's URI) and the lightweight
54
>
* {@link SessionSummary} (carried in the root-channel session catalog).
55
>
*
56
>
* These fields describe the session at a glance and appear in both places.
57
>
* `SessionState` owns the authoritative values for a subscribed session;
58
>
* `SessionSummary` mirrors them into the catalog so clients that only render a
59
>
* session list don't have to subscribe to every session URI. The host keeps
60
>
* the catalog in sync via `root/sessionSummaryChanged`.
61
>
*
62
>
* @category Session State
63
>
*/
64
>
export interface SessionMetadata {
65
>
/** Agent provider ID */
66
>
provider: string;
67
>
/** Session title */
68
>
title: string;
69
>
/** Current session status */
70
>
status: SessionStatus;
71
>
/** Human-readable description of what the session is currently doing */
72
>
activity?: string;
73
>
/** Server-owned project for this session */
74
>
project?: ProjectInfo;
75
>
/**
76
>
* The working directories the session's agent has tool access to, as
77
>
* maintained by the `session/workingDirectorySet` /
78
>
* `session/workingDirectoryRemoved` actions. Directories are **equal peers** —
79
>
* the session has no primary. Individual chats MAY restrict to a subset via
80
>
* {@link ChatSummary.workingDirectories | their own `workingDirectories`} and
81
>
* designate one of their own directories as primary (see
82
>
* {@link ChatState.primaryWorkingDirectory}); a chat that sets no subset
83
>
* operates against this full set.
84
>
*/
85
>
workingDirectories?: URI[];
86
>
/**
87
>
* Lightweight summary of this session's inline annotations channel
88
>
* (`ahp-session:/<uuid>/annotations`). Surfaced so badge UI can render
89
>
* annotation / entry counts without subscribing. Absent when the session
90
>
* does not expose an annotations channel.
91
>
*/
92
>
annotations?: AnnotationsSummary;
93
>
}
94
>
95
>
/**
96
>
* Full state for a single session, loaded when a client subscribes to the session's URI.
97
>
*
98
>
* Inlines (denormalizes) every {@link SessionMetadata} field directly onto
99
>
* itself so subscribers receive one flat object instead of a nested summary.
100
>
* The lightweight catalog representation is {@link SessionSummary}, surfaced on
101
>
* the root channel; the host keeps the two in sync via
102
>
* `root/sessionSummaryChanged`.
103
>
*
104
>
* @category Session State
105
>
*/
106
>
export interface SessionState extends SessionMetadata {
107
>
/** Session initialization state */
108
>
lifecycle: SessionLifecycle;
109
>
/** Error details if creation failed */
110
>
creationError?: ErrorInfo;
111
>
/** Tools provided by the server (agent host) for this session */
112
>
serverTools?: ToolDefinition[];
113
>
/**
114
>
* The clients currently providing tools and interactive capabilities to this
115
>
* session. If multiple tools or customizations are provided by the same
116
>
* active client, an agent host MAY deduplicate them when exposed to a model,
117
>
* with a preference given to the client that started the turn.
118
>
*
119
>
* Membership is host-managed: clients add (or refresh) themselves with
120
>
* `session/activeClientSet`, and the host removes them with
121
>
* `session/activeClientRemoved` when they unsubscribe, disconnect without
122
>
* reconnecting in time, or reconnect without resubscribing to the session.
123
>
*/
124
>
activeClients: SessionActiveClient[];
125
>
/** Catalog of chats in this session. */
126
>
chats: ChatSummary[];
127
>
/**
128
>
* The chat that receives input when the user addresses the session without
129
>
* selecting a specific chat. This is a UI routing hint, not a hierarchy
130
>
* marker — chats remain equal peers at the protocol level. Hosts MAY change
131
>
* this over the session's lifetime.
132
>
*/
133
>
defaultChat?: URI;
134
>
/** Session configuration schema and current values */
135
>
config?: SessionConfigState;
136
>
/**
137
>
* Top-level customizations active in this session.
138
>
*
139
>
* Always one of the {@link Customization} variants:
140
>
*
141
>
* - Container customizations ({@link PluginCustomization},
142
>
* {@link DirectoryCustomization}) whose children — agents, skills,
143
>
* prompts, rules, hooks, MCP servers — live in each container's
144
>
* {@link ContainerCustomizationBase.children | `children`} array.
145
>
* - Top-level {@link McpServerCustomization} entries the host
146
>
* surfaces directly (for example a globally-configured MCP server
147
>
* that isn't bundled in a plugin or directory). MCP servers may
148
>
* also appear as children of a container.
149
>
*
150
>
* Client-published plugins arrive via
151
>
* {@link SessionActiveClient.customizations | `activeClients[].customizations`}
152
>
* and the host propagates them into this list (typically with the
153
>
* container's `clientId` set and `children` populated). Clients
154
>
* publish in container shape only; bare MCP servers at the top level
155
>
* are server-originated.
156
>
*/
157
>
customizations?: Customization[];
158
>
/**
159
>
* Catalogue of changesets the server can produce for this session. Each
160
>
* entry advertises a subscribable view of file changes (uncommitted,
161
>
* session-wide, per-turn, etc.) and the URI template the client expands
162
>
* before subscribing. See {@link Changeset} for the full shape and
163
>
* {@link /guide/changesets | Changesets} for an overview of the model.
164
>
*/
165
>
changesets?: Changeset[];
166
>
/**
167
>
* Outstanding input the session is blocked on, aggregated across every chat
168
>
* so a client can discover and answer it from the session channel alone,
169
>
* without subscribing to individual chats.
170
>
*
171
>
* Each entry is self-sufficient: it carries the owning chat's URI plus every
172
>
* identifier the client needs to respond. A client answers by dispatching the
173
>
* ordinary `chat/*` action to that chat's channel — see
174
>
* {@link SessionInputRequest} for the per-variant response path. A present,
175
>
* non-empty list implies {@link SessionStatus.InputNeeded} on
176
>
* {@link SessionSummary.status}.
177
>
*
178
>
* Host-managed: the host upserts entries with `session/inputNeededSet` as
179
>
* chats raise requests and removes them with `session/inputNeededRemoved`
180
>
* once the underlying request resolves.
181
>
*/
182
>
inputNeeded?: SessionInputRequest[];
183
>
/**
184
>
* Additional provider-specific metadata for this session.
185
>
*
186
>
* Clients MAY look for well-known keys here to provide enhanced UI.
187
>
* For example, a `git` key may provide extra git metadata about the session's
188
>
* working directories.
189
>
*/
190
>
_meta?: Record<string, unknown>;
191
>
}
192
>
193
>
/**
194
>
* A client currently providing tools and interactive capabilities to a session.
195
>
*
196
>
* A session MAY have several active clients at once; entries in
197
>
* {@link SessionState.activeClients} are keyed by `clientId`. The server SHOULD
198
>
* automatically remove an active client when that client disconnects.
199
>
*
200
>
* @category Session State
201
>
*/
202
>
export interface SessionActiveClient {
203
>
/** Client identifier (matches `clientId` from `initialize`) */
204
>
clientId: string;
205
>
/** Human-readable client name (e.g. `"VS Code"`) */
206
>
displayName?: string;
207
>
/** Tools this client provides to the session */
208
>
tools: ToolDefinition[];
209
>
/**
210
>
* Plugin customizations this client contributes to the session.
211
>
*
212
>
* Clients publish in [Open Plugins](https://open-plugins.com/) format
213
>
* — i.e. always container-shaped plugins. They MAY synthesize virtual
214
>
* plugins in memory and rely on the host to expand them into concrete
215
>
* children inside {@link SessionState.customizations}.
216
>
*/
217
>
customizations?: ClientPluginCustomization[];
218
>
}
219
>
220
>
// ─── Session Input Requests ──────────────────────────────────────────────────
221
>
222
>
/**
223
>
* Discriminant for the kinds of outstanding input a session can surface in
224
>
* {@link SessionState.inputNeeded}.
225
>
*
226
>
* This is a general/typological union (not a lifecycle), so the discriminant is
227
>
* a `*Kind`.
228
>
*
229
>
* @category Session Input Types
230
>
*/
231
>
export const enum SessionInputRequestKind {
232
>
/** A user-facing elicitation mirrored from an unresolved chat response part. */
233
>
ChatInput = 'chatInput',
234
>
/** A tool call awaiting parameter- or result-confirmation. */
235
>
ToolConfirmation = 'toolConfirmation',
236
>
/** A running tool the session wants an active client to execute. */
237
>
ToolClientExecution = 'toolClientExecution',
238
>
/** A tool call blocked on MCP authentication mid-execution. */
239
>
ToolAuthentication = 'toolAuthentication',
240
>
}
241
>
242
>
/**
243
>
* Fields common to every {@link SessionInputRequest} variant.
244
>
*
245
>
* @category Session Input Types
246
>
*/
247
>
interface SessionInputRequestBase {
248
>
/**
249
>
* Stable key for this entry, unique within the session's
250
>
* {@link SessionState.inputNeeded} list. The host derives it however it likes
251
>
* (for example from the chat URI plus the underlying request or tool-call
252
>
* id); consumers MUST treat it as opaque. It is the key for the
253
>
* `session/inputNeededSet` / `session/inputNeededRemoved` upsert convention.
254
>
*/
255
>
id: string;
256
>
/**
257
>
* The chat the underlying request lives in. This is the channel a client
258
>
* dispatches its response to — it does not need to have subscribed to that
259
>
* chat first.
260
>
*/
261
>
chat: URI;
262
>
}
263
>
264
>
/**
265
>
* A user-input elicitation surfaced at the session level, mirroring the request
266
>
* from an unresolved {@link InputRequestResponsePart} in the owning chat.
267
>
*
268
>
* Respond by dispatching `chat/inputCompleted` (or syncing drafts with
269
>
* `chat/inputAnswerChanged`) to {@link SessionInputRequestBase.chat | `chat`},
270
>
* keyed by {@link ChatInputRequest.id | `request.id`}.
271
>
*
272
>
* @category Session Input Types
273
>
*/
274
>
export interface SessionChatInputRequest extends SessionInputRequestBase {
275
>
kind: SessionInputRequestKind.ChatInput;
276
>
/** The mirrored chat input request. */
277
>
request: ChatInputRequest;
278
>
}
279
>
280
>
/**
281
>
* A tool call blocked on confirmation — either parameter confirmation before
282
>
* execution or result confirmation after — surfaced at the session level.
283
>
*
284
>
* Respond by dispatching `chat/toolCallConfirmed` (for
285
>
* {@link ToolCallPendingConfirmationState}) or `chat/toolCallResultConfirmed`
286
>
* (for {@link ToolCallPendingResultConfirmationState}) to
287
>
* {@link SessionInputRequestBase.chat | `chat`}, keyed by `turnId` and
288
>
* `toolCall.toolCallId`.
289
>
*
290
>
* @category Session Input Types
291
>
*/
292
>
export interface SessionToolConfirmationRequest extends SessionInputRequestBase {
293
>
kind: SessionInputRequestKind.ToolConfirmation;
294
>
/** The turn the tool call belongs to. */
295
>
turnId: string;
296
>
/** The tool call awaiting confirmation. */
297
>
toolCall: ToolCallConfirmationState;
298
>
}
299
>
300
>
/**
301
>
* A running tool whose execution is delegated to an active client. Surfaced so
302
>
* a client that provides the tool can pick up the work without subscribing to
303
>
* the owning chat.
304
>
*
305
>
* The {@link toolCall} is always a {@link ToolCallRunningState} (a
306
>
* {@link ToolCallState} in `running` status) whose
307
>
* {@link ToolCallRunningState.contributor | `contributor`} is a client
308
>
* {@link ToolCallClientContributor} whose `clientId` matches the denormalized
309
>
* {@link clientId} here. Execute and report the result by dispatching
310
>
* `chat/toolCallComplete` (and optionally streaming with
311
>
* `chat/toolCallContentChanged`) to {@link SessionInputRequestBase.chat |
312
>
* `chat`}, keyed by `turnId` and `toolCall.toolCallId`.
313
>
*
314
>
* @category Session Input Types
315
>
*/
316
>
export interface SessionToolClientExecutionRequest extends SessionInputRequestBase {
317
>
kind: SessionInputRequestKind.ToolClientExecution;
318
>
/** The turn the tool call belongs to. */
319
>
turnId: string;
320
>
/**
321
>
* The `clientId` expected to execute the tool. Matches the `clientId` of the
322
>
* tool call's client {@link ToolCallContributor}.
323
>
*/
324
>
clientId: string;
325
>
/**
326
>
* The running tool call the session wants the owning client to execute. The
327
>
* host only ever populates this with a {@link ToolCallRunningState} (i.e. a
328
>
* {@link ToolCallState} in `running` status).
329
>
*/
330
>
toolCall: ToolCallState;
331
>
}
332
>
333
>
/**
334
>
* A tool call blocked on MCP authentication mid-execution, surfaced at the
335
>
* session level.
336
>
*
337
>
* The {@link toolCall} is always a {@link ToolCallAuthRequiredState} (a
338
>
* {@link ToolCallState} in `auth-required` status). Unlike
339
>
* {@link SessionToolConfirmationRequest}, this is **not** answered by
340
>
* dispatching a `chat/*` action directly: the client obtains a token for
341
>
* {@link ToolCallAuthRequiredState.auth | `toolCall.auth`}`.resource` and
342
>
* pushes it via the existing `authenticate` command (see
343
>
* {@link /specification/authentication | Authentication}). The host resumes
344
>
* the tool call and dispatches `chat/toolCallAuthResolved` once the token is
345
>
* accepted, at which point it also removes this entry with
346
>
* `session/inputNeededRemoved`.
347
>
*
348
>
* @category Session Input Types
349
>
*/
350
>
export interface SessionToolAuthenticationRequest extends SessionInputRequestBase {
351
>
kind: SessionInputRequestKind.ToolAuthentication;
352
>
/** The turn the tool call belongs to. */
353
>
turnId: string;
354
>
/** The tool call awaiting authentication. */
355
>
toolCall: ToolCallAuthRequiredState;
356
>
}
357
>
358
>
/**
359
>
* One outstanding piece of input a session is blocked on, aggregated across all
360
>
* chats in {@link SessionState.inputNeeded}.
361
>
*
362
>
* Each entry is self-sufficient: it carries the owning
363
>
* {@link SessionInputRequestBase.chat | `chat`} URI plus every identifier needed
364
>
* to construct the response, so a client can answer by dispatching the ordinary
365
>
* `chat/*` action (`chat/inputCompleted`, `chat/toolCallConfirmed`,
366
>
* `chat/toolCallComplete`, …) to that chat's channel **without having subscribed
367
>
* to the chat** — except {@link SessionToolAuthenticationRequest}, which is
368
>
* resolved via the `authenticate` command instead. The host removes the entry
369
>
* with `session/inputNeededRemoved` once the underlying request resolves.
370
>
*
371
>
* @category Session Input Types
372
>
*/
373
>
export type SessionInputRequest =
374
>
| SessionChatInputRequest
375
>
| SessionToolConfirmationRequest
376
>
| SessionToolClientExecutionRequest
377
>
| SessionToolAuthenticationRequest;
378
>
379
>
/**
380
>
* Server-owned project metadata for a session.
381
>
*
382
>
* @category Session State
383
>
*/
384
>
export interface ProjectInfo {
385
>
/** Project URI */
386
>
uri: URI;
387
>
/** Human-readable project name */
388
>
displayName: string;
389
>
}
390
>
391
>
/**
392
>
* Lightweight catalog entry summarizing one session. Surfaced via
393
>
* {@link RootChannelCommands.listSessions | `root/listSessions`} and
394
>
* `root/sessionAdded`/`root/sessionSummaryChanged` notifications.
395
>
*
396
>
* **Aggregation across chats.** Once a session contains more than one chat,
397
>
* several `SessionSummary` fields are derived from the underlying
398
>
* {@link SessionState.chats | chat catalog}. Producers SHOULD follow these
399
>
* rules so clients that only consume the session summary (e.g. a session
400
>
* list) still see meaningful state:
401
>
*
402
>
* - `status`: take the activity bits (`Idle` / `InProgress` / `InputNeeded` /
403
>
* `Error` — bits 0–4) from the
404
>
* {@link SessionState.defaultChat | default chat} when present, else from
405
>
* the most recently modified chat. **Promote** `InputNeeded` whenever any
406
>
* chat in the session needs input, and **promote** `Error` whenever any
407
>
* chat is in an error state — both override the default-chat bits. The
408
>
* orthogonal flag bits (`IsRead`, `IsArchived`) remain session-scoped.
409
>
* - `activity`: mirror the activity string of the default chat, or of the
410
>
* chat currently driving the promoted status bits when a non-default chat
411
>
* wins (e.g. the chat that raised `InputNeeded`).
412
>
* - `modifiedAt`: the max of all chats' `modifiedAt`.
413
>
* - `workingDirectories`: the session-level set. Individual chats MAY restrict
414
>
* to a subset via {@link ChatSummary.workingDirectories}; aggregating these
415
>
* up is meaningless and SHOULD NOT be attempted.
416
>
* - `changes`: optional roll-up across all chats. Producers MAY sum the
417
>
* per-chat changeset stats or report the most expensive chat's stats —
418
>
* whichever is cheaper for the host to compute.
419
>
*
420
>
* Sessions with a single chat trivially satisfy all of the above (the chat's
421
>
* values pass through unchanged). The rules only matter once a session
422
>
* carries multiple chats.
423
>
*
424
>
* @category Session State
425
>
*/
426
>
export interface SessionSummary extends SessionMetadata {
427
>
/** Session URI */
428
>
resource: URI;
429
>
/** Creation timestamp (ISO 8601, e.g. `"2025-03-10T18:42:03.123Z"`) */
430
>
createdAt: string;
431
>
/** Last modification timestamp (ISO 8601, e.g. `"2025-03-10T18:42:03.123Z"`) */
432
>
modifiedAt: string;
433
>
/**
434
>
* Aggregate summary of file changes associated with this session. Servers
435
>
* may populate this to give clients a quick at-a-glance view of the
436
>
* session's footprint (e.g., for list rendering) without requiring the
437
>
* client to subscribe to a changeset.
438
>
*/
439
>
changes?: ChangesSummary;
440
>
/**
441
>
* Lightweight server-defined metadata clients may use for the session
442
>
* presentation. The protocol does not interpret these values; producers
443
>
* SHOULD keep the payload small because summaries appear in session lists
444
>
* and session notifications.
445
>
*/
446
>
_meta?: Record<string, unknown>;
447
>
}
448
>
449
>
/**
450
>
* Aggregate counts describing the file changes associated with a session.
451
>
*
452
>
* All fields are optional so servers can populate only the metrics they
453
>
* cheaply have available.
454
>
*
455
>
* @category Session State
456
>
*/
457
>
export interface ChangesSummary {
458
>
/** Total number of inserted lines across all changed files. */
459
>
additions?: number;
460
>
/** Total number of deleted lines across all changed files. */
461
>
deletions?: number;
462
>
/** Number of files that have changes. */
463
>
files?: number;
464
>
}
465
>
466
>
// ─── Agent Selection ─────────────────────────────────────────────────────────
467
>
468
>
/**
469
>
* A selected custom agent for a session.
470
>
*
471
>
* The `uri` identifies a specific custom agent (matching an
472
>
* {@link AgentCustomization.uri | `AgentCustomization.uri`} exposed via
473
>
* the session's effective customizations). Consumers resolve the agent's
474
>
* display name by looking up `uri` in the session's customization tree.
475
>
*
476
>
* A message with no `agent` selected uses the provider's default behavior.
477
>
*
478
>
* @category Session State
479
>
*/
480
>
export interface AgentSelection {
481
>
/** Stable agent URI (matches an {@link AgentCustomization.uri}). */
482
>
uri: URI;
483
>
}
484
>
485
>
// ─── Session Config Types ────────────────────────────────────────────────────
486
>
487
>
/**
488
>
* A session configuration property descriptor.
489
>
*
490
>
* Extends the generic {@link ConfigPropertySchema} with session-specific
491
>
* display extensions.
492
>
*
493
>
* @category Session Config Types
494
>
*/
495
>
export interface SessionConfigPropertySchema extends ConfigPropertySchema {
496
>
/**
497
>
* Display extension: when `true`, the full set of allowed values is too large
498
>
* to enumerate statically. The client SHOULD use `sessionConfigCompletions`
499
>
* to fetch matching values based on user input. Any values in `enum` are
500
>
* seed/recent values for initial display.
501
>
*/
502
>
enumDynamic?: boolean;
503
>
/** When `true`, the user may change this property after session creation */
504
>
sessionMutable?: boolean;
505
>
}
506
>
507
>
/**
508
>
* A JSON Schema object describing available session configuration metadata.
509
>
*
510
>
* @category Session Config Types
511
>
*/
512
>
export interface SessionConfigSchema {
513
>
/** JSON Schema: always `'object'` */
514
>
type: 'object';
515
>
/** JSON Schema: property descriptors keyed by property id */
516
>
properties: Record<string, SessionConfigPropertySchema>;
517
>
/** JSON Schema: list of required property ids */
518
>
required?: string[];
519
>
}
520
>
521
>
/**
522
>
* Live session configuration metadata.
523
>
*
524
>
* The schema describes the available configuration properties and the values
525
>
* contain the current value for each resolved property.
526
>
*
527
>
* @category Session Config Types
528
>
*/
529
>
export interface SessionConfigState {
530
>
/** JSON Schema describing available configuration properties */
531
>
schema: SessionConfigSchema;
532
>
/** Current configuration values */
533
>
values: Record<string, unknown>;
534
>
}
535
>
536
>
// ─── Tool Definition Types ───────────────────────────────────────────────────
537
>
538
>
/**
539
>
* Describes a tool available in a session, provided by either the server or the active client.
540
>
*
541
>
* @category Tool Definition Types
542
>
*/
543
>
export interface ToolDefinition {
544
>
/** Unique tool identifier */
545
>
name: string;
546
>
/** Human-readable display name */
547
>
title?: string;
548
>
/** Description of what the tool does */
549
>
description?: string;
550
>
/**
551
>
* JSON Schema defining the expected input parameters.
552
>
*
553
>
* Optional because client-provided tools may not have formal schemas.
554
>
* Mirrors MCP `Tool.inputSchema`.
555
>
*/
556
>
inputSchema?: {
557
>
type: 'object';
558
>
properties?: Record<string, object>;
559
>
required?: string[];
560
>
};
561
>
/**
562
>
* JSON Schema defining the structure of the tool's output.
563
>
*
564
>
* Mirrors MCP `Tool.outputSchema`.
565
>
*/
566
>
outputSchema?: {
567
>
type: 'object';
568
>
properties?: Record<string, object>;
569
>
required?: string[];
570
>
};
571
>
/** Behavioral hints about the tool. All properties are advisory. */
572
>
annotations?: ToolAnnotations;
573
>
/**
574
>
* Additional provider-specific metadata.
575
>
*
576
>
* Mirrors the MCP `_meta` convention.
577
>
*/
578
>
_meta?: Record<string, unknown>;
579
>
}
580
>
581
>
/**
582
>
* Behavioral hints about a tool. All properties are advisory and not
583
>
* guaranteed to faithfully describe tool behavior.
584
>
*
585
>
* Mirrors MCP `ToolAnnotations` from the Model Context Protocol specification.
586
>
*
587
>
* @category Tool Definition Types
588
>
*/
589
>
export interface ToolAnnotations {
590
>
/** Alternate human-readable title */
591
>
title?: string;
592
>
/** Tool does not modify its environment (default: false) */
593
>
readOnlyHint?: boolean;
594
>
/** Tool may perform destructive updates (default: true) */
595
>
destructiveHint?: boolean;
596
>
/** Repeated calls with the same arguments have no additional effect (default: false) */
597
>
idempotentHint?: boolean;
598
>
/** Tool may interact with external entities (default: true) */
599
>
openWorldHint?: boolean;
600
>
}
601
>
602
>
// ─── Customization Types ─────────────────────────────────────────────────────
603
>
604
>
/**
605
>
* Discriminant for the kind of customization.
606
>
*
607
>
* Top-level entries in {@link SessionState.customizations} and
608
>
* {@link AgentInfo.customizations} are either container customizations
609
>
* ({@link CustomizationType.Plugin | `Plugin`} or
610
>
* {@link CustomizationType.Directory | `Directory`}) or
611
>
* {@link CustomizationType.McpServer | `McpServer`} entries surfaced
612
>
* directly by the host. The remaining types appear only as children of
613
>
* a container.
614
>
*
615
>
* @category Customization Types
616
>
*/
617
>
export const enum CustomizationType {
618
>
Plugin = 'plugin',
619
>
Directory = 'directory',
620
>
Agent = 'agent',
621
>
Skill = 'skill',
622
>
Prompt = 'prompt',
623
>
Rule = 'rule',
624
>
Hook = 'hook',
625
>
McpServer = 'mcpServer',
626
>
}
627
>
628
>
/**
629
>
* Customization types that appear as children of a
630
>
* {@link PluginCustomization} or {@link DirectoryCustomization}.
631
>
*
632
>
* @category Customization Types
633
>
*/
634
>
export type ChildCustomizationType =
635
>
| CustomizationType.Agent
636
>
| CustomizationType.Skill
637
>
| CustomizationType.Prompt
638
>
| CustomizationType.Rule
639
>
| CustomizationType.Hook
640
>
| CustomizationType.McpServer;
641
>
642
>
/**
643
>
* Fields shared by every customization variant.
644
>
*
645
>
* @category Customization Types
646
>
*/
647
>
interface CustomizationBase {
648
>
/**
649
>
* Session-unique opaque identifier. Used by every action that targets a
650
>
* specific customization. Minted by whoever publishes the customization
651
>
* (typically the agent host).
652
>
*/
653
>
id: string;
654
>
/**
655
>
* Source URI for this customization. A plugin URL, a file URI, or a
656
>
* directory URI.
657
>
*
658
>
* For declarations that live inside a larger file — e.g. an MCP
659
>
* server declared inline in a `plugins.json` manifest — `uri` points
660
>
* to the containing file and {@link CustomizationBase.range | `range`}
661
>
* narrows it to the declaration's span.
662
>
*/
663
>
uri: URI;
664
>
/** Human-readable name. */
665
>
name: string;
666
>
/** Icons for UI display. */
667
>
icons?: Icon[];
668
>
/**
669
>
* Optional span within {@link CustomizationBase.uri | `uri`} when this
670
>
* customization is a subset of a larger file (for example, one entry
671
>
* in an inline `mcpServers` block of a `plugins.json` manifest).
672
>
* Absent when the customization covers the whole resource.
673
>
*/
674
>
range?: TextRange;
675
>
/**
676
>
* Additional provider-specific metadata for this customization.
677
>
*
678
>
* Mirrors the MCP `_meta` convention. Optional and opaque to the
679
>
* protocol; producers and consumers agree on its contents
680
>
* out-of-band.
681
>
*/
682
>
_meta?: Record<string, unknown>;
683
>
}
684
>
685
>
/**
686
>
* Discriminant values for {@link CustomizationLoadState}.
687
>
*
688
>
* @category Customization Types
689
>
*/
690
>
export const enum CustomizationLoadStatus {
691
>
Loading = 'loading',
692
>
Loaded = 'loaded',
693
>
Degraded = 'degraded',
694
>
Error = 'error',
695
>
}
696
>
697
>
/**
698
>
* Container is being loaded by the host.
699
>
*
700
>
* @category Customization Types
701
>
*/
702
>
export interface CustomizationLoadingState {
703
>
kind: CustomizationLoadStatus.Loading;
704
>
}
705
>
706
>
/**
707
>
* Container loaded successfully.
708
>
*
709
>
* @category Customization Types
710
>
*/
711
>
export interface CustomizationLoadedState {
712
>
kind: CustomizationLoadStatus.Loaded;
713
>
}
714
>
715
>
/**
716
>
* Container partially loaded but has warnings.
717
>
*
718
>
* @category Customization Types
719
>
*/
720
>
export interface CustomizationDegradedState {
721
>
kind: CustomizationLoadStatus.Degraded;
722
>
/** Human-readable description of the warning. */
723
>
message: string;
724
>
}
725
>
726
>
/**
727
>
* Container failed to load.
728
>
*
729
>
* @category Customization Types
730
>
*/
731
>
export interface CustomizationErrorState {
732
>
kind: CustomizationLoadStatus.Error;
733
>
/** Human-readable error message. */
734
>
message: string;
735
>
}
736
>
737
>
/**
738
>
* Discriminated load state for a container customization
739
>
* ({@link PluginCustomization} or {@link DirectoryCustomization}).
740
>
*
741
>
* @category Customization Types
742
>
*/
743
>
export type CustomizationLoadState =
744
>
| CustomizationLoadingState
745
>
| CustomizationLoadedState
746
>
| CustomizationDegradedState
747
>
| CustomizationErrorState;
748
>
749
>
/**
750
>
* Fields shared by container customizations.
751
>
*
752
>
* @category Customization Types
753
>
*/
754
>
interface ContainerCustomizationBase extends CustomizationBase {
755
>
/** Whether this container is currently enabled. */
756
>
enabled: boolean;
757
>
/**
758
>
* `clientId` of the client that contributed this container. Absent for
759
>
* server-originated entries.
760
>
*/
761
>
clientId?: string;
762
>
/**
763
>
* Host-reported load state. Absent means the host has not yet reported
764
>
* a load state for this container.
765
>
*/
766
>
load?: CustomizationLoadState;
767
>
/**
768
>
* Children discovered inside this container.
769
>
*
770
>
* Absent means the host has not parsed this container yet. An empty
771
>
* array means the host parsed the container and it contributes
772
>
* nothing.
773
>
*/
774
>
children?: ChildCustomization[];
775
>
}
776
>
777
>
/**
778
>
* An [Open Plugins](https://open-plugins.com/) plugin.
779
>
*
780
>
* @category Customization Types
781
>
*/
782
>
export interface PluginCustomization extends ContainerCustomizationBase {
783
>
type: CustomizationType.Plugin;
784
>
/**
785
>
* Version of the plugin, sourced from the
786
>
* [Open Plugins](https://open-plugins.com/) manifest's optional
787
>
* `version` field (semver, e.g. `"1.2.0"`). Absent when the manifest
788
>
* declares no version — the field is optional there — or the source
789
>
* has no version concept. Provenance / display only: the host neither
790
>
* parses nor enforces it.
791
>
*/
792
>
version?: string;
793
>
}
794
>
795
>
/**
796
>
* A {@link PluginCustomization} as published by a client. Extends the
797
>
* server-facing shape with an opaque `nonce` so the host can detect when
798
>
* the client's view of a plugin has changed and re-parse only as needed.
799
>
*
800
>
* Clients SHOULD include a `nonce`. Server-side fields like
801
>
* {@link ContainerCustomizationBase.children | `children`} and
802
>
* {@link ContainerCustomizationBase.load | `load`} are typically left
803
>
* absent on publication and populated by the host when the resolved
804
>
* plugin appears in {@link SessionState.customizations}.
805
>
*
806
>
* @category Customization Types
807
>
*/
808
>
export interface ClientPluginCustomization extends PluginCustomization {
809
>
/** Opaque version token used by the host to detect changes. */
810
>
nonce?: string;
811
>
}
812
>
813
>
/**
814
>
* A directory the host watches for this session.
815
>
*
816
>
* Presence in the customization list signals that the host may discover
817
>
* customizations from this directory. When `writable` is `true`, clients
818
>
* MAY persist new customizations into the directory using
819
>
* [`resourceWrite`](/reference/common#resourcewrite); the host will
820
>
* then surface the resulting child via the customization actions.
821
>
*
822
>
* The directory may not yet exist on disk.
823
>
*
824
>
* @category Customization Types
825
>
*/
826
>
export interface DirectoryCustomization extends ContainerCustomizationBase {
827
>
type: CustomizationType.Directory;
828
>
/** Which child customization type this directory holds. */
829
>
contents: ChildCustomizationType;
830
>
/** Whether clients may write into this directory. */
831
>
writable: boolean;
832
>
}
833
>
834
>
/**
835
>
* Fields shared by the leaf child customizations that live inside a
836
>
* container — {@link AgentCustomization}, {@link SkillCustomization},
837
>
* {@link PromptCustomization}, {@link RuleCustomization}, and
838
>
* {@link HookCustomization}.
839
>
*
840
>
* {@link McpServerCustomization} is also a child but does not extend this
841
>
* base: it always carries an explicit {@link McpServerCustomization.enabled}
842
>
* because it can appear as a top-level customization too.
843
>
*
844
>
* @category Customization Types
845
>
*/
846
>
interface ChildCustomizationBase extends CustomizationBase {
847
>
/**
848
>
* Whether this child is individually enabled. Absent means enabled, so a
849
>
* producer only needs to set it to surface a child that exists but is
850
>
* turned off on its own.
851
>
*
852
>
* This flag is independent of the parent container's: the **effective**
853
>
* enabled state of a child is
854
>
* `container.enabled && (child.enabled ?? true)`, so a disabled container
855
>
* disables every child regardless of each child's own flag.
856
>
*
857
>
* A child is turned on or off by id with
858
>
* {@link SessionCustomizationToggledAction | `session/customizationToggled`}.
859
>
*/
860
>
enabled?: boolean;
861
>
}
862
>
863
>
/**
864
>
* A custom agent contributed by a plugin or directory.
865
>
*
866
>
* Mirrors the [Open Plugins agent](https://open-plugins.com/agent-builders/components/agents)
867
>
* format: a markdown file with YAML frontmatter, where the body is the
868
>
* agent's system prompt.
869
>
*
870
>
* @category Customization Types
871
>
*/
872
>
export interface AgentCustomization extends ChildCustomizationBase {
873
>
type: CustomizationType.Agent;
874
>
/**
875
>
* Short description of what the agent specializes in and when to
876
>
* invoke it. Sourced from the agent file's frontmatter `description`.
877
>
*/
878
>
description?: string;
879
>
/**
880
>
* Model the agent is pinned to, sourced from the agent file's
881
>
* frontmatter `model`. Absent means the agent inherits the session's
882
>
* default model.
883
>
*/
884
>
model?: string;
885
>
/**
886
>
* Allowlist of tool names the agent is scoped to, sourced from the
887
>
* agent file's frontmatter `tools`. A non-empty list restricts the
888
>
* agent to exactly those tools. Absent — or an empty list — imposes no
889
>
* restriction beyond the session default: the agent may use any
890
>
* available tool. Producers express "no restriction" by omitting the
891
>
* field rather than sending an empty array, so an empty list carries no
892
>
* meaning distinct from absence.
893
>
*/
894
>
tools?: string[];
895
>
/**
896
>
* When `true`, the agent will not auto-delegate to this custom agent
897
>
* as a sub-agent; it can only be selected by the user. Absent or
898
>
* `false` means the agent may delegate to it.
899
>
*/
900
>
disableModelInvocation?: boolean;
901
>
/**
902
>
* When `true`, the user cannot select this custom agent (for example,
903
>
* in a picker); it remains available for the agent to auto-delegate
904
>
* to. Absent or `false` means the user may select it.
905
>
*/
906
>
disableUserInvocation?: boolean;
907
>
}
908
>
909
>
/**
910
>
* A skill contributed by a plugin or directory.
911
>
*
912
>
* Covers both [Open Plugins skill formats](https://open-plugins.com/agent-builders/components/skills)
913
>
* — the `skills/` directory layout (one subdirectory per skill, each with
914
>
* a `SKILL.md`) and the flatter `commands/` directory of slash-command
915
>
* skills.
916
>
*
917
>
* @category Customization Types
918
>
*/
919
>
export interface SkillCustomization extends ChildCustomizationBase {
920
>
type: CustomizationType.Skill;
921
>
/**
922
>
* Short description used for help text and auto-invocation matching.
923
>
* Sourced from the skill's frontmatter `description`.
924
>
*/
925
>
description?: string;
926
>
/**
927
>
* When `true`, only the user can invoke this skill — the agent will not
928
>
* auto-invoke it. Sourced from the command skill's frontmatter
929
>
* `disable-model-invocation` flag.
930
>
*/
931
>
disableModelInvocation?: boolean;
932
>
/**
933
>
* When `true`, the user cannot directly invoke this skill (for example,
934
>
* as a slash command); it remains available for the agent to
935
>
* auto-invoke. Absent or `false` means the user may invoke it.
936
>
*/
937
>
disableUserInvocation?: boolean;
938
>
}
939
>
940
>
/**
941
>
* A prompt contributed by a plugin or directory.
942
>
*
943
>
* @category Customization Types
944
>
*/
945
>
export interface PromptCustomization extends ChildCustomizationBase {
946
>
type: CustomizationType.Prompt;
947
>
/** Short description of what the prompt does. */
948
>
description?: string;
949
>
}
950
>
951
>
/**
952
>
* A rule contributed by a plugin or directory.
953
>
*
954
>
* Mirrors the [Open Plugins rule](https://open-plugins.com/agent-builders/components/rules)
955
>
* format: a markdown file (e.g. `.mdc`) whose body is injected into
956
>
* context while the rule is active. This type also covers tool-specific
957
>
* "instruction" formats (e.g. VS Code Copilot's
958
>
* `.github/instructions/*.md`), which differ only in naming — they
959
>
* share the same semantics of `description`, optional always-on
960
>
* activation, and optional glob scoping.
961
>
*
962
>
* @category Customization Types
963
>
*/
964
>
export interface RuleCustomization extends ChildCustomizationBase {
965
>
type: CustomizationType.Rule;
966
>
/**
967
>
* Description of what the rule enforces.
968
>
*/
969
>
description?: string;
970
>
/**
971
>
* When `true`, the rule is always active (subject to `globs` if any).
972
>
* When `false` or absent, the agent or user decides whether to apply
973
>
* the rule.
974
>
*/
975
>
alwaysApply?: boolean;
976
>
/**
977
>
* Glob patterns the rule applies to. When present, the rule is only
978
>
* active for matching files.
979
>
*/
980
>
globs?: string[];
981
>
}
982
>
983
>
/**
984
>
* A hook manifest contributed by a plugin or directory.
985
>
*
986
>
* @category Customization Types
987
>
*/
988
>
export interface HookCustomization extends ChildCustomizationBase {
989
>
type: CustomizationType.Hook;
990
>
}
991
>
992
>
/**
993
>
* An MCP server contributed by a plugin or directory.
994
>
*
995
>
* When the server is declared inline in the containing plugin manifest,
996
>
* `uri` points at the manifest file and
997
>
* {@link CustomizationBase.range | `range`} narrows it to the
998
>
* declaration's span.
999
>
*
1000
>
* The MCP server customization also reflects its current status.
1001
>
*
1002
>
* @category Customization Types
1003
>
*/
1004
>
export interface McpServerCustomization extends CustomizationBase {
1005
>
type: CustomizationType.McpServer;
1006
>
/**
1007
>
* Whether this MCP server is currently enabled.
1008
>
*/
1009
>
enabled: boolean;
1010
>
/**
1011
>
* Current lifecycle state of the MCP server.
1012
>
*/
1013
>
state: McpServerState;
1014
>
/**
1015
>
* An `mcp://`-protocol channel the client uses to side-channel traffic
1016
>
* into the upstream MCP server itself. The channel is NOT a fresh raw MCP
1017
>
* connection: it piggybacks on the AHP transport
1018
>
* and skips the MCP `initialize` sequence.
1019
>
*
1020
>
* The agent host MAY only serve a subset of MCP on this
1021
>
* channel; the served subset is described by domain-specific
1022
>
* capabilities such as those in
1023
>
* {@link McpServerCustomizationApps.capabilities}.
1024
>
*
1025
>
* The channel URI SHOULD be stable across the server's lifetime, but
1026
>
* the agent host MAY change it (for example across a restart) and
1027
>
* MAY only expose it while the server is in
1028
>
* {@link McpServerStatus.Ready | `Ready`}. Absence means no
1029
>
* side-channel is currently available.
1030
>
*/
1031
>
channel?: URI;
1032
>
/**
1033
>
* MCP App support. This property SHOULD be advertised for MCP servers
1034
>
* which support apps.
1035
>
*/
1036
>
mcpApp?: McpServerCustomizationApps;
1037
>
}
1038
>
1039
>
/**
1040
>
* Information from the agent host needed to render MCP Apps served
1041
>
* by this MCP server.
1042
>
*
1043
>
* @category MCP Server State
1044
>
*/
1045
>
export interface McpServerCustomizationApps {
1046
>
/**
1047
>
* The subset of MCP App
1048
>
* [`HostCapabilities`](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)
1049
>
* the AHP host can satisfy for Views backed by this server. The
1050
>
* client feeds these straight through into the `hostCapabilities` of
1051
>
* the `ui/initialize` response delivered to the View.
1052
>
*/
1053
>
capabilities: AhpMcpUiHostCapabilities;
1054
>
}
1055
>
1056
>
/**
1057
>
* The subset of MCP App
1058
>
* [`HostCapabilities`](https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx)
1059
>
* an AHP host can derive from the upstream MCP server (and from AHP's own
1060
>
* forwarding plumbing). Advertised on
1061
>
* {@link McpServerCustomizationApps.capabilities} so clients can pass it
1062
>
* through into the `hostCapabilities` of the `ui/initialize` response
1063
>
* delivered to an MCP App View.
1064
>
*
1065
>
* Field names mirror the MCP Apps spec exactly, so the AHP-side producer
1066
>
* can pass them straight through into the `hostCapabilities` of the
1067
>
* `ui/initialize` response delivered to the View.
1068
>
*
1069
>
* Capabilities outside this set (`openLinks`, `downloadFile`, `sandbox`,
1070
>
* `experimental`) are decided locally by whichever AHP client renders the
1071
>
* View and are NOT part of this AHP-level advertisement — only the
1072
>
* server-derived subset is.
1073
>
*
1074
>
* An agent host MUST only advertise a capability when it actually accepts the
1075
>
* corresponding methods/notifications on the `mcp://` channel:
1076
>
*
1077
>
* - {@link serverTools}: host proxies `tools/list` and `tools/call` to
1078
>
* the MCP server. When `listChanged` is `true`, the host also forwards
1079
>
* `notifications/tools/list_changed`.
1080
>
* - {@link serverResources}: host proxies `resources/read`,
1081
>
* `resources/list`, and `resources/templates/list` to the MCP server.
1082
>
* When `listChanged` is `true`, the host also forwards
1083
>
* `notifications/resources/list_changed`.
1084
>
* - {@link logging}: host accepts `notifications/message` log entries
1085
>
* from the App and forwards them via `mcpNotification` (and forwards
1086
>
* `logging/setLevel` calls to the server).
1087
>
* - {@link sampling}: host serves `sampling/createMessage` via
1088
>
* `mcpMethodCall`. When `sampling.tools` is present, the host also
1089
>
* accepts SEP-1577 `tools` / `toolChoice` / `tool_use` content blocks
1090
>
* inside `CreateMessageRequest`.
1091
>
*
1092
>
* @category MCP Server State
1093
>
* @see {@link https://github.com/modelcontextprotocol/ext-apps/blob/main/specification/draft/apps.mdx | MCP Apps spec (SEP-1865)}
1094
>
*/
1095
>
export interface AhpMcpUiHostCapabilities {
1096
>
/** Producer proxies the MCP `tools/*` methods to the upstream server. */
1097
>
serverTools?: {
1098
>
/** Producer forwards `notifications/tools/list_changed` from the server. */
1099
>
listChanged?: boolean;
1100
>
};
1101
>
/** Producer proxies the MCP `resources/*` methods to the upstream server. */
1102
>
serverResources?: {
1103
>
/** Producer forwards `notifications/resources/list_changed` from the server. */
1104
>
listChanged?: boolean;
1105
>
};
1106
>
/** Producer accepts `notifications/message` log entries from the App via `mcpNotification`. */
1107
>
logging?: Record<string, never>;
1108
>
/** Producer serves `sampling/createMessage` via `mcpMethodCall`. */
1109
>
sampling?: {
1110
>
/**
1111
>
* Producer accepts SEP-1577 `tools` / `toolChoice` / `tool_use` content
1112
>
* blocks inside `CreateMessageRequest`.
1113
>
*/
1114
>
tools?: Record<string, never>;
1115
>
};
1116
>
}
1117
>
1118
>
/**
1119
>
* Child customizations that live inside a {@link PluginCustomization} or
1120
>
* {@link DirectoryCustomization}.
1121
>
*
1122
>
* @category Customization Types
1123
>
*/
1124
>
export type ChildCustomization =
1125
>
| AgentCustomization
1126
>
| SkillCustomization
1127
>
| PromptCustomization
1128
>
| RuleCustomization
1129
>
| HookCustomization
1130
>
| McpServerCustomization;
1131
>
1132
>
/**
1133
>
* A top-level customization active in a session. Either a container
1134
>
* ({@link PluginCustomization} or {@link DirectoryCustomization}) whose
1135
>
* leaf customizations live in its
1136
>
* {@link ContainerCustomizationBase.children | `children`} array, or a
1137
>
* bare {@link McpServerCustomization} surfaced directly by the host.
1138
>
*
1139
>
* @category Customization Types
1140
>
*/
1141
>
export type Customization =
1142
>
| PluginCustomization
1143
>
| DirectoryCustomization
1144
>
| McpServerCustomization;
1145
>
1146
>
1147
>
// ─── MCP Server State ────────────────────────────────────────────────────────
1148
>
1149
>
/**
1150
>
* Discriminant for the {@link McpServerState} union.
1151
>
*
1152
>
* @category MCP Server State
1153
>
*/
1154
>
export const enum McpServerStatus {
1155
>
/** Server has been registered but is not yet running. */
1156
>
Starting = 'starting',
1157
>
/** Server is running and serving requests. */
1158
>
Ready = 'ready',
1159
>
/**
1160
>
* Server is reachable but requires additional authentication before it
1161
>
* can start, or before it can serve a particular request. Carries the
1162
>
* RFC 9728 Protected Resource Metadata the client needs to obtain a
1163
>
* token; the client then pushes the token via the existing
1164
>
* `authenticate` command.
1165
>
*/
1166
>
AuthRequired = 'authRequired',
1167
>
/** Server failed to start, crashed, or otherwise transitioned to a fatal error. */
1168
>
Error = 'error',
1169
>
/** Server has been shut down. */
1170
>
Stopped = 'stopped',
1171
>
}
1172
>
1173
>
/**
1174
>
* Why an MCP server is currently in the {@link McpServerStatus.AuthRequired}
1175
>
* state. Mirrors the three failure modes defined by the
1176
>
* [MCP authorization spec](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization.md).
1177
>
*
1178
>
* @category MCP Server State
1179
>
*/
1180
>
export const enum McpAuthRequiredReason {
1181
>
/** No token has been provided yet (HTTP 401, no prior token). */
1182
>
Required = 'required',
1183
>
/** A previously valid token expired or was revoked (HTTP 401). */
1184
>
Expired = 'expired',
1185
>
/**
1186
>
* Step-up auth: a token is present but its scopes are insufficient for
1187
>
* the requested operation (HTTP 403 with
1188
>
* `WWW-Authenticate: Bearer error="insufficient_scope"`).
1189
>
*
1190
>
* Unlike {@link Required} and {@link Expired} — which typically surface
1191
>
* before any tool work is in flight — `InsufficientScope` is almost
1192
>
* always triggered by an MCP request issued mid-turn (a `tools/call`,
1193
>
* `resources/read`, etc.). The host SHOULD pair the
1194
>
* {@link McpServerAuthRequiredState} transition with
1195
>
* {@link SessionStatus.InputNeeded} on
1196
>
* {@link SessionSummary.status | the session} so the activity becomes
1197
>
* visible at the session-summary level, and clients SHOULD watch for
1198
>
* this kind on any
1199
>
* {@link McpServerCustomization | MCP server} backing a running tool
1200
>
* call so they can present an explicit "grant more access" affordance
1201
>
* tied to the blocked tool call.
1202
>
*/
1203
>
InsufficientScope = 'insufficientScope',
1204
>
}
1205
>
1206
>
/**
1207
>
* Server is registered with the host but has not yet started.
1208
>
*
1209
>
* @category MCP Server State
1210
>
*/
1211
>
export interface McpServerStartingState {
1212
>
kind: McpServerStatus.Starting;
1213
>
}
1214
>
1215
>
/**
1216
>
* Server is running and serving requests.
1217
>
*
1218
>
* @category MCP Server State
1219
>
*/
1220
>
export interface McpServerReadyState {
1221
>
kind: McpServerStatus.Ready;
1222
>
}
1223
>
1224
>
/**
1225
>
* A pre-registered OAuth client that clients use instead of dynamic client
1226
>
* registration when resolving an MCP authentication challenge.
1227
>
*
1228
>
* @category MCP Server State
1229
>
*/
1230
>
export interface McpOAuthClient {
1231
>
/** OAuth client identifier registered with the authorization server. */
1232
>
clientId: string;
1233
>
/**
1234
>
* OAuth client secret for a confidential client. Absence means the client is
1235
>
* public and uses a secretless flow such as authorization code with PKCE.
1236
>
*/
1237
>
clientSecret?: string;
1238
>
}
1239
>
1240
>
/**
1241
>
* Reusable MCP authentication challenge — the RFC 9728 discovery info a
1242
>
* client needs to obtain a token and push it via the `authenticate` command.
1243
>
* Deliberately carries **no token**: this describes what is being asked for,
1244
>
* never the ****** itself.
1245
>
*
1246
>
* Shared by two independent state machines that describe the same OAuth
1247
>
* challenge from different vantage points:
1248
>
*
1249
>
* - {@link McpServerAuthRequiredState} — the MCP server itself cannot serve
1250
>
* *any* request until the client authenticates.
1251
>
* - {@link ToolCallAuthRequiredState} — a specific in-flight tool call is
1252
>
* paused pending authentication (typically
1253
>
* {@link McpAuthRequiredReason.InsufficientScope} step-up auth
1254
>
* mid-execution). The server state and the tool-call state remain
1255
>
* separate on purpose: the server saying "I need auth" and a tool
1256
>
* invocation saying "I am waiting on that auth" are different facts that
1257
>
* can be true independently.
1258
>
*
1259
>
* @category MCP Server State
1260
>
*/
1261
>
export interface McpAuthRequirement {
1262
>
/** Why authentication is required. */
1263
>
reason: McpAuthRequiredReason;
1264
>
/**
1265
>
* Pre-registered OAuth client to use for authorization. When present, clients
1266
>
* MUST use these credentials instead of dynamic client registration.
1267
>
*/
1268
>
oauthClient?: McpOAuthClient;
1269
>
/**
1270
>
* RFC 9728 Protected Resource Metadata. The `resource` field is the
1271
>
* canonical MCP server URI per RFC 8707, used as the OAuth `resource`
1272
>
* indicator. `authorization_servers` is REQUIRED by the MCP
1273
>
* authorization spec.
1274
>
*/
1275
>
resource: ProtectedResourceMetadata;
1276
>
/**
1277
>
* Scopes required for the current challenge, parsed from the
1278
>
* `WWW-Authenticate: ******"…"` header (or `scopes_supported`
1279
>
* fallback). Authoritative for the next authorization request — clients
1280
>
* MUST NOT assume any subset/superset relationship to
1281
>
* `resource.scopes_supported`.
1282
>
*/
1283
>
requiredScopes?: string[];
1284
>
/** Human-readable hint, typically from the OAuth `error_description`. */
1285
>
description?: string;
1286
>
}
1287
>
1288
>
/**
1289
>
* Server is reachable but cannot serve requests until the client
1290
>
* authenticates. Mirrors the discovery flow defined by
1291
>
* [RFC 9728](https://datatracker.ietf.org/doc/html/rfc9728)
1292
>
* (Protected Resource Metadata) and the OAuth 2.1 / RFC 6750 challenge
1293
>
* semantics required by the MCP authorization spec.
1294
>
*
1295
>
* Clients react to this state by calling the existing `authenticate`
1296
>
* command with the {@link ProtectedResourceMetadata.resource | resource}
1297
>
* carried here. There is **no** `notify/authRequired` notification for
1298
>
* MCP servers — the action stream is the single source of truth.
1299
>
*
1300
>
* When the transition is triggered by a request issued during a turn
1301
>
* — most commonly
1302
>
* {@link McpAuthRequiredReason.InsufficientScope | `InsufficientScope`}
1303
>
* surfacing mid-tool-call — the host SHOULD also raise
1304
>
* {@link SessionStatus.InputNeeded} on the session so the block is
1305
>
* visible at the summary level. Clients SHOULD watch this status on
1306
>
* any MCP server backing a running tool call and surface an explicit
1307
>
* affordance (e.g. a "grant additional access" prompt) tied to that
1308
>
* tool call, rather than relying on the user to notice the
1309
>
* customization’s status badge.
1310
>
*
1311
>
* @category MCP Server State
1312
>
*/
1313
>
export interface McpServerAuthRequiredState extends McpAuthRequirement {
1314
>
kind: McpServerStatus.AuthRequired;
1315
>
}
1316
>
1317
>
/**
1318
>
* Server failed to start, crashed, or otherwise transitioned to a
1319
>
* non-recoverable error. Use {@link McpServerStatus.AuthRequired}
1320
>
* for authentication failures.
1321
>
*
1322
>
* @category MCP Server State
1323
>
*/
1324
>
export interface McpServerErrorState {
1325
>
kind: McpServerStatus.Error;
1326
>
/** Error details. */
1327
>
error: ErrorInfo;
1328
>
}
1329
>
1330
>
/**
1331
>
* Server has been shut down. The host MAY remove the server from the
1332
>
* session entirely shortly after this state.
1333
>
*
1334
>
* @category MCP Server State
1335
>
*/
1336
>
export interface McpServerStoppedState {
1337
>
kind: McpServerStatus.Stopped;
1338
>
}
1339
>
1340
>
/**
1341
>
* Discriminated union of all MCP server lifecycle states.
1342
>
* Discriminated by `kind` (a {@link McpServerStatus} value).
1343
>
*
1344
>
* @category MCP Server State
1345
>
*/
1346
>
export type McpServerState =
1347
>
| McpServerStartingState
1348
>
| McpServerReadyState
1349
>
| McpServerAuthRequiredState
1350
>
| McpServerErrorState
1351
>
| McpServerStoppedState;