ai-pro-sdk 2.0.1

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.
@@ -0,0 +1,1296 @@
1
+ # Usage Guide (AI SDK v5)
2
+
3
+ > **Historical:** Covers legacy provider versions 1.x–2.x (AI SDK v5). Much still applies to 3.x; new features are documented in the [main README](../../README.md).
4
+
5
+ ## Essential Examples
6
+
7
+ ### Streaming Responses
8
+
9
+ ```typescript
10
+ import { streamText } from 'ai';
11
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
12
+
13
+ const result = streamText({
14
+ model: claudeCode('sonnet'),
15
+ prompt: 'Write a haiku about programming',
16
+ });
17
+
18
+ // v5 returns promises for different parts
19
+ const text = await result.text;
20
+ console.log(text);
21
+
22
+ // Or stream chunks
23
+ for await (const chunk of result.textStream) {
24
+ process.stdout.write(chunk);
25
+ }
26
+ ```
27
+
28
+ ### Multi-turn Conversations
29
+
30
+ ```typescript
31
+ import { generateText } from 'ai';
32
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
33
+
34
+ const messages: ModelMessage[] = [];
35
+
36
+ // First turn
37
+ messages.push({ role: 'user', content: [{ type: 'text', text: 'My name is Alice' }] });
38
+ const response1 = await generateText({
39
+ model: claudeCode('sonnet'),
40
+ messages,
41
+ });
42
+ messages.push({ role: 'assistant', content: response1.content });
43
+
44
+ // Second turn - remembers context
45
+ messages.push({ role: 'user', content: [{ type: 'text', text: 'What is my name?' }] });
46
+ const response2 = await generateText({
47
+ model: claudeCode('sonnet'),
48
+ messages,
49
+ });
50
+ console.log(response2.text); // "Alice"
51
+ ```
52
+
53
+ ### Object Generation with Native Structured Outputs
54
+
55
+ This provider uses Claude Agent SDK's native structured outputs (v0.1.45+) which guarantee schema compliance through constrained decoding:
56
+
57
+ ```typescript
58
+ import { generateObject } from 'ai';
59
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
60
+ import { z } from 'zod';
61
+
62
+ const result = await generateObject({
63
+ model: claudeCode('sonnet'),
64
+ schema: z.object({
65
+ name: z.string().describe('Full name'),
66
+ age: z.number().describe('Age in years'),
67
+ email: z.string().email().describe('Email address'),
68
+ interests: z.array(z.string()).describe('List of hobbies'),
69
+ }),
70
+ prompt: 'Generate a profile for a software developer',
71
+ });
72
+
73
+ console.log(result.object);
74
+ // {
75
+ // name: "Alex Chen",
76
+ // age: 28,
77
+ // email: "alex.chen@example.com",
78
+ // interests: ["coding", "open source", "machine learning"]
79
+ // }
80
+ ```
81
+
82
+ **Key Benefits:**
83
+
84
+ - ✅ **Guaranteed schema compliance** - SDK's constrained decoding ensures valid output
85
+ - ✅ **No JSON parsing errors** - Output always matches your schema
86
+ - ✅ **No retry logic needed** - SDK handles validation internally
87
+
88
+ > **Important: Schema Required for JSON Output**
89
+ >
90
+ > Using `responseFormat: { type: 'json' }` without a schema is **not supported** by this provider. This matches the behavior of Anthropic's official AI SDK provider, as Claude Code (like the Anthropic API) only supports structured outputs with schemas—there is no native "JSON mode" without schema validation.
91
+ >
92
+ > If you request JSON output without providing a schema:
93
+ >
94
+ > - An `unsupported-setting` warning will be emitted
95
+ > - The response content is returned as-is (no JSON parsing or enforcement)
96
+ > - **Streaming note**: The response will be buffered and emitted as a single chunk at the end, not streamed token-by-token. If you need true incremental streaming, omit `responseFormat` entirely or provide a schema.
97
+ >
98
+ > **Always use `generateObject()` or `streamObject()` with a Zod schema** for guaranteed JSON output.
99
+
100
+ ### Handling Long-Running Tasks
101
+
102
+ For complex tasks with Claude Opus 4's extended thinking, use AbortSignal with custom timeouts:
103
+
104
+ ```typescript
105
+ import { generateText } from 'ai';
106
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
107
+
108
+ // Create a custom timeout
109
+ const controller = new AbortController();
110
+ const timeoutId = setTimeout(() => {
111
+ controller.abort(new Error('Request timeout after 10 minutes'));
112
+ }, 600000); // 10 minutes
113
+
114
+ try {
115
+ const result = await generateText({
116
+ model: claudeCode('opus'),
117
+ prompt: 'Analyze this complex problem in detail...',
118
+ abortSignal: controller.signal,
119
+ });
120
+
121
+ clearTimeout(timeoutId);
122
+ console.log(result.text);
123
+ } catch (error) {
124
+ if (error.name === 'AbortError') {
125
+ console.log('Request was cancelled');
126
+ }
127
+ }
128
+ ```
129
+
130
+ ### Session Management (Experimental)
131
+
132
+ ```typescript
133
+ import { generateText } from 'ai';
134
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
135
+
136
+ // First message
137
+ const result = await generateText({
138
+ model: claudeCode('sonnet'),
139
+ messages: [{ role: 'user', content: [{ type: 'text', text: 'My name is Bob.' }] }],
140
+ });
141
+
142
+ // Resume using the session ID
143
+ const sessionId = result.providerMetadata?.['claude-code']?.sessionId;
144
+
145
+ const response = await generateText({
146
+ model: claudeCode('sonnet', { resume: sessionId }),
147
+ messages: [{ role: 'user', content: [{ type: 'text', text: 'What is my name?' }] }],
148
+ });
149
+ ```
150
+
151
+ `resume` continues a previous CLI session instead of starting a new one.
152
+
153
+ You can also pass a deterministic `sessionId` for correlation and tracking:
154
+
155
+ ```typescript
156
+ const result = await generateText({
157
+ model: claudeCode('sonnet', { sessionId: 'my-custom-session-id' }),
158
+ messages: [{ role: 'user', content: [{ type: 'text', text: 'Hello' }] }],
159
+ });
160
+ ```
161
+
162
+ ---
163
+
164
+ ## Key Changes in v5
165
+
166
+ ### Message Format
167
+
168
+ In v5, user messages must have content as an array of parts:
169
+
170
+ ```typescript
171
+ // v4 format (NOT supported)
172
+ { role: 'user', content: 'Hello' }
173
+
174
+ // v5 format (required)
175
+ { role: 'user', content: [{ type: 'text', text: 'Hello' }] }
176
+ ```
177
+
178
+ ### Streaming API
179
+
180
+ The streaming API returns a result object with promises:
181
+
182
+ ```typescript
183
+ const result = streamText({
184
+ model: claudeCode('sonnet'),
185
+ prompt: 'Hello',
186
+ });
187
+
188
+ // Access different parts as promises
189
+ const text = await result.text;
190
+ const usage = await result.usage;
191
+ const finishReason = await result.finishReason;
192
+ ```
193
+
194
+ ### Token Usage Properties
195
+
196
+ Token usage properties have been renamed:
197
+
198
+ ```typescript
199
+ // v4
200
+ { promptTokens: 10, completionTokens: 5 }
201
+
202
+ // v5
203
+ { inputTokens: 10, outputTokens: 5, totalTokens: 15 }
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Detailed Configuration
209
+
210
+ ## Performance and Limitations
211
+
212
+ The provider optimizes tool streaming with pragmatic thresholds:
213
+
214
+ - Delta streaming: Only for tool inputs ≤ 10KB and for prefix-only updates (appends). Non-prefix or large updates skip deltas and are captured in the final `tool-call` payload.
215
+ - Size limits: Tool inputs exceeding 1MB throw an error; inputs above 100KB log a warning due to potential performance impact.
216
+ - Memory: Tool state is retained until stream completion to prevent duplicate `tool-call` emissions for multi-chunk results or errors.
217
+
218
+ See Tool Streaming Support for details and event semantics: docs/ai-sdk-v5/TOOL_STREAMING_SUPPORT.md#performance-considerations
219
+
220
+ ### AbortSignal Support
221
+
222
+ The provider fully supports the standard AbortSignal for request cancellation, following Vercel AI SDK patterns:
223
+
224
+ ```typescript
225
+ import { generateText } from 'ai';
226
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
227
+
228
+ // Using AbortController for cancellation
229
+ const controller = new AbortController();
230
+
231
+ // Cancel after user action
232
+ button.addEventListener('click', () => {
233
+ controller.abort();
234
+ });
235
+
236
+ const result = await generateText({
237
+ model: claudeCode('opus'),
238
+ prompt: 'Write a story...',
239
+ abortSignal: controller.signal,
240
+ });
241
+ ```
242
+
243
+ **For Long-Running Tasks**: Claude Opus 4's extended thinking may require longer timeouts than typical HTTP requests. See the "Handling Long-Running Tasks" section above for implementing custom timeouts using AbortSignal.
244
+
245
+ ### Configuration Options
246
+
247
+ | Option | Type | Default | Description |
248
+ | ---------------------------- | ----------------------------------------- | ----------- | ------------------------------------------------------------------ |
249
+ | `model` | `'opus' \| 'sonnet' \| 'haiku' \| string` | `'opus'` | Model alias or full model ID (e.g., `claude-opus-4-5`) |
250
+ | `pathToClaudeCodeExecutable` | `string` | `'claude'` | Path to Claude CLI executable |
251
+ | `customSystemPrompt` | `string` | `undefined` | Custom system prompt |
252
+ | `appendSystemPrompt` | `string` | `undefined` | Append to system prompt |
253
+ | `maxTurns` | `number` | `undefined` | Maximum conversation turns |
254
+ | `maxThinkingTokens` | `number` | `undefined` | Maximum thinking tokens |
255
+ | `permissionMode` | `string` | `'default'` | Permission mode for tools |
256
+ | `allowedTools` | `string[]` | `undefined` | Tools to explicitly allow |
257
+ | `disallowedTools` | `string[]` | `undefined` | Tools to restrict |
258
+ | `mcpServers` | `object` | `undefined` | MCP server configuration |
259
+ | `env` | `Record<string, string>` | `undefined` | Environment variables passed to CLI |
260
+ | `resume` | `string` | `undefined` | Resume an existing session |
261
+ | `sessionId` | `string` | `undefined` | Use a specific session ID for tracking and correlation |
262
+ | `hooks` | `object` | `undefined` | Lifecycle hooks (e.g., PreToolUse, PostToolUse) |
263
+ | `canUseTool` | `(name, input, opts) => Promise` | `undefined` | Runtime permission callback. Requires streaming input at SDK level |
264
+ | `debug` | `boolean` | `undefined` | Enable SDK-level debug logging |
265
+ | `debugFile` | `string` | `undefined` | Path to a file for SDK debug log output |
266
+
267
+ ### Custom Configuration
268
+
269
+ ```typescript
270
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
271
+
272
+ const claude = createClaudeCode({
273
+ defaultSettings: {
274
+ pathToClaudeCodeExecutable: '/usr/local/bin/claude',
275
+ permissionMode: 'default', // Ask for permissions
276
+ customSystemPrompt: 'You are a helpful coding assistant.',
277
+ },
278
+ });
279
+
280
+ const result = await generateText({
281
+ model: claude('opus'),
282
+ prompt: 'Hello, Claude!',
283
+ });
284
+ ```
285
+
286
+ ### Logging Configuration
287
+
288
+ Control how the provider logs execution information, warnings, and errors. The logger now supports multiple log levels and a verbose mode for detailed debugging.
289
+
290
+ #### Log Levels
291
+
292
+ The provider supports four log levels:
293
+
294
+ - **`debug`**: Detailed execution tracing (request/response, tool calls, stream events)
295
+ - **`info`**: General execution flow information (session initialization, completion)
296
+ - **`warn`**: Warnings about configuration issues or unexpected behavior
297
+ - **`error`**: Error messages for failures and exceptions
298
+
299
+ #### Basic Configuration
300
+
301
+ ```typescript
302
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
303
+
304
+ // Default: logs warnings and errors to console
305
+ const defaultClaude = createClaudeCode();
306
+
307
+ // Disable all logging
308
+ const silentClaude = createClaudeCode({
309
+ defaultSettings: {
310
+ logger: false,
311
+ },
312
+ });
313
+
314
+ // Custom logger - must implement all four log levels
315
+ const customClaude = createClaudeCode({
316
+ defaultSettings: {
317
+ logger: {
318
+ debug: (message) => myLogger.debug('Claude:', message),
319
+ info: (message) => myLogger.info('Claude:', message),
320
+ warn: (message) => myLogger.warn('Claude:', message),
321
+ error: (message) => myLogger.error('Claude:', message),
322
+ },
323
+ },
324
+ });
325
+
326
+ // Model-specific logger override
327
+ const model = customClaude('opus', {
328
+ logger: false, // Disable logging for this model only
329
+ });
330
+ ```
331
+
332
+ #### Verbose Mode (Debug Logging)
333
+
334
+ Enable verbose mode to see detailed execution logs, including:
335
+
336
+ - Request/response tracing
337
+ - Tool execution lifecycle (tool calls, results, errors)
338
+ - Stream event processing
339
+ - Message flow and token usage
340
+ - Session management
341
+
342
+ **Without verbose mode**, only `warn` and `error` messages are logged.
343
+ **With verbose mode enabled**, `debug` and `info` messages are also logged.
344
+
345
+ ```typescript
346
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
347
+
348
+ // Enable verbose logging for debugging
349
+ const claudeWithDebug = createClaudeCode({
350
+ defaultSettings: {
351
+ verbose: true, // Enable debug and info logging
352
+ },
353
+ });
354
+
355
+ // Use with custom logger
356
+ const claudeCustom = createClaudeCode({
357
+ defaultSettings: {
358
+ verbose: true,
359
+ logger: {
360
+ debug: (msg) => console.log(`[DEBUG] ${msg}`),
361
+ info: (msg) => console.log(`[INFO] ${msg}`),
362
+ warn: (msg) => console.warn(`[WARN] ${msg}`),
363
+ error: (msg) => console.error(`[ERROR] ${msg}`),
364
+ },
365
+ },
366
+ });
367
+
368
+ // Model-specific verbose override
369
+ const model = claudeWithDebug('sonnet', {
370
+ verbose: false, // Disable verbose for this specific model
371
+ });
372
+ ```
373
+
374
+ #### What Gets Logged in Verbose Mode
375
+
376
+ With `verbose: true`, you'll see intermediate process logs including:
377
+
378
+ ```
379
+ [DEBUG] Starting doGenerate request with model: sonnet
380
+ [DEBUG] Request mode: regular, response format: none
381
+ [DEBUG] Converted 2 messages, hasImageParts: false
382
+ [DEBUG] Executing query with streamingInput: false, session: new
383
+ [DEBUG] Received message type: assistant
384
+ [INFO] Request completed - Session: abc123, Cost: $0.0012, Duration: 1523ms
385
+ [DEBUG] Token usage - Input: 245, Output: 128, Total: 373
386
+ [DEBUG] Finish reason: stop
387
+ ```
388
+
389
+ For streaming requests with tools:
390
+
391
+ ```
392
+ [DEBUG] Starting doStream request with model: sonnet
393
+ [DEBUG] Stream received message type: assistant
394
+ [DEBUG] New tool use detected - Tool: Read, ID: tool_abc123
395
+ [DEBUG] Tool input started - Tool: Read, ID: tool_abc123
396
+ [DEBUG] Tool result received - Tool: Read, ID: tool_abc123
397
+ [INFO] Stream completed - Session: xyz789, Cost: $0.0045, Duration: 3241ms
398
+ [DEBUG] Stream token usage - Input: 512, Output: 256, Total: 768
399
+ ```
400
+
401
+ #### Logger Options
402
+
403
+ - `undefined` (default): Uses `console.debug`, `console.info`, `console.warn`, and `console.error`
404
+ - `false`: Disables all logging
405
+ - Custom `Logger` object: Must implement `debug`, `info`, `warn`, and `error` methods
406
+
407
+ #### Combining with Error Metadata
408
+
409
+ For comprehensive debugging, combine verbose logging with error metadata:
410
+
411
+ ```typescript
412
+ import { createClaudeCode, getErrorMetadata } from 'ai-sdk-provider-claude-code';
413
+
414
+ const claude = createClaudeCode({
415
+ defaultSettings: {
416
+ verbose: true,
417
+ logger: {
418
+ debug: (msg) => myLogger.debug(msg),
419
+ info: (msg) => myLogger.info(msg),
420
+ warn: (msg) => myLogger.warn(msg),
421
+ error: (msg) => myLogger.error(msg),
422
+ },
423
+ },
424
+ });
425
+
426
+ try {
427
+ const result = await generateText({
428
+ model: claude('sonnet'),
429
+ prompt: 'Hello!',
430
+ });
431
+ } catch (error) {
432
+ const metadata = getErrorMetadata(error);
433
+ console.error('Error details:', {
434
+ code: metadata?.code,
435
+ exitCode: metadata?.exitCode,
436
+ stderr: metadata?.stderr,
437
+ });
438
+ }
439
+ ```
440
+
441
+ #### SDK Debug Logging
442
+
443
+ Separate from the provider-level verbose/logger system, you can enable the underlying Agent SDK's own debug logging. This captures lower-level SDK internals and can be written to a file:
444
+
445
+ ```typescript
446
+ const model = claudeCode('sonnet', {
447
+ debug: true, // Enable SDK debug output
448
+ debugFile: '/tmp/sdk.log', // Write SDK debug logs to file
449
+ });
450
+ ```
451
+
452
+ ### Tool Management
453
+
454
+ Control which tools Claude Code can use with either `allowedTools` (allowlist) or `disallowedTools` (denylist). These flags work for **both built-in Claude tools and MCP tools**, providing session-only permission overrides.
455
+
456
+ #### Tool Types
457
+
458
+ - **Built-in tools**: `Bash`, `Edit`, `Read`, `Write`, `LS`, `Grep`, etc.
459
+ - **MCP tools**: `mcp__serverName__toolName` format
460
+
461
+ #### Using allowedTools (Allowlist)
462
+
463
+ ```typescript
464
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
465
+
466
+ // Only allow specific built-in tools
467
+ const readOnlyClaude = createClaudeCode({
468
+ allowedTools: ['Read', 'LS', 'Grep'],
469
+ });
470
+
471
+ // Allow specific Bash commands using specifiers
472
+ const gitOnlyClaude = createClaudeCode({
473
+ allowedTools: ['Bash(git log:*)', 'Bash(git diff:*)', 'Bash(git status)'],
474
+ });
475
+
476
+ // Mix built-in and MCP tools
477
+ const mixedClaude = createClaudeCode({
478
+ allowedTools: ['Read', 'Bash(npm test:*)', 'mcp__filesystem__read_text_file', 'mcp__git__status'],
479
+ });
480
+ ```
481
+
482
+ #### Using disallowedTools (Denylist)
483
+
484
+ ```typescript
485
+ // Block dangerous operations
486
+ const safeClaude = createClaudeCode({
487
+ disallowedTools: ['Write', 'Edit', 'Delete', 'Bash(rm:*)', 'Bash(sudo:*)'],
488
+ });
489
+
490
+ // Block all Bash and MCP write operations
491
+ const restrictedClaude = createClaudeCode({
492
+ disallowedTools: ['Bash', 'mcp__filesystem__write_file', 'mcp__git__commit', 'mcp__git__push'],
493
+ });
494
+ ```
495
+
496
+ #### Model-Specific Overrides
497
+
498
+ ```typescript
499
+ const baseClaude = createClaudeCode({
500
+ disallowedTools: ['Write', 'Edit'],
501
+ });
502
+
503
+ // Override for a specific call
504
+ const result = await generateText({
505
+ model: baseClaude('opus', {
506
+ disallowedTools: [], // Allow everything for this call
507
+ }),
508
+ prompt: 'Create a simple config file...',
509
+ });
510
+ ```
511
+
512
+ **Key Points**:
513
+
514
+ - These are **session-only permission overrides** (same syntax as settings.json)
515
+ - Higher priority than settings files
516
+ - Works for both built-in tools AND MCP tools
517
+ - Cannot use both `allowedTools` and `disallowedTools` together
518
+ - Empty `allowedTools: []` = Explicit empty allowlist (no tools allowed)
519
+ - Omitting the flags entirely = Falls back to normal permission system
520
+ - Use `/permissions` in Claude to see all available tool names
521
+
522
+ **Common Patterns**:
523
+
524
+ - Read-only mode: `disallowedTools: ['Write', 'Edit', 'Delete']`
525
+ - No shell access: `disallowedTools: ['Bash']`
526
+ - Safe git: `allowedTools: ['Bash(git log:*)', 'Bash(git diff:*)']`
527
+ - No MCP: `disallowedTools: ['mcp__*']`
528
+
529
+ **Permission Behavior**:
530
+ | Configuration | CLI Flag Behavior | Result |
531
+ |--------------|-------------------|---------|
532
+ | No config | No `--allowedTools` or `--disallowedTools` | Falls back to settings.json and interactive prompts |
533
+ | `allowedTools: []` | `--allowedTools` (empty) | Explicit empty allowlist - blocks all tools |
534
+ | `allowedTools: ['Read']` | `--allowedTools Read` | Only allows Read tool |
535
+ | `disallowedTools: []` | `--disallowedTools` (empty) | No effect - normal permissions apply |
536
+ | `disallowedTools: ['Write']` | `--disallowedTools Write` | Blocks Write tool, others follow normal permissions |
537
+
538
+ ## Advanced Configuration
539
+
540
+ ### MCP Server Support
541
+
542
+ The provider supports Model Context Protocol (MCP) servers for extended functionality:
543
+
544
+ ```typescript
545
+ const claude = createClaudeCode({
546
+ mcpServers: {
547
+ filesystem: {
548
+ command: 'npx',
549
+ args: ['-y', '@modelcontextprotocol/server-filesystem'],
550
+ },
551
+ github: {
552
+ type: 'sse',
553
+ url: 'https://mcp.github.com/api',
554
+ headers: { Authorization: 'Bearer YOUR_TOKEN' },
555
+ },
556
+ },
557
+ });
558
+ ```
559
+
560
+ ### Permission Modes
561
+
562
+ Control how Claude Code handles tool permissions:
563
+
564
+ ```typescript
565
+ const claude = createClaudeCode({
566
+ permissionMode: 'bypassPermissions', // Skip all permission prompts
567
+ // Other options: 'default', 'acceptEdits', 'plan'
568
+ });
569
+ ```
570
+
571
+ ### Custom SDK Tools (callbacks)
572
+
573
+ Define in-process tools using the Claude Code SDK and wire them directly through this provider. This avoids managing external MCP server processes and enables type-safe tool definitions.
574
+
575
+ ```typescript
576
+ import { z } from 'zod';
577
+ import { createClaudeCode, createSdkMcpServer, tool } from 'ai-sdk-provider-claude-code';
578
+
579
+ // 1) Define a tool with a Zod schema
580
+ const add = tool('add', 'Add two numbers', { a: z.number(), b: z.number() }, async ({ a, b }) => ({
581
+ content: [{ type: 'text', text: String(a + b) }],
582
+ }));
583
+
584
+ // 2) Create an SDK MCP server with your tools
585
+ const sdkServer = createSdkMcpServer({ name: 'local', tools: [add] });
586
+
587
+ // 3) Wire it into the provider, restrict to this tool
588
+ const claude = createClaudeCode({
589
+ defaultSettings: {
590
+ mcpServers: { local: sdkServer },
591
+ allowedTools: ['mcp__local__add'],
592
+ },
593
+ });
594
+
595
+ // 4) Use it as usual
596
+ const { text } = await generateText({
597
+ model: claude('sonnet'),
598
+ prompt: 'Use the add tool to sum 3 and 4.',
599
+ });
600
+ ```
601
+
602
+ Notes:
603
+
604
+ - Tool naming for allow/deny: `mcp__<serverName>__<toolName>`; to allow an entire server: `mcp__<serverName>`.
605
+ - Security: only allow the tools you intend; prefer allowlists in sensitive environments.
606
+
607
+ #### Tool Annotations
608
+
609
+ You can add MCP tool annotations to hint tool behavior. The `createCustomMcpServer` convenience helper accepts an optional `annotations` field per tool:
610
+
611
+ ```typescript
612
+ import { z } from 'zod';
613
+ import { createClaudeCode, createCustomMcpServer } from 'ai-sdk-provider-claude-code';
614
+
615
+ const server = createCustomMcpServer({
616
+ name: 'my-tools',
617
+ tools: {
618
+ lookup: {
619
+ description: 'Look up a value by key',
620
+ inputSchema: z.object({ key: z.string() }),
621
+ handler: async ({ key }) => ({
622
+ content: [{ type: 'text', text: `value for ${key}` }],
623
+ }),
624
+ annotations: {
625
+ readOnlyHint: true,
626
+ idempotentHint: true,
627
+ },
628
+ },
629
+ },
630
+ });
631
+
632
+ const claude = createClaudeCode({
633
+ defaultSettings: { mcpServers: { 'my-tools': server } },
634
+ });
635
+ ```
636
+
637
+ Supported annotation hints: `readOnlyHint`, `destructiveHint`, `openWorldHint`, `idempotentHint`.
638
+
639
+ ### Hooks and Runtime Permissions
640
+
641
+ You can intercept lifecycle events and apply custom runtime permissions:
642
+
643
+ ```typescript
644
+ import type { HookCallback } from 'ai-sdk-provider-claude-code';
645
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
646
+
647
+ const preTool: HookCallback = async (input) => {
648
+ if (input.hook_event_name === 'PreToolUse') {
649
+ console.log('About to run:', input.tool_name);
650
+ return {
651
+ continue: true,
652
+ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'allow' },
653
+ };
654
+ }
655
+ return { continue: true };
656
+ };
657
+
658
+ const claude = createClaudeCode({
659
+ defaultSettings: {
660
+ hooks: {
661
+ PreToolUse: [{ hooks: [preTool] }],
662
+ PostToolUse: [{ hooks: [async () => ({ continue: true })] }],
663
+ },
664
+ // Enable runtime permission callback (requires streaming input)
665
+ // streamingInput: 'auto', // default when canUseTool is provided; or set 'always'
666
+ // canUseTool: async (toolName, input) => ({ behavior: 'allow', updatedInput: input }),
667
+ },
668
+ });
669
+ ```
670
+
671
+ Important:
672
+
673
+ - `canUseTool` requires the SDK's stream-json input mode. This provider supports it via `streamingInput`:
674
+ - `'auto'` (default): if you supply `canUseTool`, the provider streams input automatically.
675
+ - `'always'`: always use streaming input.
676
+ - `'off'`: never stream (SDK will reject `canUseTool`).
677
+
678
+ ### Image Inputs (Streaming Only)
679
+
680
+ Image parts are forwarded to the Claude Code SDK only when streaming input is enabled.
681
+
682
+ - Set `streamingInput: 'always'` (or provide `canUseTool`, which causes `'auto'` to stream) before including images.
683
+ - Supported payloads:
684
+ - Data URLs: `data:image/png;base64,<base64Data>`
685
+ - Explicit base64 strings: `'base64:image/png,<base64Data>'`
686
+ - Objects: `{ type: 'image', image: { data: '<base64>', mimeType: 'image/png' } }`
687
+ - Remote HTTP(S) image URLs are ignored with the warning `Image URLs are not supported by this provider; supply base64/data URLs.` (`supportsImageUrls` remains `false`).
688
+ - If streaming is disabled, the provider emits the streaming prerequisite warning ("Claude Code SDK features (hooks/MCP/images) require streaming input...") and drops the image content.
689
+ - Use realistic image payloads—very small or malformed data URLs may lead Claude to request a different image.
690
+
691
+ Example message:
692
+
693
+ ```typescript
694
+ const messages = [
695
+ {
696
+ role: 'user',
697
+ content: [
698
+ { type: 'text', text: 'Describe this image in one sentence.' },
699
+ {
700
+ type: 'image',
701
+ image:
702
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAklEQVR4AewaftIAAAAfSURBVFXBwQ0AMBACIJo4mPsvdX0L7ziKoqJG1IgaH8ddA4x8aVGeAAAAAElFTkSuQmCC',
703
+ },
704
+ ],
705
+ },
706
+ ];
707
+
708
+ const result = streamText({
709
+ model: claudeCode('sonnet', { streamingInput: 'always' }),
710
+ messages,
711
+ });
712
+ ```
713
+
714
+ See `examples/images.ts` for a full script.
715
+
716
+ Run it against a local file (PNG/JPG/GIF/WebP) to convert to a data URL automatically:
717
+
718
+ ```bash
719
+ npm run build
720
+ npx tsx examples/images.ts /absolute/path/to/image.png
721
+ ```
722
+
723
+ ### Custom System Prompts
724
+
725
+ ```typescript
726
+ const claude = createClaudeCode({
727
+ customSystemPrompt: 'You are an expert Python developer.',
728
+ // Or append to existing prompt:
729
+ appendSystemPrompt: 'Always use type hints in Python code.',
730
+ });
731
+ ```
732
+
733
+ ### Environment Configuration
734
+
735
+ Set an `env` key in the options to configure Claude Code's environment using any of the keys in Claude's [settings documentation](https://docs.claude.com/en/docs/claude-code/settings#environment-variables). The env object will be merged with `process.env` to maintain you existing environment.
736
+
737
+ ```typescript
738
+ const env = {
739
+ // e.g. to prevent a system key from interfering with auth
740
+ ANTHROPIC_API_KEY: undefined,
741
+ // e.g. any Claude Code setting from:
742
+ // https://docs.claude.com/en/docs/claude-code/settings#environment-variables
743
+ BASH_DEFAULT_TIMEOUT_MS: '10',
744
+ };
745
+
746
+ const customProvider = createClaudeCode({
747
+ defaultSettings: {
748
+ // Provide custom environment variables to the CLI
749
+ env,
750
+ },
751
+ });
752
+ ```
753
+
754
+ ## Request Cancellation
755
+
756
+ The provider supports request cancellation through the standard AbortController API:
757
+
758
+ ```typescript
759
+ import { generateText } from 'ai';
760
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
761
+
762
+ // Create abort controller
763
+ const controller = new AbortController();
764
+
765
+ // Start request
766
+ const promise = generateText({
767
+ model: claudeCode('opus'),
768
+ prompt: 'Write a long story...',
769
+ abortSignal: controller.signal,
770
+ });
771
+
772
+ // Cancel the request
773
+ controller.abort();
774
+
775
+ // The promise rejects with AbortError
776
+ try {
777
+ await promise;
778
+ } catch (error) {
779
+ if (error.name === 'AbortError') {
780
+ console.log('Request was cancelled');
781
+ }
782
+ }
783
+ ```
784
+
785
+ This is especially useful in UI scenarios where users might cancel requests or navigate away.
786
+
787
+ ## Implementation Details
788
+
789
+ ### SDK Message Types
790
+
791
+ The SDK provides structured message types for different events:
792
+
793
+ #### Assistant Message
794
+
795
+ ```typescript
796
+ {
797
+ type: 'assistant',
798
+ message: {
799
+ content: [{ type: 'text', text: 'Hello!' }],
800
+ // ... other fields
801
+ },
802
+ session_id: 'abc-123-def'
803
+ }
804
+ ```
805
+
806
+ #### Result Message
807
+
808
+ ```typescript
809
+ {
810
+ type: 'result',
811
+ subtype: 'success' | 'error_max_turns' | 'error_during_execution',
812
+ session_id: 'abc-123-def',
813
+ usage: { /* token counts */ },
814
+ total_cost_usd: 0.001,
815
+ duration_ms: 1500
816
+ }
817
+ ```
818
+
819
+ #### System Message
820
+
821
+ ```typescript
822
+ {
823
+ type: 'system',
824
+ subtype: 'init',
825
+ session_id: 'abc-123-def',
826
+ tools: ['Read', 'Write', 'Bash'],
827
+ model: 'opus'
828
+ }
829
+ ```
830
+
831
+ ### SDK Implementation
832
+
833
+ The provider uses the official `@anthropic-ai/claude-agent-sdk` which provides:
834
+
835
+ - **AsyncGenerator pattern**: Native streaming support with `query()` function
836
+ - **Structured messages**: Rich message types (assistant, result, system, error)
837
+ - **Built-in features**: AbortController, session management, MCP servers
838
+ - **Automatic handling**: Process management, error handling, and output parsing
839
+
840
+ ### Provider Metadata
841
+
842
+ The provider returns rich metadata including token usage, timing, and cost information:
843
+
844
+ ```typescript
845
+ const result = await generateText({
846
+ model: claudeCode('sonnet'),
847
+ prompt: 'Hello!',
848
+ });
849
+
850
+ console.log(result.providerMetadata);
851
+ // {
852
+ // "claude-code": {
853
+ // "sessionId": "abc-123-def",
854
+ // "costUsd": 0.0285561, // Note: Always non-zero for tracking
855
+ // "durationMs": 3056,
856
+ // "rawUsage": {
857
+ // "inputTokens": 4,
858
+ // "outputTokens": 7,
859
+ // "cacheCreationInputTokens": 5924,
860
+ // "cacheReadInputTokens": 10075
861
+ // }
862
+ // }
863
+ // }
864
+ ```
865
+
866
+ **Important Note about Costs**: The `costUsd` field shows the cost of the API usage:
867
+
868
+ - **For Pro/Max subscribers**: This is informational only - usage is covered by your monthly subscription
869
+ - **For API key users**: This represents actual charges that will be billed to your account
870
+
871
+ ## Object Generation
872
+
873
+ The provider supports object generation through prompt engineering, allowing you to generate structured data with JSON schema validation:
874
+
875
+ ```typescript
876
+ import { generateObject, streamObject } from 'ai';
877
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
878
+ import { z } from 'zod';
879
+
880
+ // Generate a complete object
881
+ const result = await generateObject({
882
+ model: claudeCode('sonnet'),
883
+ schema: z.object({
884
+ recipe: z.object({
885
+ name: z.string(),
886
+ ingredients: z.array(z.string()),
887
+ instructions: z.array(z.string()),
888
+ prepTime: z.number(),
889
+ servings: z.number(),
890
+ }),
891
+ }),
892
+ prompt: 'Generate a recipe for chocolate chip cookies',
893
+ });
894
+
895
+ // Note: streamObject waits for complete response before parsing
896
+ // Use generateObject for clarity since streaming doesn't provide benefits
897
+ const analysisResult = await generateObject({
898
+ model: claudeCode('sonnet'),
899
+ schema: z.object({
900
+ analysis: z.string(),
901
+ sentiment: z.enum(['positive', 'negative', 'neutral']),
902
+ score: z.number(),
903
+ }),
904
+ prompt: 'Analyze this review: "Great product!"',
905
+ });
906
+
907
+ console.log(analysisResult.object);
908
+ ```
909
+
910
+ **How it works**: The provider appends JSON generation instructions to your prompt and uses a tolerant JSON parser to extract valid output from Claude's response. Minor issues like trailing commas or comments are automatically handled, though this is still not as strict as native JSON mode.
911
+
912
+ **Important notes**:
913
+
914
+ - **Object mode support**: Only `object-json` mode is supported (via `generateObject`/`streamObject`). The provider uses prompt engineering and JSON extraction to ensure reliable object generation.
915
+ - **Streaming behavior**: While `streamObject` is supported, it accumulates the full response before extracting JSON to ensure validity. Regular text streaming works in real-time.
916
+
917
+ ## Object Generation Cookbook
918
+
919
+ ### Quick Start Examples
920
+
921
+ #### Basic Objects
922
+
923
+ Start with simple schemas and clear prompts:
924
+
925
+ ```typescript
926
+ const result = await generateObject({
927
+ model: claudeCode('sonnet'),
928
+ schema: z.object({
929
+ name: z.string(),
930
+ age: z.number(),
931
+ email: z.string().email(),
932
+ }),
933
+ prompt: 'Generate a developer profile',
934
+ });
935
+ ```
936
+
937
+ [Full example](../../examples/generate-object-basic.ts)
938
+
939
+ #### Nested Structures
940
+
941
+ Build complex hierarchical data:
942
+
943
+ ```typescript
944
+ const result = await generateObject({
945
+ model: claudeCode('sonnet'),
946
+ schema: z.object({
947
+ company: z.object({
948
+ departments: z.array(
949
+ z.object({
950
+ name: z.string(),
951
+ teams: z.array(
952
+ z.object({
953
+ name: z.string(),
954
+ members: z.number(),
955
+ })
956
+ ),
957
+ })
958
+ ),
959
+ }),
960
+ }),
961
+ prompt: 'Generate a company org structure',
962
+ });
963
+ ```
964
+
965
+ [Full example](../../examples/generate-object-nested.ts)
966
+
967
+ #### Constrained Generation
968
+
969
+ Use Zod's validation features:
970
+
971
+ ```typescript
972
+ const result = await generateObject({
973
+ model: claudeCode('sonnet'),
974
+ schema: z.object({
975
+ status: z.enum(['pending', 'active', 'completed']),
976
+ priority: z.number().min(1).max(5),
977
+ tags: z.array(z.string()).min(1).max(3),
978
+ }),
979
+ prompt: 'Generate a task with medium priority',
980
+ });
981
+ ```
982
+
983
+ [Full example](../../examples/generate-object-constraints.ts)
984
+
985
+ ### Best Practices
986
+
987
+ 1. **Start Simple**: Begin with basic schemas and add complexity gradually
988
+ 2. **Clear Prompts**: Be specific about what you want generated
989
+ 3. **Use Descriptions**: Add `.describe()` to schema fields for better results
990
+ 4. **Handle Errors**: Implement retry logic for production use
991
+ 5. **Test Schemas**: Validate your schemas work before deployment
992
+
993
+ ### Common Patterns
994
+
995
+ - **Data Models**: [User profiles, products, orders](../../examples/generate-object-nested.ts)
996
+ - **Validation**: [Enums, constraints, regex patterns](../../examples/generate-object-constraints.ts)
997
+ - **Basic Objects**: [Simple schemas and arrays](../../examples/generate-object-basic.ts)
998
+ - **Note**: For object generation, use `generateObject` instead of `streamObject` as streaming provides no benefits
999
+
1000
+ ## Object Generation Troubleshooting
1001
+
1002
+ ### Common Issues and Solutions
1003
+
1004
+ #### 1. Invalid JSON Response
1005
+
1006
+ **Problem**: Claude returns text instead of valid JSON
1007
+
1008
+ **Solutions**:
1009
+
1010
+ - Simplify your schema - start with fewer fields
1011
+ - Make your prompt more explicit: "Generate only valid JSON"
1012
+ - Check the schema for overly complex constraints
1013
+ - Use the retry pattern:
1014
+
1015
+ ```typescript
1016
+ async function generateWithRetry(schema, prompt, maxRetries = 3) {
1017
+ for (let i = 0; i < maxRetries; i++) {
1018
+ try {
1019
+ return await generateObject({ model, schema, prompt });
1020
+ } catch (error) {
1021
+ if (i === maxRetries - 1) throw error;
1022
+ await new Promise((r) => setTimeout(r, 1000 * Math.pow(2, i)));
1023
+ }
1024
+ }
1025
+ }
1026
+ ```
1027
+
1028
+ #### 2. Missing Required Fields
1029
+
1030
+ **Problem**: Generated objects missing required properties
1031
+
1032
+ **Solutions**:
1033
+
1034
+ - Emphasize requirements in your prompt
1035
+ - Use descriptive field names
1036
+ - Add field descriptions with `.describe()`
1037
+ - Example:
1038
+
1039
+ ```typescript
1040
+ z.object({
1041
+ // Bad: vague field name
1042
+ val: z.number(),
1043
+
1044
+ // Good: clear field name with description
1045
+ totalPrice: z.number().describe('Total price in USD'),
1046
+ });
1047
+ ```
1048
+
1049
+ #### 3. Type Mismatches
1050
+
1051
+ **Problem**: String when expecting number, wrong date format, etc.
1052
+
1053
+ **Solutions**:
1054
+
1055
+ - Be explicit in descriptions: "age as a number" not just "age"
1056
+ - For dates, specify format: `.describe('Date in YYYY-MM-DD format')`
1057
+ - Use regex patterns for strings: `z.string().regex(/^\d{4}-\d{2}-\d{2}$/)`
1058
+
1059
+ #### 4. Schema Too Complex
1060
+
1061
+ **Problem**: Very complex schemas fail or timeout
1062
+
1063
+ **Solutions**:
1064
+
1065
+ - Break into smaller parts and combine:
1066
+
1067
+ ```typescript
1068
+ // Instead of one huge schema, compose smaller ones
1069
+ const userSchema = z.object({
1070
+ /* user fields */
1071
+ });
1072
+ const settingsSchema = z.object({
1073
+ /* settings */
1074
+ });
1075
+ const profileSchema = z.object({
1076
+ user: userSchema,
1077
+ settings: settingsSchema,
1078
+ });
1079
+ ```
1080
+
1081
+ - Generate in steps and merge results
1082
+ - Increase timeout for complex generations
1083
+
1084
+ #### 5. Inconsistent Results
1085
+
1086
+ **Problem**: Same prompt gives different structure each time
1087
+
1088
+ **Solutions**:
1089
+
1090
+ - Make schemas more constrained (use enums, min/max)
1091
+ - Provide example in prompt
1092
+ - Use consistent field naming conventions
1093
+ - Consider using `opus` model for complex schemas
1094
+
1095
+ ### Debugging Tips
1096
+
1097
+ 1. **Enable Debug Logging**:
1098
+
1099
+ ```typescript
1100
+ const result = await generateObject({
1101
+ model: claudeCode('sonnet'),
1102
+ schema: yourSchema,
1103
+ prompt: yourPrompt,
1104
+ });
1105
+ console.log('Tokens used:', result.usage);
1106
+ console.log('Warnings:', result.warnings);
1107
+ ```
1108
+
1109
+ 2. **Test Schema Separately**:
1110
+
1111
+ ```typescript
1112
+ // Validate your schema works
1113
+ const testData = {
1114
+ /* your test object */
1115
+ };
1116
+ try {
1117
+ schema.parse(testData);
1118
+ console.log('Schema is valid');
1119
+ } catch (e) {
1120
+ console.log('Schema errors:', e.errors);
1121
+ }
1122
+ ```
1123
+
1124
+ 3. **Progressive Enhancement**:
1125
+ Start with minimal schema, test, then add fields one by one
1126
+
1127
+ 4. **Check Examples**:
1128
+ Review our examples for implementation patterns
1129
+
1130
+ ## Limitations
1131
+
1132
+ - **Image inputs require streaming**: Provide base64/data URLs and enable streaming input; remote URLs are ignored
1133
+ - **No embedding support**: Text embeddings are not available through this provider
1134
+ - **Object-tool mode not supported**: Only `object-json` mode works via `generateObject`/`streamObject`. The AI SDK's tool calling interface is not implemented
1135
+ - **Schema required for JSON output**: `responseFormat: { type: 'json' }` without a schema is unsupported (matching Anthropic's official provider). Such calls emit a warning, return content as-is (no JSON enforcement), and buffer the response (no incremental streaming). Use `generateObject()`/`streamObject()` with a Zod schema for guaranteed JSON
1136
+ - **Text-only responses**: No support for file generation or other modalities
1137
+ - **Session management**: While sessions are supported, message history is the recommended approach
1138
+ - **Unsupported generation settings**: The following AI SDK settings are ignored and will generate warnings:
1139
+ - `temperature` - Claude Code SDK doesn't expose temperature control
1140
+ - `maxOutputTokens` - Token limits aren't configurable via CLI
1141
+ - `topP`, `topK` - Sampling parameters aren't available
1142
+ - `presencePenalty`, `frequencyPenalty` - Penalty parameters aren't supported
1143
+ - `stopSequences` - Custom stop sequences aren't available
1144
+ - `seed` - Deterministic generation isn't supported
1145
+
1146
+ ## Error Handling
1147
+
1148
+ The provider uses standard AI SDK error classes for better ecosystem compatibility:
1149
+
1150
+ ```typescript
1151
+ import { generateText } from 'ai';
1152
+ import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
1153
+ import {
1154
+ claudeCode,
1155
+ isAuthenticationError,
1156
+ isTimeoutError,
1157
+ getErrorMetadata,
1158
+ } from 'ai-sdk-provider-claude-code';
1159
+
1160
+ try {
1161
+ const result = await generateText({
1162
+ model: claudeCode('opus'),
1163
+ prompt: 'Hello!',
1164
+ });
1165
+ } catch (error) {
1166
+ if (isAuthenticationError(error)) {
1167
+ console.error('Please run "claude login" to authenticate');
1168
+ } else if (isTimeoutError(error)) {
1169
+ console.error('Request timed out. Consider using AbortController with a custom timeout.');
1170
+ } else if (error instanceof APICallError) {
1171
+ // Get CLI-specific metadata
1172
+ const metadata = getErrorMetadata(error);
1173
+ console.error('CLI error:', {
1174
+ message: error.message,
1175
+ isRetryable: error.isRetryable,
1176
+ exitCode: metadata?.exitCode,
1177
+ stderr: metadata?.stderr,
1178
+ });
1179
+ } else {
1180
+ console.error('Error:', error);
1181
+ }
1182
+ }
1183
+ ```
1184
+
1185
+ ### Error Types
1186
+
1187
+ - **`LoadAPIKeyError`**: Authentication failures (exit code 401)
1188
+ - **`APICallError`**: All other CLI failures
1189
+ - `isRetryable: true` for timeouts
1190
+ - `isRetryable: false` for SDK errors, authentication failures, etc.
1191
+ - Contains metadata with `exitCode`, `stderr`, `promptExcerpt`
1192
+
1193
+ ## Troubleshooting
1194
+
1195
+ See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for solutions to common issues including:
1196
+
1197
+ - Authentication problems
1198
+ - SDK installation issues
1199
+ - Session management
1200
+ - Platform-specific issues
1201
+ - Timeout handling with AbortSignal
1202
+
1203
+ ## Project Structure
1204
+
1205
+ ```
1206
+ ai-sdk-provider-claude-code/
1207
+ ├── src/ # Source code
1208
+ │ ├── index.ts # Main exports
1209
+ │ ├── claude-code-provider.ts # Provider factory
1210
+ │ ├── claude-code-language-model.ts # AI SDK implementation using Agent SDK
1211
+ │ ├── convert-to-claude-code-messages.ts # Message format converter
1212
+ │ ├── extract-json.ts # JSON extraction for object generation
1213
+ │ ├── errors.ts # Error handling utilities
1214
+ │ ├── logger.ts # Configurable logger support
1215
+ │ ├── map-claude-code-finish-reason.ts # Finish reason mapping utilities
1216
+ │ ├── mcp-helpers.ts # Helper for creating SDK MCP servers
1217
+ │ ├── types.ts # TypeScript types and interfaces
1218
+ │ ├── validation.ts # Input validation utilities
1219
+ │ ├── *.test.ts # Test files for each module
1220
+ │ └── logger.integration.test.ts # Logger integration tests
1221
+ ├── examples/ # Example usage scripts
1222
+ │ ├── README.md # Examples documentation
1223
+ │ ├── abort-signal.ts # Request cancellation examples
1224
+ │ ├── basic-usage.ts # Simple text generation with metadata
1225
+ │ ├── bull.webp # Default test image for images.ts example
1226
+ │ ├── check-cli.ts # CLI installation verification
1227
+ │ ├── conversation-history.ts # Multi-turn conversation with message history
1228
+ │ ├── custom-config.ts # Provider configuration options
1229
+ │ ├── generate-object.ts # Original object generation example
1230
+ │ ├── generate-object-basic.ts # Basic object generation patterns
1231
+ │ ├── generate-object-constraints.ts # Validation and constraints
1232
+ │ ├── generate-object-nested.ts # Complex nested structures
1233
+ │ ├── hooks-callbacks.ts # Hook examples (PreToolUse/PostToolUse)
1234
+ │ ├── images.ts # Image input with streaming mode
1235
+ │ ├── integration-test.ts # Comprehensive integration tests
1236
+ │ ├── limitations.ts # Provider limitations demo
1237
+ │ ├── long-running-tasks.ts # Timeout handling with AbortSignal
1238
+ │ ├── sdk-tools-callbacks.ts # In-process SDK tools example
1239
+ │ ├── streaming.ts # Streaming response demo
1240
+ │ ├── tool-management.ts # Tool access control (allow/disallow)
1241
+ │ └── tool-streaming.ts # Tool streaming events demo
1242
+ ├── docs/ # Documentation
1243
+ │ ├── ai-sdk-v4/ # v4-specific documentation
1244
+ │ └── ai-sdk-v5/ # v5 documentation
1245
+ │ ├── GUIDE.md # This guide
1246
+ │ ├── TROUBLESHOOTING.md # Common issues and solutions
1247
+ │ ├── DEVELOPMENT-STATUS.md # Development status
1248
+ │ └── V5_*.md # Migration documentation
1249
+ ├── CHANGELOG.md # Version history
1250
+ ├── LICENSE # MIT License
1251
+ ├── README.md # Main project documentation
1252
+ ├── eslint.config.js # ESLint configuration
1253
+ ├── package.json # Project metadata and dependencies
1254
+ ├── package-lock.json # Dependency lock file
1255
+ ├── run-all-examples.sh # Script to run all examples
1256
+ ├── tsconfig.json # TypeScript configuration
1257
+ ├── tsup.config.ts # Build configuration
1258
+ └── vitest.config.ts # Test runner configuration
1259
+ ```
1260
+
1261
+ ## Known Limitations
1262
+
1263
+ 1. **Image support requires streaming mode**: Image inputs are supported via streaming mode with base64/data URLs. See `examples/images.ts` for usage. Remote HTTP(S) image URLs are not supported.
1264
+ 2. **Authentication required**: Requires separate Claude Agent SDK authentication (`claude login`)
1265
+ 3. **Session IDs change**: Each request gets a new session ID, even when using `--resume`
1266
+ 4. **No AI SDK tool calling interface**: The AI SDK's function/tool calling interface is not implemented, but Claude can use tools via MCP servers and built-in CLI tools
1267
+
1268
+ ## Contributing
1269
+
1270
+ Contributions are welcome! Please read our contributing guidelines before submitting a PR.
1271
+
1272
+ **Focus Areas:**
1273
+
1274
+ - v5 compatibility improvements
1275
+ - Performance optimizations
1276
+ - Better error handling patterns
1277
+ - TypeScript type improvements
1278
+ - Additional example use cases
1279
+
1280
+ ### Dependency Management
1281
+
1282
+ This project uses exact dependency versions to ensure consistent behavior across all installations. When updating dependencies:
1283
+
1284
+ 1. Update the exact version in `package.json`
1285
+ 2. Run `npm install` to update the lock file
1286
+ 3. Test thoroughly before committing
1287
+
1288
+ Note: Peer dependencies (like `zod`) use version ranges as per npm best practices.
1289
+
1290
+ ## License
1291
+
1292
+ MIT - see [LICENSE](../../LICENSE) for details.
1293
+
1294
+ ## Acknowledgments
1295
+
1296
+ This provider is built for the [Vercel AI SDK](https://sdk.vercel.ai/) and uses the [Claude Agent SDK](https://www.npmjs.com/package/@anthropic-ai/claude-agent-sdk) by Anthropic.