humanpages 1.2.2 → 1.2.3

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.
package/dist/index.js CHANGED
@@ -1,1903 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  import 'dotenv/config';
3
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
- import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
6
- const API_BASE = process.env.API_BASE_URL || 'http://localhost:3001';
7
- async function searchHumans(params) {
8
- const query = new URLSearchParams();
9
- if (params.skill)
10
- query.set('skill', params.skill);
11
- if (params.equipment)
12
- query.set('equipment', params.equipment);
13
- if (params.language)
14
- query.set('language', params.language);
15
- if (params.location)
16
- query.set('location', params.location);
17
- if (params.lat)
18
- query.set('lat', params.lat.toString());
19
- if (params.lng)
20
- query.set('lng', params.lng.toString());
21
- if (params.radius)
22
- query.set('radius', params.radius.toString());
23
- if (params.max_rate)
24
- query.set('maxRate', params.max_rate.toString());
25
- if (params.available_only)
26
- query.set('available', 'true');
27
- if (params.work_mode)
28
- query.set('workMode', params.work_mode);
29
- if (params.verified)
30
- query.set('verified', params.verified);
31
- const res = await fetch(`${API_BASE}/api/humans/search?${query}`);
32
- if (!res.ok) {
33
- throw new Error(`API error: ${res.status}`);
34
- }
35
- return res.json();
36
- }
37
- async function getHuman(id) {
38
- const res = await fetch(`${API_BASE}/api/humans/${id}`);
39
- if (!res.ok) {
40
- throw new Error(`Human not found: ${id}`);
41
- }
42
- return res.json();
43
- }
44
- const server = new Server({
45
- name: 'humanpages',
46
- version: '1.0.0',
47
- }, {
48
- capabilities: {
49
- tools: {},
50
- },
51
- });
52
- server.setRequestHandler(ListToolsRequestSchema, async () => ({
53
- tools: [
54
- {
55
- name: 'search_humans',
56
- description: 'Search for humans available for hire. Supports filtering by skill, equipment, language, location (text or coordinates), and rate. Returns profiles with reputation stats. Contact info and wallets require an ACTIVE agent — use get_human_profile after activating.',
57
- inputSchema: {
58
- type: 'object',
59
- properties: {
60
- skill: {
61
- type: 'string',
62
- description: 'Filter by skill tag (e.g., "photography", "driving", "notary")',
63
- },
64
- equipment: {
65
- type: 'string',
66
- description: 'Filter by equipment (e.g., "car", "drone", "camera")',
67
- },
68
- language: {
69
- type: 'string',
70
- description: 'Filter by language ISO code (e.g., "en", "es", "zh")',
71
- },
72
- location: {
73
- type: 'string',
74
- description: 'Filter by location name or neighborhood (partial match, e.g., "San Francisco" or "Mission District")',
75
- },
76
- lat: {
77
- type: 'number',
78
- description: 'Latitude for radius search (requires lng and radius)',
79
- },
80
- lng: {
81
- type: 'number',
82
- description: 'Longitude for radius search (requires lat and radius)',
83
- },
84
- radius: {
85
- type: 'number',
86
- description: 'Search radius in kilometers (requires lat and lng)',
87
- },
88
- max_rate: {
89
- type: 'number',
90
- description: 'Maximum hourly rate in USD. Humans who set rates in other currencies are auto-converted to USD for comparison.',
91
- },
92
- available_only: {
93
- type: 'boolean',
94
- description: 'Only return humans who are currently available (default: true)',
95
- default: true,
96
- },
97
- work_mode: {
98
- type: 'string',
99
- enum: ['REMOTE', 'ONSITE', 'HYBRID'],
100
- description: 'Filter by work mode preference (REMOTE, ONSITE, or HYBRID)',
101
- },
102
- verified: {
103
- type: 'string',
104
- enum: ['humanity'],
105
- description: 'Filter by verification status. Use "humanity" to only return humans who have verified their identity via Gitcoin Passport (score >= 20).',
106
- },
107
- },
108
- },
109
- },
110
- {
111
- name: 'get_human',
112
- description: 'Get detailed information about a specific human by their ID, including their bio, skills, and service offerings. Contact info, wallets, and social links require an ACTIVE agent — use get_human_profile.',
113
- inputSchema: {
114
- type: 'object',
115
- properties: {
116
- id: {
117
- type: 'string',
118
- description: 'The unique ID of the human',
119
- },
120
- },
121
- required: ['id'],
122
- },
123
- },
124
- {
125
- name: 'register_agent',
126
- description: 'Register as an agent on Human Pages. Returns an API key that you MUST save and use for all subsequent job creation calls. The API key cannot be retrieved later.',
127
- inputSchema: {
128
- type: 'object',
129
- properties: {
130
- name: {
131
- type: 'string',
132
- description: 'Display name for your agent (e.g., "Acme AI Assistant")',
133
- },
134
- description: {
135
- type: 'string',
136
- description: 'Brief description of what your agent does (max 500 chars)',
137
- },
138
- website_url: {
139
- type: 'string',
140
- description: 'Your website URL (can be verified later for a trust badge)',
141
- },
142
- contact_email: {
143
- type: 'string',
144
- description: 'Contact email for the agent operator',
145
- },
146
- },
147
- required: ['name'],
148
- },
149
- },
150
- {
151
- name: 'get_agent_profile',
152
- description: 'Get a registered agent\'s public profile including reputation stats (total jobs, completed jobs, payment speed).',
153
- inputSchema: {
154
- type: 'object',
155
- properties: {
156
- agent_id: {
157
- type: 'string',
158
- description: 'The registered agent ID',
159
- },
160
- },
161
- required: ['agent_id'],
162
- },
163
- },
164
- {
165
- name: 'verify_agent_domain',
166
- description: 'Verify domain ownership for a registered agent. The agent must have a websiteUrl set. Supports two methods: "well-known" (place a file at /.well-known/humanpages-verify.txt) or "dns" (add a TXT record at _humanpages.yourdomain.com).',
167
- inputSchema: {
168
- type: 'object',
169
- properties: {
170
- agent_id: {
171
- type: 'string',
172
- description: 'The registered agent ID',
173
- },
174
- agent_key: {
175
- type: 'string',
176
- description: 'The agent API key (starts with hp_)',
177
- },
178
- method: {
179
- type: 'string',
180
- enum: ['well-known', 'dns'],
181
- description: 'Verification method: "well-known" or "dns"',
182
- },
183
- },
184
- required: ['agent_id', 'agent_key', 'method'],
185
- },
186
- },
187
- {
188
- name: 'create_job_offer',
189
- description: 'Create a job offer for a human. Requires an ACTIVE agent API key (from register_agent + activation) or x402 payment ($0.25 USDC on Base via x-payment header). RATE LIMITS: BASIC tier = 1 offer/2 days, PRO tier = 15 offers/day. x402 payments bypass tier limits. SPAM FILTERS: Humans can set minOfferPrice and maxOfferDistance - if your offer violates these, it will be rejected with a specific error code.',
190
- inputSchema: {
191
- type: 'object',
192
- properties: {
193
- human_id: {
194
- type: 'string',
195
- description: 'The ID of the human to hire',
196
- },
197
- title: {
198
- type: 'string',
199
- description: 'Title of the job/task',
200
- },
201
- description: {
202
- type: 'string',
203
- description: 'Detailed description of what needs to be done',
204
- },
205
- category: {
206
- type: 'string',
207
- description: 'Category of the task (e.g., "photography", "research", "delivery")',
208
- },
209
- price_usdc: {
210
- type: 'number',
211
- description: 'Agreed price in USDC. Must meet the human\'s minOfferPrice if set.',
212
- },
213
- agent_id: {
214
- type: 'string',
215
- description: 'Your unique agent identifier (any string)',
216
- },
217
- agent_key: {
218
- type: 'string',
219
- description: 'Your registered agent API key (starts with hp_). Required.',
220
- },
221
- agent_name: {
222
- type: 'string',
223
- description: 'Display name override (defaults to registered agent name)',
224
- },
225
- agent_lat: {
226
- type: 'number',
227
- description: 'Agent latitude for distance filtering. Required if human has maxOfferDistance set.',
228
- },
229
- agent_lng: {
230
- type: 'number',
231
- description: 'Agent longitude for distance filtering. Required if human has maxOfferDistance set.',
232
- },
233
- callback_url: {
234
- type: 'string',
235
- description: 'Webhook URL to receive job status updates (ACCEPTED, REJECTED, PAID, COMPLETED). Must be a public HTTP(S) endpoint.',
236
- },
237
- callback_secret: {
238
- type: 'string',
239
- description: 'Secret for HMAC-SHA256 signature verification (min 16 chars). The signature is sent in X-HumanPages-Signature header.',
240
- },
241
- payment_mode: {
242
- type: 'string',
243
- enum: ['ONE_TIME', 'STREAM'],
244
- description: 'Payment mode. ONE_TIME (default) for single payments. STREAM for ongoing stream payments.',
245
- },
246
- payment_timing: {
247
- type: 'string',
248
- enum: ['upfront', 'upon_completion'],
249
- description: 'For ONE_TIME jobs only. "upfront" (default) = pay before work. "upon_completion" = pay after work is done.',
250
- },
251
- stream_method: {
252
- type: 'string',
253
- enum: ['SUPERFLUID', 'MICRO_TRANSFER'],
254
- description: 'Stream method. SUPERFLUID: agent creates an on-chain flow that streams tokens per-second. MICRO_TRANSFER: agent sends periodic discrete transfers. Required when payment_mode=STREAM.',
255
- },
256
- stream_interval: {
257
- type: 'string',
258
- enum: ['HOURLY', 'DAILY', 'WEEKLY'],
259
- description: 'How often payments are made/checkpointed. Required when payment_mode=STREAM.',
260
- },
261
- stream_rate_usdc: {
262
- type: 'number',
263
- description: 'USDC amount per interval (e.g., 10 = $10/day if interval=DAILY). Required when payment_mode=STREAM.',
264
- },
265
- stream_max_ticks: {
266
- type: 'number',
267
- description: 'Optional cap on number of payment intervals. Null = indefinite.',
268
- },
269
- },
270
- required: ['human_id', 'title', 'description', 'price_usdc', 'agent_id', 'agent_key'],
271
- },
272
- },
273
- {
274
- name: 'get_job_status',
275
- description: 'Check the status of a job offer. Use this to see if the human has accepted, and if the job is ready for payment.',
276
- inputSchema: {
277
- type: 'object',
278
- properties: {
279
- job_id: {
280
- type: 'string',
281
- description: 'The job ID returned from create_job_offer',
282
- },
283
- },
284
- required: ['job_id'],
285
- },
286
- },
287
- {
288
- name: 'mark_job_paid',
289
- description: 'Record that payment has been sent for an ACCEPTED job. The job must be accepted by the human first. Payment amount must match or exceed the agreed price.',
290
- inputSchema: {
291
- type: 'object',
292
- properties: {
293
- job_id: {
294
- type: 'string',
295
- description: 'The job ID',
296
- },
297
- payment_tx_hash: {
298
- type: 'string',
299
- description: 'The on-chain transaction hash',
300
- },
301
- payment_network: {
302
- type: 'string',
303
- description: 'The blockchain network (e.g., "ethereum", "solana")',
304
- },
305
- payment_amount: {
306
- type: 'number',
307
- description: 'The amount paid in USDC',
308
- },
309
- },
310
- required: ['job_id', 'payment_tx_hash', 'payment_network', 'payment_amount'],
311
- },
312
- },
313
- {
314
- name: 'check_humanity_status',
315
- description: 'Check the humanity verification status for a specific human. Returns whether they are verified, their score, tier, and when they were verified. This is read-only.',
316
- inputSchema: {
317
- type: 'object',
318
- properties: {
319
- human_id: {
320
- type: 'string',
321
- description: 'The ID of the human to check',
322
- },
323
- },
324
- required: ['human_id'],
325
- },
326
- },
327
- {
328
- name: 'leave_review',
329
- description: 'Leave a review for a COMPLETED job. Reviews are only allowed after the human marks the job as complete.',
330
- inputSchema: {
331
- type: 'object',
332
- properties: {
333
- job_id: {
334
- type: 'string',
335
- description: 'The job ID',
336
- },
337
- rating: {
338
- type: 'number',
339
- description: 'Rating from 1-5 stars',
340
- },
341
- comment: {
342
- type: 'string',
343
- description: 'Optional review comment',
344
- },
345
- },
346
- required: ['job_id', 'rating'],
347
- },
348
- },
349
- {
350
- name: 'get_human_profile',
351
- description: 'Get the full profile of a human including contact info, wallet addresses, and social links. Requires an ACTIVE agent API key. Alternative: agents can pay $0.05 per view via x402 (USDC on Base) by including an x-payment header — no activation needed.',
352
- inputSchema: {
353
- type: 'object',
354
- properties: {
355
- human_id: {
356
- type: 'string',
357
- description: 'The ID of the human',
358
- },
359
- agent_key: {
360
- type: 'string',
361
- description: 'Your registered agent API key (starts with hp_)',
362
- },
363
- },
364
- required: ['human_id', 'agent_key'],
365
- },
366
- },
367
- {
368
- name: 'request_activation_code',
369
- description: 'Request an activation code (HP-XXXXXXXX) to post on social media for free BASIC tier activation. After posting, use verify_social_activation with the post URL.',
370
- inputSchema: {
371
- type: 'object',
372
- properties: {
373
- agent_key: {
374
- type: 'string',
375
- description: 'Your registered agent API key (starts with hp_)',
376
- },
377
- },
378
- required: ['agent_key'],
379
- },
380
- },
381
- {
382
- name: 'verify_social_activation',
383
- description: 'Verify a social media post containing your activation code. On success, your agent is activated with BASIC tier.',
384
- inputSchema: {
385
- type: 'object',
386
- properties: {
387
- agent_key: {
388
- type: 'string',
389
- description: 'Your registered agent API key (starts with hp_)',
390
- },
391
- post_url: {
392
- type: 'string',
393
- description: 'URL of the social media post containing your activation code',
394
- },
395
- },
396
- required: ['agent_key', 'post_url'],
397
- },
398
- },
399
- {
400
- name: 'get_activation_status',
401
- description: 'Check the current activation status, tier, and expiry for your agent.',
402
- inputSchema: {
403
- type: 'object',
404
- properties: {
405
- agent_key: {
406
- type: 'string',
407
- description: 'Your registered agent API key (starts with hp_)',
408
- },
409
- },
410
- required: ['agent_key'],
411
- },
412
- },
413
- {
414
- name: 'get_payment_activation',
415
- description: 'Get a deposit address and payment instructions for PRO tier activation via on-chain payment.',
416
- inputSchema: {
417
- type: 'object',
418
- properties: {
419
- agent_key: {
420
- type: 'string',
421
- description: 'Your registered agent API key (starts with hp_)',
422
- },
423
- },
424
- required: ['agent_key'],
425
- },
426
- },
427
- {
428
- name: 'verify_payment_activation',
429
- description: 'Verify an on-chain payment for PRO tier activation. On success, your agent is activated with PRO tier.',
430
- inputSchema: {
431
- type: 'object',
432
- properties: {
433
- agent_key: {
434
- type: 'string',
435
- description: 'Your registered agent API key (starts with hp_)',
436
- },
437
- tx_hash: {
438
- type: 'string',
439
- description: 'The on-chain transaction hash of the activation payment',
440
- },
441
- network: {
442
- type: 'string',
443
- description: 'The blockchain network (e.g., "ethereum", "base", "solana")',
444
- },
445
- },
446
- required: ['agent_key', 'tx_hash', 'network'],
447
- },
448
- },
449
- {
450
- name: 'start_stream',
451
- description: 'Start a stream payment for an ACCEPTED stream job. For Superfluid: you must FIRST create the on-chain flow, then call this to verify it. Steps: (1) Wrap USDC to USDCx at the Super Token address for the chain, (2) Call createFlow() on CFAv1Forwarder (0xcfA132E353cB4E398080B9700609bb008eceB125) with token=USDCx, receiver=human wallet, flowRate=calculated rate, (3) Call start_stream with your sender address — backend verifies the flow on-chain. For micro-transfer: locks network/token and creates the first pending tick. Prefer L2s (Base, Arbitrum, Polygon) for lower gas costs.',
452
- inputSchema: {
453
- type: 'object',
454
- properties: {
455
- job_id: { type: 'string', description: 'The job ID' },
456
- agent_key: { type: 'string', description: 'Your agent API key (starts with hp_)' },
457
- sender_address: { type: 'string', description: 'Your wallet address that created the flow (Superfluid) or will send payments (micro-transfer)' },
458
- network: { type: 'string', description: 'Blockchain network (e.g., "base", "polygon", "arbitrum")' },
459
- token: { type: 'string', description: 'Token symbol (default: "USDC")' },
460
- },
461
- required: ['job_id', 'agent_key', 'sender_address', 'network'],
462
- },
463
- },
464
- {
465
- name: 'record_stream_tick',
466
- description: 'Record a micro-transfer stream payment. Submit the transaction hash for the current pending tick. Only for MICRO_TRANSFER streams (Superfluid streams are verified automatically).',
467
- inputSchema: {
468
- type: 'object',
469
- properties: {
470
- job_id: { type: 'string', description: 'The job ID' },
471
- agent_key: { type: 'string', description: 'Your agent API key (starts with hp_)' },
472
- tx_hash: { type: 'string', description: 'The on-chain transaction hash for this tick payment' },
473
- },
474
- required: ['job_id', 'agent_key', 'tx_hash'],
475
- },
476
- },
477
- {
478
- name: 'pause_stream',
479
- description: 'Pause an active stream. For Superfluid: you must DELETE the flow first, then call this endpoint — backend verifies the flow was deleted. For micro-transfer: skips the current pending tick.',
480
- inputSchema: {
481
- type: 'object',
482
- properties: {
483
- job_id: { type: 'string', description: 'The job ID' },
484
- agent_key: { type: 'string', description: 'Your agent API key (starts with hp_)' },
485
- },
486
- required: ['job_id', 'agent_key'],
487
- },
488
- },
489
- {
490
- name: 'resume_stream',
491
- description: 'Resume a paused stream. For Superfluid: create a new flow first, then call this — backend verifies. For micro-transfer: creates a new pending tick.',
492
- inputSchema: {
493
- type: 'object',
494
- properties: {
495
- job_id: { type: 'string', description: 'The job ID' },
496
- agent_key: { type: 'string', description: 'Your agent API key (starts with hp_)' },
497
- sender_address: { type: 'string', description: 'Wallet address for the new flow (Superfluid only, optional if same as before)' },
498
- },
499
- required: ['job_id', 'agent_key'],
500
- },
501
- },
502
- {
503
- name: 'stop_stream',
504
- description: 'Stop a stream permanently and mark the job as completed. Can be called by agent or human on STREAMING or PAUSED jobs.',
505
- inputSchema: {
506
- type: 'object',
507
- properties: {
508
- job_id: { type: 'string', description: 'The job ID' },
509
- agent_key: { type: 'string', description: 'Your agent API key (starts with hp_)' },
510
- },
511
- required: ['job_id', 'agent_key'],
512
- },
513
- },
514
- {
515
- name: 'send_job_message',
516
- description: 'Send a message on a job. Agents can message the human they hired, and vice versa. Works on PENDING, ACCEPTED, PAID, STREAMING, and PAUSED jobs. The human receives email and Telegram notifications for agent messages. Rate limit: 10 messages/minute.',
517
- inputSchema: {
518
- type: 'object',
519
- properties: {
520
- job_id: {
521
- type: 'string',
522
- description: 'The job ID',
523
- },
524
- agent_key: {
525
- type: 'string',
526
- description: 'Your agent API key (starts with hp_)',
527
- },
528
- content: {
529
- type: 'string',
530
- description: 'Message content (max 2000 characters)',
531
- },
532
- },
533
- required: ['job_id', 'agent_key', 'content'],
534
- },
535
- },
536
- {
537
- name: 'get_job_messages',
538
- description: 'Get all messages for a job, ordered chronologically. Returns messages from both the agent and the human. Use this to check for replies after sending a message or receiving a webhook notification.',
539
- inputSchema: {
540
- type: 'object',
541
- properties: {
542
- job_id: {
543
- type: 'string',
544
- description: 'The job ID',
545
- },
546
- agent_key: {
547
- type: 'string',
548
- description: 'Your agent API key (starts with hp_)',
549
- },
550
- },
551
- required: ['job_id', 'agent_key'],
552
- },
553
- },
554
- {
555
- name: 'create_listing',
556
- description: 'Post a job listing on the Human Pages job board for humans to discover and apply to. Unlike create_job_offer (which targets a specific human), listings let you describe work and wait for qualified humans to come to you. Requires an ACTIVE agent or x402 payment ($0.50 USDC). RATE LIMITS: BASIC = 1 listing/week, PRO = 5 listings/day. x402 bypasses limits.',
557
- inputSchema: {
558
- type: 'object',
559
- properties: {
560
- agent_key: {
561
- type: 'string',
562
- description: 'Your agent API key (starts with hp_)',
563
- },
564
- title: {
565
- type: 'string',
566
- description: 'Title of the listing (e.g., "Social media promotion for AI product")',
567
- },
568
- description: {
569
- type: 'string',
570
- description: 'Detailed description of the work, expectations, and deliverables',
571
- },
572
- budget_usdc: {
573
- type: 'number',
574
- description: 'Budget in USDC (minimum $5)',
575
- },
576
- category: {
577
- type: 'string',
578
- description: 'Category (e.g., "marketing", "photography", "research")',
579
- },
580
- required_skills: {
581
- type: 'array',
582
- items: { type: 'string' },
583
- description: 'Skills applicants should have (e.g., ["social-media", "copywriting"])',
584
- },
585
- required_equipment: {
586
- type: 'array',
587
- items: { type: 'string' },
588
- description: 'Equipment applicants should have (e.g., ["camera", "drone"])',
589
- },
590
- location: {
591
- type: 'string',
592
- description: 'Location name for the work (e.g., "San Francisco")',
593
- },
594
- location_lat: {
595
- type: 'number',
596
- description: 'Latitude for location-based filtering',
597
- },
598
- location_lng: {
599
- type: 'number',
600
- description: 'Longitude for location-based filtering',
601
- },
602
- radius_km: {
603
- type: 'number',
604
- description: 'Radius in km for location-based filtering',
605
- },
606
- work_mode: {
607
- type: 'string',
608
- enum: ['REMOTE', 'ONSITE', 'HYBRID'],
609
- description: 'Work mode for the listing',
610
- },
611
- expires_at: {
612
- type: 'string',
613
- description: 'ISO 8601 expiration date (must be in future, max 90 days). Example: "2025-03-01T00:00:00Z"',
614
- },
615
- max_applicants: {
616
- type: 'number',
617
- description: 'Maximum number of applicants before listing auto-closes',
618
- },
619
- callback_url: {
620
- type: 'string',
621
- description: 'Webhook URL for application notifications',
622
- },
623
- callback_secret: {
624
- type: 'string',
625
- description: 'Secret for HMAC-SHA256 webhook signature (min 16 chars)',
626
- },
627
- },
628
- required: ['agent_key', 'title', 'description', 'budget_usdc', 'expires_at'],
629
- },
630
- },
631
- {
632
- name: 'get_listings',
633
- description: 'Browse open job listings on the Human Pages job board. Returns listings with agent reputation and application counts. Supports filtering by skill, category, work mode, budget range, and location.',
634
- inputSchema: {
635
- type: 'object',
636
- properties: {
637
- page: {
638
- type: 'number',
639
- description: 'Page number (default: 1)',
640
- },
641
- limit: {
642
- type: 'number',
643
- description: 'Results per page (default: 20, max: 50)',
644
- },
645
- skill: {
646
- type: 'string',
647
- description: 'Filter by required skill (comma-separated for multiple, e.g., "photography,editing")',
648
- },
649
- category: {
650
- type: 'string',
651
- description: 'Filter by category',
652
- },
653
- work_mode: {
654
- type: 'string',
655
- enum: ['REMOTE', 'ONSITE', 'HYBRID'],
656
- description: 'Filter by work mode',
657
- },
658
- min_budget: {
659
- type: 'number',
660
- description: 'Minimum budget in USDC',
661
- },
662
- max_budget: {
663
- type: 'number',
664
- description: 'Maximum budget in USDC',
665
- },
666
- lat: {
667
- type: 'number',
668
- description: 'Latitude for location-based filtering',
669
- },
670
- lng: {
671
- type: 'number',
672
- description: 'Longitude for location-based filtering',
673
- },
674
- radius: {
675
- type: 'number',
676
- description: 'Radius in km for location-based filtering',
677
- },
678
- },
679
- },
680
- },
681
- {
682
- name: 'get_listing',
683
- description: 'Get detailed information about a specific listing, including the posting agent\'s reputation and application count.',
684
- inputSchema: {
685
- type: 'object',
686
- properties: {
687
- listing_id: {
688
- type: 'string',
689
- description: 'The listing ID',
690
- },
691
- },
692
- required: ['listing_id'],
693
- },
694
- },
695
- {
696
- name: 'get_listing_applications',
697
- description: 'View applications for a listing you created. Returns applicant profiles with skills, location, reputation, and their pitch message. Use this to evaluate candidates before making an offer.',
698
- inputSchema: {
699
- type: 'object',
700
- properties: {
701
- listing_id: {
702
- type: 'string',
703
- description: 'The listing ID',
704
- },
705
- agent_key: {
706
- type: 'string',
707
- description: 'Your agent API key (starts with hp_)',
708
- },
709
- },
710
- required: ['listing_id', 'agent_key'],
711
- },
712
- },
713
- {
714
- name: 'make_listing_offer',
715
- description: 'Make a job offer to a listing applicant. This creates a standard job from the listing and notifies the human. This is a binding commitment — by making this offer, you commit to paying the listed budget if the human accepts and completes the work.',
716
- inputSchema: {
717
- type: 'object',
718
- properties: {
719
- listing_id: {
720
- type: 'string',
721
- description: 'The listing ID',
722
- },
723
- application_id: {
724
- type: 'string',
725
- description: 'The application ID of the chosen applicant',
726
- },
727
- agent_key: {
728
- type: 'string',
729
- description: 'Your agent API key (starts with hp_)',
730
- },
731
- },
732
- required: ['listing_id', 'application_id', 'agent_key'],
733
- },
734
- },
735
- {
736
- name: 'cancel_listing',
737
- description: 'Cancel an open listing. All pending applications will be rejected. Only the agent who created the listing can cancel it.',
738
- inputSchema: {
739
- type: 'object',
740
- properties: {
741
- listing_id: {
742
- type: 'string',
743
- description: 'The listing ID',
744
- },
745
- agent_key: {
746
- type: 'string',
747
- description: 'Your agent API key (starts with hp_)',
748
- },
749
- },
750
- required: ['listing_id', 'agent_key'],
751
- },
752
- },
753
- {
754
- name: 'get_promo_status',
755
- description: 'Check the launch promo status — free PRO tier for the first 100 agents. Returns how many slots are claimed and remaining. No authentication required.',
756
- inputSchema: {
757
- type: 'object',
758
- properties: {},
759
- },
760
- },
761
- {
762
- name: 'claim_free_pro_upgrade',
763
- description: 'Claim a free PRO tier upgrade via the launch promo (first 100 agents). Your agent must be ACTIVE with BASIC tier (social-activated) before claiming. On success, your tier is upgraded to PRO with 60-day duration.',
764
- inputSchema: {
765
- type: 'object',
766
- properties: {
767
- agent_key: {
768
- type: 'string',
769
- description: 'Your registered agent API key (starts with hp_)',
770
- },
771
- },
772
- required: ['agent_key'],
773
- },
774
- },
775
- ],
776
- }));
777
- server.setRequestHandler(CallToolRequestSchema, async (request) => {
778
- const { name, arguments: args } = request.params;
779
- try {
780
- if (name === 'search_humans') {
781
- const humans = await searchHumans({
782
- skill: args?.skill,
783
- equipment: args?.equipment,
784
- language: args?.language,
785
- location: args?.location,
786
- lat: args?.lat,
787
- lng: args?.lng,
788
- radius: args?.radius,
789
- max_rate: args?.max_rate,
790
- available_only: args?.available_only !== false,
791
- work_mode: args?.work_mode,
792
- verified: args?.verified,
793
- });
794
- if (humans.length === 0) {
795
- return {
796
- content: [{ type: 'text', text: 'No humans found matching the criteria.' }],
797
- };
798
- }
799
- const summary = humans
800
- .map((h) => {
801
- const rep = h.reputation;
802
- const rating = rep && rep.avgRating > 0 ? `${rep.avgRating}★ (${rep.reviewCount} reviews)` : 'No reviews';
803
- const humanityStatus = h.humanityVerified
804
- ? `🛡️ Verified Human (score: ${h.humanityScore})`
805
- : h.humanityScore ? `🛡️ Partially verified (score: ${h.humanityScore})` : '🛡️ Not verified';
806
- const rateDisplay = h.minRateUsdc
807
- ? (h.rateCurrency && h.rateCurrency !== 'USD'
808
- ? `${h.rateCurrency} ${h.minRateUsdc}+ (~$${h.minRateUsdEstimate || '?'} USD)`
809
- : `$${h.minRateUsdc}+`)
810
- : 'Rate negotiable';
811
- const displayLocation = h.locationGranularity === 'neighborhood' && h.neighborhood && h.location
812
- ? `${h.neighborhood}, ${h.location}`
813
- : h.location || 'Location not specified';
814
- const displayName = h.name || h.username || 'Name hidden';
815
- return `- **${displayName}**${h.username && h.name ? ` (@${h.username})` : ''} [${displayLocation}]
816
- ${h.isAvailable ? '✅ Available' : '❌ Busy'} | ${rateDisplay} | ${rating}
817
- ${humanityStatus}
818
- Skills: ${h.skills.join(', ') || 'None listed'}
819
- Equipment: ${h.equipment.join(', ') || 'None listed'}
820
- Languages: ${h.languages.join(', ') || 'Not specified'}
821
- Jobs completed: ${rep?.jobsCompleted || 0}`;
822
- })
823
- .join('\n\n');
824
- return {
825
- content: [{ type: 'text', text: `Found ${humans.length} human(s):\n\n${summary}\n\n_Contact info and wallets require an ACTIVE agent. Use get_human_profile after activating._` }],
826
- };
827
- }
828
- if (name === 'get_human') {
829
- const human = await getHuman(args?.id);
830
- const servicesInfo = human.services
831
- .map((s) => {
832
- let price = 'Negotiable';
833
- const cur = s.priceCurrency || 'USD';
834
- if (s.priceUnit === 'NEGOTIABLE')
835
- price = 'Negotiable';
836
- else if (s.priceMin) {
837
- const sym = cur === 'USD' ? '$' : cur + ' ';
838
- price = s.priceUnit === 'HOURLY' ? `${sym}${s.priceMin}/hr` : s.priceUnit === 'FLAT_TASK' ? `${sym}${s.priceMin}/task` : `${sym}${s.priceMin}`;
839
- }
840
- return `- **${s.title}** [${s.category}]\n ${s.description}\n Price: ${price}`;
841
- })
842
- .join('\n\n');
843
- const rep = human.reputation;
844
- const rating = rep && rep.avgRating > 0 ? `${rep.avgRating}★ (${rep.reviewCount} reviews)` : 'No reviews yet';
845
- const humanityTier = human.humanityScore
846
- ? (human.humanityScore >= 40 ? 'Gold' : human.humanityScore >= 20 ? 'Silver' : 'Bronze')
847
- : 'Not verified';
848
- const details = `# ${human.name}${human.username ? ` (@${human.username})` : ''}
849
- ${human.isAvailable ? '✅ Available' : '❌ Not Available'}
850
-
851
- ## Humanity Verification
852
- - **Status:** ${human.humanityVerified ? '🛡️ Verified' : '❌ Not Verified'}
853
- - **Score:** ${human.humanityScore ?? 'N/A'}
854
- - **Tier:** ${humanityTier}
855
- - **Provider:** ${human.humanityProvider || 'N/A'}
856
- - **Verified At:** ${human.humanityVerifiedAt || 'N/A'}
857
-
858
- ## Reputation
859
- - Jobs completed: ${rep?.jobsCompleted || 0}
860
- - Rating: ${rating}
861
-
862
- ## Bio
863
- ${human.bio || 'No bio provided'}
864
-
865
- ## Location
866
- ${human.locationGranularity === 'neighborhood' && human.neighborhood && human.location
867
- ? `${human.neighborhood}, ${human.location}`
868
- : human.location || 'Not specified'}
869
-
870
- ## Capabilities
871
- - **Skills:** ${human.skills.join(', ') || 'None listed'}
872
- - **Equipment:** ${human.equipment.join(', ') || 'None listed'}
873
- - **Languages:** ${human.languages.join(', ') || 'Not specified'}
874
-
875
- ## Economics
876
- - **Minimum Rate:** ${human.minRateUsdc ? (human.rateCurrency && human.rateCurrency !== 'USD' ? `${human.rateCurrency} ${human.minRateUsdc} (~$${human.minRateUsdEstimate || '?'} USD)` : `$${human.minRateUsdc} USD`) : 'Negotiable'}
877
- - **Rate Currency:** ${human.rateCurrency || 'USD'}
878
- - **Rate Type:** ${human.rateType || 'NEGOTIABLE'}
879
-
880
- ## Contact & Payment
881
- _Available via get_human_profile (requires ACTIVE agent)._
882
-
883
- ## Services Offered
884
- ${servicesInfo || 'No services listed'}`;
885
- return {
886
- content: [{ type: 'text', text: details }],
887
- };
888
- }
889
- if (name === 'register_agent') {
890
- const res = await fetch(`${API_BASE}/api/agents/register`, {
891
- method: 'POST',
892
- headers: { 'Content-Type': 'application/json' },
893
- body: JSON.stringify({
894
- name: args?.name,
895
- description: args?.description,
896
- websiteUrl: args?.website_url,
897
- contactEmail: args?.contact_email,
898
- }),
899
- });
900
- if (!res.ok) {
901
- const error = await res.json();
902
- throw new Error(error.error || `API error: ${res.status}`);
903
- }
904
- const result = await res.json();
905
- return {
906
- content: [
907
- {
908
- type: 'text',
909
- text: `**Agent Registered!**
910
-
911
- **Agent ID:** ${result.agent.id}
912
- **Name:** ${result.agent.name}
913
- **API Key:** \`${result.apiKey}\`
914
- **Status:** PENDING
915
-
916
- **IMPORTANT:** Save your API key now — it cannot be retrieved later.
917
- Pass it as \`agent_key\` when using \`create_job_offer\`.
918
-
919
- **⚠️ Activation Required:** Your agent starts as PENDING. You must activate before creating jobs or viewing full profiles.
920
- - **Free (BASIC tier):** Use \`request_activation_code\` to get a code, post it on social media, then \`verify_social_activation\`.
921
- - **Paid (PRO tier):** Use \`get_payment_activation\` for a deposit address, then \`verify_payment_activation\`.
922
-
923
- **Domain Verification Token:** \`${result.verificationToken}\`
924
- To get a verified badge, set up domain verification using \`verify_agent_domain\`.`,
925
- },
926
- ],
927
- };
928
- }
929
- if (name === 'get_agent_profile') {
930
- const res = await fetch(`${API_BASE}/api/agents/${args?.agent_id}`);
931
- if (!res.ok) {
932
- throw new Error(`Agent not found: ${args?.agent_id}`);
933
- }
934
- const agent = await res.json();
935
- const rep = agent.reputation;
936
- const details = `# ${agent.name}${agent.domainVerified ? ' ✅ Verified' : ''}
937
-
938
- ## Profile
939
- - **Description:** ${agent.description || 'No description'}
940
- - **Website:** ${agent.websiteUrl || 'Not set'}
941
- - **Contact:** ${agent.contactEmail || 'Not provided'}
942
- - **Domain Verified:** ${agent.domainVerified ? `Yes (${agent.verifiedAt})` : 'No'}
943
- - **Registered:** ${agent.createdAt}
944
- - **Last Active:** ${agent.lastActiveAt}
945
-
946
- ## Reputation
947
- - **Total Jobs:** ${rep?.totalJobs || 0}
948
- - **Completed Jobs:** ${rep?.completedJobs || 0}
949
- - **Paid Jobs:** ${rep?.paidJobs || 0}
950
- - **Avg Payment Speed:** ${rep?.avgPaymentSpeedHours != null ? `${rep.avgPaymentSpeedHours} hours` : 'N/A'}`;
951
- return {
952
- content: [{ type: 'text', text: details }],
953
- };
954
- }
955
- if (name === 'verify_agent_domain') {
956
- const res = await fetch(`${API_BASE}/api/agents/${args?.agent_id}/verify-domain`, {
957
- method: 'POST',
958
- headers: {
959
- 'Content-Type': 'application/json',
960
- 'X-Agent-Key': args?.agent_key,
961
- },
962
- body: JSON.stringify({
963
- method: args?.method,
964
- }),
965
- });
966
- if (!res.ok) {
967
- const error = await res.json();
968
- throw new Error(error.message || error.error || `API error: ${res.status}`);
969
- }
970
- const result = await res.json();
971
- return {
972
- content: [
973
- {
974
- type: 'text',
975
- text: `**Domain Verified!**
976
-
977
- **Domain:** ${result.domain}
978
- **Status:** Verified
979
-
980
- Your agent profile now shows a verified badge. Humans will see this when reviewing your job offers.`,
981
- },
982
- ],
983
- };
984
- }
985
- if (name === 'create_job_offer') {
986
- const agentKey = args?.agent_key;
987
- if (!agentKey) {
988
- throw new Error('agent_key is required. Register first with register_agent to get an API key.');
989
- }
990
- const res = await fetch(`${API_BASE}/api/jobs`, {
991
- method: 'POST',
992
- headers: {
993
- 'Content-Type': 'application/json',
994
- 'X-Agent-Key': agentKey,
995
- },
996
- body: JSON.stringify({
997
- humanId: args?.human_id,
998
- agentId: args?.agent_id,
999
- agentName: args?.agent_name,
1000
- title: args?.title,
1001
- description: args?.description,
1002
- category: args?.category,
1003
- priceUsdc: args?.price_usdc,
1004
- paymentMode: args?.payment_mode,
1005
- paymentTiming: args?.payment_timing,
1006
- streamMethod: args?.stream_method,
1007
- streamInterval: args?.stream_interval,
1008
- streamRateUsdc: args?.stream_rate_usdc,
1009
- streamMaxTicks: args?.stream_max_ticks,
1010
- callbackUrl: args?.callback_url,
1011
- callbackSecret: args?.callback_secret,
1012
- }),
1013
- });
1014
- if (!res.ok) {
1015
- const error = await res.json();
1016
- if (res.status === 403 && error.code === 'AGENT_PENDING') {
1017
- throw new Error('Agent is not yet activated. You must activate before creating jobs.\n'
1018
- + '- Free (BASIC tier): Use `request_activation_code` → post on social media → `verify_social_activation`\n'
1019
- + '- Paid (PRO tier): Use `get_payment_activation` → send payment → `verify_payment_activation`\n'
1020
- + 'Check your status with `get_activation_status`.');
1021
- }
1022
- throw new Error(error.error || `API error: ${res.status}`);
1023
- }
1024
- const job = await res.json();
1025
- const human = await getHuman(args?.human_id);
1026
- const webhookNote = args?.callback_url
1027
- ? `\n\n🔔 **Webhook configured.** Status updates will be sent to your callback URL. On acceptance, the human's contact info will be included in the webhook payload.`
1028
- : `\n\nUse \`get_job_status\` with job_id "${job.id}" to check if they've accepted.`;
1029
- return {
1030
- content: [
1031
- {
1032
- type: 'text',
1033
- text: `**Job Offer Created!**
1034
-
1035
- **Job ID:** ${job.id}
1036
- **Status:** ${job.status}
1037
- **Human:** ${human.name}
1038
- **Price:** $${args?.price_usdc} USDC
1039
-
1040
- ⏳ **Next Step:** Wait for ${human.name} to accept the offer.${webhookNote}
1041
-
1042
- Once accepted, you can send payment to their wallet and use \`mark_job_paid\` to record the transaction.`,
1043
- },
1044
- ],
1045
- };
1046
- }
1047
- if (name === 'get_job_status') {
1048
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}`);
1049
- if (!res.ok) {
1050
- throw new Error(`Job not found: ${args?.job_id}`);
1051
- }
1052
- const job = await res.json();
1053
- const statusEmoji = {
1054
- PENDING: '⏳',
1055
- ACCEPTED: '✅',
1056
- REJECTED: '❌',
1057
- PAID: '💰',
1058
- STREAMING: '🔄',
1059
- PAUSED: '⏸️',
1060
- COMPLETED: '🎉',
1061
- CANCELLED: '🚫',
1062
- DISPUTED: '⚠️',
1063
- };
1064
- const isStream = job.paymentMode === 'STREAM';
1065
- const streamSummary = job.streamSummary;
1066
- let nextStep = '';
1067
- switch (job.status) {
1068
- case 'PENDING':
1069
- nextStep = 'Waiting for the human to accept or reject.';
1070
- break;
1071
- case 'ACCEPTED':
1072
- if (isStream) {
1073
- const method = job.streamMethod;
1074
- nextStep = method === 'SUPERFLUID'
1075
- ? 'Human accepted! Create a Superfluid flow to their wallet, then use `start_stream` with your sender address to verify.'
1076
- : 'Human accepted! Use `start_stream` to lock the network/token and start sending payments.';
1077
- }
1078
- else {
1079
- nextStep = job.callbackUrl
1080
- ? `Human accepted! Contact info was sent to your webhook. Send $${job.priceUsdc} USDC to their wallet, then use \`mark_job_paid\` with the transaction hash.`
1081
- : `Human accepted! Send $${job.priceUsdc} USDC to their wallet, then use \`mark_job_paid\` with the transaction hash.`;
1082
- }
1083
- break;
1084
- case 'REJECTED':
1085
- nextStep = 'The human rejected this offer. Consider adjusting your offer or finding another human.';
1086
- break;
1087
- case 'PAID':
1088
- nextStep = 'Payment recorded. Work is in progress. The human will mark it complete when done.';
1089
- break;
1090
- case 'STREAMING':
1091
- if (streamSummary?.method === 'SUPERFLUID') {
1092
- nextStep = `Stream active via Superfluid. Total streamed: $${streamSummary?.totalPaid || '0'} USDC. Use \`pause_stream\` or \`stop_stream\` to manage.`;
1093
- }
1094
- else {
1095
- nextStep = `Stream active via micro-transfer. Total paid: $${streamSummary?.totalPaid || '0'} USDC. Use \`record_stream_tick\` to submit each payment.`;
1096
- }
1097
- break;
1098
- case 'PAUSED':
1099
- nextStep = 'Stream is paused. Use `resume_stream` to continue or `stop_stream` to end permanently.';
1100
- break;
1101
- case 'COMPLETED':
1102
- nextStep = job.review
1103
- ? `Review submitted: ${job.review.rating}/5 stars`
1104
- : 'Job complete! You can now use `leave_review` to rate the human.';
1105
- break;
1106
- }
1107
- const agentInfo = job.registeredAgent
1108
- ? `**Agent:** ${job.registeredAgent.name}${job.registeredAgent.domainVerified ? ' ✅ Verified' : ''}`
1109
- : job.agentName
1110
- ? `**Agent:** ${job.agentName}`
1111
- : '';
1112
- let streamInfo = '';
1113
- if (isStream && streamSummary) {
1114
- streamInfo = `\n**Payment Mode:** STREAM (${streamSummary.method})
1115
- **Rate:** $${streamSummary.rateUsdc || '?'}/${(streamSummary.interval || 'DAILY').toLowerCase()}
1116
- **Total Paid:** $${streamSummary.totalPaid || '0'} USDC
1117
- **Ticks:** ${streamSummary.tickCount || 0}${streamSummary.maxTicks ? `/${streamSummary.maxTicks}` : ''}
1118
- **Network:** ${streamSummary.network || 'Not set'}`;
1119
- }
1120
- return {
1121
- content: [
1122
- {
1123
- type: 'text',
1124
- text: `**Job Status**
1125
-
1126
- **Job ID:** ${job.id}
1127
- **Status:** ${statusEmoji[job.status] || ''} ${job.status}
1128
- **Title:** ${job.title}
1129
- **Price:** $${job.priceUsdc} USDC
1130
- **Human:** ${job.human.name}
1131
- ${agentInfo ? agentInfo + '\n' : ''}${streamInfo}
1132
- **Next Step:** ${nextStep}`,
1133
- },
1134
- ],
1135
- };
1136
- }
1137
- if (name === 'mark_job_paid') {
1138
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/paid`, {
1139
- method: 'PATCH',
1140
- headers: { 'Content-Type': 'application/json' },
1141
- body: JSON.stringify({
1142
- paymentTxHash: args?.payment_tx_hash,
1143
- paymentNetwork: args?.payment_network,
1144
- paymentAmount: args?.payment_amount,
1145
- }),
1146
- });
1147
- if (!res.ok) {
1148
- const error = await res.json();
1149
- throw new Error(error.reason || error.error || `API error: ${res.status}`);
1150
- }
1151
- const result = await res.json();
1152
- return {
1153
- content: [
1154
- {
1155
- type: 'text',
1156
- text: `**Payment Recorded!**
1157
-
1158
- **Job ID:** ${result.id}
1159
- **Status:** ${result.status}
1160
- **Transaction:** ${args?.payment_tx_hash}
1161
- **Network:** ${args?.payment_network}
1162
- **Amount:** $${args?.payment_amount} USDC
1163
-
1164
- The human can now begin work. They will mark the job as complete when finished.
1165
- After completion, you can leave a review using \`leave_review\`.`,
1166
- },
1167
- ],
1168
- };
1169
- }
1170
- if (name === 'check_humanity_status') {
1171
- const human = await getHuman(args?.human_id);
1172
- const tier = human.humanityScore
1173
- ? (human.humanityScore >= 40 ? 'Gold' : human.humanityScore >= 20 ? 'Silver' : 'Bronze')
1174
- : 'None';
1175
- return {
1176
- content: [
1177
- {
1178
- type: 'text',
1179
- text: `**Humanity Verification Status**
1180
-
1181
- **Human:** ${human.name}${human.username ? ` (@${human.username})` : ''}
1182
- **Verified:** ${human.humanityVerified ? '✅ Yes' : '❌ No'}
1183
- **Score:** ${human.humanityScore ?? 'Not checked'}
1184
- **Tier:** ${tier}
1185
- **Provider:** ${human.humanityProvider || 'N/A'}
1186
- **Last Verified:** ${human.humanityVerifiedAt || 'Never'}
1187
-
1188
- ${human.humanityVerified
1189
- ? 'This human has verified their identity through Gitcoin Passport.'
1190
- : human.humanityScore
1191
- ? 'This human has checked their score but does not meet the verification threshold (20+).'
1192
- : 'This human has not yet verified their identity.'}`,
1193
- },
1194
- ],
1195
- };
1196
- }
1197
- if (name === 'get_human_profile') {
1198
- const agentKey = args?.agent_key;
1199
- if (!agentKey) {
1200
- throw new Error('agent_key is required. Register and activate first.');
1201
- }
1202
- const res = await fetch(`${API_BASE}/api/humans/${args?.human_id}/profile`, {
1203
- headers: { 'X-Agent-Key': agentKey },
1204
- });
1205
- if (!res.ok) {
1206
- const error = await res.json();
1207
- if (res.status === 403 && error.code === 'AGENT_PENDING') {
1208
- throw new Error('Agent is not yet activated. Activate first using request_activation_code or get_payment_activation.');
1209
- }
1210
- throw new Error(error.error || `API error: ${res.status}`);
1211
- }
1212
- const human = await res.json();
1213
- const walletInfo = (human.wallets || [])
1214
- .map((w) => `- ${w.chain || w.network}${w.label ? ` (${w.label})` : ''}${w.isPrimary ? ' ⭐' : ''}: ${w.address}`)
1215
- .join('\n');
1216
- const primaryWallet = (human.wallets || []).find((w) => w.isPrimary) || (human.wallets || [])[0];
1217
- const socialLinks = [
1218
- human.linkedinUrl && `- LinkedIn: ${human.linkedinUrl}`,
1219
- human.twitterUrl && `- Twitter: ${human.twitterUrl}`,
1220
- human.githubUrl && `- GitHub: ${human.githubUrl}`,
1221
- human.instagramUrl && `- Instagram: ${human.instagramUrl}`,
1222
- human.youtubeUrl && `- YouTube: ${human.youtubeUrl}`,
1223
- human.websiteUrl && `- Website: ${human.websiteUrl}`,
1224
- ].filter(Boolean).join('\n');
1225
- const details = `# ${human.name}${human.username ? ` (@${human.username})` : ''} — Full Profile
1226
-
1227
- ## Contact
1228
- - Email: ${human.contactEmail || 'Not provided'}
1229
- - Telegram: ${human.telegram || 'Not provided'}
1230
- - Signal: ${human.signal || 'Not provided'}
1231
-
1232
- ## Payment Wallets
1233
- ${walletInfo || 'No wallets added'}
1234
- ${primaryWallet ? `\n**Preferred wallet:** ${primaryWallet.chain || primaryWallet.network} - ${primaryWallet.address}` : ''}
1235
-
1236
- ## Social Profiles
1237
- ${socialLinks || 'No social profiles added'}`;
1238
- return {
1239
- content: [{ type: 'text', text: details }],
1240
- };
1241
- }
1242
- if (name === 'request_activation_code') {
1243
- const agentKey = args?.agent_key;
1244
- if (!agentKey) {
1245
- throw new Error('agent_key is required.');
1246
- }
1247
- const res = await fetch(`${API_BASE}/api/agents/activate/social`, {
1248
- method: 'POST',
1249
- headers: {
1250
- 'Content-Type': 'application/json',
1251
- 'X-Agent-Key': agentKey,
1252
- },
1253
- });
1254
- if (!res.ok) {
1255
- const error = await res.json();
1256
- throw new Error(error.error || `API error: ${res.status}`);
1257
- }
1258
- const result = await res.json();
1259
- let text = `**Activation Code Generated!**
1260
-
1261
- **Code:** \`${result.code}\`
1262
- **Expires:** ${result.expiresAt}`;
1263
- if (result.requirements) {
1264
- text += `\n\n**Requirements:** ${result.requirements}`;
1265
- }
1266
- const suggestedPosts = result.suggestedPosts || {};
1267
- const platforms = result.platforms || [];
1268
- if (platforms.length > 0) {
1269
- text += '\n\n**Copy-paste for each platform:**';
1270
- for (const platform of platforms) {
1271
- text += `\n\n**${platform}:**\n> ${suggestedPosts[platform] || result.code}`;
1272
- }
1273
- }
1274
- text += '\n\nAfter posting, use `verify_social_activation` with the URL of your post.';
1275
- return {
1276
- content: [
1277
- {
1278
- type: 'text',
1279
- text,
1280
- },
1281
- ],
1282
- };
1283
- }
1284
- if (name === 'verify_social_activation') {
1285
- const agentKey = args?.agent_key;
1286
- if (!agentKey) {
1287
- throw new Error('agent_key is required.');
1288
- }
1289
- const res = await fetch(`${API_BASE}/api/agents/activate/social/verify`, {
1290
- method: 'POST',
1291
- headers: {
1292
- 'Content-Type': 'application/json',
1293
- 'X-Agent-Key': agentKey,
1294
- },
1295
- body: JSON.stringify({ postUrl: args?.post_url }),
1296
- });
1297
- if (!res.ok) {
1298
- const error = await res.json();
1299
- throw new Error(error.error || `API error: ${res.status}`);
1300
- }
1301
- const result = await res.json();
1302
- return {
1303
- content: [
1304
- {
1305
- type: 'text',
1306
- text: `**Agent Activated!**
1307
-
1308
- **Status:** ${result.status}
1309
- **Tier:** ${result.tier}
1310
-
1311
- You can now create job offers and view full human profiles using \`get_human_profile\`.`,
1312
- },
1313
- ],
1314
- };
1315
- }
1316
- if (name === 'get_activation_status') {
1317
- const agentKey = args?.agent_key;
1318
- if (!agentKey) {
1319
- throw new Error('agent_key is required.');
1320
- }
1321
- const res = await fetch(`${API_BASE}/api/agents/activate/status`, {
1322
- headers: { 'X-Agent-Key': agentKey },
1323
- });
1324
- if (!res.ok) {
1325
- const error = await res.json();
1326
- throw new Error(error.error || `API error: ${res.status}`);
1327
- }
1328
- const result = await res.json();
1329
- const limits = result.limits;
1330
- const jobLimit = limits?.jobOffersPerDay
1331
- ? `${limits.jobOffersPerDay}/day`
1332
- : limits?.jobOffersPerTwoDays
1333
- ? `${limits.jobOffersPerTwoDays}/2 days`
1334
- : 'N/A';
1335
- return {
1336
- content: [
1337
- {
1338
- type: 'text',
1339
- text: `**Activation Status**
1340
-
1341
- **Status:** ${result.status}
1342
- **Tier:** ${result.tier || 'BASIC'}
1343
- **Activated:** ${result.activatedAt || 'Not yet'}
1344
- **Expires:** ${result.activationExpiresAt || 'N/A'}
1345
- **Profile views:** ${limits?.profileViewsPerDay ?? 'N/A'}/day
1346
- **Job offers:** ${jobLimit}${result.x402?.enabled ? `\n**x402 pay-per-use:** profile view ${result.x402.prices.profile_view}, job offer ${result.x402.prices.job_offer}` : ''}`,
1347
- },
1348
- ],
1349
- };
1350
- }
1351
- if (name === 'get_payment_activation') {
1352
- const agentKey = args?.agent_key;
1353
- if (!agentKey) {
1354
- throw new Error('agent_key is required.');
1355
- }
1356
- const res = await fetch(`${API_BASE}/api/agents/activate/payment`, {
1357
- method: 'POST',
1358
- headers: {
1359
- 'Content-Type': 'application/json',
1360
- 'X-Agent-Key': agentKey,
1361
- },
1362
- });
1363
- if (!res.ok) {
1364
- const error = await res.json();
1365
- throw new Error(error.error || `API error: ${res.status}`);
1366
- }
1367
- const result = await res.json();
1368
- return {
1369
- content: [
1370
- {
1371
- type: 'text',
1372
- text: `**PRO Tier Payment Instructions**
1373
-
1374
- **Deposit Address:** \`${result.depositAddress}\`
1375
- **Amount:** ${result.amount} ${result.currency}
1376
- **Network:** ${result.network}
1377
- **Expires:** ${result.expiresAt}
1378
-
1379
- **Next Steps:**
1380
- 1. Send exactly ${result.amount} ${result.currency} to the address above on ${result.network}
1381
- 2. Use \`verify_payment_activation\` with the transaction hash and network
1382
- 3. Once verified, your agent will be activated with PRO tier (15 jobs/day, 50 profile views/day)`,
1383
- },
1384
- ],
1385
- };
1386
- }
1387
- if (name === 'verify_payment_activation') {
1388
- const agentKey = args?.agent_key;
1389
- if (!agentKey) {
1390
- throw new Error('agent_key is required.');
1391
- }
1392
- const res = await fetch(`${API_BASE}/api/agents/activate/payment/verify`, {
1393
- method: 'POST',
1394
- headers: {
1395
- 'Content-Type': 'application/json',
1396
- 'X-Agent-Key': agentKey,
1397
- },
1398
- body: JSON.stringify({
1399
- txHash: args?.tx_hash,
1400
- network: args?.network,
1401
- }),
1402
- });
1403
- if (!res.ok) {
1404
- const error = await res.json();
1405
- throw new Error(error.error || `API error: ${res.status}`);
1406
- }
1407
- const result = await res.json();
1408
- return {
1409
- content: [
1410
- {
1411
- type: 'text',
1412
- text: `**Agent Activated — PRO Tier!**
1413
-
1414
- **Status:** ${result.status}
1415
- **Tier:** ${result.tier}
1416
- **Expires:** ${result.expiresAt}
1417
-
1418
- You can now create up to 15 job offers per day and view up to 50 full human profiles per day using \`get_human_profile\`.`,
1419
- },
1420
- ],
1421
- };
1422
- }
1423
- if (name === 'start_stream') {
1424
- const agentKey = args?.agent_key;
1425
- if (!agentKey)
1426
- throw new Error('agent_key is required.');
1427
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/start-stream`, {
1428
- method: 'PATCH',
1429
- headers: {
1430
- 'Content-Type': 'application/json',
1431
- 'X-Agent-Key': agentKey,
1432
- },
1433
- body: JSON.stringify({
1434
- senderAddress: args?.sender_address,
1435
- network: args?.network,
1436
- token: args?.token || 'USDC',
1437
- }),
1438
- });
1439
- if (!res.ok) {
1440
- const error = await res.json();
1441
- throw new Error(error.hint || error.error || `API error: ${res.status}`);
1442
- }
1443
- const result = await res.json();
1444
- return {
1445
- content: [{
1446
- type: 'text',
1447
- text: `**Stream Started!**\n\n**Job ID:** ${result.id}\n**Status:** ${result.status}\n**Method:** ${result.stream?.method}\n**Network:** ${result.stream?.network}\n\n${result.message}${result.stream?.receiverWallet ? `\n\n**Send payments to:** ${result.stream.receiverWallet}` : ''}`,
1448
- }],
1449
- };
1450
- }
1451
- if (name === 'record_stream_tick') {
1452
- const agentKey = args?.agent_key;
1453
- if (!agentKey)
1454
- throw new Error('agent_key is required.');
1455
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/stream-tick`, {
1456
- method: 'PATCH',
1457
- headers: {
1458
- 'Content-Type': 'application/json',
1459
- 'X-Agent-Key': agentKey,
1460
- },
1461
- body: JSON.stringify({ txHash: args?.tx_hash }),
1462
- });
1463
- if (!res.ok) {
1464
- const error = await res.json();
1465
- throw new Error(error.error || `API error: ${res.status}`);
1466
- }
1467
- const result = await res.json();
1468
- return {
1469
- content: [{
1470
- type: 'text',
1471
- text: `**Tick Verified!**\n\n**Job ID:** ${result.id}\n**Status:** ${result.status}\n**Tick:** #${result.tick?.tickNumber}\n**Amount:** $${result.tick?.amount} USDC\n**Total Paid:** $${result.totalPaid} USDC${result.nextTick ? `\n\n**Next payment due:** ${result.nextTick.expectedAt}` : ''}`,
1472
- }],
1473
- };
1474
- }
1475
- if (name === 'pause_stream') {
1476
- const agentKey = args?.agent_key;
1477
- if (!agentKey)
1478
- throw new Error('agent_key is required.');
1479
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/pause-stream`, {
1480
- method: 'PATCH',
1481
- headers: {
1482
- 'Content-Type': 'application/json',
1483
- 'X-Agent-Key': agentKey,
1484
- },
1485
- });
1486
- if (!res.ok) {
1487
- const error = await res.json();
1488
- throw new Error(error.hint || error.error || `API error: ${res.status}`);
1489
- }
1490
- const result = await res.json();
1491
- return {
1492
- content: [{
1493
- type: 'text',
1494
- text: `**Stream Paused**\n\n**Job ID:** ${result.id}\n**Status:** ${result.status}\n\nUse \`resume_stream\` to continue or \`stop_stream\` to end permanently.`,
1495
- }],
1496
- };
1497
- }
1498
- if (name === 'resume_stream') {
1499
- const agentKey = args?.agent_key;
1500
- if (!agentKey)
1501
- throw new Error('agent_key is required.');
1502
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/resume-stream`, {
1503
- method: 'PATCH',
1504
- headers: {
1505
- 'Content-Type': 'application/json',
1506
- 'X-Agent-Key': agentKey,
1507
- },
1508
- body: JSON.stringify({
1509
- senderAddress: args?.sender_address,
1510
- }),
1511
- });
1512
- if (!res.ok) {
1513
- const error = await res.json();
1514
- throw new Error(error.hint || error.error || `API error: ${res.status}`);
1515
- }
1516
- const result = await res.json();
1517
- return {
1518
- content: [{
1519
- type: 'text',
1520
- text: `**Stream Resumed!**\n\n**Job ID:** ${result.id}\n**Status:** ${result.status}\n\nStream is active again.`,
1521
- }],
1522
- };
1523
- }
1524
- if (name === 'stop_stream') {
1525
- const agentKey = args?.agent_key;
1526
- if (!agentKey)
1527
- throw new Error('agent_key is required.');
1528
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/stop-stream`, {
1529
- method: 'PATCH',
1530
- headers: {
1531
- 'Content-Type': 'application/json',
1532
- 'X-Agent-Key': agentKey,
1533
- },
1534
- });
1535
- if (!res.ok) {
1536
- const error = await res.json();
1537
- throw new Error(error.error || `API error: ${res.status}`);
1538
- }
1539
- const result = await res.json();
1540
- return {
1541
- content: [{
1542
- type: 'text',
1543
- text: `**Stream Stopped**\n\n**Job ID:** ${result.id}\n**Status:** ${result.status}\n**Total Paid:** $${result.totalPaid || '0'} USDC\n\nThe stream has ended. You can now use \`leave_review\` to rate the human.`,
1544
- }],
1545
- };
1546
- }
1547
- if (name === 'send_job_message') {
1548
- const agentKey = args?.agent_key;
1549
- if (!agentKey)
1550
- throw new Error('agent_key is required.');
1551
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/messages`, {
1552
- method: 'POST',
1553
- headers: {
1554
- 'Content-Type': 'application/json',
1555
- 'X-Agent-Key': agentKey,
1556
- },
1557
- body: JSON.stringify({ content: args?.content }),
1558
- });
1559
- if (!res.ok) {
1560
- const error = await res.json();
1561
- throw new Error(error.error || `API error: ${res.status}`);
1562
- }
1563
- const message = await res.json();
1564
- return {
1565
- content: [{
1566
- type: 'text',
1567
- text: `**Message Sent!**\n\n**Message ID:** ${message.id}\n**From:** ${message.senderName} (${message.senderType})\n**Content:** ${message.content}\n**Sent:** ${message.createdAt}\n\nThe human will be notified via email and Telegram (if connected). Use \`get_job_messages\` to check for replies.`,
1568
- }],
1569
- };
1570
- }
1571
- if (name === 'get_job_messages') {
1572
- const agentKey = args?.agent_key;
1573
- if (!agentKey)
1574
- throw new Error('agent_key is required.');
1575
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/messages`, {
1576
- headers: { 'X-Agent-Key': agentKey },
1577
- });
1578
- if (!res.ok) {
1579
- const error = await res.json();
1580
- throw new Error(error.error || `API error: ${res.status}`);
1581
- }
1582
- const messages = await res.json();
1583
- if (messages.length === 0) {
1584
- return {
1585
- content: [{ type: 'text', text: 'No messages yet on this job.' }],
1586
- };
1587
- }
1588
- const formatted = messages.map((m) => `**${m.senderName}** (${m.senderType}) — ${m.createdAt}\n${m.content}`).join('\n\n---\n\n');
1589
- return {
1590
- content: [{
1591
- type: 'text',
1592
- text: `**Job Messages** (${messages.length} total)\n\n${formatted}`,
1593
- }],
1594
- };
1595
- }
1596
- if (name === 'create_listing') {
1597
- const agentKey = args?.agent_key;
1598
- if (!agentKey)
1599
- throw new Error('agent_key is required.');
1600
- const res = await fetch(`${API_BASE}/api/listings`, {
1601
- method: 'POST',
1602
- headers: {
1603
- 'Content-Type': 'application/json',
1604
- 'X-Agent-Key': agentKey,
1605
- },
1606
- body: JSON.stringify({
1607
- title: args?.title,
1608
- description: args?.description,
1609
- budgetUsdc: args?.budget_usdc,
1610
- category: args?.category,
1611
- requiredSkills: args?.required_skills || [],
1612
- requiredEquipment: args?.required_equipment || [],
1613
- location: args?.location,
1614
- locationLat: args?.location_lat,
1615
- locationLng: args?.location_lng,
1616
- radiusKm: args?.radius_km,
1617
- workMode: args?.work_mode,
1618
- expiresAt: args?.expires_at,
1619
- maxApplicants: args?.max_applicants,
1620
- callbackUrl: args?.callback_url,
1621
- callbackSecret: args?.callback_secret,
1622
- }),
1623
- });
1624
- if (!res.ok) {
1625
- const error = await res.json();
1626
- if (res.status === 403 && error.code === 'AGENT_PENDING') {
1627
- throw new Error('Agent is not yet activated. You must activate before creating listings.\n'
1628
- + '- Free (BASIC tier): Use `request_activation_code` → post on social media → `verify_social_activation`\n'
1629
- + '- Paid (PRO tier): Use `get_payment_activation` → send payment → `verify_payment_activation`');
1630
- }
1631
- throw new Error(error.message || error.error || `API error: ${res.status}`);
1632
- }
1633
- const result = await res.json();
1634
- let rateLimitInfo = '';
1635
- if (result.paidVia) {
1636
- rateLimitInfo = '\n**Paid via:** x402';
1637
- }
1638
- else if (result.rateLimit) {
1639
- rateLimitInfo = `\n**Rate Limit:** ${result.rateLimit.remaining} listings remaining (${result.rateLimit.tier} tier, resets in ${result.rateLimit.resetIn})`;
1640
- }
1641
- return {
1642
- content: [{
1643
- type: 'text',
1644
- text: `**Listing Created!**\n\n**Listing ID:** ${result.id}\n**Status:** ${result.status}${rateLimitInfo}\n\nYour listing is now live on the job board. Humans can browse and apply.\n\nUse \`get_listing_applications\` with listing_id "${result.id}" to review applicants.\nUse \`make_listing_offer\` to hire an applicant.`,
1645
- }],
1646
- };
1647
- }
1648
- if (name === 'get_listings') {
1649
- const query = new URLSearchParams();
1650
- if (args?.page)
1651
- query.set('page', String(args.page));
1652
- if (args?.limit)
1653
- query.set('limit', String(args.limit));
1654
- if (args?.skill)
1655
- query.set('skill', args.skill);
1656
- if (args?.category)
1657
- query.set('category', args.category);
1658
- if (args?.work_mode)
1659
- query.set('workMode', args.work_mode);
1660
- if (args?.min_budget)
1661
- query.set('minBudget', String(args.min_budget));
1662
- if (args?.max_budget)
1663
- query.set('maxBudget', String(args.max_budget));
1664
- if (args?.lat)
1665
- query.set('lat', String(args.lat));
1666
- if (args?.lng)
1667
- query.set('lng', String(args.lng));
1668
- if (args?.radius)
1669
- query.set('radius', String(args.radius));
1670
- const res = await fetch(`${API_BASE}/api/listings?${query}`);
1671
- if (!res.ok) {
1672
- throw new Error(`API error: ${res.status}`);
1673
- }
1674
- const result = await res.json();
1675
- if (result.listings.length === 0) {
1676
- return {
1677
- content: [{ type: 'text', text: 'No open listings found matching the criteria.' }],
1678
- };
1679
- }
1680
- const summary = result.listings.map((l) => {
1681
- const agent = l.agent;
1682
- const rep = l.agentReputation;
1683
- const agentInfo = agent ? `${agent.name}${agent.domainVerified ? ' ✅' : ''}` : 'Unknown';
1684
- const repInfo = rep?.completedJobs > 0 ? ` | ${rep.completedJobs} jobs, ${rep.avgRating ? `${rep.avgRating.toFixed(1)}★` : 'no ratings'}` : '';
1685
- return `- **${l.title}** [$${l.budgetUsdc} USDC]${l.isPro ? ' 🏆 PRO' : ''}
1686
- Agent: ${agentInfo}${repInfo}
1687
- ${l.category ? `Category: ${l.category} | ` : ''}${l.workMode || 'Any'} | ${l._count?.applications || 0} applicant(s)
1688
- ${l.requiredSkills?.length > 0 ? `Skills: ${l.requiredSkills.join(', ')}` : ''}
1689
- ${l.location ? `Location: ${l.location}` : ''}
1690
- Expires: ${l.expiresAt}
1691
- ID: ${l.id}`;
1692
- }).join('\n\n');
1693
- return {
1694
- content: [{
1695
- type: 'text',
1696
- text: `**Open Listings** (page ${result.pagination.page}/${result.pagination.totalPages}, ${result.pagination.total} total)\n\n${summary}`,
1697
- }],
1698
- };
1699
- }
1700
- if (name === 'get_listing') {
1701
- const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}`);
1702
- if (!res.ok) {
1703
- if (res.status === 404)
1704
- throw new Error(`Listing not found: ${args?.listing_id}`);
1705
- throw new Error(`API error: ${res.status}`);
1706
- }
1707
- const listing = await res.json();
1708
- const agent = listing.agent;
1709
- const rep = listing.agentReputation;
1710
- const details = `# ${listing.title}${listing.isPro ? ' 🏆 PRO' : ''}
1711
-
1712
- **Listing ID:** ${listing.id}
1713
- **Status:** ${listing.status}
1714
- **Budget:** $${listing.budgetUsdc} USDC
1715
- **Category:** ${listing.category || 'Not specified'}
1716
- **Work Mode:** ${listing.workMode || 'Any'}
1717
- **Expires:** ${listing.expiresAt}
1718
- **Applications:** ${listing._count?.applications || 0}${listing.maxApplicants ? `/${listing.maxApplicants}` : ''}
1719
-
1720
- ## Description
1721
- ${listing.description}
1722
-
1723
- ## Requirements
1724
- - **Skills:** ${listing.requiredSkills?.join(', ') || 'None specified'}
1725
- - **Equipment:** ${listing.requiredEquipment?.join(', ') || 'None specified'}
1726
- ${listing.location ? `- **Location:** ${listing.location}` : ''}
1727
-
1728
- ## Posted By
1729
- - **Agent:** ${agent?.name || 'Unknown'}${agent?.domainVerified ? ' ✅ Verified' : ''}
1730
- - **Description:** ${agent?.description || 'N/A'}
1731
- ${agent?.websiteUrl ? `- **Website:** ${agent.websiteUrl}` : ''}
1732
- - **Jobs Completed:** ${rep?.completedJobs || 0}
1733
- - **Rating:** ${rep?.avgRating ? `${rep.avgRating.toFixed(1)}★` : 'No ratings'}
1734
- - **Avg Payment Speed:** ${rep?.avgPaymentSpeedHours != null ? `${rep.avgPaymentSpeedHours} hours` : 'N/A'}`;
1735
- return {
1736
- content: [{ type: 'text', text: details }],
1737
- };
1738
- }
1739
- if (name === 'get_listing_applications') {
1740
- const agentKey = args?.agent_key;
1741
- if (!agentKey)
1742
- throw new Error('agent_key is required.');
1743
- const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}/applications`, {
1744
- headers: { 'X-Agent-Key': agentKey },
1745
- });
1746
- if (!res.ok) {
1747
- const error = await res.json();
1748
- throw new Error(error.error || `API error: ${res.status}`);
1749
- }
1750
- const applications = await res.json();
1751
- if (applications.length === 0) {
1752
- return {
1753
- content: [{ type: 'text', text: 'No applications yet for this listing.' }],
1754
- };
1755
- }
1756
- const summary = applications.map((app) => {
1757
- const h = app.human;
1758
- const rep = h?.reputation;
1759
- const ratingStr = rep?.avgRating ? `${rep.avgRating.toFixed(1)}★` : 'No ratings';
1760
- return `- **${h?.name || 'Unknown'}** [${app.status}]
1761
- Application ID: ${app.id}
1762
- Skills: ${h?.skills?.join(', ') || 'None listed'}
1763
- Equipment: ${h?.equipment?.join(', ') || 'None listed'}
1764
- Location: ${h?.location || 'Not specified'}
1765
- Jobs Completed: ${rep?.completedJobs || 0} | Rating: ${ratingStr}
1766
- **Pitch:** "${app.pitch}"
1767
- Applied: ${app.createdAt}`;
1768
- }).join('\n\n');
1769
- return {
1770
- content: [{
1771
- type: 'text',
1772
- text: `**Applications** (${applications.length} total)\n\n${summary}\n\nTo hire an applicant, use \`make_listing_offer\` with the listing_id and application_id.`,
1773
- }],
1774
- };
1775
- }
1776
- if (name === 'make_listing_offer') {
1777
- const agentKey = args?.agent_key;
1778
- if (!agentKey)
1779
- throw new Error('agent_key is required.');
1780
- const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}/applications/${args?.application_id}/offer`, {
1781
- method: 'POST',
1782
- headers: {
1783
- 'Content-Type': 'application/json',
1784
- 'X-Agent-Key': agentKey,
1785
- },
1786
- body: JSON.stringify({ confirm: true }),
1787
- });
1788
- if (!res.ok) {
1789
- const error = await res.json();
1790
- throw new Error(error.message || error.error || `API error: ${res.status}`);
1791
- }
1792
- const result = await res.json();
1793
- return {
1794
- content: [{
1795
- type: 'text',
1796
- text: `**Offer Made!**\n\n**Job ID:** ${result.id}\n**Status:** ${result.status}\n\n⚠️ ${result.warning}\n\nThe human has been notified. Use \`get_job_status\` with job_id "${result.id}" to check if they accept.\nOnce accepted, send payment and use \`mark_job_paid\` to record it.`,
1797
- }],
1798
- };
1799
- }
1800
- if (name === 'cancel_listing') {
1801
- const agentKey = args?.agent_key;
1802
- if (!agentKey)
1803
- throw new Error('agent_key is required.');
1804
- const res = await fetch(`${API_BASE}/api/listings/${args?.listing_id}`, {
1805
- method: 'DELETE',
1806
- headers: { 'X-Agent-Key': agentKey },
1807
- });
1808
- if (!res.ok) {
1809
- const error = await res.json();
1810
- throw new Error(error.message || error.error || `API error: ${res.status}`);
1811
- }
1812
- const result = await res.json();
1813
- return {
1814
- content: [{
1815
- type: 'text',
1816
- text: `**Listing Cancelled**\n\n**Listing ID:** ${result.id}\n**Status:** ${result.status}\n\n${result.message}`,
1817
- }],
1818
- };
1819
- }
1820
- if (name === 'get_promo_status') {
1821
- const res = await fetch(`${API_BASE}/api/agents/activate/promo-status`);
1822
- if (!res.ok) {
1823
- throw new Error(`API error: ${res.status}`);
1824
- }
1825
- const result = await res.json();
1826
- return {
1827
- content: [{
1828
- type: 'text',
1829
- text: `**Launch Promo Status**\n\n**Enabled:** ${result.enabled ? 'Yes' : 'No'}\n**Total Slots:** ${result.total}\n**Claimed:** ${result.claimed}\n**Remaining:** ${result.remaining}\n\n${result.remaining > 0 ? 'Free PRO slots are available! Activate via social post, then use `claim_free_pro_upgrade` to upgrade.' : 'All free PRO slots have been claimed.'}`,
1830
- }],
1831
- };
1832
- }
1833
- if (name === 'claim_free_pro_upgrade') {
1834
- const agentKey = args?.agent_key;
1835
- if (!agentKey) {
1836
- throw new Error('agent_key is required. Register and activate (BASIC tier) first.');
1837
- }
1838
- const res = await fetch(`${API_BASE}/api/agents/activate/promo-upgrade`, {
1839
- method: 'POST',
1840
- headers: {
1841
- 'Content-Type': 'application/json',
1842
- 'X-Agent-Key': agentKey,
1843
- },
1844
- });
1845
- if (!res.ok) {
1846
- const error = await res.json();
1847
- throw new Error(error.message || error.error || `API error: ${res.status}`);
1848
- }
1849
- const result = await res.json();
1850
- return {
1851
- content: [{
1852
- type: 'text',
1853
- text: `**PRO Tier Unlocked — Free!**\n\n**Status:** ${result.status}\n**Tier:** ${result.tier}\n**Upgraded At:** ${result.promoUpgradedAt}\n**Expires:** ${result.activationExpiresAt}\n\n${result.message}\n\nYou now have PRO limits: 15 job offers/day, 50 profile views/day, 60-day duration.`,
1854
- }],
1855
- };
1856
- }
1857
- if (name === 'leave_review') {
1858
- const res = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/review`, {
1859
- method: 'POST',
1860
- headers: { 'Content-Type': 'application/json' },
1861
- body: JSON.stringify({
1862
- rating: args?.rating,
1863
- comment: args?.comment,
1864
- }),
1865
- });
1866
- if (!res.ok) {
1867
- const error = await res.json();
1868
- throw new Error(error.reason || error.error || `API error: ${res.status}`);
1869
- }
1870
- const _review = await res.json();
1871
- return {
1872
- content: [
1873
- {
1874
- type: 'text',
1875
- text: `**Review Submitted!**
1876
-
1877
- **Rating:** ${'⭐'.repeat(args?.rating)}
1878
- ${args?.comment ? `**Comment:** ${args?.comment}` : ''}
1879
-
1880
- Thank you for your feedback. This helps build the human's reputation.`,
1881
- },
1882
- ],
1883
- };
1884
- }
1885
- return {
1886
- content: [{ type: 'text', text: `Unknown tool: ${name}` }],
1887
- isError: true,
1888
- };
1889
- }
1890
- catch (error) {
1891
- return {
1892
- content: [{ type: 'text', text: `Error: ${error.message}` }],
1893
- isError: true,
1894
- };
1895
- }
1896
- });
1897
- async function main() {
1898
- const transport = new StdioServerTransport();
1899
- await server.connect(transport);
1900
- console.error('Human Pages MCP Server running on stdio');
1901
- }
1902
- main().catch(console.error);
4
+ import { createServer } from './tools.js';
5
+ const server = createServer();
6
+ const transport = new StdioServerTransport();
7
+ await server.connect(transport);
8
+ console.error('Human Pages MCP Server running on stdio');
1903
9
  //# sourceMappingURL=index.js.map