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