@ai-sdk/harness-claude-code 1.0.66 → 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.
@@ -92,19 +97,53 @@ export type ClaudeCodeHarnessSettings = {
92
97
  readonly port?: number;
93
98
  /** Maximum milliseconds to wait for the bridge to advertise its port. Defaults to 120000. */
94
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;
95
105
  };
96
106
 
97
107
  /*
98
108
  * Every native tool the Claude Code CLI can invoke, declared as a `ToolSet`
99
109
  * keyed by what the bridge emits as `toolName` on the wire
100
- * (`commonName ?? nativeName`). Schemas transcribed from
101
- * `@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.
102
113
  *
103
114
  * `MCP` (the generic proxy tool inside the Claude Code SDK) is intentionally
104
115
  * omitted — the bridge filters out `mcp__harness-tools__*` tool names before
105
116
  * emitting them, and other MCP invocations come through with their own
106
117
  * server-tool names rather than the literal `'Mcp'` token.
107
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
+
108
147
  const CLAUDE_CODE_BUILTIN_TOOLS = {
109
148
  read: commonTool('read', {
110
149
  nativeName: 'Read',
@@ -296,23 +335,66 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
296
335
  timeout: z.number(),
297
336
  }),
298
337
  }),
299
- Monitor: tool({
300
- description: 'Run and monitor a shell command',
301
- inputSchema: z.object({
302
- command: z.string(),
303
- description: z.string().optional(),
304
- timeout_ms: z.number().optional(),
305
- 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
+ }),
306
353
  }),
307
- }),
308
- ListMcpResources: tool({
309
- description: 'List resources available from MCP servers',
310
- inputSchema: z.object({ server: z.string().optional() }),
311
- }),
312
- ReadMcpResource: tool({
313
- description: 'Read a specific MCP resource by URI',
314
- inputSchema: z.object({ server: z.string(), uri: z.string() }),
315
- }),
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
+ },
316
398
  ExitPlanMode: tool({
317
399
  description: 'Exit plan mode with optional permission approvals',
318
400
  inputSchema: z.looseObject({
@@ -326,6 +408,13 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
326
408
  .optional(),
327
409
  }),
328
410
  }),
411
+ EnterPlanMode: {
412
+ ...tool({
413
+ description: 'Enter plan mode',
414
+ inputSchema: z.object({}),
415
+ }),
416
+ toolUseKind: 'readonly',
417
+ },
329
418
  EnterWorktree: tool({
330
419
  description: 'Create or enter an isolated git worktree',
331
420
  inputSchema: z.object({
@@ -391,6 +480,271 @@ const CLAUDE_CODE_BUILTIN_TOOLS = {
391
480
  max_results: z.number().optional(),
392
481
  }),
393
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
+ },
394
748
  } as const satisfies Record<string, HarnessV1BuiltinTool<any, any>>;
395
749
 
396
750
  /*
@@ -437,6 +791,14 @@ type ClaudeCodeBridgeCoords = z.infer<typeof claudeCodeBridgeCoordsSchema>;
437
791
  export function createClaudeCode(
438
792
  settings: ClaudeCodeHarnessSettings = {},
439
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
+ }
440
802
  let cachedBootstrap: HarnessV1Bootstrap | undefined;
441
803
  const thinking = settings.thinking ?? {
442
804
  type: 'adaptive',
@@ -566,6 +928,7 @@ export function createClaudeCode(
566
928
  permissionMode: startOpts.permissionMode,
567
929
  builtinToolFiltering: startOpts.builtinToolFiltering,
568
930
  skills: startOpts.skills ?? [],
931
+ mcpServers: settings.mcpServers,
569
932
  });
570
933
  } catch {
571
934
  // Bridge no longer reachable — recover by respawning below.
@@ -604,7 +967,10 @@ export function createClaudeCode(
604
967
  })
605
968
  : undefined;
606
969
  const port = resolveBridgePort(sandboxSession, settings.port);
607
- const token = randomBytes(32).toString('hex');
970
+ const token =
971
+ settings.mintBridgeToken == null
972
+ ? randomBytes(32).toString('hex')
973
+ : settings.mintBridgeToken(sandboxId);
608
974
  const authEnv = resolveClaudeCodeEnv(settings.auth);
609
975
  const env = {
610
976
  ...authEnv,
@@ -737,6 +1103,7 @@ export function createClaudeCode(
737
1103
  permissionMode: startOpts.permissionMode,
738
1104
  builtinToolFiltering: startOpts.builtinToolFiltering,
739
1105
  skills: startOpts.skills ?? [],
1106
+ mcpServers: settings.mcpServers,
740
1107
  });
741
1108
  },
742
1109
  };
@@ -980,6 +1347,7 @@ function createSession({
980
1347
  permissionMode,
981
1348
  builtinToolFiltering,
982
1349
  skills,
1350
+ mcpServers,
983
1351
  }: {
984
1352
  sessionId: string;
985
1353
  channel: ClaudeCodeChannel;
@@ -999,6 +1367,7 @@ function createSession({
999
1367
  permissionMode: HarnessV1PermissionMode | undefined;
1000
1368
  builtinToolFiltering: HarnessV1BuiltinToolFiltering | undefined;
1001
1369
  skills: ReadonlyArray<HarnessV1Skill>;
1370
+ mcpServers: Record<string, unknown> | undefined;
1002
1371
  }): HarnessV1Session {
1003
1372
  let stopped = false;
1004
1373
  let stopPromise: Promise<void> | undefined;
@@ -1183,6 +1552,7 @@ function createSession({
1183
1552
  ...(skills.length > 0
1184
1553
  ? { skills: skills.map(skill => skill.name) }
1185
1554
  : {}),
1555
+ ...(mcpServers == null ? {} : { mcpServers }),
1186
1556
  ...(permissionMode ? { permissionMode } : {}),
1187
1557
  ...(builtinToolFiltering ? { builtinToolFiltering } : {}),
1188
1558
  ...(debug ? { debug } : {}),
@@ -1236,6 +1606,7 @@ function createSession({
1236
1606
  ...(skills.length > 0
1237
1607
  ? { skills: skills.map(skill => skill.name) }
1238
1608
  : {}),
1609
+ ...(mcpServers == null ? {} : { mcpServers }),
1239
1610
  ...(permissionMode ? { permissionMode } : {}),
1240
1611
  ...(builtinToolFiltering ? { builtinToolFiltering } : {}),
1241
1612
  ...(debug ? { debug } : {}),