@hapticpaper/mcp-server 1.0.17 → 1.0.19

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.
@@ -2,6 +2,7 @@ import { z } from "zod";
2
2
  import { requireScopes } from "../auth/access.js";
3
3
  import { HAPTICPAPER_WIDGET_URI } from "../constants/widget.js";
4
4
  import crypto from 'node:crypto';
5
+ // --- Shared Helpers ---
5
6
  function stableSessionId(prefix, value) {
6
7
  const raw = value ?? 'unknown';
7
8
  const hash = crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
@@ -9,10 +10,7 @@ function stableSessionId(prefix, value) {
9
10
  }
10
11
  function oauthSecuritySchemes(scopes) {
11
12
  return [
12
- {
13
- type: 'oauth2',
14
- scopes,
15
- },
13
+ { type: 'oauth2', scopes },
16
14
  ];
17
15
  }
18
16
  function toolDescriptorMeta(invoking, invoked, scopes) {
@@ -31,6 +29,7 @@ function toolInvocationMeta(invoking, invoked, widgetSessionId) {
31
29
  'openai/widgetSessionId': widgetSessionId,
32
30
  };
33
31
  }
32
+ // --- Schema Definitions ---
34
33
  const SearchWorkersSchema = z.object({
35
34
  taskDescription: z.string().describe("Description of what needs to be done"),
36
35
  location: z.object({
@@ -39,82 +38,63 @@ const SearchWorkersSchema = z.object({
39
38
  }).optional(),
40
39
  skills: z.array(z.string()).optional()
41
40
  });
41
+ const WorkersToolSchema = z.object({
42
+ action: z.enum(['search', 'get_profile']).describe("Action to perform on the workers resource"),
43
+ // Search params
44
+ taskDescription: SearchWorkersSchema.shape.taskDescription.optional(),
45
+ location: SearchWorkersSchema.shape.location.optional(),
46
+ skills: SearchWorkersSchema.shape.skills.optional(),
47
+ // Get params
48
+ workerId: z.string().uuid().optional().describe("Worker ID to retrieve profile"),
49
+ }).describe("Find and view worker profiles. Actions: search, get_profile.");
42
50
  export function registerWorkerTools(server, client) {
43
- const searchWorkersInvoking = 'Searching nearby workers';
44
- const searchWorkersInvoked = 'Worker results ready';
45
- const searchWorkersHandler = async (args, extra) => {
51
+ const workersHandler = async (args, extra) => {
46
52
  try {
47
- const auth = requireScopes(extra, ['workers:read']);
48
- const result = await client.searchWorkers(args, auth?.token);
49
- const widgetSessionId = stableSessionId('workers', JSON.stringify({ userId: auth?.userId ?? auth?.clientId, args }));
50
- if (!result.workers || result.workers.length === 0) {
51
- return {
52
- structuredContent: { workers: [], suggestedBudget: result?.suggestedBudget },
53
- content: [{ type: 'text', text: 'No workers found matching criteria.' }],
54
- _meta: {
55
- ...toolInvocationMeta(searchWorkersInvoking, searchWorkersInvoked, widgetSessionId),
56
- result,
57
- },
58
- };
53
+ switch (args.action) {
54
+ case 'search': {
55
+ const auth = requireScopes(extra, ['workers:read']);
56
+ if (!args.taskDescription)
57
+ throw new Error("Missing taskDescription for search action");
58
+ const searchArgs = {
59
+ taskDescription: args.taskDescription,
60
+ location: args.location,
61
+ skills: args.skills
62
+ };
63
+ const result = await client.searchWorkers(searchArgs, auth?.token);
64
+ const widgetSessionId = stableSessionId('workers', JSON.stringify({ userId: auth?.userId ?? auth?.clientId, args: searchArgs }));
65
+ if (!result.workers || result.workers.length === 0) {
66
+ return {
67
+ structuredContent: { workers: [], suggestedBudget: result?.suggestedBudget },
68
+ content: [{ type: 'text', text: 'No workers found matching criteria.' }],
69
+ _meta: { ...toolInvocationMeta('Searching workers', 'Worker results ready', widgetSessionId), result }
70
+ };
71
+ }
72
+ return {
73
+ structuredContent: {
74
+ workers: result.workers.map((w) => ({
75
+ id: w.id, name: w.name, rating: w.rating, distanceMiles: w.distanceMiles, estimatedArrival: w.estimatedArrival,
76
+ })),
77
+ suggestedBudget: result.suggestedBudget,
78
+ },
79
+ content: [{ type: 'text', text: `Found ${result.workers.length} workers. Suggested budget: $${result.suggestedBudget}` }],
80
+ _meta: { ...toolInvocationMeta('Searching workers', 'Worker results ready', widgetSessionId), result }
81
+ };
82
+ }
83
+ case 'get_profile': {
84
+ const auth = requireScopes(extra, ['workers:read']);
85
+ if (!args.workerId)
86
+ throw new Error("Missing workerId for get_profile action");
87
+ const worker = await client.getWorkerProfile(args.workerId, auth?.token);
88
+ const widgetSessionId = `worker:${args.workerId}`;
89
+ return {
90
+ structuredContent: { worker },
91
+ content: [{ type: 'text', text: `Worker profile loaded: ${worker?.id ?? args.workerId}` }],
92
+ _meta: { ...toolInvocationMeta('Loading profile', 'Worker profile ready', widgetSessionId), worker }
93
+ };
94
+ }
95
+ default:
96
+ throw new Error(`Unknown action: ${args.action}`);
59
97
  }
60
- return {
61
- structuredContent: {
62
- workers: result.workers.map((w) => ({
63
- id: w.id,
64
- name: w.name,
65
- rating: w.rating,
66
- distanceMiles: w.distanceMiles,
67
- estimatedArrival: w.estimatedArrival,
68
- })),
69
- suggestedBudget: result.suggestedBudget,
70
- },
71
- content: [
72
- {
73
- type: 'text',
74
- text: `Found ${result.workers.length} workers. Suggested budget: $${result.suggestedBudget}`,
75
- },
76
- ],
77
- _meta: {
78
- ...toolInvocationMeta(searchWorkersInvoking, searchWorkersInvoked, widgetSessionId),
79
- result,
80
- },
81
- };
82
- }
83
- catch (err) {
84
- return {
85
- content: [{ type: 'text', text: `Error: ${err.message}` }],
86
- isError: true,
87
- };
88
- }
89
- };
90
- for (const toolName of ['search_workers', 'workers_search']) {
91
- server.registerTool(toolName, {
92
- title: 'Search workers',
93
- description: 'Search for available workers. Returns anonymized profiles.',
94
- inputSchema: SearchWorkersSchema,
95
- annotations: {
96
- readOnlyHint: true,
97
- openWorldHint: false,
98
- destructiveHint: false,
99
- },
100
- _meta: toolDescriptorMeta(searchWorkersInvoking, searchWorkersInvoked, ['workers:read']),
101
- }, searchWorkersHandler);
102
- }
103
- const getWorkerInvoking = 'Loading worker profile';
104
- const getWorkerInvoked = 'Worker profile ready';
105
- const getWorkerProfileHandler = async (args, extra) => {
106
- try {
107
- const auth = requireScopes(extra, ['workers:read']);
108
- const worker = await client.getWorkerProfile(args.workerId, auth?.token);
109
- const widgetSessionId = `worker:${args.workerId}`;
110
- return {
111
- structuredContent: { worker },
112
- content: [{ type: 'text', text: `Worker profile loaded: ${worker?.id ?? args.workerId}` }],
113
- _meta: {
114
- ...toolInvocationMeta(getWorkerInvoking, getWorkerInvoked, widgetSessionId),
115
- worker,
116
- },
117
- };
118
98
  }
119
99
  catch (err) {
120
100
  return {
@@ -123,17 +103,10 @@ export function registerWorkerTools(server, client) {
123
103
  };
124
104
  }
125
105
  };
126
- for (const toolName of ['get_worker_profile', 'workers_get_profile']) {
127
- server.registerTool(toolName, {
128
- title: 'Get worker profile',
129
- description: 'Get a detailed worker profile by ID.',
130
- inputSchema: z.object({ workerId: z.string().uuid() }),
131
- annotations: {
132
- readOnlyHint: true,
133
- openWorldHint: false,
134
- destructiveHint: false,
135
- },
136
- _meta: toolDescriptorMeta(getWorkerInvoking, getWorkerInvoked, ['workers:read']),
137
- }, getWorkerProfileHandler);
138
- }
106
+ server.registerTool('workers', {
107
+ title: 'Manage Workers',
108
+ description: 'Find and view worker profiles. Actions: search, get_profile. Use this to find talent or check worker details.',
109
+ inputSchema: WorkersToolSchema,
110
+ _meta: toolDescriptorMeta('Managing workers', 'Worker operation complete', ['workers:read']),
111
+ }, workersHandler);
139
112
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hapticpaper/mcp-server",
3
3
  "mcpName": "com.hapticpaper/mcp",
4
- "version": "1.0.17",
4
+ "version": "1.0.19",
5
5
  "description": "Official MCP Server for Haptic Paper - Connect your account to create human tasks from agentic pipelines.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
package/server.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "subfolder": "packages/mcp-server"
26
26
  },
27
27
  "websiteUrl": "https://hapticpaper.com/developer",
28
- "version": "1.0.17",
28
+ "version": "1.0.19",
29
29
  "remotes": [
30
30
  {
31
31
  "type": "streamable-http",
@@ -37,7 +37,7 @@
37
37
  "registryType": "npm",
38
38
  "registryBaseUrl": "https://registry.npmjs.org",
39
39
  "identifier": "@hapticpaper/mcp-server",
40
- "version": "1.0.17",
40
+ "version": "1.0.19",
41
41
  "transport": {
42
42
  "type": "stdio"
43
43
  },