clawd-code-sdk 1.0.6__tar.gz → 1.0.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/PKG-INFO +1 -1
  2. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/pyproject.toml +1 -1
  3. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/__init__.py +10 -0
  4. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_internal/query.py +39 -0
  5. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/anthropic_types.py +24 -26
  6. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/client.py +65 -16
  7. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/__init__.py +11 -3
  8. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/agents.py +6 -2
  9. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/base.py +9 -1
  10. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/content_blocks.py +1 -3
  11. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/control.py +73 -4
  12. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/hooks.py +4 -3
  13. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/input_types.py +2 -2
  14. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/messages.py +7 -10
  15. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/options.py +12 -2
  16. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/output_types.py +9 -9
  17. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/permissions.py +20 -1
  18. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/server_info.py +8 -4
  19. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/settings.py +1 -1
  20. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/system_messages.py +15 -1
  21. clawd_code_sdk-1.0.8/src/clawd_code_sdk/models/thinking.py +56 -0
  22. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_subprocess_buffering.py +14 -14
  23. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_tool_callbacks.py +28 -21
  24. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_transport.py +7 -5
  25. clawd_code_sdk-1.0.6/src/clawd_code_sdk/models/thinking.py +0 -30
  26. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/.gitignore +0 -0
  27. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/LICENSE +0 -0
  28. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/README.md +0 -0
  29. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_bundled/.gitignore +0 -0
  30. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_errors.py +0 -0
  31. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_internal/__init__.py +0 -0
  32. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_internal/message_parser.py +0 -0
  33. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_internal/transport/__init__.py +0 -0
  34. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_internal/transport/subprocess_cli.py +0 -0
  35. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/_version.py +0 -0
  36. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/list_sessions.py +0 -0
  37. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/mcp_utils.py +0 -0
  38. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/mcp.py +0 -0
  39. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/prompt_requests.py +0 -0
  40. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/prompts.py +0 -0
  41. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/models/session.py +0 -0
  42. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/py.typed +0 -0
  43. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/query.py +0 -0
  44. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/session.py +0 -0
  45. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/storage/ARCHITECTURE.md +0 -0
  46. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/storage/__init__.py +0 -0
  47. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/storage/helpers.py +0 -0
  48. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/storage/models.py +0 -0
  49. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/storage/replay.py +0 -0
  50. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/src/clawd_code_sdk/usage.py +0 -0
  51. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/__init__.py +0 -0
  52. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/conftest.py +0 -0
  53. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/__init__.py +0 -0
  54. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_agents_and_settings.py +0 -0
  55. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_dynamic_control.py +0 -0
  56. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_hook_events.py +0 -0
  57. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_hooks.py +0 -0
  58. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_include_partial_messages.py +0 -0
  59. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_mcp_resources.py +0 -0
  60. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_mcp_tools.py +0 -0
  61. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_sdk_mcp_resources.py +0 -0
  62. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_sdk_mcp_tools.py +0 -0
  63. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_slash_commands.py +0 -0
  64. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_stderr_callback.py +0 -0
  65. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_storage_parsing.py +0 -0
  66. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_structured_output.py +0 -0
  67. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_subagent_invocation.py +0 -0
  68. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/e2e/test_tool_permissions.py +0 -0
  69. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/mcp_server.py +0 -0
  70. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/mock_claude_server.py +0 -0
  71. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_changelog.py +0 -0
  72. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_client.py +0 -0
  73. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_errors.py +0 -0
  74. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_image.png +0 -0
  75. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_integration.py +0 -0
  76. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_message_parser.py +0 -0
  77. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_sdk_mcp_integration.py +0 -0
  78. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_session.py +0 -0
  79. {clawd_code_sdk-1.0.6 → clawd_code_sdk-1.0.8}/tests/test_streaming_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clawd-code-sdk
3
- Version: 1.0.6
3
+ Version: 1.0.8
4
4
  Summary: Python SDK for Claude Code
5
5
  Project-URL: Documentation, https://github.com/phil65/claude-agent-sdk-python
6
6
  Project-URL: Homepage, https://github.com/phil65/claude-agent-sdk-python
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "clawd-code-sdk"
3
- version = "1.0.6"
3
+ version = "1.0.8"
4
4
  description = "Python SDK for Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
@@ -51,8 +51,13 @@ from .models import (
51
51
  McpSdkServerConfig,
52
52
  McpSdkServerConfigWithInstance,
53
53
  McpServerConfig,
54
+ AppliedSettings,
55
+ GetSettingsResponse,
56
+ McpAuthenticateResponse,
54
57
  McpSetServersResult,
58
+ SettingsSource,
55
59
  Message,
60
+ RemoteControlResponse,
56
61
  NotificationHookInput,
57
62
  NotificationHookSpecificOutput,
58
63
  PermissionMode,
@@ -138,6 +143,7 @@ __all__ = [
138
143
  # Hook support
139
144
  "AgentHookHandler",
140
145
  "AgentHooksConfig",
146
+ "AppliedSettings",
141
147
  "AssistantMessage",
142
148
  "AuthenticationError",
143
149
  "BaseHookInput",
@@ -163,6 +169,7 @@ __all__ = [
163
169
  "DocumentMediaType",
164
170
  "Filesystem",
165
171
  "FromPR",
172
+ "GetSettingsResponse",
166
173
  "HookCallback",
167
174
  "HookContext",
168
175
  "HookHandler",
@@ -177,6 +184,7 @@ __all__ = [
177
184
  "InitSystemMessage",
178
185
  "InvalidRequestError",
179
186
  "ListSessionsOptions",
187
+ "McpAuthenticateResponse",
180
188
  "McpSdkServerConfig",
181
189
  "McpSdkServerConfigWithInstance",
182
190
  "McpServerConfig",
@@ -206,6 +214,7 @@ __all__ = [
206
214
  "ProcessError",
207
215
  "PromptHookHandler",
208
216
  "RateLimitError",
217
+ "RemoteControlResponse",
209
218
  "ResultErrorMessage",
210
219
  "ResultMessage",
211
220
  "ResultSuccessMessage",
@@ -225,6 +234,7 @@ __all__ = [
225
234
  "SessionSnapshot",
226
235
  "SessionState",
227
236
  "SettingSource",
237
+ "SettingsSource",
228
238
  "StopHookInput",
229
239
  "SubagentStartHookInput",
230
240
  "SubagentStartHookSpecificOutput",
@@ -522,6 +522,45 @@ class Query:
522
522
  """End the current session."""
523
523
  return await self._send_control_request({"subtype": "end_session"})
524
524
 
525
+ async def remote_control(self, *, enabled: bool) -> dict[str, Any]:
526
+ """Toggle the remote control REPL bridge for external session access.
527
+
528
+ When enabled, starts a bridge that allows remote clients to send prompts,
529
+ permission responses, interrupts, and model changes into the session.
530
+ The response includes ``session_url``, ``connect_url``, and
531
+ ``environment_id`` when enabling.
532
+ """
533
+ req = {"subtype": "remote_control", "enabled": enabled}
534
+ return await self._send_control_request(req)
535
+
536
+ async def apply_flag_settings(self, settings: dict[str, Any]) -> dict[str, Any]:
537
+ """Apply runtime flag settings."""
538
+ req = {"subtype": "apply_flag_settings", "settings": settings}
539
+ return await self._send_control_request(req)
540
+
541
+ async def get_settings(self) -> dict[str, Any]:
542
+ """Get the effective merged settings and raw per-source settings."""
543
+ return await self._send_control_request({"subtype": "get_settings"})
544
+
545
+ async def mcp_authenticate(self, server_name: str) -> dict[str, Any]:
546
+ """Trigger OAuth authentication for an MCP server."""
547
+ req = {"subtype": "mcp_authenticate", "serverName": server_name}
548
+ return await self._send_control_request(req)
549
+
550
+ async def mcp_clear_auth(self, server_name: str) -> dict[str, Any]:
551
+ """Clear OAuth credentials for an MCP server."""
552
+ req = {"subtype": "mcp_clear_auth", "serverName": server_name}
553
+ return await self._send_control_request(req)
554
+
555
+ async def mcp_oauth_callback_url(self, server_name: str, callback_url: str) -> dict[str, Any]:
556
+ """Provide an OAuth redirect callback URL to complete an MCP server OAuth flow."""
557
+ req = {
558
+ "subtype": "mcp_oauth_callback_url",
559
+ "serverName": server_name,
560
+ "callbackUrl": callback_url,
561
+ }
562
+ return await self._send_control_request(req)
563
+
525
564
  async def rewind_files(self, user_message_id: str) -> dict[str, Any]:
526
565
  """Rewind tracked files to their state at a specific user message.
527
566
 
@@ -7,6 +7,7 @@ used to provide proper typing for ToolResultBlock.content.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import functools
10
11
  from typing import Annotated
11
12
 
12
13
  from anthropic.types.beta import (
@@ -73,35 +74,32 @@ ToolResultContentBlock = Annotated[
73
74
  | BetaTextEditorCodeExecutionToolResultError,
74
75
  Field(discriminator="type"),
75
76
  ]
76
- _tool_result_content_adapter: TypeAdapter[list[ToolResultContentBlock]] | None = None
77
77
 
78
78
 
79
+ @functools.cache
79
80
  def _get_adapter() -> TypeAdapter[list[ToolResultContentBlock]]:
80
- global _tool_result_content_adapter # noqa: PLW0603
81
- if _tool_result_content_adapter is None:
82
- # Force schema build for Anthropic models (deferred by default)
83
- for model in [
84
- BetaTextBlock,
85
- BetaWebSearchResultBlock,
86
- BetaWebSearchToolResultError,
87
- BetaBashCodeExecutionResultBlock,
88
- BetaBashCodeExecutionToolResultError,
89
- BetaCodeExecutionResultBlock,
90
- BetaCodeExecutionToolResultError,
91
- BetaTextEditorCodeExecutionCreateResultBlock,
92
- BetaTextEditorCodeExecutionStrReplaceResultBlock,
93
- BetaTextEditorCodeExecutionToolResultError,
94
- BetaTextEditorCodeExecutionViewResultBlock,
95
- BetaToolReferenceBlock,
96
- BetaToolSearchToolResultError,
97
- BetaToolSearchToolSearchResultBlock,
98
- BetaWebFetchBlock,
99
- BetaWebFetchToolResultErrorBlock,
100
- ]:
101
- if isinstance(model, type) and issubclass(model, BaseModel):
102
- model.model_rebuild()
103
- _tool_result_content_adapter = TypeAdapter(list[ToolResultContentBlock])
104
- return _tool_result_content_adapter
81
+ # Force schema build for Anthropic models (deferred by default)
82
+ for model in [
83
+ BetaTextBlock,
84
+ BetaWebSearchResultBlock,
85
+ BetaWebSearchToolResultError,
86
+ BetaBashCodeExecutionResultBlock,
87
+ BetaBashCodeExecutionToolResultError,
88
+ BetaCodeExecutionResultBlock,
89
+ BetaCodeExecutionToolResultError,
90
+ BetaTextEditorCodeExecutionCreateResultBlock,
91
+ BetaTextEditorCodeExecutionStrReplaceResultBlock,
92
+ BetaTextEditorCodeExecutionToolResultError,
93
+ BetaTextEditorCodeExecutionViewResultBlock,
94
+ BetaToolReferenceBlock,
95
+ BetaToolSearchToolResultError,
96
+ BetaToolSearchToolSearchResultBlock,
97
+ BetaWebFetchBlock,
98
+ BetaWebFetchToolResultErrorBlock,
99
+ ]:
100
+ if isinstance(model, type) and issubclass(model, BaseModel):
101
+ model.model_rebuild()
102
+ return TypeAdapter(list[ToolResultContentBlock])
105
103
 
106
104
 
107
105
  def validate_tool_result_content(content: list[dict[str, object]]) -> list[ToolResultContentBlock]:
@@ -7,16 +7,18 @@ import os
7
7
  from typing import TYPE_CHECKING, Any, Literal, Self, cast
8
8
 
9
9
  import anyenv
10
- from pydantic import TypeAdapter
11
10
 
12
11
  from clawd_code_sdk._errors import CLIConnectionError
13
12
  from clawd_code_sdk.models import (
14
13
  AssistantMessage,
15
14
  ClaudeAgentOptions,
16
15
  ClaudeCodeAgentInfo, # noqa: TC001
16
+ GetSettingsResponse,
17
+ McpAuthenticateResponse,
17
18
  McpSdkServerConfigWithInstance,
18
19
  McpSetServersResult,
19
20
  McpStatusResponse,
21
+ RemoteControlResponse,
20
22
  ResultErrorMessage,
21
23
  ResultMessage,
22
24
  ResultSuccessMessage,
@@ -33,6 +35,7 @@ if TYPE_CHECKING:
33
35
  from clawd_code_sdk._internal.query import Query
34
36
  from clawd_code_sdk.models import (
35
37
  ClaudeCodeServerInfo,
38
+ ClaudeCodeSettings,
36
39
  McpServerConfig,
37
40
  Message,
38
41
  PermissionMode,
@@ -91,9 +94,6 @@ class ClaudeSDKClient:
91
94
  from clawd_code_sdk._internal.query import Query
92
95
  from clawd_code_sdk._internal.transport.subprocess_cli import SubprocessCLITransport
93
96
 
94
- # Validate and configure permission settings (matching TypeScript SDK logic)
95
- self.options.validate()
96
-
97
97
  # If on_permission is a callback, extract it for Query and replace with
98
98
  # "stdio" so the CLI routes permission requests through the control protocol.
99
99
  if callable(self.options.on_permission):
@@ -130,20 +130,10 @@ class ClaudeSDKClient:
130
130
  else:
131
131
  system_prompt = self.options.system_prompt
132
132
 
133
- # JSON schema for structured output
134
- json_schema: dict[str, Any] | None
135
- match self.options.output_schema:
136
- case type() as typ:
137
- json_schema = TypeAdapter(typ).json_schema()
138
- case dict() as schema:
139
- json_schema = schema
140
- case None:
141
- json_schema = None
142
-
143
133
  # Create Query to handle control protocol
144
134
  self._query = Query(
145
135
  transport=self._transport,
146
- can_use_tool=can_use_tool,
136
+ can_use_tool=can_use_tool, # ty:ignore[invalid-argument-type]
147
137
  on_user_question=self.options.on_user_question,
148
138
  on_elicitation=self.options.on_elicitation,
149
139
  hooks=self.options.hooks,
@@ -152,7 +142,7 @@ class ClaudeSDKClient:
152
142
  agents=self.options.agents,
153
143
  system_prompt=system_prompt,
154
144
  append_system_prompt=append_system_prompt,
155
- json_schema=json_schema,
145
+ json_schema=self.options.get_json_schema(),
156
146
  prompt_suggestions=self.options.prompt_suggestions,
157
147
  agent_progress_summaries=self.options.agent_progress_summaries,
158
148
  )
@@ -338,6 +328,65 @@ class ClaudeSDKClient:
338
328
  query = self._ensure_connected()
339
329
  await query.end_session()
340
330
 
331
+ async def remote_control(self, *, enabled: bool) -> RemoteControlResponse | None:
332
+ """Toggle the remote control REPL bridge for external session access.
333
+
334
+ When enabled, starts a bridge that allows remote clients to send prompts,
335
+ permission responses, interrupts, and model changes into the session.
336
+
337
+ Returns:
338
+ A ``RemoteControlResponse`` with session URLs and environment ID
339
+ when enabling, or ``None`` when disabling.
340
+ """
341
+ query = self._ensure_connected()
342
+ result = await query.remote_control(enabled=enabled)
343
+ if not result:
344
+ return None
345
+ return RemoteControlResponse.model_validate(result)
346
+
347
+ async def apply_flag_settings(self, settings: ClaudeCodeSettings) -> None:
348
+ """Apply runtime settings overrides without restarting the session.
349
+
350
+ Flag settings are an in-memory settings source that gets merged with
351
+ other sources (user, project, etc.) to produce the effective settings.
352
+
353
+ Args:
354
+ settings: Settings to apply. Only non-None fields will be sent.
355
+ """
356
+ query = self._ensure_connected()
357
+ serialized = settings.model_dump(by_alias=True, exclude_none=True)
358
+ await query.apply_flag_settings(serialized)
359
+
360
+ async def get_settings(self) -> GetSettingsResponse:
361
+ """Get the effective merged settings and raw per-source settings."""
362
+ query = self._ensure_connected()
363
+ result = await query.get_settings()
364
+ return GetSettingsResponse.model_validate(result)
365
+
366
+ async def mcp_authenticate(self, server_name: str) -> McpAuthenticateResponse:
367
+ """Trigger OAuth authentication for an MCP server.
368
+
369
+ Returns:
370
+ Response indicating whether user action is required and the auth URL if so.
371
+ """
372
+ query = self._ensure_connected()
373
+ result = await query.mcp_authenticate(server_name)
374
+ return McpAuthenticateResponse.model_validate(result)
375
+
376
+ async def mcp_clear_auth(self, server_name: str) -> None:
377
+ """Clear OAuth credentials for an MCP server."""
378
+ query = self._ensure_connected()
379
+ await query.mcp_clear_auth(server_name)
380
+
381
+ async def mcp_oauth_callback_url(self, server_name: str, callback_url: str) -> None:
382
+ """Provide an OAuth redirect callback URL to complete an MCP server OAuth flow.
383
+
384
+ After the user completes browser-based OAuth, call this with the full
385
+ redirect URL (containing the authorization code) to finish the flow.
386
+ """
387
+ query = self._ensure_connected()
388
+ await query.mcp_oauth_callback_url(server_name, callback_url)
389
+
341
390
  async def set_max_thinking_tokens(self, max_thinking_tokens: int) -> None:
342
391
  """Set the maximum number of thinking tokens for extended thinking."""
343
392
  query = self._ensure_connected()
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from .agents import AgentDefinition, AgentInfo, ToolsPreset
6
6
  from .base import (
7
7
  ApiKeySource,
8
+ AssistantMessageError,
8
9
  ElicitationAction,
9
10
  ElicitationMode,
10
11
  PermissionMode,
@@ -34,7 +35,12 @@ from .control import (
34
35
  SDKControlMcpMessageRequest,
35
36
  SDKControlMcpOAuthCallbackUrlRequest,
36
37
  SDKControlPermissionRequest,
38
+ AppliedSettings,
39
+ GetSettingsResponse,
40
+ McpAuthenticateResponse,
41
+ RemoteControlResponse,
37
42
  SDKControlRemoteControlRequest,
43
+ SettingsSource,
38
44
  SDKControlRequest,
39
45
  SDKControlResponse,
40
46
  SDKControlRewindFilesRequest,
@@ -214,12 +220,10 @@ from .content_blocks import (
214
220
  ThinkingBlock,
215
221
  ToolResultBlock,
216
222
  ToolUseBlock,
217
- content_block_adapter,
218
223
  )
219
224
  from .session import SDKSessionInfo, ListSessionsOptions
220
225
  from .messages import (
221
226
  AssistantMessage,
222
- AssistantMessageError,
223
227
  ModelUsage,
224
228
  BaseResultMessage,
225
229
  PromptSuggestionMessage,
@@ -367,6 +371,7 @@ __all__ = [
367
371
  "AgentOutputUsage",
368
372
  "AgentServerToolUse",
369
373
  "ApiKeySource",
374
+ "AppliedSettings",
370
375
  "AskUserQuestion",
371
376
  "AskUserQuestionInput",
372
377
  "AskUserQuestionItem",
@@ -424,6 +429,7 @@ __all__ = [
424
429
  "FilesPersistedSystemMessage",
425
430
  "Filesystem",
426
431
  "FromPR",
432
+ "GetSettingsResponse",
427
433
  "GitDiff",
428
434
  "GlobInput",
429
435
  "GlobOutput",
@@ -460,6 +466,7 @@ __all__ = [
460
466
  "ListMcpResourcesOutput",
461
467
  "ListSessionsOptions",
462
468
  "LocalCommandOutputMessage",
469
+ "McpAuthenticateResponse",
463
470
  "McpClaudeAIProxyServerConfig",
464
471
  "McpConnectionStatus",
465
472
  "McpHttpServerConfig",
@@ -532,6 +539,7 @@ __all__ = [
532
539
  "ReadPdfOutput",
533
540
  "ReadTextOutput",
534
541
  "ReasoningEffort",
542
+ "RemoteControlResponse",
535
543
  "RequestId",
536
544
  "ResultErrorMessage",
537
545
  "ResultMessage",
@@ -564,6 +572,7 @@ __all__ = [
564
572
  "SessionStartHookInput",
565
573
  "SessionStartHookSpecificOutput",
566
574
  "SettingSource",
575
+ "SettingsSource",
567
576
  "SetupHookInput",
568
577
  "SkillInput",
569
578
  "SkillOutput",
@@ -631,7 +640,6 @@ __all__ = [
631
640
  "WorktreeCreateHookInput",
632
641
  "WorktreeRemoveHookInput",
633
642
  "WriteOutput",
634
- "content_block_adapter",
635
643
  "control_request_adapter",
636
644
  "message_adapter",
637
645
  "system_message_adapter",
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  from dataclasses import dataclass, fields
6
- from typing import Any, Literal, TypedDict
6
+ from typing import TYPE_CHECKING, Any, Literal, TypedDict
7
7
 
8
8
  from anthropic.types import Model # noqa: TC002
9
9
 
@@ -12,6 +12,10 @@ from clawd_code_sdk.models.hooks import AgentHooksConfig # noqa: TC001
12
12
  from clawd_code_sdk.models.mcp import ExternalMcpServerConfig # noqa: TC001
13
13
 
14
14
 
15
+ if TYPE_CHECKING:
16
+ from collections.abc import Mapping
17
+
18
+
15
19
  # Agent MCP server spec: either a string name or a {name: config} dict.
16
20
  # Matches the TypeScript type: AgentMcpServerSpec =
17
21
  # string | Record<string, McpServerConfigForProcessTransport>
@@ -57,7 +61,7 @@ class AgentDefinition:
57
61
  tools: list[str] | None = None
58
62
  model: ModelName | Literal["inherit"] | str | None = None # noqa: PYI051
59
63
  memory: SettingSource | None = None
60
- mcp_servers: list[AgentMcpServerSpec] | dict[str, ExternalMcpServerConfig] | None = None
64
+ mcp_servers: list[AgentMcpServerSpec] | Mapping[str, ExternalMcpServerConfig] | None = None
61
65
  disallowed_tools: list[str] | None = None
62
66
  critical_system_reminder_experimental: str | None = None
63
67
  skills: list[str] | None = None
@@ -26,7 +26,15 @@ CompactionTrigger = Literal["auto", "manual"]
26
26
  ElicitationMode = Literal["form", "url"]
27
27
  ElicitationAction = Literal["accept", "decline", "cancel"]
28
28
  FastModeState = Literal["off", "cooldown", "on"]
29
-
29
+ EffortLevel = Literal["low", "medium", "high", "max"]
30
+ AssistantMessageError = Literal[
31
+ "authentication_failed",
32
+ "billing_error",
33
+ "rate_limit",
34
+ "invalid_request",
35
+ "server_error",
36
+ "unknown",
37
+ ]
30
38
  StopReason = Literal[
31
39
  "end_turn",
32
40
  "max_tokens",
@@ -11,7 +11,7 @@ from collections.abc import Sequence
11
11
  from typing import TYPE_CHECKING, Annotated, Any, Literal
12
12
 
13
13
  from anthropic.types.beta.beta_tool_use_block import Caller
14
- from pydantic import BaseModel, ConfigDict, Discriminator, TypeAdapter
14
+ from pydantic import BaseModel, ConfigDict, Discriminator
15
15
 
16
16
  from clawd_code_sdk.models import ToolInput
17
17
  from clawd_code_sdk.models.base import ClaudeCodeBaseModel, StopReason, ToolName
@@ -110,8 +110,6 @@ ContentBlock = Annotated[
110
110
  Discriminator("type"),
111
111
  ]
112
112
 
113
- content_block_adapter = TypeAdapter[ContentBlock](ContentBlock)
114
-
115
113
 
116
114
  # =============================================================================
117
115
  # Message-level models
@@ -5,12 +5,19 @@ from __future__ import annotations
5
5
  from dataclasses import dataclass
6
6
  from typing import Annotated, Any, Literal, TypedDict
7
7
 
8
- from pydantic import Discriminator, TypeAdapter
8
+ from pydantic import BaseModel, Discriminator, TypeAdapter
9
9
 
10
10
  from clawd_code_sdk.models.agents import AgentDefinition # noqa: TC001
11
- from clawd_code_sdk.models.base import ElicitationMode, PermissionMode # noqa: TC001
11
+ from clawd_code_sdk.models.base import ( # noqa: TC001
12
+ ClaudeCodeBaseModel,
13
+ EffortLevel,
14
+ ElicitationMode,
15
+ ModelName,
16
+ PermissionMode,
17
+ )
12
18
  from clawd_code_sdk.models.hooks import HookEvent, HookInput # noqa: TC001
13
19
  from clawd_code_sdk.models.mcp import ExternalMcpServerConfig, JSONRPCMessage # noqa: TC001
20
+ from clawd_code_sdk.models.permissions import PermissionUpdate # noqa: TC001
14
21
 
15
22
 
16
23
  # SDK Control Protocol
@@ -28,10 +35,12 @@ class SDKControlPermissionRequest:
28
35
  subtype: Literal["can_use_tool"] = "can_use_tool"
29
36
  tool_name: str
30
37
  input: dict[str, Any]
31
- tool_use_id: str
32
- permission_suggestions: list[Any] | None = None
38
+ permission_suggestions: list[PermissionUpdate] | None = None
33
39
  blocked_path: str | None = None
34
40
  decision_reason: str | None = None
41
+ title: str | None = None
42
+ display_name: str | None = None
43
+ tool_use_id: str
35
44
  agent_id: str | None = None
36
45
  description: str | None = None
37
46
 
@@ -209,6 +218,66 @@ class SDKControlRemoteControlRequest:
209
218
  enabled: bool = False
210
219
 
211
220
 
221
+ class RemoteControlResponse(BaseModel):
222
+ """Response from enabling remote control.
223
+
224
+ Contains the URLs and identifiers needed for remote clients to connect
225
+ to the session.
226
+ """
227
+
228
+ session_url: str
229
+ """URL for the remote control session."""
230
+
231
+ connect_url: str
232
+ """URL for remote clients to connect to the session."""
233
+
234
+ environment_id: str
235
+ """Identifier for the environment hosting the session."""
236
+
237
+
238
+ class McpAuthenticateResponse(ClaudeCodeBaseModel):
239
+ """Response from authenticating with an MCP server."""
240
+
241
+ auth_url: str | None = None
242
+ """OAuth authorization URL for the user to visit, if user action is required."""
243
+
244
+ requires_user_action: bool
245
+ """Whether the user needs to complete an OAuth flow in the browser."""
246
+
247
+
248
+ class AppliedSettings(BaseModel):
249
+ """The currently applied model and effort settings."""
250
+
251
+ model: ModelName | str
252
+ """The active model identifier."""
253
+
254
+ effort: EffortLevel | None = None
255
+ """The active effort level, or None if not set."""
256
+
257
+
258
+ class SettingsSource(BaseModel):
259
+ """A single settings source with its raw settings."""
260
+
261
+ source: str
262
+ """The source name (e.g. 'user', 'project', 'flagSettings')."""
263
+
264
+ settings: dict[str, Any]
265
+ """The raw settings from this source."""
266
+
267
+
268
+ class GetSettingsResponse(BaseModel):
269
+ """Response from get_settings() containing effective and per-source settings."""
270
+
271
+ effective: dict[str, Any]
272
+ """The merged effective settings (ClaudeCodeSettings shape, camelCase keys)."""
273
+
274
+ sources: list[SettingsSource]
275
+ """Raw settings from each source."""
276
+
277
+ applied: AppliedSettings
278
+ """The currently applied model and effort."""
279
+
280
+
212
281
  @dataclass(frozen=True, slots=True, kw_only=True)
213
282
  class SDKControlElicitationRequest:
214
283
  """Requests the SDK consumer to handle an MCP elicitation (user input request)."""
@@ -11,6 +11,7 @@ from clawd_code_sdk.models.base import ( # noqa: TC001
11
11
  ElicitationAction,
12
12
  ElicitationMode,
13
13
  )
14
+ from clawd_code_sdk.models.permissions import PermissionUpdate # noqa: TC001
14
15
 
15
16
 
16
17
  if TYPE_CHECKING:
@@ -41,7 +42,7 @@ HookEvent = Literal[
41
42
  "WorktreeRemove",
42
43
  "InstructionsLoaded",
43
44
  ]
44
-
45
+ LoadReason = Literal["session_start", "nested_traversal", "path_glob_match", "include", "compact"]
45
46
 
46
47
  # ---------------------------------------------------------------------------
47
48
  # Declarative hook handler configs (for agent/skill frontmatter & settings)
@@ -237,7 +238,7 @@ class PermissionRequestHookInput(BaseHookInput):
237
238
  hook_event_name: Literal["PermissionRequest"]
238
239
  tool_name: str
239
240
  tool_input: dict[str, Any]
240
- permission_suggestions: NotRequired[list[Any]]
241
+ permission_suggestions: NotRequired[list[PermissionUpdate]]
241
242
  agent_id: NotRequired[str]
242
243
  agent_type: NotRequired[str]
243
244
 
@@ -337,7 +338,7 @@ class InstructionsLoadedHookInput(BaseHookInput):
337
338
  hook_event_name: Literal["InstructionsLoaded"]
338
339
  file_path: str
339
340
  memory_type: Literal["User", "Project", "Local", "Managed"]
340
- load_reason: Literal["session_start", "nested_traversal", "path_glob_match", "include"]
341
+ load_reason: LoadReason
341
342
  globs: NotRequired[list[str]]
342
343
  trigger_file_path: NotRequired[str]
343
344
  parent_file_path: NotRequired[str]
@@ -33,8 +33,8 @@ class AgentInput(TypedDict):
33
33
 
34
34
  Takes precedence over the agent definition's model frontmatter.
35
35
  If omitted, uses the agent definition's model, or inherits from the parent."""
36
- resume: NotRequired[str]
37
- """Optional agent ID to resume from in order to continue from the previous exec transcript."""
36
+ # resume: NotRequired[str]
37
+ # """Optional agent ID to resume from in order to continue from the previous exec transcript."""
38
38
  run_in_background: NotRequired[bool]
39
39
  """Whether to run the agent in the background."""
40
40
  name: NotRequired[str]
@@ -31,7 +31,12 @@ from clawd_code_sdk._errors import (
31
31
  RateLimitError,
32
32
  ServerError,
33
33
  )
34
- from clawd_code_sdk.models.base import FastModeState, StopReason, ToolName # noqa: TC001
34
+ from clawd_code_sdk.models.base import ( # noqa: TC001
35
+ AssistantMessageError,
36
+ FastModeState,
37
+ StopReason,
38
+ ToolName,
39
+ )
35
40
  from clawd_code_sdk.models.content_blocks import (
36
41
  AssistantMessageContent,
37
42
  MessageParam,
@@ -46,15 +51,6 @@ if TYPE_CHECKING:
46
51
 
47
52
 
48
53
  # Message types
49
- AssistantMessageError = Literal[
50
- "authentication_failed",
51
- "billing_error",
52
- "rate_limit",
53
- "invalid_request",
54
- "server_error",
55
- "unknown",
56
- ]
57
-
58
54
  ErrorSubType = Literal[
59
55
  "error_during_execution",
60
56
  "error_max_turns",
@@ -173,6 +169,7 @@ class UserMessage(BaseMessage):
173
169
  is_synthetic: bool | None = Field(default=None, validation_alias="isSynthetic")
174
170
  priority: Literal["now", "next", "later"] | None = None
175
171
  message: MessageParam
172
+ timestamp: str | None = None
176
173
 
177
174
  @property
178
175
  def content(self) -> str | Sequence[ContentBlock]:
@@ -334,5 +334,15 @@ class ClaudeAgentOptions:
334
334
 
335
335
  return anyenv.dump_json(settings_obj) if settings_obj else None
336
336
 
337
- def validate(self) -> None:
338
- """Validate option constraints."""
337
+ def get_json_schema(self) -> dict[str, Any] | None:
338
+ from pydantic import TypeAdapter
339
+
340
+ match self.output_schema:
341
+ case type() as typ:
342
+ return TypeAdapter(typ).json_schema()
343
+ case dict() as schema:
344
+ return schema
345
+ case None:
346
+ return None
347
+ case _ as unreachable:
348
+ assert_never(unreachable)