@@ -5458,7 +5476,26 @@ pub struct McpListToolsRequest {
pub server_name: String,
}
-/// MCP tool metadata with tool name and optional description.
+/// Normalized MCP Apps discovery metadata from a tool's `_meta.ui` block.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct McpToolUi {
+ /// URI of the tool's MCP App resource, typically a `ui://` resource identifier. Use `session.mcp.resources.read` to fetch its HTML and resource metadata.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub resource_uri: Option
,
+ /// Tool visibility advertised by the server. When absent, MCP Apps defaults apply.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub visibility: Option>,
+}
+
+/// MCP tool metadata with tool name, optional description, and normalized MCP Apps discovery metadata.
///
///
///
@@ -5474,6 +5511,9 @@ pub struct McpTools {
pub description: Option
,
/// Tool name.
pub name: String,
+ /// Normalized MCP Apps discovery metadata. An empty object indicates that a valid `_meta.ui` block was present without recognized fields.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub ui: Option,
}
/// Tools exposed by the connected MCP server. Throws when the server is not connected.
@@ -11657,6 +11697,21 @@ pub struct SessionMetadataSnapshot {
pub workspace_path: Option,
}
+/// Cost-category metadata for a CAPI model.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SessionModelPriceCategory {
+ pub id: String,
+ pub price_category: ModelPickerPriceCategory,
+}
+
/// The list of models available to this session.
///
///
@@ -11670,6 +11725,9 @@ pub struct SessionMetadataSnapshot {
pub struct SessionModelList {
/// Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`).
pub list: Vec
,
+ /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub model_price_categories: Option>,
/// Per-quota snapshots returned alongside the model list, keyed by quota type.
#[serde(skip_serializing_if = "Option::is_none")]
pub quota_snapshots: Option>,
@@ -13222,6 +13280,9 @@ pub struct SessionUpdateOptionsParams {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionUpdateOptionsResult {
+ /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub plugin_hook_count: Option,
/// Whether the operation succeeded
pub success: bool,
}
@@ -16514,6 +16575,9 @@ pub struct SessionModelSetReasoningEffortResult {
pub struct SessionModelListResult {
/// Available models, ordered with the most preferred default first. Includes both Copilot (CAPI) models and any registry BYOK models; a BYOK model appears under its provider-qualified selection id (`provider/id`).
pub list: Vec,
+ /// Cost categories for the full CAPI catalog, including picker-disabled models that Auto may select. Metadata only; entries absent from `list` are not manually selectable.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub model_price_categories: Option>,
/// Per-quota snapshots returned alongside the model list, keyed by quota type.
#[serde(skip_serializing_if = "Option::is_none")]
pub quota_snapshots: Option>,
@@ -17914,6 +17978,9 @@ pub struct SessionProviderAddResult {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SessionOptionsUpdateResult {
+ /// Number of hooks loaded from installed plugins, returned when installedPlugins is updated
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub plugin_hook_count: Option,
/// Whether the operation succeeded
pub success: bool,
}
@@ -20781,6 +20848,63 @@ pub enum HMACAuthInfoType {
Hmac,
}
+/// Hook event name dispatched through the SDK callback transport.
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub enum HookType {
+ /// Runs before a tool is invoked.
+ #[serde(rename = "preToolUse")]
+ PreToolUse,
+ /// Runs before an MCP tool is invoked.
+ #[serde(rename = "preMcpToolCall")]
+ PreMcpToolCall,
+ /// Runs after a tool completes successfully.
+ #[serde(rename = "postToolUse")]
+ PostToolUse,
+ /// Runs after a tool fails.
+ #[serde(rename = "postToolUseFailure")]
+ PostToolUseFailure,
+ /// Runs after the user submits a prompt.
+ #[serde(rename = "userPromptSubmitted")]
+ UserPromptSubmitted,
+ /// Runs when a session starts.
+ #[serde(rename = "sessionStart")]
+ SessionStart,
+ /// Runs when a session ends.
+ #[serde(rename = "sessionEnd")]
+ SessionEnd,
+ /// Runs after an agent result is produced.
+ #[serde(rename = "postResult")]
+ PostResult,
+ /// Runs before a pull request description is generated.
+ #[serde(rename = "prePRDescription")]
+ PrePRDescription,
+ /// Runs when the agent encounters an error.
+ #[serde(rename = "errorOccurred")]
+ ErrorOccurred,
+ /// Runs when the agent stops.
+ #[serde(rename = "agentStop")]
+ AgentStop,
+ /// Runs when a subagent starts.
+ #[serde(rename = "subagentStart")]
+ SubagentStart,
+ /// Runs when a subagent stops.
+ #[serde(rename = "subagentStop")]
+ SubagentStop,
+ /// Runs before conversation context is compacted.
+ #[serde(rename = "preCompact")]
+ PreCompact,
+ /// Runs when the agent requests permission.
+ #[serde(rename = "permissionRequest")]
+ PermissionRequest,
+ /// Runs when the agent emits a notification.
+ #[serde(rename = "notification")]
+ Notification,
+ /// Unknown variant for forward compatibility.
+ #[default]
+ #[serde(other)]
+ Unknown,
+}
+
/// Constant value. Always "github".
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum InstalledPluginSourceGitHubSource {
@@ -21205,6 +21329,28 @@ pub enum McpHeadersHandlePendingHeadersRefreshRequest {
None(McpHeadersHandlePendingHeadersRefreshRequestNone),
}
+/// Consumer allowed to call an MCP tool.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub enum McpToolUiVisibility {
+ /// The model may call the tool.
+ #[serde(rename = "model")]
+ Model,
+ /// An MCP App view may call the tool.
+ #[serde(rename = "app")]
+ App,
+ /// Unknown variant for forward compatibility.
+ #[default]
+ #[serde(other)]
+ Unknown,
+}
+
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum McpOauthPendingRequestResponseTokenKind {
#[serde(rename = "token")]
diff --git a/rust/src/generated/rpc.rs b/rust/src/generated/rpc.rs
index 64b663e59e..403933a27c 100644
--- a/rust/src/generated/rpc.rs
+++ b/rust/src/generated/rpc.rs
@@ -4349,7 +4349,7 @@ impl<'a> SessionRpcMcp<'a> {
Ok(serde_json::from_value(_value)?)
}
- /// Lists the tools exposed by a connected MCP server on this session's host.
+ /// Lists the tools exposed by a connected MCP server on this session's host. This performs a live `tools/list` request. Tool UI metadata is returned independently of whether MCP Apps rendering is enabled for the session.
///
/// Wire method: `session.mcp.listTools`.
///
diff --git a/rust/src/generated/session_events.rs b/rust/src/generated/session_events.rs
index aa2f1e3a2a..cf670c4856 100644
--- a/rust/src/generated/session_events.rs
+++ b/rust/src/generated/session_events.rs
@@ -77,6 +77,8 @@ pub enum SessionEventType {
AssistantTurnStart,
#[serde(rename = "assistant.intent")]
AssistantIntent,
+ #[serde(rename = "assistant.server_tool_progress")]
+ AssistantServerToolProgress,
#[serde(rename = "assistant.reasoning")]
AssistantReasoning,
#[serde(rename = "assistant.reasoning_delta")]
@@ -195,6 +197,15 @@ pub enum SessionEventType {
///
#[serde(rename = "session.auto_mode_resolved")]
SessionAutoModeResolved,
+ ///
+ ///
+ ///
+ /// **Experimental.** This type is part of an experimental wire-protocol surface
+ /// and may change or be removed in future SDK or CLI releases.
+ ///
+ ///
+ #[serde(rename = "session.managed_settings_resolved")]
+ SessionManagedSettingsResolved,
#[serde(rename = "commands.changed")]
CommandsChanged,
#[serde(rename = "capabilities.changed")]
@@ -359,6 +370,8 @@ pub enum SessionEventData {
AssistantTurnStart(AssistantTurnStartData),
#[serde(rename = "assistant.intent")]
AssistantIntent(AssistantIntentData),
+ #[serde(rename = "assistant.server_tool_progress")]
+ AssistantServerToolProgress(AssistantServerToolProgressData),
#[serde(rename = "assistant.reasoning")]
AssistantReasoning(AssistantReasoningData),
#[serde(rename = "assistant.reasoning_delta")]
@@ -470,6 +483,15 @@ pub enum SessionEventData {
///
#[serde(rename = "session.auto_mode_resolved")]
SessionAutoModeResolved(SessionAutoModeResolvedData),
+ ///
+ ///
+ ///
+ /// **Experimental.** This type is part of an experimental wire-protocol surface
+ /// and may change or be removed in future SDK or CLI releases.
+ ///
+ ///
+ #[serde(rename = "session.managed_settings_resolved")]
+ SessionManagedSettingsResolved(SessionManagedSettingsResolvedData),
#[serde(rename = "commands.changed")]
CommandsChanged(CommandsChangedData),
#[serde(rename = "capabilities.changed")]
@@ -1461,6 +1483,18 @@ pub struct AssistantIntentData {
pub intent: String,
}
+/// Session event "assistant.server_tool_progress". Live progress signal for a provider-hosted server tool (e.g. hosted web search) while it runs, before the finalized serverTools envelope lands on the terminal assistant.message
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct AssistantServerToolProgressData {
+ /// Kind of hosted server tool that is running. Only `web_search` is emitted today.
+ pub kind: String,
+ /// Position of the hosted tool call in the response output. Stable across the call's lifecycle events (unlike the provider's per-event item id, which CAPI rotates), so the host keys the live in-progress row on it.
+ pub output_index: i64,
+ /// Lifecycle status of the hosted call: `in_progress`, `searching`, or `completed`.
+ pub status: String,
+}
+
/// Session event "assistant.reasoning". Assistant reasoning content for timeline display with complete thinking text
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -3653,6 +3687,29 @@ pub struct SamplingCompletedData {
pub request_id: RequestId,
}
+/// Single HTTP header entry as a name/value pair.
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct HeaderEntry {
+ /// HTTP response header name as observed by the runtime.
+ pub name: String,
+ /// HTTP response header value as observed by the runtime.
+ pub value: String,
+}
+
+/// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime.
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct McpOauthHttpResponse {
+ /// Complete UTF-8 response body for host-specific challenge handling, including an empty string for an empty body. Omitted when the complete body is not valid UTF-8; body read failures fail the HTTP operation rather than exposing a partial response.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub body: Option,
+ /// HTTP response headers as observed by the runtime. Order and casing are transport-dependent, and duplicate header names may appear multiple times.
+ pub headers: Vec,
+ /// HTTP status code returned with the auth challenge.
+ pub status_code: i32,
+}
+
/// Static OAuth client configuration, if the server specifies one
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -3689,6 +3746,9 @@ pub struct McpOauthWWWAuthenticateParams {
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpOauthRequiredData {
+ /// Raw HTTP response details from the OAuth auth challenge, as observed by the runtime. Header order and casing are transport-dependent, and duplicate header names may appear multiple times.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub http_response: Option,
/// Why the runtime is requesting host-provided OAuth credentials.
pub reason: McpOauthRequestReason,
/// Unique identifier for this OAuth request; used to respond via session.mcp.oauth.handlePendingRequest
@@ -3916,6 +3976,34 @@ pub struct SessionAutoModeResolvedData {
pub reasoning_bucket: Option,
}
+/// Session event "session.managed_settings_resolved". Enterprise managed-settings resolution: the effective managed settings the session applied and where they came from, so SDK clients can show users what is enterprise-managed and by which authority. Fires whenever managed policy is (re)applied — at session start, on resume, and on account switch. This is an ephemeral live snapshot (delivered to subscribers but not persisted to the session event log), because at session start it resolves before `session.start` is emitted; for a session-independent pull, use the SDK `getManagedSettings()` API, which returns the identical payload. Managed settings have a single authoritative source, so the highest-authority present layer (server > device) wins wholesale; `bypassPermissionsDisabled` is deny-wins across layers. Marked experimental while the managed-settings surface stabilizes.
+///
+///
+///
+/// **Experimental.** This type is part of an experimental wire-protocol surface
+/// and may change or be removed in future SDK or CLI releases.
+///
+///
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SessionManagedSettingsResolvedData {
+ /// Whether enterprise policy disables bypass-permissions ("yolo") mode for this session. Deny-wins across layers, and forced on when `failClosed` is true.
+ pub bypass_permissions_disabled: bool,
+ /// Whether the device (MDM/plist/registry/file) managed-settings layer was present
+ pub device_managed: bool,
+ /// Whether managed policy could not be determined (e.g. a failed server fetch) and the session fell back to the fail-closed restriction. When true, restrictions such as disabling bypass-permissions are enforced even though `settings` may be absent.
+ pub fail_closed: bool,
+ /// The setting keys under enterprise management in the effective managed settings (e.g. `model`, `enabledPlugins`, `permissions`). Empty when no managed settings are in force.
+ pub managed_keys: Vec,
+ /// Whether the server (account/org) managed-settings layer was present
+ pub server_managed: bool,
+ /// The effective (resolved) managed settings values, so clients can render exactly what is enforced. Absent when no managed policy is in force.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub settings: Option,
+ /// Which channel supplied the effective managed settings (the winning layer), or `none` when no policy is in force
+ pub source: ManagedSettingsResolvedSource,
+}
+
/// A single slash command available in the session, as listed by the `commands.changed` event.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -4119,7 +4207,7 @@ pub struct SessionMcpServerStatusChangedData {
pub status: McpServerStatus,
}
-/// Session event "mcp.tools.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed.
+/// Session event "mcp.tools.list_changed". Payload identifying the MCP server associated with a list change.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpToolsListChangedData {
@@ -4127,7 +4215,7 @@ pub struct McpToolsListChangedData {
pub server_name: String,
}
-/// Session event "mcp.resources.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed.
+/// Session event "mcp.resources.list_changed". Payload identifying the MCP server associated with a list change.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpResourcesListChangedData {
@@ -4135,7 +4223,7 @@ pub struct McpResourcesListChangedData {
pub server_name: String,
}
-/// Session event "mcp.prompts.list_changed". Payload of MCP `list_changed` notification events, emitted when an MCP server announces at runtime that one of its advertised lists changed.
+/// Session event "mcp.prompts.list_changed". Payload identifying the MCP server associated with a list change.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct McpPromptsListChangedData {
@@ -5570,6 +5658,24 @@ pub enum AutoModeResolvedReasoningBucket {
Unknown,
}
+/// Which channel supplied the effective enterprise managed settings (highest-authority present layer wins wholesale)
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
+pub enum ManagedSettingsResolvedSource {
+ /// Account/org policy self-fetched from the GitHub managed-settings endpoint (higher authority).
+ #[serde(rename = "server")]
+ Server,
+ /// Device-level MDM policy discovered from plist/registry/file (lower authority).
+ #[serde(rename = "device")]
+ Device,
+ /// No managed policy is in force (no layer contributed).
+ #[serde(rename = "none")]
+ None,
+ /// Unknown variant for forward compatibility.
+ #[default]
+ #[serde(other)]
+ Unknown,
+}
+
/// Exit plan mode action
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExitPlanModeAction {
diff --git a/rust/src/hooks.rs b/rust/src/hooks.rs
index ec8cdfa3a3..0c3d64076f 100644
--- a/rust/src/hooks.rs
+++ b/rust/src/hooks.rs
@@ -27,8 +27,8 @@ pub struct HookContext {
pub struct PreToolUseInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -65,8 +65,8 @@ pub struct PreToolUseOutput {
pub struct PreMcpToolCallInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -104,8 +104,8 @@ pub struct PreMcpToolCallOutput {
pub struct PostToolUseInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -144,8 +144,8 @@ pub struct PostToolUseOutput {
pub struct PostToolUseFailureInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -175,8 +175,8 @@ pub struct PostToolUseFailureOutput {
pub struct UserPromptSubmittedInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -205,8 +205,8 @@ pub struct UserPromptSubmittedOutput {
pub struct SessionStartInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -235,8 +235,8 @@ pub struct SessionStartOutput {
pub struct SessionEndInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
@@ -271,8 +271,8 @@ pub struct SessionEndOutput {
pub struct ErrorOccurredInput {
/// The runtime session ID of the session that triggered the hook.
pub session_id: String,
- /// Unix timestamp (ms).
- pub timestamp: i64,
+ /// Unix timestamp in ms (the runtime serializes this as a JSON float).
+ pub timestamp: f64,
/// Working directory.
#[serde(rename = "cwd")]
pub working_directory: PathBuf,
diff --git a/rust/tests/e2e/hooks_extended.rs b/rust/tests/e2e/hooks_extended.rs
index 259d462d56..ab93a0c3cd 100644
--- a/rust/tests/e2e/hooks_extended.rs
+++ b/rust/tests/e2e/hooks_extended.rs
@@ -36,7 +36,7 @@ async fn should_invoke_onsessionstart_hook_on_new_session() {
session.send_and_wait("Say hi").await.expect("send");
let input = recv_with_timeout(&mut rx, "sessionStart hook").await;
assert_eq!(input.source, "new");
- assert!(input.timestamp > 0);
+ assert!(input.timestamp > 0.0);
assert!(!input.working_directory.as_os_str().is_empty());
session.disconnect().await.expect("disconnect session");
@@ -68,7 +68,7 @@ async fn should_invoke_onuserpromptsubmitted_hook_when_sending_a_message() {
session.send_and_wait("Say hello").await.expect("send");
let input = recv_with_timeout(&mut rx, "userPromptSubmitted hook").await;
assert!(input.prompt.contains("Say hello"));
- assert!(input.timestamp > 0);
+ assert!(input.timestamp > 0.0);
assert!(!input.working_directory.as_os_str().is_empty());
session.disconnect().await.expect("disconnect session");
@@ -100,7 +100,7 @@ async fn should_invoke_onsessionend_hook_when_session_is_disconnected() {
session.send_and_wait("Say hi").await.expect("send");
session.disconnect().await.expect("disconnect session");
let input = recv_with_timeout(&mut rx, "sessionEnd hook").await;
- assert!(input.timestamp > 0);
+ assert!(input.timestamp > 0.0);
assert!(!input.working_directory.as_os_str().is_empty());
client.stop().await.expect("stop client");
@@ -237,7 +237,7 @@ async fn should_invoke_sessionend_hook() {
session.send_and_wait("Say bye").await.expect("send");
session.disconnect().await.expect("disconnect session");
let input = recv_with_timeout(&mut rx, "sessionEnd hook").await;
- assert!(input.timestamp > 0);
+ assert!(input.timestamp > 0.0);
client.stop().await.expect("stop client");
})
@@ -412,7 +412,7 @@ async fn should_invoke_posttoolusefailure_hook_for_failed_tool_result() {
.as_str()
.is_some_and(|path| path.contains("missing.txt"))
);
- assert!(input.timestamp > 0);
+ assert!(input.timestamp > 0.0);
assert!(!input.working_directory.as_os_str().is_empty());
assert!(
assistant_message_content(&answer).contains("HOOK_FAILURE_GUIDANCE_APPLIED")
diff --git a/rust/tests/e2e/pre_mcp_tool_call_hook.rs b/rust/tests/e2e/pre_mcp_tool_call_hook.rs
index 5dc782c963..fd05796fcd 100644
--- a/rust/tests/e2e/pre_mcp_tool_call_hook.rs
+++ b/rust/tests/e2e/pre_mcp_tool_call_hook.rs
@@ -126,7 +126,7 @@ async fn should_set_meta_via_premcptoolcall_hook() {
assert_eq!(input.server_name, "meta-echo");
assert_eq!(input.tool_name, "echo_meta");
assert!(!input.working_directory.as_os_str().is_empty());
- assert!(input.timestamp > 0);
+ assert!(input.timestamp > 0.0);
session.disconnect().await.expect("disconnect session");
client.stop().await.expect("stop client");
diff --git a/scripts/codegen/csharp.ts b/scripts/codegen/csharp.ts
index caa67e1682..24ec217f5b 100644
--- a/scripts/codegen/csharp.ts
+++ b/scripts/codegen/csharp.ts
@@ -27,6 +27,7 @@ import {
findSharedSchemaDefinitions,
postProcessSchema,
propagateInternalVisibility,
+ filterNodeByVisibility,
resolveRef,
resolveObjectSchema,
resolveSchema,
@@ -2490,11 +2491,23 @@ function generateRpcCode(
let sessionRpcParts: string[] = [];
if (schema.session) sessionRpcParts = emitSessionRpcClasses(schema.session, classes);
+ // Client handler surfaces (interfaces, handler properties, RPC registration)
+ // are only generated for public methods. Internal client methods (e.g.
+ // `hooks.invoke`) are runtime transport plumbing and must not surface any
+ // generated code — including their request/result DTOs, which would
+ // otherwise leak as `internal` types referenced by a `public` handler
+ // interface (CS0050/CS0051 inconsistent accessibility).
let clientSessionParts: string[] = [];
- if (schema.clientSession) clientSessionParts = emitClientSessionApiRegistration(schema.clientSession, classes);
+ if (schema.clientSession) {
+ const publicClientSession = filterNodeByVisibility(schema.clientSession, "public");
+ if (publicClientSession) clientSessionParts = emitClientSessionApiRegistration(publicClientSession, classes);
+ }
let clientGlobalParts: string[] = [];
- if (schema.clientGlobal) clientGlobalParts = emitClientGlobalApiRegistration(schema.clientGlobal, classes);
+ if (schema.clientGlobal) {
+ const publicClientGlobal = filterNodeByVisibility(schema.clientGlobal, "public");
+ if (publicClientGlobal) clientGlobalParts = emitClientGlobalApiRegistration(publicClientGlobal, classes);
+ }
const lines: string[] = [];
lines.push(`${COPYRIGHT}
diff --git a/test/harness/package-lock.json b/test/harness/package-lock.json
index 6405fdcdd4..1a8462d661 100644
--- a/test/harness/package-lock.json
+++ b/test/harness/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
- "@github/copilot": "^1.0.71-2",
+ "@github/copilot": "^1.0.71",
"@modelcontextprotocol/sdk": "^1.26.0",
"@types/node": "^25.3.3",
"@types/node-forge": "^1.3.14",
@@ -501,9 +501,9 @@
}
},
"node_modules/@github/copilot": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71-2.tgz",
- "integrity": "sha512-En1onRCWjPy64wnjB9B6T6nXgPI7/myHksMotpKHzh8+CnVbaYQr2n/rBKM1BVScWgpA0zn19HmKZZlg82LW7A==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot/-/copilot-1.0.71.tgz",
+ "integrity": "sha512-F3axBi+sXSLYDJbxCBW36bM6MYKNC2rlyAf3Ivo/MjiHKKJW7j5AmaR1IRYS9Gt8r9mxOwWFM1cJFA+CuLaR8g==",
"dev": true,
"license": "SEE LICENSE IN LICENSE.md",
"dependencies": {
@@ -513,20 +513,20 @@
"copilot": "npm-loader.js"
},
"optionalDependencies": {
- "@github/copilot-darwin-arm64": "1.0.71-2",
- "@github/copilot-darwin-x64": "1.0.71-2",
- "@github/copilot-linux-arm64": "1.0.71-2",
- "@github/copilot-linux-x64": "1.0.71-2",
- "@github/copilot-linuxmusl-arm64": "1.0.71-2",
- "@github/copilot-linuxmusl-x64": "1.0.71-2",
- "@github/copilot-win32-arm64": "1.0.71-2",
- "@github/copilot-win32-x64": "1.0.71-2"
+ "@github/copilot-darwin-arm64": "1.0.71",
+ "@github/copilot-darwin-x64": "1.0.71",
+ "@github/copilot-linux-arm64": "1.0.71",
+ "@github/copilot-linux-x64": "1.0.71",
+ "@github/copilot-linuxmusl-arm64": "1.0.71",
+ "@github/copilot-linuxmusl-x64": "1.0.71",
+ "@github/copilot-win32-arm64": "1.0.71",
+ "@github/copilot-win32-x64": "1.0.71"
}
},
"node_modules/@github/copilot-darwin-arm64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71-2.tgz",
- "integrity": "sha512-6DA1W3RFbyDPL/MXPeAzM9HxS7hvZOnToJHupGf4QGGs4dG2dzmTyEv0LkD4rFtyc1dx6QtyOjRt5vUsWw39Xw==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-darwin-arm64/-/copilot-darwin-arm64-1.0.71.tgz",
+ "integrity": "sha512-mEWzyqbqRAWgyU7i2uuSRoVPx/TwaFQX0nZmw0bc30aJ0BnO7cy2kYQyCHw8ykmf/tfxT0xauZ6k0BOFmWizzQ==",
"cpu": [
"arm64"
],
@@ -541,9 +541,9 @@
}
},
"node_modules/@github/copilot-darwin-x64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71-2.tgz",
- "integrity": "sha512-u/DWMhTCFaGf9TE70IgXKk0/9kWiijiHfEMVRqedbzdUNxGQ5u4lsaquEirHj3sSegVw8MZzgfKAzrT9CTr0aA==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-darwin-x64/-/copilot-darwin-x64-1.0.71.tgz",
+ "integrity": "sha512-Md9yEg406OBVBx3w4PeEj62TubulVLBcHleqmCoOoUmPgUxPZotUbrqz3rtbzADbXfrrD7JWvVsbd2UiNL194w==",
"cpu": [
"x64"
],
@@ -558,9 +558,9 @@
}
},
"node_modules/@github/copilot-linux-arm64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71-2.tgz",
- "integrity": "sha512-xKKk5o+zFJZ3EcoDOiCOdn1sbc8N/tHkn2j2sFQahE2SDp18ZpA2lzZp6XZ32VgxQJx0qlhPWh5BRn32Sc9csw==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linux-arm64/-/copilot-linux-arm64-1.0.71.tgz",
+ "integrity": "sha512-ykLJYOqBj3jRB5IJCDugLClAqbr7DmtTbUjlNY7+Jdq/n6i+d7xUQGclf1IWL5gnxbGQVAf+zkToD+sRM389Kg==",
"cpu": [
"arm64"
],
@@ -575,9 +575,9 @@
}
},
"node_modules/@github/copilot-linux-x64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71-2.tgz",
- "integrity": "sha512-We/kZNlEAXxhm9vMBae63Yy07RXUsTlOLsg5WrG7aNm0kh5ocUFpf5EodvtBbTmVIlGCvvm/RM1cYic6lPtD+w==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linux-x64/-/copilot-linux-x64-1.0.71.tgz",
+ "integrity": "sha512-pC0FNHG+BBwZd6yZlM85kkAGN+uJhM6o+THi76N2GnnSxmw7+remb1mvYxdgRVbdCm+LBUIbCKRWJLuMwrfb6A==",
"cpu": [
"x64"
],
@@ -592,9 +592,9 @@
}
},
"node_modules/@github/copilot-linuxmusl-arm64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71-2.tgz",
- "integrity": "sha512-Hnyz/C4uNAXZVt6/RMtQBI89W0fxvUizm0mlp/ZohSthN03fv/PppbEsY7U5WqbTuDBuOKIU4kf3fCDC2elMCQ==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-arm64/-/copilot-linuxmusl-arm64-1.0.71.tgz",
+ "integrity": "sha512-hBmDljFTjacxqZTasCEy43H8EIzuXB/hHEBBCMFjhB9J00nIxsO6Dh0woTifKpx7knTYZdpTjjca3D0pAoZlUA==",
"cpu": [
"arm64"
],
@@ -609,9 +609,9 @@
}
},
"node_modules/@github/copilot-linuxmusl-x64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71-2.tgz",
- "integrity": "sha512-CQDeUqq2wbM+a/Tec68O+6Gp8f3cUZ5DLAqcNwxzN+86b5Gsu9xyGMV2eIF/sEYxakhY4mpHhjwdZBySue7IBA==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-linuxmusl-x64/-/copilot-linuxmusl-x64-1.0.71.tgz",
+ "integrity": "sha512-CfTXU8pa5dxRz22xQzoi3TiG1PJo9+WR8PRDiPSdkIBSyPJ1NvX87DJmfXjTgeAfR+wkjt/p0keDCaBBVhNmUA==",
"cpu": [
"x64"
],
@@ -626,9 +626,9 @@
}
},
"node_modules/@github/copilot-win32-arm64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71-2.tgz",
- "integrity": "sha512-lb81urkKJ+yWIy62yw9NmyjX5eFiw0y2TVIkVje26ot+gCiCJPisqyRWwhkfq0g/YKRbS8uoX4r+KHAhRH1wlg==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-win32-arm64/-/copilot-win32-arm64-1.0.71.tgz",
+ "integrity": "sha512-+HI1DokixXhHUahj06Fw67ZAigBuXKC58BFma4UJOGrQsDgwOSbqeTQHCw6vuymzjKlg3sactfsCUTaefkjscQ==",
"cpu": [
"arm64"
],
@@ -643,9 +643,9 @@
}
},
"node_modules/@github/copilot-win32-x64": {
- "version": "1.0.71-2",
- "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71-2.tgz",
- "integrity": "sha512-j3mQO2OUVoO+ZGE5KGF3iiekTikQZDQTnZO9RquWPXLwQs6ZdgRw9pPhbpXh0eoM1osZAW1/tmafcyYNpdBgWA==",
+ "version": "1.0.71",
+ "resolved": "https://registry.npmjs.org/@github/copilot-win32-x64/-/copilot-win32-x64-1.0.71.tgz",
+ "integrity": "sha512-02kXOBd9CwBbCaztuf71WYWn+uGapCuiaasomN4tcMH3HBVZ4gi3J0ZUoRcgcS80xh81uQyeBHbnUKzb/RE/9A==",
"cpu": [
"x64"
],
diff --git a/test/harness/package.json b/test/harness/package.json
index bfc8879147..18b19e21ac 100644
--- a/test/harness/package.json
+++ b/test/harness/package.json
@@ -14,7 +14,7 @@
"node": "^20.19.0 || >=22.12.0"
},
"devDependencies": {
- "@github/copilot": "^1.0.71-2",
+ "@github/copilot": "^1.0.71",
"@modelcontextprotocol/sdk": "^1.26.0",
"@types/node": "^25.3.3",
"@types/node-forge": "^1.3.14",