@harness-fe/mcp-server 3.4.0 → 3.4.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.
Files changed (3) hide show
  1. package/dist/mcp.js +77 -71
  2. package/package.json +3 -3
  3. package/src/mcp.ts +93 -87
package/dist/mcp.js CHANGED
@@ -452,77 +452,83 @@ function registerTools(server, bridge) {
452
452
  return ok(out);
453
453
  });
454
454
  // ─── tasks.* tools (user annotations submitted from page) ─────────────
455
- const taskStatusEnum = z.enum(['pending', 'claimed', 'resolved', 'all']);
456
- server.registerTool(COMMAND.TASKS_PENDING, {
457
- description: 'List user-submitted annotation tasks. Default `status="pending"`. Returns id/question/selector/url — call tasks.claim to fetch full element payload.',
458
- inputSchema: {
459
- status: taskStatusEnum.optional(),
460
- limit: z.number().int().positive().optional(),
461
- },
462
- }, async ({ status, limit }) => {
463
- const tasks = await bridge.listTasks({ status: status ?? 'pending', limit });
464
- const summary = tasks.map((t) => ({
465
- id: t.id,
466
- status: t.status,
467
- question: t.question,
468
- selector: t.selector,
469
- url: t.url,
470
- tabId: t.tabId,
471
- createdAt: t.createdAt,
472
- claimedAt: t.claimedAt,
473
- resolvedAt: t.resolvedAt,
474
- note: t.note,
475
- }));
476
- return ok({ count: summary.length, tasks: summary });
477
- });
478
- server.registerTool(COMMAND.TASKS_CLAIM, {
479
- description: 'Claim a task by id. Marks it claimed, returns full payload (selector + element outerHTML + rect).',
480
- inputSchema: {
481
- taskId: z.string(),
482
- },
483
- }, async ({ taskId }) => {
484
- const task = await bridge.claimTask(taskId);
485
- if (!task) {
486
- throw new Error(`tasks.claim: no task with id "${taskId}"`);
487
- }
488
- return ok(task);
489
- });
490
- server.registerTool(COMMAND.TASKS_RESOLVE, {
491
- description: 'Mark a task as resolved with an optional note. Use after addressing the user request.',
492
- inputSchema: {
493
- taskId: z.string(),
494
- note: z.string().optional(),
495
- },
496
- }, async ({ taskId, note }) => {
497
- const task = await bridge.resolveTask(taskId, note);
498
- if (!task) {
499
- throw new Error(`tasks.resolve: no task with id "${taskId}"`);
500
- }
501
- return ok({ ok: true, task });
502
- });
503
- server.registerTool('tasks.get_attachment', {
504
- description: 'Return a task screenshot attachment as a vision-ready image block. ' +
505
- 'Call after tasks.claim when the task summary includes an attachment pointer. ' +
506
- 'Compatible with Claude vision and GPT-4V.',
507
- inputSchema: {
508
- taskId: z.string().describe('Task id (from tasks.pending or tasks.claim).'),
509
- attachmentId: z.string().describe('Attachment id (from task.attachments[].id).'),
510
- },
511
- }, async ({ taskId, attachmentId }) => {
512
- const base64 = await bridge.getTaskAttachmentData(taskId, attachmentId);
513
- if (!base64) {
514
- throw new Error(`tasks.get_attachment: attachment not found (taskId=${taskId}, attachmentId=${attachmentId})`);
515
- }
516
- return {
517
- content: [
518
- {
519
- type: 'image',
520
- mimeType: 'image/png',
521
- data: base64,
522
- },
523
- ],
524
- };
525
- });
455
+ // TODO: temporarily hidden re-enable when the report feature is ready.
456
+ // To re-enable: remove the `if (false)` wrapper below and restore the block.
457
+ /* eslint-disable no-constant-condition */
458
+ if (false) {
459
+ const taskStatusEnum = z.enum(['pending', 'claimed', 'resolved', 'all']);
460
+ server.registerTool(COMMAND.TASKS_PENDING, {
461
+ description: 'List user-submitted annotation tasks. Default `status="pending"`. Returns id/question/selector/url — call tasks.claim to fetch full element payload.',
462
+ inputSchema: {
463
+ status: taskStatusEnum.optional(),
464
+ limit: z.number().int().positive().optional(),
465
+ },
466
+ }, async ({ status, limit }) => {
467
+ const tasks = await bridge.listTasks({ status: status ?? 'pending', limit });
468
+ const summary = tasks.map((t) => ({
469
+ id: t.id,
470
+ status: t.status,
471
+ question: t.question,
472
+ selector: t.selector,
473
+ url: t.url,
474
+ tabId: t.tabId,
475
+ createdAt: t.createdAt,
476
+ claimedAt: t.claimedAt,
477
+ resolvedAt: t.resolvedAt,
478
+ note: t.note,
479
+ }));
480
+ return ok({ count: summary.length, tasks: summary });
481
+ });
482
+ server.registerTool(COMMAND.TASKS_CLAIM, {
483
+ description: 'Claim a task by id. Marks it claimed, returns full payload (selector + element outerHTML + rect).',
484
+ inputSchema: {
485
+ taskId: z.string(),
486
+ },
487
+ }, async ({ taskId }) => {
488
+ const task = await bridge.claimTask(taskId);
489
+ if (!task) {
490
+ throw new Error(`tasks.claim: no task with id "${taskId}"`);
491
+ }
492
+ return ok(task);
493
+ });
494
+ server.registerTool(COMMAND.TASKS_RESOLVE, {
495
+ description: 'Mark a task as resolved with an optional note. Use after addressing the user request.',
496
+ inputSchema: {
497
+ taskId: z.string(),
498
+ note: z.string().optional(),
499
+ },
500
+ }, async ({ taskId, note }) => {
501
+ const task = await bridge.resolveTask(taskId, note);
502
+ if (!task) {
503
+ throw new Error(`tasks.resolve: no task with id "${taskId}"`);
504
+ }
505
+ return ok({ ok: true, task });
506
+ });
507
+ server.registerTool('tasks.get_attachment', {
508
+ description: 'Return a task screenshot attachment as a vision-ready image block. ' +
509
+ 'Call after tasks.claim when the task summary includes an attachment pointer. ' +
510
+ 'Compatible with Claude vision and GPT-4V.',
511
+ inputSchema: {
512
+ taskId: z.string().describe('Task id (from tasks.pending or tasks.claim).'),
513
+ attachmentId: z.string().describe('Attachment id (from task.attachments[].id).'),
514
+ },
515
+ }, async ({ taskId, attachmentId }) => {
516
+ const base64 = await bridge.getTaskAttachmentData(taskId, attachmentId);
517
+ if (!base64) {
518
+ throw new Error(`tasks.get_attachment: attachment not found (taskId=${taskId}, attachmentId=${attachmentId})`);
519
+ }
520
+ return {
521
+ content: [
522
+ {
523
+ type: 'image',
524
+ mimeType: 'image/png',
525
+ data: base64,
526
+ },
527
+ ],
528
+ };
529
+ });
530
+ }
531
+ /* eslint-enable no-constant-condition */
526
532
  }
527
533
  // ─── Experimental tools (gated by HARNESS_FE_EXPERIMENTAL) ────────────────────
528
534
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@harness-fe/mcp-server",
3
- "version": "3.4.0",
3
+ "version": "3.4.1",
4
4
  "description": "Unified MCP daemon: stdio MCP for AI agents + WS bridge for Vite plugin and runtime client.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -38,8 +38,8 @@
38
38
  "rrweb-player": "1.0.0-alpha.4",
39
39
  "ws": "^8.18.0",
40
40
  "zod": "^4.4.3",
41
- "@harness-fe/protocol": "3.2.0",
42
- "@harness-fe/dashboard-ui": "0.2.0"
41
+ "@harness-fe/dashboard-ui": "0.2.0",
42
+ "@harness-fe/protocol": "3.2.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/ws": "^8.5.10",
package/src/mcp.ts CHANGED
@@ -729,102 +729,108 @@ function registerTools(server: McpServer, bridge: IBridge): void {
729
729
  );
730
730
 
731
731
  // ─── tasks.* tools (user annotations submitted from page) ─────────────
732
+ // TODO: temporarily hidden — re-enable when the report feature is ready.
733
+ // To re-enable: remove the `if (false)` wrapper below and restore the block.
732
734
 
733
- const taskStatusEnum = z.enum(['pending', 'claimed', 'resolved', 'all']);
735
+ /* eslint-disable no-constant-condition */
736
+ if (false) {
737
+ const taskStatusEnum = z.enum(['pending', 'claimed', 'resolved', 'all']);
734
738
 
735
- server.registerTool(
736
- COMMAND.TASKS_PENDING,
737
- {
738
- description:
739
- 'List user-submitted annotation tasks. Default `status="pending"`. Returns id/question/selector/url — call tasks.claim to fetch full element payload.',
740
- inputSchema: {
741
- status: taskStatusEnum.optional(),
742
- limit: z.number().int().positive().optional(),
739
+ server.registerTool(
740
+ COMMAND.TASKS_PENDING,
741
+ {
742
+ description:
743
+ 'List user-submitted annotation tasks. Default `status="pending"`. Returns id/question/selector/url — call tasks.claim to fetch full element payload.',
744
+ inputSchema: {
745
+ status: taskStatusEnum.optional(),
746
+ limit: z.number().int().positive().optional(),
747
+ },
743
748
  },
744
- },
745
- async ({ status, limit }) => {
746
- const tasks = await bridge.listTasks({ status: status ?? 'pending', limit });
747
- const summary = tasks.map((t) => ({
748
- id: t.id,
749
- status: t.status,
750
- question: t.question,
751
- selector: t.selector,
752
- url: t.url,
753
- tabId: t.tabId,
754
- createdAt: t.createdAt,
755
- claimedAt: t.claimedAt,
756
- resolvedAt: t.resolvedAt,
757
- note: t.note,
758
- }));
759
- return ok({ count: summary.length, tasks: summary });
760
- },
761
- );
749
+ async ({ status, limit }) => {
750
+ const tasks = await bridge.listTasks({ status: status ?? 'pending', limit });
751
+ const summary = tasks.map((t) => ({
752
+ id: t.id,
753
+ status: t.status,
754
+ question: t.question,
755
+ selector: t.selector,
756
+ url: t.url,
757
+ tabId: t.tabId,
758
+ createdAt: t.createdAt,
759
+ claimedAt: t.claimedAt,
760
+ resolvedAt: t.resolvedAt,
761
+ note: t.note,
762
+ }));
763
+ return ok({ count: summary.length, tasks: summary });
764
+ },
765
+ );
762
766
 
763
- server.registerTool(
764
- COMMAND.TASKS_CLAIM,
765
- {
766
- description:
767
- 'Claim a task by id. Marks it claimed, returns full payload (selector + element outerHTML + rect).',
768
- inputSchema: {
769
- taskId: z.string(),
767
+ server.registerTool(
768
+ COMMAND.TASKS_CLAIM,
769
+ {
770
+ description:
771
+ 'Claim a task by id. Marks it claimed, returns full payload (selector + element outerHTML + rect).',
772
+ inputSchema: {
773
+ taskId: z.string(),
774
+ },
770
775
  },
771
- },
772
- async ({ taskId }) => {
773
- const task = await bridge.claimTask(taskId);
774
- if (!task) {
775
- throw new Error(`tasks.claim: no task with id "${taskId}"`);
776
- }
777
- return ok(task);
778
- },
779
- );
776
+ async ({ taskId }) => {
777
+ const task = await bridge.claimTask(taskId);
778
+ if (!task) {
779
+ throw new Error(`tasks.claim: no task with id "${taskId}"`);
780
+ }
781
+ return ok(task);
782
+ },
783
+ );
780
784
 
781
- server.registerTool(
782
- COMMAND.TASKS_RESOLVE,
783
- {
784
- description:
785
- 'Mark a task as resolved with an optional note. Use after addressing the user request.',
786
- inputSchema: {
787
- taskId: z.string(),
788
- note: z.string().optional(),
785
+ server.registerTool(
786
+ COMMAND.TASKS_RESOLVE,
787
+ {
788
+ description:
789
+ 'Mark a task as resolved with an optional note. Use after addressing the user request.',
790
+ inputSchema: {
791
+ taskId: z.string(),
792
+ note: z.string().optional(),
793
+ },
789
794
  },
790
- },
791
- async ({ taskId, note }) => {
792
- const task = await bridge.resolveTask(taskId, note);
793
- if (!task) {
794
- throw new Error(`tasks.resolve: no task with id "${taskId}"`);
795
- }
796
- return ok({ ok: true, task });
797
- },
798
- );
795
+ async ({ taskId, note }) => {
796
+ const task = await bridge.resolveTask(taskId, note);
797
+ if (!task) {
798
+ throw new Error(`tasks.resolve: no task with id "${taskId}"`);
799
+ }
800
+ return ok({ ok: true, task });
801
+ },
802
+ );
799
803
 
800
- server.registerTool(
801
- 'tasks.get_attachment',
802
- {
803
- description:
804
- 'Return a task screenshot attachment as a vision-ready image block. ' +
805
- 'Call after tasks.claim when the task summary includes an attachment pointer. ' +
806
- 'Compatible with Claude vision and GPT-4V.',
807
- inputSchema: {
808
- taskId: z.string().describe('Task id (from tasks.pending or tasks.claim).'),
809
- attachmentId: z.string().describe('Attachment id (from task.attachments[].id).'),
804
+ server.registerTool(
805
+ 'tasks.get_attachment',
806
+ {
807
+ description:
808
+ 'Return a task screenshot attachment as a vision-ready image block. ' +
809
+ 'Call after tasks.claim when the task summary includes an attachment pointer. ' +
810
+ 'Compatible with Claude vision and GPT-4V.',
811
+ inputSchema: {
812
+ taskId: z.string().describe('Task id (from tasks.pending or tasks.claim).'),
813
+ attachmentId: z.string().describe('Attachment id (from task.attachments[].id).'),
814
+ },
810
815
  },
811
- },
812
- async ({ taskId, attachmentId }) => {
813
- const base64 = await bridge.getTaskAttachmentData(taskId, attachmentId);
814
- if (!base64) {
815
- throw new Error(`tasks.get_attachment: attachment not found (taskId=${taskId}, attachmentId=${attachmentId})`);
816
- }
817
- return {
818
- content: [
819
- {
820
- type: 'image' as const,
821
- mimeType: 'image/png' as const,
822
- data: base64,
823
- },
824
- ],
825
- };
826
- },
827
- );
816
+ async ({ taskId, attachmentId }) => {
817
+ const base64 = await bridge.getTaskAttachmentData(taskId, attachmentId);
818
+ if (!base64) {
819
+ throw new Error(`tasks.get_attachment: attachment not found (taskId=${taskId}, attachmentId=${attachmentId})`);
820
+ }
821
+ return {
822
+ content: [
823
+ {
824
+ type: 'image' as const,
825
+ mimeType: 'image/png' as const,
826
+ data: base64,
827
+ },
828
+ ],
829
+ };
830
+ },
831
+ );
832
+ }
833
+ /* eslint-enable no-constant-condition */
828
834
  }
829
835
 
830
836
  // ─── Experimental tools (gated by HARNESS_FE_EXPERIMENTAL) ────────────────────