@ai-sdk/harness-claude-code 1.0.65 → 1.0.67

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.
@@ -63,6 +63,11 @@ const CLAUDE_CODE_CLIENT_APP = `ai-sdk/harness-claude-code/${VERSION}`;
63
63
 
64
64
  export type ClaudeCodeHarnessSettings = {
65
65
  readonly auth?: ClaudeCodeAuthOptions;
66
+ /**
67
+ * MCP server definitions keyed by server name. Each definition uses the
68
+ * underlying runtime's native MCP server configuration format.
69
+ */
70
+ readonly mcpServers?: Record<string, unknown>;
66
71
  /**
67
72
  * Anthropic model id the underlying `claude` CLI should use. Leaving this
68
73
  * unset defers to the CLI's default.
@@ -73,6 +78,11 @@ export type ClaudeCodeHarnessSettings = {
73
78
  * back to the caller. Unset means the CLI's default.
74
79
  */
75
80
  readonly maxTurns?: number;
81
+ /**
82
+ * Environment variables for the Claude Code process. These values are
83
+ * merged over the sandbox bridge process environment.
84
+ */
85
+ readonly env?: Readonly<Record<string, string>>;
76
86
  /**
77
87
  * Controls extended-thinking behavior and whether reasoning is summarized or
78
88
  * omitted. Defaults to `{ type: 'adaptive', display: 'summarized' }`.
@@ -87,19 +97,53 @@ export type ClaudeCodeHarnessSettings = {
87
97
  readonly port?: number;
88
98
  /** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
89
99
  readonly startupTimeoutMs?: number;
100
+ /**
101
+ * Creates the authentication token used by the sandbox bridge. Defaults to
102
+ * a random 32-byte hexadecimal token.
103
+ */
104
+ readonly mintBridgeToken?: (sandboxId: string) => string;
90
105
  };
91
106
 
92
107
  /*
93
108
  * Every native tool the Claude Code CLI can invoke, declared as a `ToolSet`
94
109
  * keyed by what the bridge emits as `toolName` on the wire
95
- * (`commonName ?? nativeName`). Schemas transcribed from
96
- * `@anthropic-ai/claude-agent-sdk`'s `agentSdkTypes.d.ts`.
110
+ * (`commonName ?? nativeName`). Schemas are transcribed from the generated
111
+ * `sdk-tools.d.ts` and the conditionally registered tool definitions in the
112
+ * pinned Claude Code executable.
97
113
  *
98
114
  * `MCP` (the generic proxy tool inside the Claude Code SDK) is intentionally
99
115
  * omitted — the bridge filters out `mcp__harness-tools__*` tool names before
100
116
  * emitting them, and other MCP invocations come through with their own
101
117
  * server-tool names rather than the literal `'Mcp'` token.
102
118
  */
119
+ const listMcpResourcesInputSchema = z.object({
120
+ server: z.string().optional(),
121
+ });
122
+
123
+ const readMcpResourceInputSchema = z.object({
124
+ server: z.string(),
125
+ uri: z.string(),
126
+ });
127
+
128
+ const structuredTeamMessageSchema = z.discriminatedUnion('type', [
129
+ z.object({
130
+ type: z.literal('shutdown_request'),
131
+ reason: z.string().optional(),
132
+ }),
133
+ z.object({
134
+ type: z.literal('shutdown_response'),
135
+ request_id: z.string(),
136
+ approve: z.boolean(),
137
+ reason: z.string().optional(),
138
+ }),
139
+ z.object({
140
+ type: z.literal('plan_approval_response'),
141
+ request_id: z.string(),
142
+ approve: z.boolean(),
143
+ feedback: z.string().optional(),
144
+ }),
145
+ ]);
146
+
103
147
  const CLAUDE_CODE_BUILTIN_TOOLS = {
104
148
  read: commonTool('read', {
105
149
  nativeName: 'Read',
@@ -291,23 +335,66 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
291
335
  timeout: z.number(),
292
336
  }),
293
337
  }),
294
- Monitor: tool({
295
- description: 'Run and monitor a shell command',
296
- inputSchema: z.object({
297
- command: z.string(),
298
- description: z.string().optional(),
299
- timeout_ms: z.number().optional(),
300
- persistent: z.boolean().optional(),
338
+ Monitor: {
339
+ ...tool({
340
+ description: 'Run and monitor a shell command or WebSocket',
341
+ inputSchema: z.object({
342
+ description: z.string().optional(),
343
+ timeout_ms: z.number().optional(),
344
+ persistent: z.boolean().optional(),
345
+ command: z.string().optional(),
346
+ ws: z
347
+ .object({
348
+ url: z.string(),
349
+ protocols: z.array(z.string()).optional(),
350
+ })
351
+ .optional(),
352
+ }),
301
353
  }),
302
- }),
303
- ListMcpResources: tool({
304
- description: 'List resources available from MCP servers',
305
- inputSchema: z.object({ server: z.string().optional() }),
306
- }),
307
- ReadMcpResource: tool({
308
- description: 'Read a specific MCP resource by URI',
309
- inputSchema: z.object({ server: z.string(), uri: z.string() }),
310
- }),
354
+ toolUseKind: 'bash',
355
+ },
356
+ ListMcpResources: {
357
+ ...tool({
358
+ description: 'List resources available from MCP servers',
359
+ inputSchema: listMcpResourcesInputSchema,
360
+ }),
361
+ toolUseKind: 'readonly',
362
+ },
363
+ ListMcpResourcesTool: {
364
+ ...tool({
365
+ description: 'List resources available from MCP servers',
366
+ inputSchema: listMcpResourcesInputSchema,
367
+ }),
368
+ toolUseKind: 'readonly',
369
+ },
370
+ ReadMcpResource: {
371
+ ...tool({
372
+ description: 'Read a specific MCP resource by URI',
373
+ inputSchema: readMcpResourceInputSchema,
374
+ }),
375
+ toolUseKind: 'readonly',
376
+ },
377
+ ReadMcpResourceTool: {
378
+ ...tool({
379
+ description: 'Read a specific MCP resource by URI',
380
+ inputSchema: readMcpResourceInputSchema,
381
+ }),
382
+ toolUseKind: 'readonly',
383
+ },
384
+ ReadMcpResourceDirTool: {
385
+ ...tool({
386
+ description: 'List direct children of an MCP directory resource',
387
+ inputSchema: readMcpResourceInputSchema,
388
+ }),
389
+ toolUseKind: 'readonly',
390
+ },
391
+ RefreshMcpTools: {
392
+ ...tool({
393
+ description: 'Refresh tools from one or all connected MCP servers',
394
+ inputSchema: z.object({ server: z.string().optional() }),
395
+ }),
396
+ toolUseKind: 'readonly',
397
+ },
311
398
  ExitPlanMode: tool({
312
399
  description: 'Exit plan mode with optional permission approvals',
313
400
  inputSchema: z.looseObject({
@@ -321,6 +408,13 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
321
408
  .optional(),
322
409
  }),
323
410
  }),
411
+ EnterPlanMode: {
412
+ ...tool({
413
+ description: 'Enter plan mode',
414
+ inputSchema: z.object({}),
415
+ }),
416
+ toolUseKind: 'readonly',
417
+ },
324
418
  EnterWorktree: tool({
325
419
  description: 'Create or enter an isolated git worktree',
326
420
  inputSchema: z.object({
@@ -386,6 +480,271 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
386
480
  max_results: z.number().optional(),
387
481
  }),
388
482
  }),
483
+ Artifact: {
484
+ ...tool({
485
+ description: 'Publish or list claude.ai artifacts',
486
+ inputSchema: z.object({
487
+ action: z.enum(['publish', 'list']).optional(),
488
+ file_path: z.string().optional(),
489
+ favicon: z.string().optional(),
490
+ limit: z.number().optional(),
491
+ scope: z.enum(['mine', 'shared', 'all']).optional(),
492
+ title: z.string().optional(),
493
+ description: z.string().optional(),
494
+ label: z.string().optional(),
495
+ url: z.string().optional(),
496
+ force: z.boolean().optional(),
497
+ }),
498
+ }),
499
+ toolUseKind: 'edit',
500
+ },
501
+ CronCreate: {
502
+ ...tool({
503
+ description: 'Schedule a recurring or one-shot prompt',
504
+ inputSchema: z.object({
505
+ cron: z.string(),
506
+ prompt: z.string(),
507
+ recurring: z.boolean().optional(),
508
+ durable: z.boolean().optional(),
509
+ }),
510
+ }),
511
+ toolUseKind: 'edit',
512
+ },
513
+ CronDelete: {
514
+ ...tool({
515
+ description: 'Delete a scheduled prompt by id',
516
+ inputSchema: z.object({ id: z.string() }),
517
+ }),
518
+ toolUseKind: 'edit',
519
+ },
520
+ CronList: {
521
+ ...tool({
522
+ description: 'List scheduled prompts for the current session',
523
+ inputSchema: z.object({}),
524
+ }),
525
+ toolUseKind: 'readonly',
526
+ },
527
+ DesignSync: {
528
+ ...tool({
529
+ description: 'Read or update claude.ai Design projects',
530
+ inputSchema: z.object({
531
+ method: z.enum([
532
+ 'list_projects',
533
+ 'get_project',
534
+ 'list_files',
535
+ 'get_file',
536
+ 'finalize_plan',
537
+ 'write_files',
538
+ 'delete_files',
539
+ 'register_assets',
540
+ 'unregister_assets',
541
+ 'create_project',
542
+ 'report_validate',
543
+ ]),
544
+ projectId: z.string().optional(),
545
+ path: z.string().optional(),
546
+ writes: z.array(z.string()).max(256).optional(),
547
+ deletes: z.array(z.string()).max(256).optional(),
548
+ planId: z.string().optional(),
549
+ files: z
550
+ .array(
551
+ z.object({
552
+ path: z.string(),
553
+ localPath: z.string().optional(),
554
+ data: z.string().optional(),
555
+ encoding: z.literal('base64').optional(),
556
+ mimeType: z.string().optional(),
557
+ }),
558
+ )
559
+ .max(256)
560
+ .optional(),
561
+ paths: z.array(z.string()).max(256).optional(),
562
+ name: z.string().optional(),
563
+ assets: z
564
+ .array(
565
+ z.object({
566
+ name: z.string(),
567
+ path: z.string(),
568
+ subtitle: z.string().optional(),
569
+ viewport: z
570
+ .object({
571
+ width: z.number(),
572
+ height: z.number().optional(),
573
+ })
574
+ .optional(),
575
+ group: z.string().optional(),
576
+ }),
577
+ )
578
+ .max(256)
579
+ .optional(),
580
+ localDir: z.string().optional(),
581
+ counts: z
582
+ .object({
583
+ total: z.number(),
584
+ bad: z.number(),
585
+ thin: z.number(),
586
+ variantsIdentical: z.number(),
587
+ iterations: z.number(),
588
+ })
589
+ .optional(),
590
+ }),
591
+ }),
592
+ toolUseKind: 'edit',
593
+ },
594
+ LSP: {
595
+ ...tool({
596
+ description: 'Query a language server for code intelligence',
597
+ inputSchema: z.object({
598
+ operation: z.enum([
599
+ 'goToDefinition',
600
+ 'findReferences',
601
+ 'hover',
602
+ 'documentSymbol',
603
+ 'workspaceSymbol',
604
+ 'goToImplementation',
605
+ 'prepareCallHierarchy',
606
+ 'incomingCalls',
607
+ 'outgoingCalls',
608
+ ]),
609
+ filePath: z.string(),
610
+ line: z.number().int().positive(),
611
+ character: z.number().int().positive(),
612
+ query: z.string().optional(),
613
+ }),
614
+ }),
615
+ toolUseKind: 'readonly',
616
+ },
617
+ PowerShell: {
618
+ ...tool({
619
+ description: 'Execute a PowerShell command, optionally in background',
620
+ inputSchema: z.object({
621
+ command: z.string(),
622
+ timeout: z.number().optional(),
623
+ description: z.string().optional(),
624
+ run_in_background: z.boolean().optional(),
625
+ dangerouslyDisableSandbox: z.boolean().optional(),
626
+ }),
627
+ }),
628
+ toolUseKind: 'bash',
629
+ },
630
+ PushNotification: {
631
+ ...tool({
632
+ description: 'Send a notification for proactive or scheduled work',
633
+ inputSchema: z.object({
634
+ message: z.string(),
635
+ status: z.literal('proactive'),
636
+ }),
637
+ }),
638
+ toolUseKind: 'edit',
639
+ },
640
+ RemoteTrigger: {
641
+ ...tool({
642
+ description: 'List, manage, or run a claude.ai Routine trigger',
643
+ inputSchema: z.object({
644
+ action: z.enum(['list', 'get', 'create', 'update', 'run']),
645
+ trigger_id: z.string().optional(),
646
+ body: z.record(z.string(), z.unknown()).optional(),
647
+ }),
648
+ }),
649
+ toolUseKind: 'edit',
650
+ },
651
+ ReportFindings: {
652
+ ...tool({
653
+ description: 'Return verified code-review findings',
654
+ inputSchema: z.object({
655
+ level: z.enum(['low', 'medium', 'high', 'xhigh', 'max']).optional(),
656
+ findings: z
657
+ .array(
658
+ z.object({
659
+ file: z.string(),
660
+ line: z.number().optional(),
661
+ summary: z.string(),
662
+ short_summary: z.string().optional(),
663
+ failure_scenario: z.string(),
664
+ category: z.string().optional(),
665
+ verdict: z.enum(['CONFIRMED', 'PLAUSIBLE']).optional(),
666
+ outcome: z
667
+ .enum(['fixed', 'skipped', 'no_change_needed'])
668
+ .optional(),
669
+ }),
670
+ )
671
+ .max(32),
672
+ }),
673
+ }),
674
+ toolUseKind: 'readonly',
675
+ },
676
+ ScheduleWakeup: {
677
+ ...tool({
678
+ description: 'Schedule or stop the next iteration of a dynamic loop',
679
+ inputSchema: z.object({
680
+ delaySeconds: z.number().optional(),
681
+ reason: z.string().optional(),
682
+ prompt: z.string().optional(),
683
+ stop: z.boolean().optional(),
684
+ }),
685
+ }),
686
+ toolUseKind: 'edit',
687
+ },
688
+ SendMessage: {
689
+ ...tool({
690
+ description: 'Send a plain-text or protocol message to another agent',
691
+ inputSchema: z.object({
692
+ to: z.string(),
693
+ summary: z.string().max(200).optional(),
694
+ message: z.union([z.string(), structuredTeamMessageSchema]),
695
+ }),
696
+ }),
697
+ toolUseKind: 'edit',
698
+ },
699
+ SendUserFile: {
700
+ ...tool({
701
+ description: 'Send one or more files to the user',
702
+ inputSchema: z.object({
703
+ files: z.union([z.string(), z.array(z.string()).min(1)]),
704
+ caption: z.string().optional(),
705
+ status: z.enum(['normal', 'proactive']),
706
+ display: z.enum(['render', 'attach']).optional(),
707
+ }),
708
+ }),
709
+ toolUseKind: 'readonly',
710
+ },
711
+ ShareOnboardingGuide: {
712
+ ...tool({
713
+ description: 'Create, update, inspect, or delete an onboarding guide',
714
+ inputSchema: z.object({
715
+ mode: z.enum(['check', 'update', 'create', 'delete']).optional(),
716
+ short_code: z
717
+ .string()
718
+ .regex(/^[A-Za-z0-9_-]{1,64}$/)
719
+ .optional(),
720
+ }),
721
+ }),
722
+ toolUseKind: 'edit',
723
+ },
724
+ WaitForMcpServers: {
725
+ ...tool({
726
+ description: 'Wait for MCP servers that are still connecting',
727
+ inputSchema: z.object({
728
+ servers: z.array(z.string()).optional(),
729
+ }),
730
+ }),
731
+ toolUseKind: 'readonly',
732
+ },
733
+ Workflow: {
734
+ ...tool({
735
+ description: 'Run or resume a dynamic multi-agent workflow',
736
+ inputSchema: z.object({
737
+ script: z.string().optional(),
738
+ name: z.string().optional(),
739
+ description: z.string().optional(),
740
+ title: z.string().optional(),
741
+ args: z.record(z.string(), z.unknown()).optional(),
742
+ scriptPath: z.string().optional(),
743
+ resumeFromRunId: z.string().optional(),
744
+ }),
745
+ }),
746
+ toolUseKind: 'edit',
747
+ },
389
748
  } as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;
390
749
 
391
750
  /*
@@ -432,6 +791,14 @@ type ClaudeCodeBridgeCoords = z.infer<typeof claudeCodeBridgeCoordsSchema>;
432
791
  export function createClaudeCode(
433
792
  settings: ClaudeCodeHarnessSettings = {},
434
793
  ): HarnessV1<typeof CLAUDE_CODE_BUILTIN_TOOLS> {
794
+ if (
795
+ settings.mcpServers != null &&
796
+ Object.prototype.hasOwnProperty.call(settings.mcpServers, 'harness-tools')
797
+ ) {
798
+ throw new Error(
799
+ 'Claude Code MCP server name "harness-tools" is reserved for HarnessAgent tools.',
800
+ );
801
+ }
435
802
  let cachedBootstrap: HarnessV1Bootstrap | undefined;
436
803
  const thinking = settings.thinking ?? {
437
804
  type: 'adaptive',
@@ -549,6 +916,7 @@ export function createClaudeCode(
549
916
  proc: undefined,
550
917
  model: settings.model,
551
918
  maxTurns: settings.maxTurns,
919
+ env: settings.env,
552
920
  thinking,
553
921
  isResume: true,
554
922
  continueOnFirstPrompt: false,
@@ -560,6 +928,7 @@ export function createClaudeCode(
560
928
  permissionMode: startOpts.permissionMode,
561
929
  builtinToolFiltering: startOpts.builtinToolFiltering,
562
930
  skills: startOpts.skills ?? [],
931
+ mcpServers: settings.mcpServers,
563
932
  });
564
933
  } catch {
565
934
  // Bridge no longer reachable — recover by respawning below.
@@ -598,7 +967,10 @@ export function createClaudeCode(
598
967
  })
599
968
  : undefined;
600
969
  const port = resolveBridgePort(sandboxSession, settings.port);
601
- const token = randomBytes(32).toString('hex');
970
+ const token =
971
+ settings.mintBridgeToken == null
972
+ ? randomBytes(32).toString('hex')
973
+ : settings.mintBridgeToken(sandboxId);
602
974
  const authEnv = resolveClaudeCodeEnv(settings.auth);
603
975
  const env = {
604
976
  ...authEnv,
@@ -719,6 +1091,7 @@ export function createClaudeCode(
719
1091
  proc,
720
1092
  model: settings.model,
721
1093
  maxTurns: settings.maxTurns,
1094
+ env: settings.env,
722
1095
  thinking,
723
1096
  isResume: respawnStrategy !== undefined,
724
1097
  continueOnFirstPrompt: respawnStrategy !== undefined,
@@ -730,6 +1103,7 @@ export function createClaudeCode(
730
1103
  permissionMode: startOpts.permissionMode,
731
1104
  builtinToolFiltering: startOpts.builtinToolFiltering,
732
1105
  skills: startOpts.skills ?? [],
1106
+ mcpServers: settings.mcpServers,
733
1107
  });
734
1108
  },
735
1109
  };
@@ -961,6 +1335,7 @@ function createSession({
961
1335
  proc,
962
1336
  model,
963
1337
  maxTurns,
1338
+ env,
964
1339
  thinking,
965
1340
  isResume,
966
1341
  continueOnFirstPrompt,
@@ -972,6 +1347,7 @@ function createSession({
972
1347
  permissionMode,
973
1348
  builtinToolFiltering,
974
1349
  skills,
1350
+ mcpServers,
975
1351
  }: {
976
1352
  sessionId: string;
977
1353
  channel: ClaudeCodeChannel;
@@ -979,6 +1355,7 @@ function createSession({
979
1355
  proc: Experimental_SandboxProcess | undefined;
980
1356
  model: string | undefined;
981
1357
  maxTurns: number | undefined;
1358
+ env: Readonly<Record<string, string>> | undefined;
982
1359
  thinking: ClaudeCodeThinkingConfig;
983
1360
  isResume: boolean;
984
1361
  continueOnFirstPrompt: boolean;
@@ -990,6 +1367,7 @@ function createSession({
990
1367
  permissionMode: HarnessV1PermissionMode | undefined;
991
1368
  builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
992
1369
  skills: ReadonlyArray<HarnessV1Skill>;
1370
+ mcpServers: Record<string, unknown> | undefined;
993
1371
  }): HarnessV1Session {
994
1372
  let stopped = false;
995
1373
  let stopPromise: Promise<void> | undefined;
@@ -1169,10 +1547,12 @@ function createSession({
1169
1547
  })),
1170
1548
  model,
1171
1549
  maxTurns,
1550
+ ...(env !== undefined ? { env } : {}),
1172
1551
  thinking,
1173
1552
  ...(skills.length > 0
1174
1553
  ? { skills: skills.map(skill => skill.name) }
1175
1554
  : {}),
1555
+ ...(mcpServers == null ? {} : { mcpServers }),
1176
1556
  ...(permissionMode ? { permissionMode } : {}),
1177
1557
  ...(builtinToolFiltering ? { builtinToolFiltering } : {}),
1178
1558
  ...(debug ? { debug } : {}),
@@ -1221,10 +1601,12 @@ function createSession({
1221
1601
  })),
1222
1602
  model,
1223
1603
  maxTurns,
1604
+ ...(env !== undefined ? { env } : {}),
1224
1605
  thinking,
1225
1606
  ...(skills.length > 0
1226
1607
  ? { skills: skills.map(skill => skill.name) }
1227
1608
  : {}),
1609
+ ...(mcpServers == null ? {} : { mcpServers }),
1228
1610
  ...(permissionMode ? { permissionMode } : {}),
1229
1611
  ...(builtinToolFiltering ? { builtinToolFiltering } : {}),
1230
1612
  ...(debug ? { debug } : {}),