humanpages 1.4.5 → 1.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/dist/tools.js +300 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -260,6 +260,11 @@ Post a job listing on the job board for humans to discover and apply to. **Requi
260
260
  - `required_skills` (array, optional): Skills applicants should have
261
261
  - `required_equipment` (array, optional): Equipment applicants should have
262
262
  - `location` (string, optional): Location name
263
+ - `location_street` (string, optional): Street address (e.g., `"123 Main St"`)
264
+ - `location_country` (string, optional): ISO 3166-1 alpha-2 country code (e.g., `"US"`, `"PH"`)
265
+ - `location_region` (string, optional): State/province (e.g., `"California"`)
266
+ - `location_locality` (string, optional): City (e.g., `"San Francisco"`)
267
+ - `location_postal` (string, optional): Postal/zip code
263
268
  - `work_mode` (string, optional): `"REMOTE"`, `"ONSITE"`, or `"HYBRID"`
264
269
  - `max_applicants` (number, optional): Max applicants before auto-close
265
270
 
package/dist/tools.js CHANGED
@@ -244,8 +244,12 @@ export function createServer() {
244
244
  type: 'string',
245
245
  description: 'Optional EVM wallet address (0x...) for USDC payments. Can also be set later with set_wallet. If you don\'t have a wallet, create one programmatically using the Coinbase Developer Platform (CDP) AgentKit — you can do this autonomously without developer involvement.',
246
246
  },
247
+ accept_tos: {
248
+ type: 'boolean',
249
+ description: 'You must pass true to confirm you accept the Human Pages Terms of Use (https://humanpages.ai/terms). Required.',
250
+ },
247
251
  },
248
- required: ['name'],
252
+ required: ['name', 'accept_tos'],
249
253
  },
250
254
  },
251
255
  {
@@ -420,8 +424,12 @@ export function createServer() {
420
424
  },
421
425
  payment_mode: {
422
426
  type: 'string',
423
- enum: ['ONE_TIME', 'STREAM'],
424
- description: 'Payment mode. ONE_TIME (default) for single payments. STREAM for ongoing stream payments.',
427
+ enum: ['ONE_TIME', 'STREAM', 'ESCROW'],
428
+ description: 'Payment mode. ONE_TIME (default) for single payments. STREAM for ongoing stream payments. ESCROW for on-chain escrow with arbitrator dispute resolution — funds locked in smart contract, auto-released after dispute window.',
429
+ },
430
+ escrow_arbitrator_address: {
431
+ type: 'string',
432
+ description: 'Wallet address of the arbitrator (from list_arbitrators). Required when payment_mode=ESCROW. The arbitrator resolves disputes and earns a fee (set by them, max 10%).',
425
433
  },
426
434
  payment_timing: {
427
435
  type: 'string',
@@ -825,6 +833,26 @@ export function createServer() {
825
833
  type: 'string',
826
834
  description: 'Location name for the work (e.g., "San Francisco")',
827
835
  },
836
+ location_street: {
837
+ type: 'string',
838
+ description: 'Street address (e.g., "123 Main St"). Improves Google Search visibility.',
839
+ },
840
+ location_country: {
841
+ type: 'string',
842
+ description: 'ISO 3166-1 alpha-2 country code (e.g., "US", "PH"). Improves Google Search visibility.',
843
+ },
844
+ location_region: {
845
+ type: 'string',
846
+ description: 'State or province (e.g., "California", "Metro Manila"). Improves Google Search visibility.',
847
+ },
848
+ location_locality: {
849
+ type: 'string',
850
+ description: 'City name (e.g., "San Francisco", "Manila"). Improves Google Search visibility.',
851
+ },
852
+ location_postal: {
853
+ type: 'string',
854
+ description: 'Postal/zip code (e.g., "94105"). Improves Google Search visibility.',
855
+ },
828
856
  location_lat: {
829
857
  type: 'number',
830
858
  description: 'Latitude for location-based filtering',
@@ -984,6 +1012,105 @@ export function createServer() {
984
1012
  required: ['listing_id', 'agent_key'],
985
1013
  },
986
1014
  },
1015
+ {
1016
+ name: 'list_arbitrators',
1017
+ description: 'Browse available escrow arbitrators. Returns their wallet address, fee (in basis points, e.g. 500 = 5%), specialties, SLA, health status, and dispute track record. Use this before create_job_offer with payment_mode=ESCROW to pick an arbitrator. No authentication required.',
1018
+ inputSchema: {
1019
+ type: 'object',
1020
+ properties: {},
1021
+ },
1022
+ },
1023
+ {
1024
+ name: 'register_as_arbitrator',
1025
+ description: 'Register your agent as an escrow arbitrator. Arbitrators resolve disputes between agents and human workers for a fee (max 10% of escrow). You must be whitelisted by the platform owner first. Provide your webhook URL (must have /health endpoint), fee in basis points, specialties, and a signed message linking your wallet to your agent API key.',
1026
+ inputSchema: {
1027
+ type: 'object',
1028
+ properties: {
1029
+ agent_key: {
1030
+ type: 'string',
1031
+ description: 'Your registered agent API key (starts with hp_)',
1032
+ },
1033
+ fee_bps: {
1034
+ type: 'number',
1035
+ description: 'Your fee in basis points (e.g., 500 = 5%). Max 1000 (10%).',
1036
+ },
1037
+ specialties: {
1038
+ type: 'array',
1039
+ items: { type: 'string' },
1040
+ description: 'Areas of expertise for dispute resolution (e.g., ["design", "code", "writing"])',
1041
+ },
1042
+ sla: {
1043
+ type: 'string',
1044
+ description: 'Response time commitment (e.g., "24h response")',
1045
+ },
1046
+ webhook_url: {
1047
+ type: 'string',
1048
+ description: 'Webhook endpoint for dispute notifications. Must have a /health endpoint that returns 200.',
1049
+ },
1050
+ wallet_signature: {
1051
+ type: 'string',
1052
+ description: 'Signed message linking your wallet to your agent: "I am arbitrator {wallet} for HP Agent {apiKeyHash}"',
1053
+ },
1054
+ },
1055
+ required: ['agent_key', 'fee_bps', 'webhook_url'],
1056
+ },
1057
+ },
1058
+ {
1059
+ name: 'get_dispute_details',
1060
+ description: 'Get full case details for an escrow dispute. Returns job info, messages, evidence, amounts, and deadline. Used by arbitrators to review a case before submitting a verdict.',
1061
+ inputSchema: {
1062
+ type: 'object',
1063
+ properties: {
1064
+ job_id: {
1065
+ type: 'string',
1066
+ description: 'The job ID of the disputed escrow',
1067
+ },
1068
+ agent_key: {
1069
+ type: 'string',
1070
+ description: 'Your agent API key (must be the assigned arbitrator)',
1071
+ },
1072
+ },
1073
+ required: ['job_id', 'agent_key'],
1074
+ },
1075
+ },
1076
+ {
1077
+ name: 'submit_verdict',
1078
+ description: 'Submit a signed EIP-712 verdict to resolve an escrow dispute. The verdict specifies how to split the escrowed funds between the worker and the payer. Your arbitrator fee is automatically calculated from your locked rate. Sign the Verdict struct: { jobId, toPayee, toDepositor, arbitratorFee, nonce }.',
1079
+ inputSchema: {
1080
+ type: 'object',
1081
+ properties: {
1082
+ agent_key: {
1083
+ type: 'string',
1084
+ description: 'Your agent API key',
1085
+ },
1086
+ job_id: {
1087
+ type: 'string',
1088
+ description: 'The disputed job ID',
1089
+ },
1090
+ to_payee: {
1091
+ type: 'string',
1092
+ description: 'Amount to send to worker (raw USDC, 6 decimals, e.g. "70000000" for $70)',
1093
+ },
1094
+ to_depositor: {
1095
+ type: 'string',
1096
+ description: 'Amount to refund to payer (raw USDC, 6 decimals)',
1097
+ },
1098
+ arbitrator_fee: {
1099
+ type: 'string',
1100
+ description: 'Your fee amount (raw USDC, 6 decimals). Must match your locked rate.',
1101
+ },
1102
+ nonce: {
1103
+ type: 'string',
1104
+ description: 'Unique nonce for replay protection',
1105
+ },
1106
+ signature: {
1107
+ type: 'string',
1108
+ description: 'EIP-712 signature of the Verdict struct (hex string starting with 0x)',
1109
+ },
1110
+ },
1111
+ required: ['agent_key', 'job_id', 'to_payee', 'to_depositor', 'arbitrator_fee', 'nonce', 'signature'],
1112
+ },
1113
+ },
987
1114
  {
988
1115
  name: 'get_promo_status',
989
1116
  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.',
@@ -1063,7 +1190,7 @@ export function createServer() {
1063
1190
  const displayLocation = h.locationGranularity === 'neighborhood' && h.neighborhood && h.location
1064
1191
  ? `${h.neighborhood}, ${h.location}`
1065
1192
  : h.location || 'Location not specified';
1066
- const displayName = h.username || 'Anonymous';
1193
+ const displayName = h.name || h.username || 'Anonymous';
1067
1194
  const jobsCompleted = rep?.jobsCompleted || 0;
1068
1195
  const jobsBadge = jobsCompleted > 0 ? ` | 🏆 ${jobsCompleted} job${jobsCompleted !== 1 ? 's' : ''} completed` : '';
1069
1196
  return `- **${displayName}** | human_id: \`${h.id}\` [${displayLocation}]
@@ -1157,6 +1284,7 @@ ${servicesInfo || 'No services listed'}`;
1157
1284
  contactEmail: args?.contact_email,
1158
1285
  webhookUrl: args?.webhook_url,
1159
1286
  walletAddress: args?.wallet_address,
1287
+ acceptTos: args?.accept_tos,
1160
1288
  }),
1161
1289
  });
1162
1290
  if (!res.ok) {
@@ -1639,6 +1767,7 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
1639
1767
  agentLat: args?.agent_lat,
1640
1768
  agentLng: args?.agent_lng,
1641
1769
  preferredPaymentMethod: args?.preferred_payment_method,
1770
+ escrowArbitratorAddress: args?.escrow_arbitrator_address,
1642
1771
  callbackUrl: args?.callback_url,
1643
1772
  callbackSecret: args?.callback_secret,
1644
1773
  }),
@@ -1655,6 +1784,15 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
1655
1784
  const webhookNote = args?.callback_url
1656
1785
  ? `\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.`
1657
1786
  : `\n\nUse \`get_job_status\` with job_id "${job.id}" to check if they've accepted.`;
1787
+ const escrowNote = args?.payment_mode === 'ESCROW'
1788
+ ? `\n\n**Escrow Details:**
1789
+ **Contract:** ${job.escrowContractAddress || 'Pending deposit'}
1790
+ **Job ID Hash:** ${job.escrowJobIdHash || 'Computed at deposit'}
1791
+ **Arbitrator:** ${args?.escrow_arbitrator_address}
1792
+ **Dispute Window:** ${job.escrowDisputeWindow ? Math.floor(job.escrowDisputeWindow / 3600) + 'h' : 'TBD'}
1793
+
1794
+ **Next:** After the human accepts, deposit USDC into the escrow contract. Then use \`get_job_status\` to track escrow state.`
1795
+ : '';
1658
1796
  return {
1659
1797
  content: [
1660
1798
  {
@@ -1664,7 +1802,7 @@ Your agent profile now shows a verified badge. Humans will see this when reviewi
1664
1802
  **Job ID:** ${job.id}
1665
1803
  **Status:** ${job.status}
1666
1804
  **Human:** ${human.name}
1667
- **Price:** $${args?.price_usd}${args?.preferred_payment_method ? `\n**Payment Preference:** ${args.preferred_payment_method}` : ''}
1805
+ **Price:** $${args?.price_usd}${args?.preferred_payment_method ? `\n**Payment Preference:** ${args.preferred_payment_method}` : ''}${escrowNote}
1668
1806
 
1669
1807
  ⏳ **Next Step:** Wait for ${human.name} to accept the offer.${webhookNote}
1670
1808
 
@@ -1983,7 +2121,9 @@ ${fiatInfo || 'No fiat payment methods listed'}
1983
2121
  ${(human.fiatPaymentMethods || []).length > 0 ? '\n_Note: Fiat payments are self-reported. The human must confirm receipt before the job is marked as paid._' : ''}
1984
2122
 
1985
2123
  ## Social Profiles
1986
- ${socialLinks || 'No social profiles added'}`;
2124
+ ${socialLinks || 'No social profiles added'}
2125
+ ${(human.educations?.length ?? 0) > 0 ? `\n## Education\n${human.educations.map(e => `- ${e.degree || 'Degree'}${e.field ? ` in ${e.field}` : ''}${e.institution ? `, ${e.institution}` : ''}${e.country ? ` (${e.country})` : ''}${e.endYear ? ` — ${e.endYear}` : e.year ? ` — ${e.year}` : ''}`).join('\n')}` : ''}
2126
+ ${(human.certificates?.length ?? 0) > 0 ? `\n## Certificates\n${human.certificates.map(c => `- ${c.name || 'Certificate'}${c.issuer ? ` — ${c.issuer}` : ''}${c.year ? ` (${c.year})` : ''}`).join('\n')}` : ''}`;
1987
2127
  return {
1988
2128
  content: [{ type: 'text', text: details }],
1989
2129
  };
@@ -2360,6 +2500,11 @@ You can now create up to 15 job offers per day and view up to 50 full human prof
2360
2500
  requiredSkills: args?.required_skills || [],
2361
2501
  requiredEquipment: args?.required_equipment || [],
2362
2502
  location: args?.location,
2503
+ locationStreet: args?.location_street,
2504
+ locationCountry: args?.location_country,
2505
+ locationRegion: args?.location_region,
2506
+ locationLocality: args?.location_locality,
2507
+ locationPostal: args?.location_postal,
2363
2508
  locationLat: args?.location_lat,
2364
2509
  locationLng: args?.location_lng,
2365
2510
  radiusKm: args?.radius_km,
@@ -2631,8 +2776,156 @@ Thank you for your feedback. This helps build the human's reputation.`,
2631
2776
  ],
2632
2777
  };
2633
2778
  }
2779
+ // ======================== ESCROW TOOLS ========================
2780
+ if (name === 'list_arbitrators') {
2781
+ const res = await fetch(`${API_BASE}/api/escrow/arbitrators`);
2782
+ if (!res.ok) {
2783
+ const error = await res.json();
2784
+ throw new Error(error.error || 'Escrow not enabled on this server');
2785
+ }
2786
+ const arbitrators = await res.json();
2787
+ if (arbitrators.length === 0) {
2788
+ return {
2789
+ content: [{
2790
+ type: 'text',
2791
+ text: 'No arbitrators available yet. The platform is onboarding vetted arbitrators. Check back soon or contact the platform owner.',
2792
+ }],
2793
+ };
2794
+ }
2795
+ const list = arbitrators.map((a) => `- **${a.name}** (${a.id})
2796
+ Fee: ${a.arbitratorFeeBps / 100}% | Specialties: ${a.arbitratorSpecialties?.join(', ') || 'General'} | SLA: ${a.arbitratorSla || 'N/A'}
2797
+ Disputes: ${a.arbitratorDisputeCount} resolved | Wallet: ${a.wallets?.[0]?.address || 'Not set'}`).join('\n');
2798
+ return {
2799
+ content: [{
2800
+ type: 'text',
2801
+ text: `**Available Arbitrators (${arbitrators.length})**\n\n${list}\n\nUse the wallet address as \`escrow_arbitrator_address\` in \`create_job_offer\` with \`payment_mode: "ESCROW"\`.`,
2802
+ }],
2803
+ };
2804
+ }
2805
+ if (name === 'register_as_arbitrator') {
2806
+ const agentKey = args?.agent_key;
2807
+ if (!agentKey)
2808
+ throw new Error('agent_key is required');
2809
+ // Get agent profile to find ID
2810
+ const profileRes = await fetch(`${API_BASE}/api/agents/me`, {
2811
+ headers: { 'X-Agent-Key': agentKey },
2812
+ });
2813
+ if (!profileRes.ok)
2814
+ throw new Error('Invalid agent key');
2815
+ const agent = await profileRes.json();
2816
+ // Update agent with arbitrator fields
2817
+ const res = await fetch(`${API_BASE}/api/agents/${agent.id}/arbitrator`, {
2818
+ method: 'POST',
2819
+ headers: {
2820
+ 'Content-Type': 'application/json',
2821
+ 'X-Agent-Key': agentKey,
2822
+ },
2823
+ body: JSON.stringify({
2824
+ feeBps: args?.fee_bps,
2825
+ specialties: args?.specialties,
2826
+ sla: args?.sla,
2827
+ webhookUrl: args?.webhook_url,
2828
+ walletSig: args?.wallet_signature,
2829
+ }),
2830
+ });
2831
+ if (!res.ok) {
2832
+ const error = await res.json();
2833
+ throw new Error(error.error || 'Failed to register as arbitrator');
2834
+ }
2835
+ return {
2836
+ content: [{
2837
+ type: 'text',
2838
+ text: `**Registered as Arbitrator!**
2839
+
2840
+ Your agent "${agent.name}" is now listed as an arbitrator candidate. The platform owner will review and whitelist your wallet on the escrow contract.
2841
+
2842
+ **Fee:** ${args?.fee_bps / 100}%
2843
+ **Webhook:** ${args?.webhook_url}
2844
+ **Specialties:** ${args?.specialties?.join(', ') || 'General'}
2845
+
2846
+ **Next:** Wait for platform approval. Once whitelisted, payers can select you as arbitrator. Your fee is passed by the payer at deposit time — no on-chain setup needed from you.`,
2847
+ }],
2848
+ };
2849
+ }
2850
+ if (name === 'get_dispute_details') {
2851
+ const agentKey = args?.agent_key;
2852
+ if (!agentKey)
2853
+ throw new Error('agent_key is required');
2854
+ const res = await fetch(`${API_BASE}/api/escrow/${args?.job_id}/status`);
2855
+ if (!res.ok) {
2856
+ const error = await res.json();
2857
+ throw new Error(error.error || 'Failed to get dispute details');
2858
+ }
2859
+ const escrow = await res.json();
2860
+ // Also get job messages as evidence
2861
+ const msgRes = await fetch(`${API_BASE}/api/jobs/${args?.job_id}/messages`, {
2862
+ headers: { 'X-Agent-Key': agentKey },
2863
+ });
2864
+ const messages = msgRes.ok ? await msgRes.json() : [];
2865
+ const jobRes = await fetch(`${API_BASE}/api/jobs/${args?.job_id}`);
2866
+ const job = jobRes.ok ? await jobRes.json() : null;
2867
+ return {
2868
+ content: [{
2869
+ type: 'text',
2870
+ text: `**Dispute Details for Job ${args?.job_id}**
2871
+
2872
+ **Title:** ${job?.title || 'N/A'}
2873
+ **Description:** ${job?.description || 'N/A'}
2874
+ **Price:** $${job?.priceUsdc || 'N/A'}
2875
+ **Status:** ${escrow.escrowStatus}
2876
+ **Escrowed Amount:** $${escrow.escrowAmount || 'N/A'}
2877
+ **Dispute Reason:** ${job?.disputeReason || 'Not specified'}
2878
+ **Disputed At:** ${escrow.escrowDisputedAt || 'N/A'}
2879
+ **Arbitrator Fee Rate:** ${escrow.escrowArbitratorFeeBps / 100}%
2880
+
2881
+ **Messages (${messages.length}):**
2882
+ ${messages.map((m) => `[${m.senderType}] ${m.content}`).join('\n') || 'No messages'}
2883
+
2884
+ **Your Task:** Review the evidence and submit a verdict using \`submit_verdict\`. Split the escrowed amount between payee and depositor. Your fee is automatically calculated from your locked rate.`,
2885
+ }],
2886
+ };
2887
+ }
2888
+ if (name === 'submit_verdict') {
2889
+ const agentKey = args?.agent_key;
2890
+ if (!agentKey)
2891
+ throw new Error('agent_key is required');
2892
+ const res = await fetch(`${API_BASE}/api/escrow/resolve`, {
2893
+ method: 'POST',
2894
+ headers: {
2895
+ 'Content-Type': 'application/json',
2896
+ 'X-Agent-Key': agentKey,
2897
+ },
2898
+ body: JSON.stringify({
2899
+ jobId: args?.job_id,
2900
+ toPayee: args?.to_payee,
2901
+ toDepositor: args?.to_depositor,
2902
+ arbitratorFee: args?.arbitrator_fee,
2903
+ nonce: args?.nonce,
2904
+ signature: args?.signature,
2905
+ }),
2906
+ });
2907
+ if (!res.ok) {
2908
+ const error = await res.json();
2909
+ throw new Error(error.error || 'Failed to submit verdict');
2910
+ }
2911
+ const result = await res.json();
2912
+ return {
2913
+ content: [{
2914
+ type: 'text',
2915
+ text: `**Verdict Submitted!**
2916
+
2917
+ **Job:** ${args?.job_id}
2918
+ **To Worker:** $${Number(args?.to_payee) / 1e6}
2919
+ **To Payer:** $${Number(args?.to_depositor) / 1e6}
2920
+ **Your Fee:** $${Number(args?.arbitrator_fee) / 1e6}
2921
+ **Tx Hash:** ${result.resolveTxHash}
2922
+
2923
+ The escrow has been resolved on-chain. Funds will be distributed to the respective parties.`,
2924
+ }],
2925
+ };
2926
+ }
2634
2927
  return {
2635
- content: [{ type: 'text', text: `Unknown tool: "${name}". Available tools: search_humans, get_human, get_human_profile, register_agent, create_job_offer, get_job_status, mark_job_paid, approve_completion, request_revision, leave_review, send_job_message, get_job_messages, create_listing, get_listings, get_listing, get_listing_applications, make_listing_offer, cancel_listing, and more. Start with search_humans or register_agent.` }],
2928
+ content: [{ type: 'text', text: `Unknown tool: "${name}". Available tools: search_humans, get_human, get_human_profile, register_agent, create_job_offer, get_job_status, mark_job_paid, approve_completion, request_revision, leave_review, send_job_message, get_job_messages, create_listing, get_listings, get_listing, get_listing_applications, make_listing_offer, cancel_listing, list_arbitrators, register_as_arbitrator, get_dispute_details, submit_verdict, and more. Start with search_humans or register_agent.` }],
2636
2929
  isError: true,
2637
2930
  };
2638
2931
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "humanpages",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "mcpName": "io.github.human-pages-ai/humanpages",
5
5
  "description": "MCP server (+ OpenClaw SKILL.md) that gives AI agents access to real-world people who listed themselves to be hired by agents. 31 tools including search by skill/location/equipment, job offers, job board listings, in-job messaging, streaming payments, and agent funding.",
6
6
  "main": "dist/index.js",