ai-sdk-provider-claude-code 0.2.1 → 1.0.0-beta.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,912 @@
1
+ # Usage Guide (AI SDK v5-beta)
2
+
3
+ ## Essential Examples
4
+
5
+ ### Streaming Responses
6
+
7
+ ```typescript
8
+ import { streamText } from 'ai';
9
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
10
+
11
+ const result = streamText({
12
+ model: claudeCode('sonnet'),
13
+ prompt: 'Write a haiku about programming',
14
+ });
15
+
16
+ // v5 returns promises for different parts
17
+ const text = await result.text;
18
+ console.log(text);
19
+
20
+ // Or stream chunks
21
+ for await (const chunk of result.textStream) {
22
+ process.stdout.write(chunk);
23
+ }
24
+ ```
25
+
26
+ ### Multi-turn Conversations
27
+
28
+ ```typescript
29
+ import { generateText } from 'ai';
30
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
31
+
32
+ const messages: ModelMessage[] = [];
33
+
34
+ // First turn
35
+ messages.push({ role: 'user', content: [{ type: 'text', text: 'My name is Alice' }] });
36
+ const response1 = await generateText({
37
+ model: claudeCode('sonnet'),
38
+ messages,
39
+ });
40
+ messages.push({ role: 'assistant', content: response1.content });
41
+
42
+ // Second turn - remembers context
43
+ messages.push({ role: 'user', content: [{ type: 'text', text: 'What is my name?' }] });
44
+ const response2 = await generateText({
45
+ model: claudeCode('sonnet'),
46
+ messages,
47
+ });
48
+ console.log(response2.text); // "Alice"
49
+ ```
50
+
51
+ ### Object Generation with JSON Schema
52
+
53
+ ```typescript
54
+ import { generateObject } from 'ai';
55
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
56
+ import { z } from 'zod';
57
+
58
+ const result = await generateObject({
59
+ model: claudeCode('sonnet'),
60
+ schema: z.object({
61
+ name: z.string().describe('Full name'),
62
+ age: z.number().describe('Age in years'),
63
+ email: z.string().email().describe('Email address'),
64
+ interests: z.array(z.string()).describe('List of hobbies'),
65
+ }),
66
+ prompt: 'Generate a profile for a software developer',
67
+ });
68
+
69
+ console.log(result.object);
70
+ // {
71
+ // name: "Alex Chen",
72
+ // age: 28,
73
+ // email: "alex.chen@example.com",
74
+ // interests: ["coding", "open source", "machine learning"]
75
+ // }
76
+ ```
77
+
78
+ ### Handling Long-Running Tasks
79
+
80
+ For complex tasks with Claude Opus 4's extended thinking, use AbortSignal with custom timeouts:
81
+
82
+ ```typescript
83
+ import { generateText } from 'ai';
84
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
85
+
86
+ // Create a custom timeout
87
+ const controller = new AbortController();
88
+ const timeoutId = setTimeout(() => {
89
+ controller.abort(new Error('Request timeout after 10 minutes'));
90
+ }, 600000); // 10 minutes
91
+
92
+ try {
93
+ const result = await generateText({
94
+ model: claudeCode('opus'),
95
+ prompt: 'Analyze this complex problem in detail...',
96
+ abortSignal: controller.signal,
97
+ });
98
+
99
+ clearTimeout(timeoutId);
100
+ console.log(result.text);
101
+ } catch (error) {
102
+ if (error.name === 'AbortError') {
103
+ console.log('Request was cancelled');
104
+ }
105
+ }
106
+ ```
107
+
108
+ ### Session Management (Experimental)
109
+
110
+ ```typescript
111
+ import { generateText } from 'ai';
112
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
113
+
114
+ // First message
115
+ const result = await generateText({
116
+ model: claudeCode('sonnet'),
117
+ messages: [{ role: 'user', content: [{ type: 'text', text: 'My name is Bob.' }] }],
118
+ });
119
+
120
+ // Resume using the session ID
121
+ const sessionId = result.providerMetadata?.['claude-code']?.sessionId;
122
+
123
+ const response = await generateText({
124
+ model: claudeCode('sonnet', { resume: sessionId }),
125
+ messages: [{ role: 'user', content: [{ type: 'text', text: 'What is my name?' }] }],
126
+ });
127
+ ```
128
+
129
+ `resume` continues a previous CLI session instead of starting a new one.
130
+
131
+ ---
132
+
133
+ ## Key Changes in v5-beta
134
+
135
+ ### Message Format
136
+ In v5, user messages must have content as an array of parts:
137
+
138
+ ```typescript
139
+ // v4 format (NOT supported)
140
+ { role: 'user', content: 'Hello' }
141
+
142
+ // v5 format (required)
143
+ { role: 'user', content: [{ type: 'text', text: 'Hello' }] }
144
+ ```
145
+
146
+ ### Streaming API
147
+ The streaming API returns a result object with promises:
148
+
149
+ ```typescript
150
+ const result = streamText({
151
+ model: claudeCode('sonnet'),
152
+ prompt: 'Hello',
153
+ });
154
+
155
+ // Access different parts as promises
156
+ const text = await result.text;
157
+ const usage = await result.usage;
158
+ const finishReason = await result.finishReason;
159
+ ```
160
+
161
+ ### Token Usage Properties
162
+ Token usage properties have been renamed:
163
+
164
+ ```typescript
165
+ // v4
166
+ { promptTokens: 10, completionTokens: 5 }
167
+
168
+ // v5
169
+ { inputTokens: 10, outputTokens: 5, totalTokens: 15 }
170
+ ```
171
+
172
+ ---
173
+
174
+ ## Detailed Configuration
175
+
176
+ ### AbortSignal Support
177
+
178
+ The provider fully supports the standard AbortSignal for request cancellation, following Vercel AI SDK patterns:
179
+
180
+ ```typescript
181
+ import { generateText } from 'ai';
182
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
183
+
184
+ // Using AbortController for cancellation
185
+ const controller = new AbortController();
186
+
187
+ // Cancel after user action
188
+ button.addEventListener('click', () => {
189
+ controller.abort();
190
+ });
191
+
192
+ const result = await generateText({
193
+ model: claudeCode('opus'),
194
+ prompt: 'Write a story...',
195
+ abortSignal: controller.signal,
196
+ });
197
+ ```
198
+
199
+ **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.
200
+
201
+ ### Configuration Options
202
+
203
+ | Option | Type | Default | Description |
204
+ |--------|------|---------|-------------|
205
+ | `model` | `'opus' \| 'sonnet'` | `'opus'` | Model to use |
206
+ | `pathToClaudeCodeExecutable` | `string` | `'claude'` | Path to Claude CLI executable |
207
+ | `customSystemPrompt` | `string` | `undefined` | Custom system prompt |
208
+ | `appendSystemPrompt` | `string` | `undefined` | Append to system prompt |
209
+ | `maxTurns` | `number` | `undefined` | Maximum conversation turns |
210
+ | `maxThinkingTokens` | `number` | `undefined` | Maximum thinking tokens |
211
+ | `permissionMode` | `string` | `'default'` | Permission mode for tools |
212
+ | `allowedTools` | `string[]` | `undefined` | Tools to explicitly allow |
213
+ | `disallowedTools` | `string[]` | `undefined` | Tools to restrict |
214
+ | `mcpServers` | `object` | `undefined` | MCP server configuration |
215
+ | `resume` | `string` | `undefined` | Resume an existing session |
216
+
217
+ ### Custom Configuration
218
+
219
+ ```typescript
220
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
221
+
222
+ const claude = createClaudeCode({
223
+ defaultSettings: {
224
+ pathToClaudeCodeExecutable: '/usr/local/bin/claude',
225
+ permissionMode: 'default', // Ask for permissions
226
+ customSystemPrompt: 'You are a helpful coding assistant.',
227
+ }
228
+ });
229
+
230
+ const result = await generateText({
231
+ model: claude('opus'),
232
+ prompt: 'Hello, Claude!',
233
+ });
234
+ ```
235
+
236
+ ### Logging Configuration
237
+
238
+ Control how warnings and errors are logged:
239
+
240
+ ```typescript
241
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
242
+
243
+ // Default: logs to console
244
+ const defaultClaude = createClaudeCode();
245
+
246
+ // Disable all logging
247
+ const silentClaude = createClaudeCode({
248
+ defaultSettings: {
249
+ logger: false
250
+ }
251
+ });
252
+
253
+ // Custom logger
254
+ const customClaude = createClaudeCode({
255
+ defaultSettings: {
256
+ logger: {
257
+ warn: (message) => myLogger.warn('Claude:', message),
258
+ error: (message) => myLogger.error('Claude:', message),
259
+ }
260
+ }
261
+ });
262
+
263
+ // Model-specific logger override
264
+ const model = customClaude('opus', {
265
+ logger: false // Disable logging for this model only
266
+ });
267
+ ```
268
+
269
+ Logger options:
270
+ - `undefined` (default): Uses `console.warn` and `console.error`
271
+ - `false`: Disables all logging
272
+ - Custom `Logger` object: Must implement `warn` and `error` methods
273
+
274
+ ### Tool Management
275
+
276
+ 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.
277
+
278
+ #### Tool Types
279
+ - **Built-in tools**: `Bash`, `Edit`, `Read`, `Write`, `LS`, `Grep`, etc.
280
+ - **MCP tools**: `mcp__serverName__toolName` format
281
+
282
+ #### Using allowedTools (Allowlist)
283
+ ```typescript
284
+ import { createClaudeCode } from 'ai-sdk-provider-claude-code';
285
+
286
+ // Only allow specific built-in tools
287
+ const readOnlyClaude = createClaudeCode({
288
+ allowedTools: ['Read', 'LS', 'Grep'],
289
+ });
290
+
291
+ // Allow specific Bash commands using specifiers
292
+ const gitOnlyClaude = createClaudeCode({
293
+ allowedTools: [
294
+ 'Bash(git log:*)',
295
+ 'Bash(git diff:*)',
296
+ 'Bash(git status)'
297
+ ],
298
+ });
299
+
300
+ // Mix built-in and MCP tools
301
+ const mixedClaude = createClaudeCode({
302
+ allowedTools: [
303
+ 'Read',
304
+ 'Bash(npm test:*)',
305
+ 'mcp__filesystem__read_file',
306
+ 'mcp__git__status'
307
+ ],
308
+ });
309
+ ```
310
+
311
+ #### Using disallowedTools (Denylist)
312
+ ```typescript
313
+ // Block dangerous operations
314
+ const safeClaude = createClaudeCode({
315
+ disallowedTools: [
316
+ 'Write',
317
+ 'Edit',
318
+ 'Delete',
319
+ 'Bash(rm:*)',
320
+ 'Bash(sudo:*)'
321
+ ],
322
+ });
323
+
324
+ // Block all Bash and MCP write operations
325
+ const restrictedClaude = createClaudeCode({
326
+ disallowedTools: [
327
+ 'Bash',
328
+ 'mcp__filesystem__write_file',
329
+ 'mcp__git__commit',
330
+ 'mcp__git__push'
331
+ ],
332
+ });
333
+ ```
334
+
335
+ #### Model-Specific Overrides
336
+ ```typescript
337
+ const baseClaude = createClaudeCode({
338
+ disallowedTools: ['Write', 'Edit'],
339
+ });
340
+
341
+ // Override for a specific call
342
+ const result = await generateText({
343
+ model: baseClaude('opus', {
344
+ disallowedTools: [], // Allow everything for this call
345
+ }),
346
+ prompt: 'Create a simple config file...',
347
+ });
348
+ ```
349
+
350
+ **Key Points**:
351
+ - These are **session-only permission overrides** (same syntax as settings.json)
352
+ - Higher priority than settings files
353
+ - Works for both built-in tools AND MCP tools
354
+ - Cannot use both `allowedTools` and `disallowedTools` together
355
+ - Empty `allowedTools: []` = Explicit empty allowlist (no tools allowed)
356
+ - Omitting the flags entirely = Falls back to normal permission system
357
+ - Use `/permissions` in Claude to see all available tool names
358
+
359
+ **Common Patterns**:
360
+ - Read-only mode: `disallowedTools: ['Write', 'Edit', 'Delete']`
361
+ - No shell access: `disallowedTools: ['Bash']`
362
+ - Safe git: `allowedTools: ['Bash(git log:*)', 'Bash(git diff:*)']`
363
+ - No MCP: `disallowedTools: ['mcp__*']`
364
+
365
+ **Permission Behavior**:
366
+ | Configuration | CLI Flag Behavior | Result |
367
+ |--------------|-------------------|---------|
368
+ | No config | No `--allowedTools` or `--disallowedTools` | Falls back to settings.json and interactive prompts |
369
+ | `allowedTools: []` | `--allowedTools` (empty) | Explicit empty allowlist - blocks all tools |
370
+ | `allowedTools: ['Read']` | `--allowedTools Read` | Only allows Read tool |
371
+ | `disallowedTools: []` | `--disallowedTools` (empty) | No effect - normal permissions apply |
372
+ | `disallowedTools: ['Write']` | `--disallowedTools Write` | Blocks Write tool, others follow normal permissions |
373
+
374
+ ## Advanced Configuration
375
+
376
+ ### MCP Server Support
377
+
378
+ The provider supports Model Context Protocol (MCP) servers for extended functionality:
379
+
380
+ ```typescript
381
+ const claude = createClaudeCode({
382
+ mcpServers: {
383
+ filesystem: {
384
+ command: 'npx',
385
+ args: ['-y', '@modelcontextprotocol/server-filesystem'],
386
+ },
387
+ github: {
388
+ type: 'sse',
389
+ url: 'https://mcp.github.com/api',
390
+ headers: { 'Authorization': 'Bearer YOUR_TOKEN' },
391
+ },
392
+ },
393
+ });
394
+ ```
395
+
396
+ ### Permission Modes
397
+
398
+ Control how Claude Code handles tool permissions:
399
+
400
+ ```typescript
401
+ const claude = createClaudeCode({
402
+ permissionMode: 'bypassPermissions', // Skip all permission prompts
403
+ // Other options: 'default', 'acceptEdits', 'plan'
404
+ });
405
+ ```
406
+
407
+ ### Custom System Prompts
408
+
409
+ ```typescript
410
+ const claude = createClaudeCode({
411
+ customSystemPrompt: 'You are an expert Python developer.',
412
+ // Or append to existing prompt:
413
+ appendSystemPrompt: 'Always use type hints in Python code.',
414
+ });
415
+ ```
416
+
417
+ ## Request Cancellation
418
+
419
+ The provider supports request cancellation through the standard AbortController API:
420
+
421
+ ```typescript
422
+ import { generateText } from 'ai';
423
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
424
+
425
+ // Create abort controller
426
+ const controller = new AbortController();
427
+
428
+ // Start request
429
+ const promise = generateText({
430
+ model: claudeCode('opus'),
431
+ prompt: 'Write a long story...',
432
+ abortSignal: controller.signal,
433
+ });
434
+
435
+ // Cancel the request
436
+ controller.abort();
437
+
438
+ // The promise rejects with AbortError
439
+ try {
440
+ await promise;
441
+ } catch (error) {
442
+ if (error.name === 'AbortError') {
443
+ console.log('Request was cancelled');
444
+ }
445
+ }
446
+ ```
447
+
448
+ This is especially useful in UI scenarios where users might cancel requests or navigate away.
449
+
450
+ ## Implementation Details
451
+
452
+ ### SDK Message Types
453
+
454
+ The SDK provides structured message types for different events:
455
+
456
+ #### Assistant Message
457
+ ```typescript
458
+ {
459
+ type: 'assistant',
460
+ message: {
461
+ content: [{ type: 'text', text: 'Hello!' }],
462
+ // ... other fields
463
+ },
464
+ session_id: 'abc-123-def'
465
+ }
466
+ ```
467
+
468
+ #### Result Message
469
+ ```typescript
470
+ {
471
+ type: 'result',
472
+ subtype: 'success' | 'error_max_turns' | 'error_during_execution',
473
+ session_id: 'abc-123-def',
474
+ usage: { /* token counts */ },
475
+ total_cost_usd: 0.001,
476
+ duration_ms: 1500
477
+ }
478
+ ```
479
+
480
+ #### System Message
481
+ ```typescript
482
+ {
483
+ type: 'system',
484
+ subtype: 'init',
485
+ session_id: 'abc-123-def',
486
+ tools: ['Read', 'Write', 'Bash'],
487
+ model: 'opus'
488
+ }
489
+ ```
490
+
491
+ ### SDK Implementation
492
+
493
+ The provider uses the official `@anthropic-ai/claude-code` SDK which provides:
494
+ - **AsyncGenerator pattern**: Native streaming support with `query()` function
495
+ - **Structured messages**: Rich message types (assistant, result, system, error)
496
+ - **Built-in features**: AbortController, session management, MCP servers
497
+ - **Automatic handling**: Process management, error handling, and output parsing
498
+
499
+ ### Provider Metadata
500
+
501
+ The provider returns rich metadata including token usage, timing, and cost information:
502
+
503
+ ```typescript
504
+ const result = await generateText({
505
+ model: claudeCode('sonnet'),
506
+ prompt: 'Hello!',
507
+ });
508
+
509
+ console.log(result.providerMetadata);
510
+ // {
511
+ // "claude-code": {
512
+ // "sessionId": "abc-123-def",
513
+ // "costUsd": 0.0285561, // Note: Always non-zero for tracking
514
+ // "durationMs": 3056,
515
+ // "rawUsage": {
516
+ // "inputTokens": 4,
517
+ // "outputTokens": 7,
518
+ // "cacheCreationInputTokens": 5924,
519
+ // "cacheReadInputTokens": 10075
520
+ // }
521
+ // }
522
+ // }
523
+ ```
524
+
525
+ **Important Note about Costs**: The `costUsd` field shows the cost of the API usage:
526
+ - **For Pro/Max subscribers**: This is informational only - usage is covered by your monthly subscription
527
+ - **For API key users**: This represents actual charges that will be billed to your account
528
+
529
+ ## Object Generation
530
+
531
+ The provider supports object generation through prompt engineering, allowing you to generate structured data with JSON schema validation:
532
+
533
+ ```typescript
534
+ import { generateObject, streamObject } from 'ai';
535
+ import { claudeCode } from 'ai-sdk-provider-claude-code';
536
+ import { z } from 'zod';
537
+
538
+ // Generate a complete object
539
+ const result = await generateObject({
540
+ model: claudeCode('sonnet'),
541
+ schema: z.object({
542
+ recipe: z.object({
543
+ name: z.string(),
544
+ ingredients: z.array(z.string()),
545
+ instructions: z.array(z.string()),
546
+ prepTime: z.number(),
547
+ servings: z.number(),
548
+ }),
549
+ }),
550
+ prompt: 'Generate a recipe for chocolate chip cookies',
551
+ });
552
+
553
+ // Note: streamObject waits for complete response before parsing
554
+ // Use generateObject for clarity since streaming doesn't provide benefits
555
+ const analysisResult = await generateObject({
556
+ model: claudeCode('sonnet'),
557
+ schema: z.object({
558
+ analysis: z.string(),
559
+ sentiment: z.enum(['positive', 'negative', 'neutral']),
560
+ score: z.number(),
561
+ }),
562
+ prompt: 'Analyze this review: "Great product!"',
563
+ });
564
+
565
+ console.log(analysisResult.object);
566
+ ```
567
+
568
+ **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.
569
+
570
+ **Important notes**:
571
+ - **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.
572
+ - **Streaming behavior**: While `streamObject` is supported, it accumulates the full response before extracting JSON to ensure validity. Regular text streaming works in real-time.
573
+
574
+ ## Object Generation Cookbook
575
+
576
+ ### Quick Start Examples
577
+
578
+ #### Basic Objects
579
+ Start with simple schemas and clear prompts:
580
+ ```typescript
581
+ const result = await generateObject({
582
+ model: claudeCode('sonnet'),
583
+ schema: z.object({
584
+ name: z.string(),
585
+ age: z.number(),
586
+ email: z.string().email(),
587
+ }),
588
+ prompt: 'Generate a developer profile',
589
+ });
590
+ ```
591
+ [Full example](../../examples/generate-object-basic.ts)
592
+
593
+ #### Nested Structures
594
+ Build complex hierarchical data:
595
+ ```typescript
596
+ const result = await generateObject({
597
+ model: claudeCode('sonnet'),
598
+ schema: z.object({
599
+ company: z.object({
600
+ departments: z.array(z.object({
601
+ name: z.string(),
602
+ teams: z.array(z.object({
603
+ name: z.string(),
604
+ members: z.number(),
605
+ })),
606
+ })),
607
+ }),
608
+ }),
609
+ prompt: 'Generate a company org structure',
610
+ });
611
+ ```
612
+ [Full example](../../examples/generate-object-nested.ts)
613
+
614
+ #### Constrained Generation
615
+ Use Zod's validation features:
616
+ ```typescript
617
+ const result = await generateObject({
618
+ model: claudeCode('sonnet'),
619
+ schema: z.object({
620
+ status: z.enum(['pending', 'active', 'completed']),
621
+ priority: z.number().min(1).max(5),
622
+ tags: z.array(z.string()).min(1).max(3),
623
+ }),
624
+ prompt: 'Generate a task with medium priority',
625
+ });
626
+ ```
627
+ [Full example](../../examples/generate-object-constraints.ts)
628
+
629
+
630
+ ### Best Practices
631
+
632
+ 1. **Start Simple**: Begin with basic schemas and add complexity gradually
633
+ 2. **Clear Prompts**: Be specific about what you want generated
634
+ 3. **Use Descriptions**: Add `.describe()` to schema fields for better results
635
+ 4. **Handle Errors**: Implement retry logic for production use
636
+ 5. **Test Schemas**: Validate your schemas work before deployment
637
+
638
+ ### Common Patterns
639
+
640
+ - **Data Models**: [User profiles, products, orders](../../examples/generate-object-nested.ts)
641
+ - **Validation**: [Enums, constraints, regex patterns](../../examples/generate-object-constraints.ts)
642
+ - **Basic Objects**: [Simple schemas and arrays](../../examples/generate-object-basic.ts)
643
+ - **Note**: For object generation, use `generateObject` instead of `streamObject` as streaming provides no benefits
644
+
645
+ ## Object Generation Troubleshooting
646
+
647
+ ### Common Issues and Solutions
648
+
649
+ #### 1. Invalid JSON Response
650
+ **Problem**: Claude returns text instead of valid JSON
651
+
652
+ **Solutions**:
653
+ - Simplify your schema - start with fewer fields
654
+ - Make your prompt more explicit: "Generate only valid JSON"
655
+ - Check the schema for overly complex constraints
656
+ - Use the retry pattern:
657
+
658
+ ```typescript
659
+ async function generateWithRetry(schema, prompt, maxRetries = 3) {
660
+ for (let i = 0; i < maxRetries; i++) {
661
+ try {
662
+ return await generateObject({ model, schema, prompt });
663
+ } catch (error) {
664
+ if (i === maxRetries - 1) throw error;
665
+ await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
666
+ }
667
+ }
668
+ }
669
+ ```
670
+
671
+ #### 2. Missing Required Fields
672
+ **Problem**: Generated objects missing required properties
673
+
674
+ **Solutions**:
675
+ - Emphasize requirements in your prompt
676
+ - Use descriptive field names
677
+ - Add field descriptions with `.describe()`
678
+ - Example:
679
+ ```typescript
680
+ z.object({
681
+ // Bad: vague field name
682
+ val: z.number(),
683
+
684
+ // Good: clear field name with description
685
+ totalPrice: z.number().describe('Total price in USD'),
686
+ })
687
+ ```
688
+
689
+ #### 3. Type Mismatches
690
+ **Problem**: String when expecting number, wrong date format, etc.
691
+
692
+ **Solutions**:
693
+ - Be explicit in descriptions: "age as a number" not just "age"
694
+ - For dates, specify format: `.describe('Date in YYYY-MM-DD format')`
695
+ - Use regex patterns for strings: `z.string().regex(/^\d{4}-\d{2}-\d{2}$/)`
696
+
697
+ #### 4. Schema Too Complex
698
+ **Problem**: Very complex schemas fail or timeout
699
+
700
+ **Solutions**:
701
+ - Break into smaller parts and combine:
702
+ ```typescript
703
+ // Instead of one huge schema, compose smaller ones
704
+ const userSchema = z.object({ /* user fields */ });
705
+ const settingsSchema = z.object({ /* settings */ });
706
+ const profileSchema = z.object({
707
+ user: userSchema,
708
+ settings: settingsSchema,
709
+ });
710
+ ```
711
+ - Generate in steps and merge results
712
+ - Increase timeout for complex generations
713
+
714
+ #### 5. Inconsistent Results
715
+ **Problem**: Same prompt gives different structure each time
716
+
717
+ **Solutions**:
718
+ - Make schemas more constrained (use enums, min/max)
719
+ - Provide example in prompt
720
+ - Use consistent field naming conventions
721
+ - Consider using `opus` model for complex schemas
722
+
723
+ ### Debugging Tips
724
+
725
+ 1. **Enable Debug Logging**:
726
+ ```typescript
727
+ const result = await generateObject({
728
+ model: claudeCode('sonnet'),
729
+ schema: yourSchema,
730
+ prompt: yourPrompt,
731
+ });
732
+ console.log('Tokens used:', result.usage);
733
+ console.log('Warnings:', result.warnings);
734
+ ```
735
+
736
+ 2. **Test Schema Separately**:
737
+ ```typescript
738
+ // Validate your schema works
739
+ const testData = { /* your test object */ };
740
+ try {
741
+ schema.parse(testData);
742
+ console.log('Schema is valid');
743
+ } catch (e) {
744
+ console.log('Schema errors:', e.errors);
745
+ }
746
+ ```
747
+
748
+ 3. **Progressive Enhancement**:
749
+ Start with minimal schema, test, then add fields one by one
750
+
751
+ 4. **Check Examples**:
752
+ Review our examples for implementation patterns
753
+
754
+ ## Limitations
755
+
756
+ - **No image support**: The Claude Code SDK doesn't support image inputs (provider sets `supportsImageUrls = false`)
757
+ - **No embedding support**: Text embeddings are not available through this provider
758
+ - **Object-tool mode not supported**: Only `object-json` mode works via `generateObject`/`streamObject`. The AI SDK's tool calling interface is not implemented
759
+ - **Text-only responses**: No support for file generation or other modalities
760
+ - **Session management**: While sessions are supported, message history is the recommended approach
761
+ - **Unsupported generation settings**: The following AI SDK settings are ignored and will generate warnings:
762
+ - `temperature` - Claude Code SDK doesn't expose temperature control
763
+ - `maxOutputTokens` - Token limits aren't configurable via CLI
764
+ - `topP`, `topK` - Sampling parameters aren't available
765
+ - `presencePenalty`, `frequencyPenalty` - Penalty parameters aren't supported
766
+ - `stopSequences` - Custom stop sequences aren't available
767
+ - `seed` - Deterministic generation isn't supported
768
+
769
+ ## Error Handling
770
+
771
+ The provider uses standard AI SDK error classes for better ecosystem compatibility:
772
+
773
+ ```typescript
774
+ import { generateText } from 'ai';
775
+ import { APICallError, LoadAPIKeyError } from '@ai-sdk/provider';
776
+ import {
777
+ claudeCode,
778
+ isAuthenticationError,
779
+ isTimeoutError,
780
+ getErrorMetadata
781
+ } from 'ai-sdk-provider-claude-code';
782
+
783
+ try {
784
+ const result = await generateText({
785
+ model: claudeCode('opus'),
786
+ prompt: 'Hello!',
787
+ });
788
+ } catch (error) {
789
+ if (isAuthenticationError(error)) {
790
+ console.error('Please run "claude login" to authenticate');
791
+ } else if (isTimeoutError(error)) {
792
+ console.error('Request timed out. Consider using AbortController with a custom timeout.');
793
+ } else if (error instanceof APICallError) {
794
+ // Get CLI-specific metadata
795
+ const metadata = getErrorMetadata(error);
796
+ console.error('CLI error:', {
797
+ message: error.message,
798
+ isRetryable: error.isRetryable,
799
+ exitCode: metadata?.exitCode,
800
+ stderr: metadata?.stderr,
801
+ });
802
+ } else {
803
+ console.error('Error:', error);
804
+ }
805
+ }
806
+ ```
807
+
808
+ ### Error Types
809
+
810
+ - **`LoadAPIKeyError`**: Authentication failures (exit code 401)
811
+ - **`APICallError`**: All other CLI failures
812
+ - `isRetryable: true` for timeouts
813
+ - `isRetryable: false` for SDK errors, authentication failures, etc.
814
+ - Contains metadata with `exitCode`, `stderr`, `promptExcerpt`
815
+
816
+ ## Troubleshooting
817
+
818
+ See [TROUBLESHOOTING.md](TROUBLESHOOTING.md) for solutions to common issues including:
819
+ - Authentication problems
820
+ - SDK installation issues
821
+ - Session management
822
+ - Platform-specific issues
823
+ - Timeout handling with AbortSignal
824
+
825
+ ## Project Structure
826
+
827
+ ```
828
+ ai-sdk-provider-claude-code/
829
+ ├── src/ # Source code
830
+ │ ├── index.ts # Main exports
831
+ │ ├── claude-code-provider.ts # Provider factory
832
+ │ ├── claude-code-language-model.ts # AI SDK implementation using SDK
833
+ │ ├── convert-to-claude-code-messages.ts # Message format converter
834
+ │ ├── extract-json.ts # JSON extraction for object generation
835
+ │ ├── errors.ts # Error handling utilities
836
+ │ ├── logger.ts # Configurable logger support
837
+ │ ├── map-claude-code-finish-reason.ts # Finish reason mapping utilities
838
+ │ ├── types.ts # TypeScript types and interfaces
839
+ │ ├── validation.ts # Input validation utilities
840
+ │ ├── *.test.ts # Test files for each module
841
+ │ └── logger.integration.test.ts # Logger integration tests
842
+ ├── examples/ # Example usage scripts
843
+ │ ├── README.md # Examples documentation
844
+ │ ├── abort-signal.ts # Request cancellation examples
845
+ │ ├── basic-usage.ts # Simple text generation with metadata
846
+ │ ├── check-cli.ts # CLI installation verification
847
+ │ ├── conversation-history.ts # Multi-turn conversation with message history
848
+ │ ├── custom-config.ts # Provider configuration options
849
+ │ ├── generate-object.ts # Original object generation example
850
+ │ ├── generate-object-basic.ts # Basic object generation patterns
851
+ │ ├── generate-object-constraints.ts # Validation and constraints
852
+ │ ├── generate-object-nested.ts # Complex nested structures
853
+ │ ├── integration-test.ts # Comprehensive integration tests
854
+ │ ├── limitations.ts # Provider limitations demo
855
+ │ ├── long-running-tasks.ts # Timeout handling with AbortSignal
856
+ │ ├── streaming.ts # Streaming response demo
857
+ │ ├── test-session.ts # Session management testing
858
+ │ └── tool-management.ts # Tool access control (allow/disallow)
859
+ ├── docs/ # Documentation
860
+ │ ├── ai-sdk-v4/ # v4-specific documentation
861
+ │ └── ai-sdk-v5/ # v5-beta documentation
862
+ │ ├── GUIDE.md # This guide
863
+ │ ├── TROUBLESHOOTING.md # Common issues and solutions
864
+ │ ├── DEVELOPMENT-STATUS.md # Development status
865
+ │ └── V5_*.md # Migration documentation
866
+ ├── CHANGELOG.md # Version history
867
+ ├── LICENSE # MIT License
868
+ ├── README.md # Main project documentation
869
+ ├── eslint.config.js # ESLint configuration
870
+ ├── package.json # Project metadata and dependencies
871
+ ├── package-lock.json # Dependency lock file
872
+ ├── run-all-examples.sh # Script to run all examples
873
+ ├── tsconfig.json # TypeScript configuration
874
+ ├── tsup.config.ts # Build configuration
875
+ └── vitest.config.ts # Test runner configuration
876
+ ```
877
+
878
+ ## Known Limitations
879
+
880
+ 1. **No image support**: The CLI doesn't accept image inputs
881
+ 2. **Authentication required**: Requires separate Claude Code SDK authentication (`claude login`)
882
+ 3. **Session IDs change**: Each request gets a new session ID, even when using `--resume`
883
+ 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
884
+
885
+ ## Contributing
886
+
887
+ Contributions are welcome! Please read our contributing guidelines before submitting a PR.
888
+
889
+ **Beta Focus Areas:**
890
+ - v5-beta compatibility improvements
891
+ - Performance optimizations
892
+ - Better error handling patterns
893
+ - TypeScript type improvements
894
+ - Additional example use cases
895
+
896
+ ### Dependency Management
897
+
898
+ This project uses exact dependency versions to ensure consistent behavior across all installations. When updating dependencies:
899
+
900
+ 1. Update the exact version in `package.json`
901
+ 2. Run `npm install` to update the lock file
902
+ 3. Test thoroughly before committing
903
+
904
+ Note: Peer dependencies (like `zod`) use version ranges as per npm best practices.
905
+
906
+ ## License
907
+
908
+ MIT - see [LICENSE](../../LICENSE) for details.
909
+
910
+ ## Acknowledgments
911
+
912
+ This provider is built for the [Vercel AI SDK](https://sdk.vercel.ai/) and uses the [Claude Code SDK](https://docs.anthropic.com/claude-code/cli) by Anthropic.