mcp-server-mturk 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,384 @@
1
+ """HIT (Human Intelligence Task) management tools."""
2
+
3
+ import json
4
+ from datetime import datetime, timezone
5
+ from mcp.types import TextContent
6
+
7
+ from ..client import get_mturk_client, format_error, wrap_html_question
8
+
9
+
10
+ def register_tools(register):
11
+ """Register HIT-related tools."""
12
+
13
+ async def create_hit(args: dict) -> list[TextContent]:
14
+ """Create a new Human Intelligence Task on MTurk."""
15
+ try:
16
+ client = get_mturk_client()
17
+ params = {
18
+ "Title": args["title"],
19
+ "Description": args["description"],
20
+ "Question": wrap_html_question(args["question_html"]),
21
+ "Reward": args["reward"],
22
+ "MaxAssignments": args.get("max_assignments", 1),
23
+ "LifetimeInSeconds": args.get("lifetime_seconds", 86400),
24
+ "AssignmentDurationInSeconds": args.get("assignment_duration_seconds", 3600),
25
+ "AutoApprovalDelayInSeconds": args.get("auto_approval_delay", 259200),
26
+ }
27
+ if args.get("keywords"):
28
+ params["Keywords"] = args["keywords"]
29
+
30
+ response = client.create_hit(**params)
31
+ hit = response["HIT"]
32
+ result = {
33
+ "hit_id": hit["HITId"],
34
+ "hit_type_id": hit["HITTypeId"],
35
+ "hit_group_id": hit.get("HITGroupId"),
36
+ "title": hit["Title"],
37
+ "status": hit["HITStatus"],
38
+ "max_assignments": hit["MaxAssignments"],
39
+ "reward": hit["Reward"],
40
+ "creation_time": str(hit["CreationTime"]),
41
+ "expiration": str(hit["Expiration"]),
42
+ }
43
+ return [TextContent(type="text", text=json.dumps(result, indent=2))]
44
+ except Exception as e:
45
+ return [TextContent(type="text", text=format_error(e))]
46
+
47
+ register(
48
+ "create_hit",
49
+ """Creates a new Human Intelligence Task (HIT) on Amazon Mechanical Turk that workers can complete for payment.
50
+
51
+ Use this tool when you need human input for tasks like surveys, data labeling, content moderation, transcription, or any task requiring human judgment. Each HIT represents a single task that one or more workers will complete.
52
+
53
+ IMPORTANT: Creating a HIT costs real money in production mode. The total cost is: reward × max_assignments + MTurk fees (20-40% depending on settings). Always verify your account balance first with get_account_balance.
54
+
55
+ The question_html parameter should contain your task's HTML form. Include form fields for worker input - the HTML will be automatically wrapped in MTurk's template with a submit button. Example:
56
+ ```html
57
+ <h2>Survey Question</h2>
58
+ <p>What is your favorite color?</p>
59
+ <input type="text" name="answer" required>
60
+ ```
61
+
62
+ Workers see this in the MTurk marketplace and can accept and complete it. Once submitted, use list_assignments to see responses.
63
+
64
+ Returns the hit_id which you'll need for all subsequent operations (checking status, reviewing assignments, deleting the HIT).""",
65
+ {
66
+ "type": "object",
67
+ "properties": {
68
+ "title": {
69
+ "type": "string",
70
+ "description": "Title displayed to workers in the MTurk marketplace (max 128 characters). Make it clear and descriptive so workers understand the task at a glance. Example: 'Answer a 5-question survey about shopping habits'"
71
+ },
72
+ "description": {
73
+ "type": "string",
74
+ "description": "Detailed description shown to workers before they accept the HIT. Explain what the task involves, approximately how long it takes, and any requirements. This helps workers decide if they want to do your task."
75
+ },
76
+ "question_html": {
77
+ "type": "string",
78
+ "description": "The HTML content for your task form. Include form inputs (text fields, radio buttons, checkboxes, etc.) with name attributes. Do NOT include <form> tags, <html>, <head>, or submit buttons - these are added automatically. Just provide the task content and input fields."
79
+ },
80
+ "reward": {
81
+ "type": "string",
82
+ "description": "Payment amount per assignment in USD as a string, e.g., '0.10' for 10 cents, '1.50' for $1.50. This is what each worker receives for completing the task. Fair pay improves response quality and speed. MTurk minimum is $0.01."
83
+ },
84
+ "max_assignments": {
85
+ "type": "integer",
86
+ "description": "Number of different workers who should complete this HIT. Use 1 for tasks needing one response, higher numbers (e.g., 100) for surveys or when you want multiple opinions. Each assignment is paid separately.",
87
+ "default": 1
88
+ },
89
+ "lifetime_seconds": {
90
+ "type": "integer",
91
+ "description": "How long the HIT stays in the marketplace for workers to accept (in seconds). Default is 86400 (24 hours). After this time, no new workers can accept it. Existing accepted assignments can still be completed.",
92
+ "default": 86400
93
+ },
94
+ "assignment_duration_seconds": {
95
+ "type": "integer",
96
+ "description": "Maximum time a worker has to complete the task after accepting it (in seconds). Default is 3600 (1 hour). If they don't submit in time, the assignment is released for another worker. Set based on task complexity.",
97
+ "default": 3600
98
+ },
99
+ "keywords": {
100
+ "type": "string",
101
+ "description": "Comma-separated keywords to help workers find your HIT in searches. Example: 'survey, research, quick, easy'. Good keywords increase visibility to relevant workers."
102
+ },
103
+ "auto_approval_delay": {
104
+ "type": "integer",
105
+ "description": "Seconds to wait before automatically approving submissions you haven't manually reviewed. Default is 259200 (3 days). After this time, pending assignments are auto-approved and workers are paid. Set longer if you need more review time.",
106
+ "default": 259200
107
+ },
108
+ },
109
+ "required": ["title", "description", "question_html", "reward"],
110
+ },
111
+ create_hit,
112
+ )
113
+
114
+ async def get_hit(args: dict) -> list[TextContent]:
115
+ """Get details of a specific HIT."""
116
+ try:
117
+ client = get_mturk_client()
118
+ response = client.get_hit(HITId=args["hit_id"])
119
+ hit = response["HIT"]
120
+ result = {
121
+ "hit_id": hit["HITId"],
122
+ "hit_type_id": hit["HITTypeId"],
123
+ "title": hit["Title"],
124
+ "description": hit["Description"],
125
+ "status": hit["HITStatus"],
126
+ "max_assignments": hit["MaxAssignments"],
127
+ "number_of_assignments_pending": hit.get("NumberOfAssignmentsPending", 0),
128
+ "number_of_assignments_available": hit.get("NumberOfAssignmentsAvailable", 0),
129
+ "number_of_assignments_completed": hit.get("NumberOfAssignmentsCompleted", 0),
130
+ "reward": hit["Reward"],
131
+ "creation_time": str(hit["CreationTime"]),
132
+ "expiration": str(hit["Expiration"]),
133
+ }
134
+ return [TextContent(type="text", text=json.dumps(result, indent=2))]
135
+ except Exception as e:
136
+ return [TextContent(type="text", text=format_error(e))]
137
+
138
+ register(
139
+ "get_hit",
140
+ """Retrieves detailed information about a specific HIT including its current status and assignment progress.
141
+
142
+ Use this tool to check on a HIT you've created - see how many workers have completed it, how many assignments are still available, and whether it's expired. This is useful for monitoring progress on your tasks.
143
+
144
+ The response includes assignment counts:
145
+ - pending: Workers have accepted but not yet submitted
146
+ - available: Open slots waiting for workers to accept
147
+ - completed: Submitted assignments (may be approved, rejected, or awaiting review)
148
+
149
+ Status values:
150
+ - 'Assignable': HIT is active and workers can accept new assignments
151
+ - 'Unassignable': All assignments taken or HIT expired, but some work pending
152
+ - 'Reviewable': Has submitted assignments ready for your review
153
+ - 'Reviewing': You've marked it as being reviewed
154
+ - 'Disposed': HIT has been deleted
155
+
156
+ Does NOT return the actual worker submissions - use list_assignments for that.""",
157
+ {
158
+ "type": "object",
159
+ "properties": {
160
+ "hit_id": {
161
+ "type": "string",
162
+ "description": "The unique HIT ID returned when you created the HIT (starts with a letter followed by alphanumeric characters). You can also get HIT IDs from list_hits."
163
+ },
164
+ },
165
+ "required": ["hit_id"],
166
+ },
167
+ get_hit,
168
+ )
169
+
170
+ async def list_hits(args: dict) -> list[TextContent]:
171
+ """List all HITs for the requester account."""
172
+ try:
173
+ client = get_mturk_client()
174
+ response = client.list_hits(MaxResults=min(args.get("max_results", 100), 100))
175
+ hits = response.get("HITs", [])
176
+ result = {
177
+ "count": len(hits),
178
+ "hits": [
179
+ {
180
+ "hit_id": hit["HITId"],
181
+ "title": hit["Title"],
182
+ "status": hit["HITStatus"],
183
+ "max_assignments": hit["MaxAssignments"],
184
+ "pending": hit.get("NumberOfAssignmentsPending", 0),
185
+ "available": hit.get("NumberOfAssignmentsAvailable", 0),
186
+ "completed": hit.get("NumberOfAssignmentsCompleted", 0),
187
+ "reward": hit["Reward"],
188
+ "creation_time": str(hit["CreationTime"]),
189
+ }
190
+ for hit in hits
191
+ ],
192
+ }
193
+ return [TextContent(type="text", text=json.dumps(result, indent=2))]
194
+ except Exception as e:
195
+ return [TextContent(type="text", text=format_error(e))]
196
+
197
+ register(
198
+ "list_hits",
199
+ """Lists all HITs you've created on your MTurk requester account, sorted by creation date (most recent first).
200
+
201
+ Use this tool to get an overview of all your tasks, find HIT IDs you need for other operations, or check the status of multiple HITs at once. Returns summary information for each HIT including assignment progress.
202
+
203
+ For each HIT, shows:
204
+ - hit_id: Unique identifier needed for other operations
205
+ - title: The task title workers see
206
+ - status: Current state (Assignable, Reviewable, etc.)
207
+ - Assignment counts: pending/available/completed
208
+ - reward: Payment per assignment
209
+ - creation_time: When you created it
210
+
211
+ This returns HITs in all states. To find only HITs with work ready to review, use list_reviewable_hits instead.
212
+
213
+ Note: Only returns up to 100 HITs. If you have more, you may not see older ones.""",
214
+ {
215
+ "type": "object",
216
+ "properties": {
217
+ "max_results": {
218
+ "type": "integer",
219
+ "description": "Maximum number of HITs to return, between 1 and 100. Defaults to 100. Returns most recent HITs first.",
220
+ "default": 100,
221
+ "minimum": 1,
222
+ "maximum": 100
223
+ },
224
+ },
225
+ "required": [],
226
+ },
227
+ list_hits,
228
+ )
229
+
230
+ async def delete_hit(args: dict) -> list[TextContent]:
231
+ """Delete a HIT. The HIT must have no pending or available assignments."""
232
+ try:
233
+ client = get_mturk_client()
234
+ hit_id = args["hit_id"]
235
+ # First, try to update the HIT to expire it (required before deletion)
236
+ try:
237
+ client.update_expiration_for_hit(
238
+ HITId=hit_id,
239
+ ExpireAt=datetime.now(timezone.utc)
240
+ )
241
+ except Exception:
242
+ pass # May already be expired
243
+
244
+ client.delete_hit(HITId=hit_id)
245
+ return [TextContent(type="text", text=json.dumps({"success": True, "hit_id": hit_id, "message": "HIT deleted successfully"}, indent=2))]
246
+ except Exception as e:
247
+ return [TextContent(type="text", text=format_error(e))]
248
+
249
+ register(
250
+ "delete_hit",
251
+ """Permanently deletes a HIT from MTurk. This cannot be undone.
252
+
253
+ Use this tool to clean up HITs you no longer need - either completed HITs after you've saved the results, or HITs created by mistake. Deleting removes the HIT from your dashboard and worker visibility.
254
+
255
+ IMPORTANT RESTRICTIONS:
256
+ - Cannot delete a HIT with pending assignments (workers currently working on it)
257
+ - Cannot delete a HIT with available assignments still open
258
+ - Must first review (approve/reject) all submitted assignments OR let them auto-approve
259
+
260
+ This tool automatically expires the HIT before deletion (required by MTurk). If the HIT can't be deleted due to pending work, you'll get an error - in that case, either wait for workers to finish or use update_hit_expiration to expire it, then try again after assignments complete.
261
+
262
+ Workflow for deleting an active HIT:
263
+ 1. Use update_hit_expiration with 'now' to stop new workers accepting
264
+ 2. Wait for pending workers to submit or timeout
265
+ 3. Approve/reject all submissions with list_assignments and approve_assignment/reject_assignment
266
+ 4. Then delete_hit will succeed""",
267
+ {
268
+ "type": "object",
269
+ "properties": {
270
+ "hit_id": {
271
+ "type": "string",
272
+ "description": "The unique HIT ID to delete. Get this from create_hit response or list_hits. The HIT must have no pending or available assignments."
273
+ },
274
+ },
275
+ "required": ["hit_id"],
276
+ },
277
+ delete_hit,
278
+ )
279
+
280
+ async def update_hit_expiration(args: dict) -> list[TextContent]:
281
+ """Update the expiration time of a HIT."""
282
+ try:
283
+ client = get_mturk_client()
284
+ hit_id = args["hit_id"]
285
+ expire_at = args["expire_at"]
286
+
287
+ if expire_at.lower() == "now":
288
+ expiration = datetime.now(timezone.utc)
289
+ else:
290
+ expiration = datetime.fromisoformat(expire_at.replace("Z", "+00:00"))
291
+
292
+ client.update_expiration_for_hit(HITId=hit_id, ExpireAt=expiration)
293
+ return [TextContent(type="text", text=json.dumps({
294
+ "success": True,
295
+ "hit_id": hit_id,
296
+ "new_expiration": str(expiration),
297
+ }, indent=2))]
298
+ except Exception as e:
299
+ return [TextContent(type="text", text=format_error(e))]
300
+
301
+ register(
302
+ "update_hit_expiration",
303
+ """Changes when a HIT expires (stops accepting new workers).
304
+
305
+ Use this tool to:
306
+ - EXTEND a HIT: Give more time for workers to find and accept your task
307
+ - EXPIRE IMMEDIATELY: Stop accepting new workers right now (use 'now' as the value)
308
+
309
+ When a HIT expires:
310
+ - No NEW workers can accept the task
311
+ - Workers who already accepted can still complete and submit
312
+ - The HIT remains until all work is reviewed and you delete it
313
+
314
+ Common use cases:
315
+ - Your HIT isn't getting enough responses - extend the deadline
316
+ - You made a mistake and want to stop the HIT - expire it immediately
317
+ - Preparing to delete a HIT - must expire first before deletion
318
+
319
+ This does NOT affect workers currently doing the task or submitted work awaiting review. To cancel everything including pending work, you'd need to expire it, wait for timeouts, then reject/approve submissions.""",
320
+ {
321
+ "type": "object",
322
+ "properties": {
323
+ "hit_id": {
324
+ "type": "string",
325
+ "description": "The unique HIT ID to update. Get this from create_hit response or list_hits."
326
+ },
327
+ "expire_at": {
328
+ "type": "string",
329
+ "description": "New expiration time. Use 'now' to expire immediately, or an ISO 8601 datetime string like '2024-12-31T23:59:59Z' for a specific time. The time must be in the future (unless using 'now')."
330
+ },
331
+ },
332
+ "required": ["hit_id", "expire_at"],
333
+ },
334
+ update_hit_expiration,
335
+ )
336
+
337
+ async def create_additional_assignments(args: dict) -> list[TextContent]:
338
+ """Add more assignments to an existing HIT."""
339
+ try:
340
+ client = get_mturk_client()
341
+ client.create_additional_assignments_for_hit(
342
+ HITId=args["hit_id"],
343
+ NumberOfAdditionalAssignments=args["additional_assignments"],
344
+ )
345
+ return [TextContent(type="text", text=json.dumps({
346
+ "success": True,
347
+ "hit_id": args["hit_id"],
348
+ "additional_assignments": args["additional_assignments"],
349
+ }, indent=2))]
350
+ except Exception as e:
351
+ return [TextContent(type="text", text=format_error(e))]
352
+
353
+ register(
354
+ "create_additional_assignments",
355
+ """Adds more worker slots to an existing HIT, allowing more workers to complete it.
356
+
357
+ Use this tool when you need more responses than originally planned - for example, if you created a survey for 50 people but now want 100 total responses. This is cheaper than creating a new HIT because you reuse the existing task setup.
358
+
359
+ IMPORTANT: This costs additional money - each new assignment will be paid at the HIT's reward rate plus MTurk fees. Verify your balance with get_account_balance first.
360
+
361
+ The new assignments are added to the existing count. For example, if a HIT had max_assignments=50 and you add 25 more, it now has 75 total slots.
362
+
363
+ Requirements:
364
+ - The HIT must still be active (not expired or disposed)
365
+ - The HIT must be extendable (not all assignment types support this)
366
+
367
+ This does NOT extend the HIT's lifetime - if the HIT is about to expire, also use update_hit_expiration to give workers time to complete the new assignments.""",
368
+ {
369
+ "type": "object",
370
+ "properties": {
371
+ "hit_id": {
372
+ "type": "string",
373
+ "description": "The unique HIT ID to add assignments to. Get this from create_hit response or list_hits."
374
+ },
375
+ "additional_assignments": {
376
+ "type": "integer",
377
+ "description": "Number of new assignment slots to add. Each slot allows one additional worker to complete the HIT. Must be at least 1.",
378
+ "minimum": 1
379
+ },
380
+ },
381
+ "required": ["hit_id", "additional_assignments"],
382
+ },
383
+ create_additional_assignments,
384
+ )