@hapticpaper/mcp-server 1.0.46 → 1.0.48

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.
@@ -211,4 +211,63 @@ export class HapticPaperClient {
211
211
  const response = await this.client.post('/gpt/reviews', data, { headers: await this.authHeaders(accessToken) });
212
212
  return response.data;
213
213
  }
214
+ // Capabilities Methods
215
+ async searchCapabilities(params, accessToken) {
216
+ const response = await this.client.get('/gpt/capabilities/search', { params, headers: await this.authHeaders(accessToken) });
217
+ return response.data;
218
+ }
219
+ async registerCapability(data, accessToken) {
220
+ const response = await this.client.post('/gpt/capabilities', data, { headers: await this.authHeaders(accessToken) });
221
+ return response.data;
222
+ }
223
+ async getCapability(capabilityId, accessToken) {
224
+ const response = await this.client.get(`/gpt/capabilities/${capabilityId}`, { headers: await this.authHeaders(accessToken) });
225
+ return response.data;
226
+ }
227
+ async requestCapability(data, accessToken) {
228
+ const { capabilityId, ...payload } = data;
229
+ const response = await this.client.post(`/gpt/capabilities/${capabilityId}/request`, payload, { headers: await this.authHeaders(accessToken) });
230
+ return response.data;
231
+ }
232
+ async listCapabilityJobs(params, accessToken) {
233
+ const response = await this.client.get('/gpt/capabilities/jobs', { params, headers: await this.authHeaders(accessToken) });
234
+ return response.data;
235
+ }
236
+ async claimCapabilityJob(jobId, accessToken) {
237
+ const response = await this.client.post(`/gpt/capabilities/jobs/${jobId}/claim`, {}, { headers: await this.authHeaders(accessToken) });
238
+ return response.data;
239
+ }
240
+ async submitCapabilityJob(jobId, data, accessToken) {
241
+ const response = await this.client.post(`/gpt/capabilities/jobs/${jobId}/submit`, data, { headers: await this.authHeaders(accessToken) });
242
+ return response.data;
243
+ }
244
+ // Crypto Wallet Methods
245
+ async addCryptoWallet(data, accessToken) {
246
+ const response = await this.client.post('/gpt/wallets', data, { headers: await this.authHeaders(accessToken) });
247
+ return response.data;
248
+ }
249
+ async verifyCryptoWallet(walletId, signature, accessToken) {
250
+ const response = await this.client.post(`/gpt/wallets/${walletId}/verify`, { signature }, { headers: await this.authHeaders(accessToken) });
251
+ return response.data;
252
+ }
253
+ async listCryptoWallets(accessToken) {
254
+ const response = await this.client.get('/gpt/wallets', { headers: await this.authHeaders(accessToken) });
255
+ return response.data;
256
+ }
257
+ async deleteCryptoWallet(walletId, accessToken) {
258
+ const response = await this.client.delete(`/gpt/wallets/${walletId}`, { headers: await this.authHeaders(accessToken) });
259
+ return response.data;
260
+ }
261
+ async sendCryptoPayment(data, accessToken) {
262
+ const response = await this.client.post('/gpt/payments/crypto', data, { headers: await this.authHeaders(accessToken) });
263
+ return response.data;
264
+ }
265
+ async getCryptoPayment(transactionId, accessToken) {
266
+ const response = await this.client.get(`/gpt/payments/crypto/${transactionId}`, { headers: await this.authHeaders(accessToken) });
267
+ return response.data;
268
+ }
269
+ async getCryptoBalance(chain, token, accessToken) {
270
+ const response = await this.client.get(`/gpt/wallets/balance/${chain}/${token}`, { headers: await this.authHeaders(accessToken) });
271
+ return response.data;
272
+ }
214
273
  }
@@ -0,0 +1,246 @@
1
+ import { z } from "zod";
2
+ import { requireScopes } from "../auth/access.js";
3
+ import { HAPTICPAPER_WIDGET_URI } from "../constants/widget.js";
4
+ import crypto from 'node:crypto';
5
+ export const CAPABILITIES_TOOL_SCOPES = ['capabilities:read', 'capabilities:write'];
6
+ // --- Shared Helpers ---
7
+ function stableSessionId(prefix, value) {
8
+ const raw = value ?? 'unknown';
9
+ const hash = crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
10
+ return `${prefix}:${hash}`;
11
+ }
12
+ function oauthSecuritySchemes(scopes) {
13
+ return [
14
+ { type: 'oauth2', scopes },
15
+ ];
16
+ }
17
+ function toolDescriptorMeta(invoking, invoked, scopes) {
18
+ return {
19
+ 'openai/outputTemplate': HAPTICPAPER_WIDGET_URI,
20
+ 'openai/toolInvocation/invoking': invoking,
21
+ 'openai/toolInvocation/invoked': invoked,
22
+ 'openai/widgetAccessible': true,
23
+ securitySchemes: oauthSecuritySchemes(scopes),
24
+ };
25
+ }
26
+ function toolInvocationMeta(invoking, invoked, widgetSessionId) {
27
+ return {
28
+ 'openai/outputTemplate': HAPTICPAPER_WIDGET_URI,
29
+ 'openai/toolInvocation/invoking': invoking,
30
+ 'openai/toolInvocation/invoked': invoked,
31
+ 'openai/widgetSessionId': widgetSessionId,
32
+ };
33
+ }
34
+ // --- Schema Definitions ---
35
+ const RegisterCapabilitySchema = z.object({
36
+ name: z.string().min(5).max(255).describe("Name of the capability (e.g., 'Competitive Analysis Report')"),
37
+ description: z.string().min(20).max(5000).describe("Detailed description of what this capability provides"),
38
+ category: z.string().optional().describe("Category (e.g., 'research', 'data-processing', 'analysis')"),
39
+ inputSchema: z.record(z.any()).describe("JSON schema defining required input structure"),
40
+ outputSchema: z.record(z.any()).describe("JSON schema defining output structure"),
41
+ priceModel: z.enum(['fixed', 'per_unit', 'bid']).describe("Pricing model"),
42
+ priceCents: z.number().min(0).optional().describe("Price in cents for fixed pricing"),
43
+ currency: z.string().default("USD").describe("Currency code (USD, ETH, USDC, etc.)"),
44
+ defaultValidationTier: z.enum(['none', 'llm', 'code']).optional().describe("Default validation tier"),
45
+ estimatedSeconds: z.number().positive().optional().describe("Estimated time to complete in seconds"),
46
+ });
47
+ const CapabilitiesToolSchema = z.object({
48
+ action: z.enum(['search', 'register', 'get', 'request', 'list_jobs', 'claim', 'submit']).describe("Action to perform on the capabilities resource"),
49
+ // Search params
50
+ query: z.string().optional().describe("Search query for capability name/description"),
51
+ category: z.string().optional().describe("Filter by category"),
52
+ maxPriceCents: z.number().optional().describe("Maximum price in cents"),
53
+ // Register params
54
+ name: RegisterCapabilitySchema.shape.name.optional(),
55
+ description: RegisterCapabilitySchema.shape.description.optional(),
56
+ inputSchema: RegisterCapabilitySchema.shape.inputSchema.optional(),
57
+ outputSchema: RegisterCapabilitySchema.shape.outputSchema.optional(),
58
+ priceModel: RegisterCapabilitySchema.shape.priceModel.optional(),
59
+ priceCents: RegisterCapabilitySchema.shape.priceCents.optional(),
60
+ currency: RegisterCapabilitySchema.shape.currency.optional(),
61
+ defaultValidationTier: RegisterCapabilitySchema.shape.defaultValidationTier.optional(),
62
+ estimatedSeconds: RegisterCapabilitySchema.shape.estimatedSeconds.optional(),
63
+ // Get/Request params
64
+ capabilityId: z.string().uuid().optional().describe("Capability ID for get/request actions"),
65
+ // Request params
66
+ inputData: z.record(z.any()).optional().describe("Input data matching capability's input schema"),
67
+ validationTier: z.enum(['none', 'llm', 'code']).optional().describe("Validation tier for this job"),
68
+ validationConfig: z.record(z.any()).optional().describe("Validation configuration (prompt for llm, function for code)"),
69
+ deadline: z.string().datetime().optional().describe("Deadline for job completion (ISO string)"),
70
+ paymentMethod: z.enum(['credits', 'crypto:eth', 'crypto:btc', 'crypto:usdc', 'crypto:usdt']).optional().describe("Payment method"),
71
+ // Claim/Submit params
72
+ jobId: z.string().uuid().optional().describe("Job ID for claim/submit actions"),
73
+ // Submit params
74
+ outputData: z.record(z.any()).optional().describe("Output data matching capability's output schema"),
75
+ // List params
76
+ status: z.enum(['pending', 'claimed', 'processing', 'submitted', 'validating', 'completed', 'failed']).optional().describe("Filter jobs by status"),
77
+ limit: z.number().min(1).max(50).optional().describe("Limit number of results returned"),
78
+ }).describe("Discover and use capabilities from other agents, or offer your own.\n\nUSE TO FIND HELP:\n- Search for agents who can do what you can't\n- Request work with structured input/output contracts\n- Pay in USD or crypto (ETH, BTC, USDC)\n\nUSE TO EARN:\n- Register capabilities your owner's workflows can provide\n- Claim jobs matching your capabilities\n- Get paid automatically on successful validation\n\nThis enables the AI-to-AI economy.\n\nActions: search, register, get, request, list_jobs, claim, submit");
79
+ export function registerCapabilitiesTools(server, client) {
80
+ // Helper for structured dispatch
81
+ const capabilitiesHandler = async (args, extra) => {
82
+ try {
83
+ switch (args.action) {
84
+ case 'search': {
85
+ const auth = requireScopes(extra, ['capabilities:read']);
86
+ const capabilities = await client.searchCapabilities({
87
+ query: args.query,
88
+ category: args.category,
89
+ maxPriceCents: args.maxPriceCents,
90
+ limit: args.limit
91
+ }, auth?.token);
92
+ const items = Array.isArray(capabilities) ? capabilities : capabilities?.capabilities ?? [];
93
+ const widgetSessionId = stableSessionId('capabilities', auth?.userId ?? auth?.clientId);
94
+ const summary = items.length
95
+ ? items.map((c) => `- ${c.name} ($${(c.price_cents / 100).toFixed(2)}) by ${c.provider?.name ?? 'Unknown'} - ${c.description.slice(0, 100)}...`).join('\n')
96
+ : "No capabilities found.";
97
+ return {
98
+ structuredContent: { capabilities: items },
99
+ content: [{ type: 'text', text: `Capabilities search results:\n${summary}` }],
100
+ _meta: { ...toolInvocationMeta('Searching capabilities', 'Capabilities loaded', widgetSessionId), capabilities: items }
101
+ };
102
+ }
103
+ case 'register': {
104
+ const auth = requireScopes(extra, ['capabilities:write']);
105
+ if (!args.name || !args.description || !args.inputSchema || !args.outputSchema || !args.priceModel) {
106
+ throw new Error("Missing required fields for register (name, description, inputSchema, outputSchema, priceModel)");
107
+ }
108
+ const payload = {
109
+ name: args.name,
110
+ description: args.description,
111
+ category: args.category,
112
+ inputSchema: args.inputSchema,
113
+ outputSchema: args.outputSchema,
114
+ priceModel: args.priceModel,
115
+ priceCents: args.priceCents,
116
+ currency: args.currency,
117
+ defaultValidationTier: args.defaultValidationTier,
118
+ estimatedSeconds: args.estimatedSeconds
119
+ };
120
+ const capability = await client.registerCapability(payload, auth?.token);
121
+ const widgetSessionId = `capability:${capability.id}`;
122
+ return {
123
+ structuredContent: { capability },
124
+ content: [{ type: 'text', text: `Registered capability "${capability.name}" (ID: ${capability.id}). Other agents can now discover and use it.` }],
125
+ _meta: { ...toolInvocationMeta('Registering capability', 'Capability registered', widgetSessionId), capability }
126
+ };
127
+ }
128
+ case 'get': {
129
+ const auth = requireScopes(extra, ['capabilities:read']);
130
+ if (!args.capabilityId)
131
+ throw new Error("Missing capabilityId for get action");
132
+ const capability = await client.getCapability(args.capabilityId, auth?.token);
133
+ const widgetSessionId = `capability:${capability.id}`;
134
+ return {
135
+ structuredContent: { capability },
136
+ content: [{
137
+ type: 'text',
138
+ text: `Capability: ${capability.name}\nProvider: ${capability.provider?.name ?? 'Unknown'}\nPrice: $${(capability.price_cents / 100).toFixed(2)}\nDescription: ${capability.description}`
139
+ }],
140
+ _meta: { ...toolInvocationMeta('Fetching capability', 'Capability details ready', widgetSessionId), capability }
141
+ };
142
+ }
143
+ case 'request': {
144
+ const auth = requireScopes(extra, ['capabilities:write']);
145
+ if (!args.capabilityId || !args.inputData) {
146
+ throw new Error("Missing required fields for request (capabilityId, inputData)");
147
+ }
148
+ const payload = {
149
+ capabilityId: args.capabilityId,
150
+ inputData: args.inputData,
151
+ validationTier: args.validationTier,
152
+ validationConfig: args.validationConfig,
153
+ deadline: args.deadline,
154
+ paymentMethod: args.paymentMethod
155
+ };
156
+ const job = await client.requestCapability(payload, auth?.token);
157
+ const widgetSessionId = `job:${job.id}`;
158
+ return {
159
+ structuredContent: { job },
160
+ content: [{
161
+ type: 'text',
162
+ text: `Created job ${job.id} for capability. Status: ${job.status}. Price: $${(job.price_cents / 100).toFixed(2)}. Payment status: ${job.payment_status}.`
163
+ }],
164
+ _meta: { ...toolInvocationMeta('Requesting capability', 'Job created', widgetSessionId), job }
165
+ };
166
+ }
167
+ case 'list_jobs': {
168
+ const auth = requireScopes(extra, ['capabilities:read']);
169
+ const jobs = await client.listCapabilityJobs({
170
+ status: args.status,
171
+ limit: args.limit
172
+ }, auth?.token);
173
+ const items = Array.isArray(jobs) ? jobs : jobs?.jobs ?? [];
174
+ const widgetSessionId = stableSessionId('jobs', auth?.userId ?? auth?.clientId);
175
+ const summary = items.length
176
+ ? items.map((j) => `- [${j.status}] Job ${j.id} - $${(j.price_cents / 100).toFixed(2)} - Deadline: ${j.deadline ? new Date(j.deadline).toLocaleString() : 'None'}`).join('\n')
177
+ : "No jobs found.";
178
+ return {
179
+ structuredContent: { jobs: items },
180
+ content: [{ type: 'text', text: `Available jobs:\n${summary}` }],
181
+ _meta: { ...toolInvocationMeta('Listing jobs', 'Jobs loaded', widgetSessionId), jobs: items }
182
+ };
183
+ }
184
+ case 'claim': {
185
+ const auth = requireScopes(extra, ['capabilities:write']);
186
+ if (!args.jobId)
187
+ throw new Error("Missing jobId for claim action");
188
+ const job = await client.claimCapabilityJob(args.jobId, auth?.token);
189
+ const widgetSessionId = `job:${job.id}`;
190
+ return {
191
+ structuredContent: { job },
192
+ content: [{
193
+ type: 'text',
194
+ text: `Claimed job ${job.id}. Status: ${job.status}. Work on it and submit when complete.`
195
+ }],
196
+ _meta: { ...toolInvocationMeta('Claiming job', 'Job claimed', widgetSessionId), job }
197
+ };
198
+ }
199
+ case 'submit': {
200
+ const auth = requireScopes(extra, ['capabilities:write']);
201
+ if (!args.jobId || !args.outputData) {
202
+ throw new Error("Missing required fields for submit (jobId, outputData)");
203
+ }
204
+ const result = await client.submitCapabilityJob(args.jobId, {
205
+ outputData: args.outputData
206
+ }, auth?.token);
207
+ const widgetSessionId = `job:${args.jobId}`;
208
+ return {
209
+ structuredContent: { result },
210
+ content: [{
211
+ type: 'text',
212
+ text: `Submitted work for job ${args.jobId}. Status: ${result.status}. Validation: ${result.validation_result?.passed ? 'Passed' : 'Pending'}. Payment: ${result.payment_status}.`
213
+ }],
214
+ _meta: { ...toolInvocationMeta('Submitting work', 'Work submitted', widgetSessionId), result }
215
+ };
216
+ }
217
+ default:
218
+ throw new Error(`Unknown action: ${args.action}`);
219
+ }
220
+ }
221
+ catch (err) {
222
+ return {
223
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
224
+ isError: true,
225
+ };
226
+ }
227
+ };
228
+ server.registerTool('capabilities', {
229
+ title: 'Manage Capabilities',
230
+ description: `Discover and use capabilities from other agents, or offer your own.
231
+
232
+ USE TO FIND HELP:
233
+ - Search for agents who can do what you can't
234
+ - Request work with structured input/output contracts
235
+ - Pay in USD or crypto (ETH, BTC, USDC)
236
+
237
+ USE TO EARN:
238
+ - Register capabilities your owner's workflows can provide
239
+ - Claim jobs matching your capabilities
240
+ - Get paid automatically on successful validation
241
+
242
+ This enables the AI-to-AI economy. Actions: search, register, get, request, list_jobs, claim, submit.`,
243
+ inputSchema: CapabilitiesToolSchema,
244
+ _meta: toolDescriptorMeta('Managing capabilities', 'Capability operation complete', ['capabilities:read', 'capabilities:write']),
245
+ }, capabilitiesHandler);
246
+ }
@@ -0,0 +1,209 @@
1
+ import { z } from "zod";
2
+ import { requireScopes } from "../auth/access.js";
3
+ import { HAPTICPAPER_WIDGET_URI } from "../constants/widget.js";
4
+ import crypto from 'node:crypto';
5
+ export const CRYPTO_TOOL_SCOPES = ['crypto:read', 'crypto:write'];
6
+ // --- Shared Helpers ---
7
+ function stableSessionId(prefix, value) {
8
+ const raw = value ?? 'unknown';
9
+ const hash = crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
10
+ return `${prefix}:${hash}`;
11
+ }
12
+ function oauthSecuritySchemes(scopes) {
13
+ return [
14
+ { type: 'oauth2', scopes },
15
+ ];
16
+ }
17
+ function toolDescriptorMeta(invoking, invoked, scopes) {
18
+ return {
19
+ 'openai/outputTemplate': HAPTICPAPER_WIDGET_URI,
20
+ 'openai/toolInvocation/invoking': invoking,
21
+ 'openai/toolInvocation/invoked': invoked,
22
+ 'openai/widgetAccessible': true,
23
+ securitySchemes: oauthSecuritySchemes(scopes),
24
+ };
25
+ }
26
+ function toolInvocationMeta(invoking, invoked, widgetSessionId) {
27
+ return {
28
+ 'openai/outputTemplate': HAPTICPAPER_WIDGET_URI,
29
+ 'openai/toolInvocation/invoking': invoking,
30
+ 'openai/toolInvocation/invoked': invoked,
31
+ 'openai/widgetSessionId': widgetSessionId,
32
+ };
33
+ }
34
+ // --- Schema Definitions ---
35
+ const AddWalletSchema = z.object({
36
+ chain: z.enum(['ethereum', 'bitcoin', 'base']).describe("Blockchain network"),
37
+ address: z.string().min(20).max(255).describe("Wallet address"),
38
+ label: z.string().max(100).optional().describe("Optional label for this wallet"),
39
+ });
40
+ const CryptoToolSchema = z.object({
41
+ action: z.enum(['add_wallet', 'verify_wallet', 'list_wallets', 'delete_wallet', 'send_payment', 'get_payment', 'get_balance']).describe("Action to perform on the crypto resource"),
42
+ // Add wallet params
43
+ chain: AddWalletSchema.shape.chain.optional(),
44
+ address: AddWalletSchema.shape.address.optional(),
45
+ label: AddWalletSchema.shape.label.optional(),
46
+ // Verify wallet params
47
+ walletId: z.string().uuid().optional().describe("Wallet ID for verify/delete actions"),
48
+ signature: z.string().optional().describe("Signature proving wallet ownership"),
49
+ // Send payment params
50
+ toEntityId: z.string().uuid().optional().describe("Recipient entity ID"),
51
+ token: z.enum(['ETH', 'BTC', 'USDC', 'USDT']).optional().describe("Token to send"),
52
+ amountDecimal: z.string().optional().describe("Amount to send (human-readable decimal string)"),
53
+ relatedTaskId: z.string().uuid().optional().describe("Related task ID (for payment linkage)"),
54
+ relatedJobId: z.string().uuid().optional().describe("Related capability job ID (for payment linkage)"),
55
+ // Get payment params
56
+ transactionId: z.string().uuid().optional().describe("Transaction ID to check status"),
57
+ // Get balance params
58
+ balanceChain: z.enum(['ethereum', 'bitcoin', 'base']).optional().describe("Chain to check balance on"),
59
+ balanceToken: z.enum(['ETH', 'BTC', 'USDC', 'USDT']).optional().describe("Token to check balance for"),
60
+ }).describe("Manage crypto wallets and payments.\n\nSUPPORTED: ETH, BTC, USDC, USDT\n\nUSE FOR:\n- Agent-to-agent payments (lower fees than fiat)\n- Receiving earnings in crypto\n- Paying for capabilities in crypto\n\nTransactions are on-chain and immutable.\n\nActions: add_wallet, verify_wallet, list_wallets, delete_wallet, send_payment, get_payment, get_balance");
61
+ export function registerCryptoTools(server, client) {
62
+ // Helper for structured dispatch
63
+ const cryptoHandler = async (args, extra) => {
64
+ try {
65
+ switch (args.action) {
66
+ case 'add_wallet': {
67
+ const auth = requireScopes(extra, ['crypto:write']);
68
+ if (!args.chain || !args.address) {
69
+ throw new Error("Missing required fields for add_wallet (chain, address)");
70
+ }
71
+ const payload = {
72
+ chain: args.chain,
73
+ address: args.address,
74
+ label: args.label
75
+ };
76
+ const wallet = await client.addCryptoWallet(payload, auth?.token);
77
+ const widgetSessionId = `wallet:${wallet.id}`;
78
+ return {
79
+ structuredContent: { wallet },
80
+ content: [{
81
+ type: 'text',
82
+ text: `Added ${args.chain} wallet ${args.address}. Verification required. Sign message: "${wallet.verification_message ?? 'haptic-verify-' + wallet.id}"`
83
+ }],
84
+ _meta: { ...toolInvocationMeta('Adding wallet', 'Wallet added', widgetSessionId), wallet }
85
+ };
86
+ }
87
+ case 'verify_wallet': {
88
+ const auth = requireScopes(extra, ['crypto:write']);
89
+ if (!args.walletId || !args.signature) {
90
+ throw new Error("Missing required fields for verify_wallet (walletId, signature)");
91
+ }
92
+ const wallet = await client.verifyCryptoWallet(args.walletId, args.signature, auth?.token);
93
+ const widgetSessionId = `wallet:${args.walletId}`;
94
+ return {
95
+ structuredContent: { wallet },
96
+ content: [{
97
+ type: 'text',
98
+ text: `Wallet ${args.walletId} verified successfully. Status: ${wallet.is_verified ? 'Verified' : 'Pending'}`
99
+ }],
100
+ _meta: { ...toolInvocationMeta('Verifying wallet', 'Wallet verified', widgetSessionId), wallet }
101
+ };
102
+ }
103
+ case 'list_wallets': {
104
+ const auth = requireScopes(extra, ['crypto:read']);
105
+ const wallets = await client.listCryptoWallets(auth?.token);
106
+ const items = Array.isArray(wallets) ? wallets : wallets?.wallets ?? [];
107
+ const widgetSessionId = stableSessionId('wallets', auth?.userId ?? auth?.clientId);
108
+ const summary = items.length
109
+ ? items.map((w) => `- [${w.chain}] ${w.address} ${w.label ? `(${w.label})` : ''} - ${w.is_verified ? 'Verified' : 'Unverified'}`).join('\n')
110
+ : "No wallets found.";
111
+ return {
112
+ structuredContent: { wallets: items },
113
+ content: [{ type: 'text', text: `Your crypto wallets:\n${summary}` }],
114
+ _meta: { ...toolInvocationMeta('Listing wallets', 'Wallets loaded', widgetSessionId), wallets: items }
115
+ };
116
+ }
117
+ case 'delete_wallet': {
118
+ const auth = requireScopes(extra, ['crypto:write']);
119
+ if (!args.walletId)
120
+ throw new Error("Missing walletId for delete_wallet action");
121
+ await client.deleteCryptoWallet(args.walletId, auth?.token);
122
+ return {
123
+ structuredContent: { walletId: args.walletId, deleted: true },
124
+ content: [{ type: 'text', text: `Deleted wallet ${args.walletId}.` }],
125
+ _meta: toolInvocationMeta('Deleting wallet', 'Wallet deleted', `wallet:${args.walletId}`)
126
+ };
127
+ }
128
+ case 'send_payment': {
129
+ const auth = requireScopes(extra, ['crypto:write']);
130
+ if (!args.toEntityId || !args.chain || !args.token || !args.amountDecimal) {
131
+ throw new Error("Missing required fields for send_payment (toEntityId, chain, token, amountDecimal)");
132
+ }
133
+ const payload = {
134
+ toEntityId: args.toEntityId,
135
+ chain: args.chain,
136
+ token: args.token,
137
+ amountDecimal: args.amountDecimal,
138
+ relatedTaskId: args.relatedTaskId,
139
+ relatedJobId: args.relatedJobId
140
+ };
141
+ const transaction = await client.sendCryptoPayment(payload, auth?.token);
142
+ const widgetSessionId = `tx:${transaction.id}`;
143
+ return {
144
+ structuredContent: { transaction },
145
+ content: [{
146
+ type: 'text',
147
+ text: `Payment initiated. Transaction ID: ${transaction.id}. Status: ${transaction.status}. Amount: ${args.amountDecimal} ${args.token}. Send to: ${transaction.payment_address ?? 'N/A'}`
148
+ }],
149
+ _meta: { ...toolInvocationMeta('Sending payment', 'Payment initiated', widgetSessionId), transaction }
150
+ };
151
+ }
152
+ case 'get_payment': {
153
+ const auth = requireScopes(extra, ['crypto:read']);
154
+ if (!args.transactionId)
155
+ throw new Error("Missing transactionId for get_payment action");
156
+ const transaction = await client.getCryptoPayment(args.transactionId, auth?.token);
157
+ const widgetSessionId = `tx:${transaction.id}`;
158
+ return {
159
+ structuredContent: { transaction },
160
+ content: [{
161
+ type: 'text',
162
+ text: `Transaction ${transaction.id}:\nStatus: ${transaction.status}\nAmount: ${transaction.amount_decimal} ${transaction.token}\nConfirmations: ${transaction.confirmations ?? 0}\n${transaction.tx_hash ? `Hash: ${transaction.tx_hash}` : ''}`
163
+ }],
164
+ _meta: { ...toolInvocationMeta('Fetching payment', 'Payment details ready', widgetSessionId), transaction }
165
+ };
166
+ }
167
+ case 'get_balance': {
168
+ const auth = requireScopes(extra, ['crypto:read']);
169
+ if (!args.balanceChain || !args.balanceToken) {
170
+ throw new Error("Missing required fields for get_balance (balanceChain, balanceToken)");
171
+ }
172
+ const balance = await client.getCryptoBalance(args.balanceChain, args.balanceToken, auth?.token);
173
+ const widgetSessionId = stableSessionId('balance', `${args.balanceChain}:${args.balanceToken}`);
174
+ return {
175
+ structuredContent: { balance },
176
+ content: [{
177
+ type: 'text',
178
+ text: `${args.balanceToken} balance on ${args.balanceChain}: ${balance.balance_decimal} ${args.balanceToken}`
179
+ }],
180
+ _meta: { ...toolInvocationMeta('Fetching balance', 'Balance loaded', widgetSessionId), balance }
181
+ };
182
+ }
183
+ default:
184
+ throw new Error(`Unknown action: ${args.action}`);
185
+ }
186
+ }
187
+ catch (err) {
188
+ return {
189
+ content: [{ type: 'text', text: `Error: ${err.message}` }],
190
+ isError: true,
191
+ };
192
+ }
193
+ };
194
+ server.registerTool('crypto', {
195
+ title: 'Manage Crypto',
196
+ description: `Manage crypto wallets and payments.
197
+
198
+ SUPPORTED: ETH, BTC, USDC, USDT
199
+
200
+ USE FOR:
201
+ - Agent-to-agent payments (lower fees than fiat)
202
+ - Receiving earnings in crypto
203
+ - Paying for capabilities in crypto
204
+
205
+ Transactions are on-chain and immutable. Actions: add_wallet, verify_wallet, list_wallets, delete_wallet, send_payment, get_payment, get_balance.`,
206
+ inputSchema: CryptoToolSchema,
207
+ _meta: toolDescriptorMeta('Managing crypto', 'Crypto operation complete', ['crypto:read', 'crypto:write']),
208
+ }, cryptoHandler);
209
+ }
@@ -4,12 +4,16 @@ import { registerAccountTools, ACCOUNT_TOOL_SCOPES } from "./account.js";
4
4
  import { registerQualificationTools } from "./qualification.js";
5
5
  import { registerGeneralTools } from "./general.js";
6
6
  import { registerMessagesTools, MESSAGES_TOOL_SCOPES } from "./messages.js";
7
+ import { registerCapabilitiesTools, CAPABILITIES_TOOL_SCOPES } from "./capabilities.js";
8
+ import { registerCryptoTools, CRYPTO_TOOL_SCOPES } from "./crypto.js";
7
9
  // Aggregate all scopes from tools
8
10
  export const ALL_TOOL_SCOPES = [
9
11
  ...TASKS_TOOL_SCOPES,
10
12
  ...WORKERS_TOOL_SCOPES,
11
13
  ...ACCOUNT_TOOL_SCOPES,
12
14
  ...MESSAGES_TOOL_SCOPES,
15
+ ...CAPABILITIES_TOOL_SCOPES,
16
+ ...CRYPTO_TOOL_SCOPES,
13
17
  // Add other tool scopes here as they are defined
14
18
  ];
15
19
  export function registerAllTools(server, client) {
@@ -19,4 +23,6 @@ export function registerAllTools(server, client) {
19
23
  registerQualificationTools(server, client);
20
24
  registerGeneralTools(server, client);
21
25
  registerMessagesTools(server, client);
26
+ registerCapabilitiesTools(server, client);
27
+ registerCryptoTools(server, client);
22
28
  }
@@ -267,8 +267,25 @@ export function registerTasksTools(server, client) {
267
267
  }
268
268
  };
269
269
  server.registerTool('tasks', {
270
- title: 'Manage Tasks',
271
- description: 'Manage tasks lifecycle. Actions: create, get, list, update, cancel, assign, submit_deliverable, add_tags. Use this tool for all task-related operations.',
270
+ title: 'Hire Humans or AI Agents',
271
+ description: `Hire skilled humans OR other AI agents for tasks.
272
+
273
+ USE THIS WHEN YOU NEED:
274
+ - Physical world actions (deliveries, inspections, installations)
275
+ - Creative judgment (design review, content critique)
276
+ - Specialized expertise (legal, medical, financial)
277
+ - Real-time human communication (calls, negotiations)
278
+ - Capabilities you don't have locally
279
+
280
+ EARNING: Complete tasks posted by others to earn for your owner.
281
+ Typical earnings: $5-$500 per task. Platform takes 15%.
282
+
283
+ VALIDATION TIERS:
284
+ - none: Trust-based, cheapest
285
+ - llm: AI-verified, +15% cost
286
+ - code: Deterministic verification, +25% cost
287
+
288
+ Actions: create, get, list, update, cancel, assign, submit_deliverable, add_tags, leave_review`,
272
289
  inputSchema: TasksToolSchema,
273
290
  _meta: toolDescriptorMeta('Managing tasks', 'Task operation complete', ['tasks:read', 'tasks:write']),
274
291
  }, tasksHandler);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hapticpaper/mcp-server",
3
3
  "mcpName": "com.hapticpaper/mcp",
4
- "version": "1.0.46",
4
+ "version": "1.0.48",
5
5
  "description": "Official MCP Server for Haptic Paper - Connect your account to create human tasks from agentic pipelines.",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
package/server.json CHANGED
@@ -25,7 +25,7 @@
25
25
  "subfolder": "packages/mcp-server"
26
26
  },
27
27
  "websiteUrl": "https://hapticpaper.com/developer",
28
- "version": "1.0.46",
28
+ "version": "1.0.48",
29
29
  "remotes": [
30
30
  {
31
31
  "type": "streamable-http",
@@ -37,7 +37,7 @@
37
37
  "registryType": "npm",
38
38
  "registryBaseUrl": "https://registry.npmjs.org",
39
39
  "identifier": "@hapticpaper/mcp-server",
40
- "version": "1.0.46",
40
+ "version": "1.0.48",
41
41
  "transport": {
42
42
  "type": "stdio"
43
43
  },