humanlypossible 0.2.3 → 0.2.4
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/cli.js +6618 -3737
- package/dist/mcp.js +1479 -1480
- package/package.json +10 -5
- package/dist/cli.d.ts +0 -1
- package/dist/mcp-7IK2TXZV.js +0 -851
package/dist/mcp-7IK2TXZV.js
DELETED
|
@@ -1,851 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/mcp.ts
|
|
4
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
-
import { z } from "zod";
|
|
7
|
-
var API_URL = process.env.HUMANLYPOSSIBLE_API_URL || "https://humanlypossible.ai";
|
|
8
|
-
var API_KEY = process.env.HUMANLYPOSSIBLE_API_KEY;
|
|
9
|
-
if (!API_KEY) {
|
|
10
|
-
console.error(
|
|
11
|
-
"HUMANLYPOSSIBLE_API_KEY not set. Starting in bootstrap mode \u2014 use the signup tool to authenticate."
|
|
12
|
-
);
|
|
13
|
-
}
|
|
14
|
-
function mapHttpStatusToErrorCode(status) {
|
|
15
|
-
switch (status) {
|
|
16
|
-
case 401:
|
|
17
|
-
return "UNAUTHORIZED";
|
|
18
|
-
case 403:
|
|
19
|
-
return "FORBIDDEN";
|
|
20
|
-
case 404:
|
|
21
|
-
return "NOT_FOUND";
|
|
22
|
-
case 409:
|
|
23
|
-
return "CONFLICT";
|
|
24
|
-
case 400:
|
|
25
|
-
case 422:
|
|
26
|
-
return "VALIDATION_ERROR";
|
|
27
|
-
default:
|
|
28
|
-
return status >= 500 ? "INTERNAL_ERROR" : "VALIDATION_ERROR";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function formatError(error) {
|
|
32
|
-
return JSON.stringify({ error }, null, 2);
|
|
33
|
-
}
|
|
34
|
-
async function apiRequest(path, options = {}) {
|
|
35
|
-
if (!API_KEY) {
|
|
36
|
-
return {
|
|
37
|
-
error: {
|
|
38
|
-
code: "UNAUTHORIZED",
|
|
39
|
-
message: "No API key configured. Call the signup tool first to authenticate."
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
const { method = "GET", body, params } = options;
|
|
44
|
-
let url = `${API_URL}/api/v0${path}`;
|
|
45
|
-
if (params) {
|
|
46
|
-
const searchParams = new URLSearchParams();
|
|
47
|
-
for (const [key, value] of Object.entries(params)) {
|
|
48
|
-
if (value !== void 0) {
|
|
49
|
-
searchParams.set(key, String(value));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const queryString = searchParams.toString();
|
|
53
|
-
if (queryString) {
|
|
54
|
-
url += `?${queryString}`;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
try {
|
|
58
|
-
const res = await fetch(url, {
|
|
59
|
-
method,
|
|
60
|
-
headers: {
|
|
61
|
-
Authorization: `Bearer ${API_KEY}`,
|
|
62
|
-
"Content-Type": "application/json"
|
|
63
|
-
},
|
|
64
|
-
body: body ? JSON.stringify(body) : void 0
|
|
65
|
-
});
|
|
66
|
-
const json = await res.json();
|
|
67
|
-
if (!res.ok) {
|
|
68
|
-
const errorMessage = json.error || `HTTP ${res.status}`;
|
|
69
|
-
return {
|
|
70
|
-
error: {
|
|
71
|
-
code: mapHttpStatusToErrorCode(res.status),
|
|
72
|
-
message: errorMessage,
|
|
73
|
-
details: json.details
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
return { data: json };
|
|
78
|
-
} catch (err) {
|
|
79
|
-
return {
|
|
80
|
-
error: {
|
|
81
|
-
code: "NETWORK_ERROR",
|
|
82
|
-
message: err instanceof Error ? err.message : "Request failed"
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
var VerificationLevel = z.enum(["L0", "L1", "L2", "L3", "L4"]);
|
|
88
|
-
var OracleType = z.enum(["hp", "requester", "executor", "third_party"]);
|
|
89
|
-
var EvidenceKind = z.enum([
|
|
90
|
-
"photo",
|
|
91
|
-
"video",
|
|
92
|
-
"audio",
|
|
93
|
-
"receipt",
|
|
94
|
-
"signature",
|
|
95
|
-
"measurement",
|
|
96
|
-
"json",
|
|
97
|
-
"pdf",
|
|
98
|
-
"log",
|
|
99
|
-
"note"
|
|
100
|
-
]);
|
|
101
|
-
var OutcomeState = z.enum([
|
|
102
|
-
"queued",
|
|
103
|
-
"matching",
|
|
104
|
-
"running",
|
|
105
|
-
"needs_input",
|
|
106
|
-
"under_review",
|
|
107
|
-
"completed",
|
|
108
|
-
"failed",
|
|
109
|
-
"expired",
|
|
110
|
-
"disputed"
|
|
111
|
-
]);
|
|
112
|
-
var EvidenceRequirement = z.object({
|
|
113
|
-
kind: EvidenceKind.optional().describe("Type of evidence required"),
|
|
114
|
-
name: z.string().optional().describe("Name/label for this evidence requirement"),
|
|
115
|
-
description: z.string().optional().describe("Description of what is needed"),
|
|
116
|
-
min: z.number().int().min(0).optional().describe("Minimum number of items required"),
|
|
117
|
-
max: z.number().int().min(0).optional().describe("Maximum number of items allowed")
|
|
118
|
-
});
|
|
119
|
-
var Verification = z.object({
|
|
120
|
-
level: VerificationLevel.optional().describe("Verification level (L0-L4)"),
|
|
121
|
-
oracle: OracleType.optional().describe("Who verifies the outcome"),
|
|
122
|
-
dispute_window_seconds: z.number().int().min(0).optional().describe("Time window for disputes")
|
|
123
|
-
});
|
|
124
|
-
var Budget = z.object({
|
|
125
|
-
max_usd: z.number().min(0).optional().describe("Maximum budget in USD"),
|
|
126
|
-
model: z.enum(["fixed", "not_to_exceed", "per_hour"]).optional().describe("Pricing model")
|
|
127
|
-
});
|
|
128
|
-
var Constraints = z.object({
|
|
129
|
-
location: z.string().optional().describe("Location constraint (address or coordinates)"),
|
|
130
|
-
tools_allowed: z.array(z.string()).optional().describe("List of allowed tools"),
|
|
131
|
-
data_handling: z.enum(["no_pii", "redacted", "allowed"]).optional().describe("Data handling policy"),
|
|
132
|
-
requires_license: z.boolean().optional().describe("Whether a license is required")
|
|
133
|
-
});
|
|
134
|
-
var Prefs = z.object({
|
|
135
|
-
prefer: z.enum(["human", "agent", "either"]).optional().describe("Preferred executor type"),
|
|
136
|
-
optimize_for: z.enum(["cost", "speed", "quality", "verification"]).optional().describe("Optimization priority")
|
|
137
|
-
});
|
|
138
|
-
var OutcomeInput = z.object({
|
|
139
|
-
kind: EvidenceKind.optional().describe("Type of input"),
|
|
140
|
-
name: z.string().optional().describe("Name/label for this input"),
|
|
141
|
-
mime_type: z.string().optional().describe("MIME type of the content"),
|
|
142
|
-
url: z.string().url().optional().describe("URL to the content"),
|
|
143
|
-
text: z.string().optional().describe("Text content"),
|
|
144
|
-
bytes_base64: z.string().optional().describe("Base64-encoded binary content")
|
|
145
|
-
});
|
|
146
|
-
var StatusProgress = z.object({
|
|
147
|
-
pct: z.number().min(0).max(100).optional().describe("Completion percentage"),
|
|
148
|
-
checkpoints: z.array(z.string()).optional().describe("Completed checkpoints")
|
|
149
|
-
});
|
|
150
|
-
var RequestedInputs = z.object({
|
|
151
|
-
prompt: z.string().describe("Prompt explaining what input is needed"),
|
|
152
|
-
evidence_kind: z.array(EvidenceKind).optional().describe("Types of evidence that can be provided")
|
|
153
|
-
});
|
|
154
|
-
var StatusUpdate = z.object({
|
|
155
|
-
state: OutcomeState.optional().describe("New state"),
|
|
156
|
-
message: z.string().optional().describe("Status message"),
|
|
157
|
-
progress: StatusProgress.optional().describe("Progress information"),
|
|
158
|
-
requested_inputs: RequestedInputs.optional().describe("Request for additional inputs")
|
|
159
|
-
});
|
|
160
|
-
var Artifact = z.object({
|
|
161
|
-
id: z.string().describe("Artifact ID"),
|
|
162
|
-
kind: EvidenceKind.describe("Type of artifact"),
|
|
163
|
-
name: z.string().optional().describe("Name of the artifact"),
|
|
164
|
-
mime_type: z.string().optional().describe("MIME type"),
|
|
165
|
-
url: z.string().optional().describe("URL to access the artifact"),
|
|
166
|
-
text_preview: z.string().optional().describe("Text preview of the content"),
|
|
167
|
-
meta: z.record(z.unknown()).optional().describe("Additional metadata")
|
|
168
|
-
});
|
|
169
|
-
var Resolution = z.object({
|
|
170
|
-
accepted: z.boolean().describe("Whether the outcome was accepted"),
|
|
171
|
-
decided_at: z.string().datetime().describe("When the decision was made"),
|
|
172
|
-
oracle: OracleType.describe("Who made the decision"),
|
|
173
|
-
reason: z.string().optional().describe("Reason for the decision"),
|
|
174
|
-
failures: z.array(z.string()).optional().describe("List of failures if not accepted")
|
|
175
|
-
});
|
|
176
|
-
var Receipt = z.object({
|
|
177
|
-
verification_level: VerificationLevel.describe("Verification level achieved"),
|
|
178
|
-
hashes: z.record(z.string()).optional().describe("Content hashes"),
|
|
179
|
-
timestamps: z.object({
|
|
180
|
-
started_at: z.string().datetime().optional(),
|
|
181
|
-
completed_at: z.string().datetime().optional()
|
|
182
|
-
}).optional().describe("Timing information"),
|
|
183
|
-
location_proof: z.object({
|
|
184
|
-
gps_points: z.array(
|
|
185
|
-
z.object({
|
|
186
|
-
lat: z.number(),
|
|
187
|
-
lon: z.number(),
|
|
188
|
-
ts: z.string().datetime()
|
|
189
|
-
})
|
|
190
|
-
).optional(),
|
|
191
|
-
radius_m: z.number().optional()
|
|
192
|
-
}).optional().describe("Location verification data"),
|
|
193
|
-
audit_log_url: z.string().url().optional().describe("URL to audit log"),
|
|
194
|
-
executor_attestation: z.string().optional().describe("Executor attestation")
|
|
195
|
-
});
|
|
196
|
-
var ResultSubmission = z.object({
|
|
197
|
-
resolution: Resolution.describe("Resolution details"),
|
|
198
|
-
artifacts: z.array(Artifact).describe("Submitted artifacts"),
|
|
199
|
-
receipt: Receipt.describe("Verification receipt")
|
|
200
|
-
});
|
|
201
|
-
var server = new McpServer({
|
|
202
|
-
name: "humanlypossible",
|
|
203
|
-
version: "0.4.0"
|
|
204
|
-
});
|
|
205
|
-
server.resource(
|
|
206
|
-
"guide",
|
|
207
|
-
"humanlypossible://guide",
|
|
208
|
-
async () => ({
|
|
209
|
-
contents: [
|
|
210
|
-
{
|
|
211
|
-
uri: "humanlypossible://guide",
|
|
212
|
-
mimeType: "text/markdown",
|
|
213
|
-
text: `# HumanlyPossible v0 Outcome Protocol
|
|
214
|
-
|
|
215
|
-
## Quick Start
|
|
216
|
-
|
|
217
|
-
1. Create an outcome request
|
|
218
|
-
2. Poll status
|
|
219
|
-
3. Provide extra inputs if requested
|
|
220
|
-
4. Fetch result when completed
|
|
221
|
-
|
|
222
|
-
## Requester Tools
|
|
223
|
-
|
|
224
|
-
- \`request_outcome\` - Create a new outcome request
|
|
225
|
-
- \`list_outcomes\` - List your outcome requests with filters
|
|
226
|
-
- \`get_status\` - Check status of an outcome
|
|
227
|
-
- \`send_inputs\` - Provide additional inputs if requested
|
|
228
|
-
- \`get_result\` - Fetch result when completed
|
|
229
|
-
|
|
230
|
-
## Executor Tools
|
|
231
|
-
|
|
232
|
-
- \`list_available_outcomes\` - List outcomes available for claiming
|
|
233
|
-
- \`claim_outcome\` - Claim an outcome for execution
|
|
234
|
-
- \`submit_result\` - Submit artifacts/result for an outcome
|
|
235
|
-
- \`update_status\` - Update outcome status during execution
|
|
236
|
-
|
|
237
|
-
## Example Request
|
|
238
|
-
|
|
239
|
-
\`\`\`json
|
|
240
|
-
{
|
|
241
|
-
"outcome": "6 exterior photos of 123 Main St are captured in daylight",
|
|
242
|
-
"resolve_by": "2026-02-06T20:00:00Z",
|
|
243
|
-
"evidence": [{ "kind": "photo", "min": 6 }],
|
|
244
|
-
"verification": { "level": "L1", "oracle": "hp" },
|
|
245
|
-
"constraints": { "location": "123 Main St, SF" }
|
|
246
|
-
}
|
|
247
|
-
\`\`\`
|
|
248
|
-
|
|
249
|
-
## Evidence Kinds
|
|
250
|
-
photo, video, audio, receipt, signature, measurement, json, pdf, log, note
|
|
251
|
-
|
|
252
|
-
## Verification Levels
|
|
253
|
-
- L0: No verification
|
|
254
|
-
- L1: Basic verification
|
|
255
|
-
- L2: Standard verification
|
|
256
|
-
- L3: Enhanced verification
|
|
257
|
-
- L4: Maximum verification
|
|
258
|
-
|
|
259
|
-
## Outcome States
|
|
260
|
-
queued, matching, running, needs_input, under_review, completed, failed, expired, disputed
|
|
261
|
-
|
|
262
|
-
## Adapter Routing
|
|
263
|
-
|
|
264
|
-
Outcomes are automatically routed to the best available service adapter:
|
|
265
|
-
|
|
266
|
-
### Available Adapters
|
|
267
|
-
|
|
268
|
-
**Uber Direct** (delivery, courier, logistics)
|
|
269
|
-
- Categories: food_delivery, courier, delivery, logistics
|
|
270
|
-
- Params: pickup_address, pickup_name, pickup_phone, dropoff_address, dropoff_name, dropoff_phone, manifest_description
|
|
271
|
-
- Example: \`{ adapter_hint: "delivery", params: { pickup_address: "123 Main St", dropoff_address: "456 Oak Ave" } }\`
|
|
272
|
-
|
|
273
|
-
**Twilio** (communication, messaging)
|
|
274
|
-
- Categories: communication, sms, voice, messaging, phone_call
|
|
275
|
-
- Params: to (E.164 phone), from (optional), body, channel (sms|voice)
|
|
276
|
-
- Example: \`{ adapter_hint: "sms", params: { to: "+15551234567", body: "Your order is ready" } }\`
|
|
277
|
-
|
|
278
|
-
**Native Marketplace** (general, manual, human)
|
|
279
|
-
- Default fallback for outcomes that don't match a service adapter
|
|
280
|
-
- Outcomes stay in 'matching' for human/AI executors to claim
|
|
281
|
-
|
|
282
|
-
### Routing Cascade
|
|
283
|
-
1. If adapter_hint is provided, route to matching adapter
|
|
284
|
-
2. If no hint, use LLM to interpret the outcome and select an adapter
|
|
285
|
-
3. If no adapter matches, fall through to native marketplace
|
|
286
|
-
|
|
287
|
-
## Error Codes
|
|
288
|
-
- UNAUTHORIZED: API key invalid or missing
|
|
289
|
-
- FORBIDDEN: Actor lacks required permissions
|
|
290
|
-
- NOT_FOUND: Outcome not found
|
|
291
|
-
- CONFLICT: Outcome already claimed or invalid state transition
|
|
292
|
-
- VALIDATION_ERROR: Invalid request parameters
|
|
293
|
-
- INTERNAL_ERROR: Server error
|
|
294
|
-
- NETWORK_ERROR: Network connectivity issue
|
|
295
|
-
`
|
|
296
|
-
}
|
|
297
|
-
]
|
|
298
|
-
})
|
|
299
|
-
);
|
|
300
|
-
server.registerTool(
|
|
301
|
-
"request_outcome",
|
|
302
|
-
{
|
|
303
|
-
title: "Request Outcome",
|
|
304
|
-
description: `Create a new outcome request. Delegates a real-world task to humans or service adapters.
|
|
305
|
-
|
|
306
|
-
The platform automatically routes outcomes to the best available adapter:
|
|
307
|
-
- Use adapter_hint to target a specific service category (e.g. "delivery", "sms", "voice")
|
|
308
|
-
- Use params to provide structured parameters matching the adapter's schema
|
|
309
|
-
- Without hints, the platform uses LLM interpretation to route to the best adapter
|
|
310
|
-
- Outcomes that don't match any adapter go to the native marketplace for human/AI claim
|
|
311
|
-
|
|
312
|
-
Available adapter categories:
|
|
313
|
-
- delivery/courier/logistics: Uber Direct (pickup_address, dropoff_address, etc.)
|
|
314
|
-
- sms/voice/messaging: Twilio (to, body, channel)
|
|
315
|
-
- general/manual: Native marketplace (any outcome)
|
|
316
|
-
|
|
317
|
-
Returns:
|
|
318
|
-
{ "id": "<uuid>" } - The outcome request ID for tracking
|
|
319
|
-
|
|
320
|
-
Error Codes:
|
|
321
|
-
- VALIDATION_ERROR: Invalid parameters
|
|
322
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
323
|
-
inputSchema: {
|
|
324
|
-
outcome: z.string().min(1).describe("Clear description of the desired outcome"),
|
|
325
|
-
resolve_by: z.string().describe("ISO 8601 deadline (e.g. 2026-02-06T20:00:00Z)"),
|
|
326
|
-
params: z.record(z.unknown()).optional().describe("Structured params for adapter routing (e.g. { to: '+1234567890', body: 'Hello' } for SMS)"),
|
|
327
|
-
adapter_hint: z.string().optional().describe("Hint for adapter selection (e.g. 'delivery', 'sms', 'voice', 'courier')"),
|
|
328
|
-
evidence: z.array(EvidenceRequirement).optional().describe("Evidence requirements"),
|
|
329
|
-
verification: Verification.optional().describe("Verification settings"),
|
|
330
|
-
budget: Budget.optional().describe("Budget constraints"),
|
|
331
|
-
constraints: Constraints.optional().describe("Execution constraints"),
|
|
332
|
-
prefs: Prefs.optional().describe("Execution preferences")
|
|
333
|
-
},
|
|
334
|
-
annotations: {
|
|
335
|
-
readOnlyHint: false,
|
|
336
|
-
destructiveHint: false,
|
|
337
|
-
idempotentHint: false,
|
|
338
|
-
openWorldHint: true
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
async (params) => {
|
|
342
|
-
const body = {
|
|
343
|
-
outcome: params.outcome,
|
|
344
|
-
resolve_by: params.resolve_by
|
|
345
|
-
};
|
|
346
|
-
if (params.params) body.params = params.params;
|
|
347
|
-
if (params.adapter_hint) body.adapter_hint = params.adapter_hint;
|
|
348
|
-
if (params.evidence) body.evidence = params.evidence;
|
|
349
|
-
if (params.verification) body.verification = params.verification;
|
|
350
|
-
if (params.budget) body.budget = params.budget;
|
|
351
|
-
if (params.constraints) body.constraints = params.constraints;
|
|
352
|
-
if (params.prefs) body.prefs = params.prefs;
|
|
353
|
-
const { data, error } = await apiRequest("/outcomes", {
|
|
354
|
-
method: "POST",
|
|
355
|
-
body
|
|
356
|
-
});
|
|
357
|
-
if (error) {
|
|
358
|
-
return {
|
|
359
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
360
|
-
isError: true
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
return {
|
|
364
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
);
|
|
368
|
-
server.registerTool(
|
|
369
|
-
"list_outcomes",
|
|
370
|
-
{
|
|
371
|
-
title: "List Outcomes",
|
|
372
|
-
description: `List your outcome requests with optional filters.
|
|
373
|
-
|
|
374
|
-
Returns:
|
|
375
|
-
{
|
|
376
|
-
"items": [...],
|
|
377
|
-
"has_more": true|false,
|
|
378
|
-
"next_cursor": "<ISO 8601 timestamp>"
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
Error Codes:
|
|
382
|
-
- VALIDATION_ERROR: Invalid filter parameters
|
|
383
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
384
|
-
inputSchema: {
|
|
385
|
-
state: OutcomeState.optional().describe("Filter by state"),
|
|
386
|
-
limit: z.number().int().min(1).max(100).optional().describe("Max results (default 20, max 100)"),
|
|
387
|
-
created_before: z.string().optional().describe("Cursor for pagination (ISO 8601 timestamp)")
|
|
388
|
-
},
|
|
389
|
-
annotations: {
|
|
390
|
-
readOnlyHint: true,
|
|
391
|
-
destructiveHint: false,
|
|
392
|
-
idempotentHint: true,
|
|
393
|
-
openWorldHint: false
|
|
394
|
-
}
|
|
395
|
-
},
|
|
396
|
-
async (params) => {
|
|
397
|
-
const { data, error } = await apiRequest("/outcomes", {
|
|
398
|
-
params: {
|
|
399
|
-
state: params.state,
|
|
400
|
-
limit: params.limit,
|
|
401
|
-
created_before: params.created_before
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
if (error) {
|
|
405
|
-
return {
|
|
406
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
407
|
-
isError: true
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
return {
|
|
411
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
);
|
|
415
|
-
server.registerTool(
|
|
416
|
-
"get_status",
|
|
417
|
-
{
|
|
418
|
-
title: "Get Outcome Status",
|
|
419
|
-
description: `Get the current status of an outcome request.
|
|
420
|
-
|
|
421
|
-
Returns:
|
|
422
|
-
{
|
|
423
|
-
"id": "<uuid>",
|
|
424
|
-
"state": "queued|matching|running|needs_input|under_review|completed|failed|expired|disputed",
|
|
425
|
-
"updated_at": "<ISO 8601 timestamp>",
|
|
426
|
-
"message": "<optional status message>",
|
|
427
|
-
"progress": { "pct": 0-100, "checkpoints": [...] },
|
|
428
|
-
"requested_inputs": { "prompt": "...", "evidence_kind": [...] }
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
Error Codes:
|
|
432
|
-
- NOT_FOUND: Outcome not found
|
|
433
|
-
- FORBIDDEN: Not authorized to view this outcome
|
|
434
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
435
|
-
inputSchema: {
|
|
436
|
-
id: z.string().describe("Outcome request ID (UUID)")
|
|
437
|
-
},
|
|
438
|
-
annotations: {
|
|
439
|
-
readOnlyHint: true,
|
|
440
|
-
destructiveHint: false,
|
|
441
|
-
idempotentHint: true,
|
|
442
|
-
openWorldHint: false
|
|
443
|
-
}
|
|
444
|
-
},
|
|
445
|
-
async (params) => {
|
|
446
|
-
const { data, error } = await apiRequest(
|
|
447
|
-
`/outcomes/${params.id}/status`
|
|
448
|
-
);
|
|
449
|
-
if (error) {
|
|
450
|
-
return {
|
|
451
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
452
|
-
isError: true
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
return {
|
|
456
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
);
|
|
460
|
-
server.registerTool(
|
|
461
|
-
"send_inputs",
|
|
462
|
-
{
|
|
463
|
-
title: "Send Inputs",
|
|
464
|
-
description: `Provide additional inputs for an outcome request.
|
|
465
|
-
|
|
466
|
-
Returns:
|
|
467
|
-
{ "ok": true }
|
|
468
|
-
|
|
469
|
-
Error Codes:
|
|
470
|
-
- NOT_FOUND: Outcome not found
|
|
471
|
-
- FORBIDDEN: Not authorized to send inputs
|
|
472
|
-
- VALIDATION_ERROR: Invalid input format
|
|
473
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
474
|
-
inputSchema: {
|
|
475
|
-
id: z.string().describe("Outcome request ID (UUID)"),
|
|
476
|
-
inputs: z.array(OutcomeInput).describe("Array of input objects")
|
|
477
|
-
},
|
|
478
|
-
annotations: {
|
|
479
|
-
readOnlyHint: false,
|
|
480
|
-
destructiveHint: false,
|
|
481
|
-
idempotentHint: false,
|
|
482
|
-
openWorldHint: false
|
|
483
|
-
}
|
|
484
|
-
},
|
|
485
|
-
async (params) => {
|
|
486
|
-
const { data, error } = await apiRequest(
|
|
487
|
-
`/outcomes/${params.id}/inputs`,
|
|
488
|
-
{
|
|
489
|
-
method: "POST",
|
|
490
|
-
body: params.inputs
|
|
491
|
-
}
|
|
492
|
-
);
|
|
493
|
-
if (error) {
|
|
494
|
-
return {
|
|
495
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
496
|
-
isError: true
|
|
497
|
-
};
|
|
498
|
-
}
|
|
499
|
-
return {
|
|
500
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
);
|
|
504
|
-
server.registerTool(
|
|
505
|
-
"get_result",
|
|
506
|
-
{
|
|
507
|
-
title: "Get Outcome Result",
|
|
508
|
-
description: `Fetch the result for a completed outcome request.
|
|
509
|
-
|
|
510
|
-
Returns:
|
|
511
|
-
{
|
|
512
|
-
"id": "<uuid>",
|
|
513
|
-
"resolution": { "accepted": true|false, "decided_at": "...", "oracle": "..." },
|
|
514
|
-
"artifacts": [...],
|
|
515
|
-
"receipt": { "verification_level": "L0-L4", ... }
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
Error Codes:
|
|
519
|
-
- NOT_FOUND: Outcome not found or not yet completed
|
|
520
|
-
- FORBIDDEN: Not authorized to view this outcome
|
|
521
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
522
|
-
inputSchema: {
|
|
523
|
-
id: z.string().describe("Outcome request ID (UUID)")
|
|
524
|
-
},
|
|
525
|
-
annotations: {
|
|
526
|
-
readOnlyHint: true,
|
|
527
|
-
destructiveHint: false,
|
|
528
|
-
idempotentHint: true,
|
|
529
|
-
openWorldHint: false
|
|
530
|
-
}
|
|
531
|
-
},
|
|
532
|
-
async (params) => {
|
|
533
|
-
const { data, error } = await apiRequest(
|
|
534
|
-
`/outcomes/${params.id}/result`
|
|
535
|
-
);
|
|
536
|
-
if (error) {
|
|
537
|
-
return {
|
|
538
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
539
|
-
isError: true
|
|
540
|
-
};
|
|
541
|
-
}
|
|
542
|
-
return {
|
|
543
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
);
|
|
547
|
-
server.registerTool(
|
|
548
|
-
"list_available_outcomes",
|
|
549
|
-
{
|
|
550
|
-
title: "List Available Outcomes",
|
|
551
|
-
description: `List outcomes available for claiming. Requires executor permissions (can_execute=true).
|
|
552
|
-
|
|
553
|
-
Returns:
|
|
554
|
-
{
|
|
555
|
-
"items": [
|
|
556
|
-
{
|
|
557
|
-
"id": "<uuid>",
|
|
558
|
-
"state": "queued|matching",
|
|
559
|
-
"resolve_by": "<ISO 8601>",
|
|
560
|
-
"outcome": "<description>",
|
|
561
|
-
"budget": { ... },
|
|
562
|
-
"constraints": { ... },
|
|
563
|
-
"created_at": "<ISO 8601>"
|
|
564
|
-
},
|
|
565
|
-
...
|
|
566
|
-
],
|
|
567
|
-
"limit": 20,
|
|
568
|
-
"offset": 0
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
Error Codes:
|
|
572
|
-
- FORBIDDEN: Actor lacks execute permissions
|
|
573
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
574
|
-
inputSchema: {
|
|
575
|
-
limit: z.number().int().min(1).max(100).optional().describe("Max results (default 20, max 100)"),
|
|
576
|
-
offset: z.number().int().min(0).optional().describe("Offset for pagination (default 0)")
|
|
577
|
-
},
|
|
578
|
-
annotations: {
|
|
579
|
-
readOnlyHint: true,
|
|
580
|
-
destructiveHint: false,
|
|
581
|
-
idempotentHint: true,
|
|
582
|
-
openWorldHint: false
|
|
583
|
-
}
|
|
584
|
-
},
|
|
585
|
-
async (params) => {
|
|
586
|
-
const { data, error } = await apiRequest("/outcomes/available", {
|
|
587
|
-
params: {
|
|
588
|
-
limit: params.limit,
|
|
589
|
-
offset: params.offset
|
|
590
|
-
}
|
|
591
|
-
});
|
|
592
|
-
if (error) {
|
|
593
|
-
return {
|
|
594
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
595
|
-
isError: true
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
return {
|
|
599
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
600
|
-
};
|
|
601
|
-
}
|
|
602
|
-
);
|
|
603
|
-
server.registerTool(
|
|
604
|
-
"claim_outcome",
|
|
605
|
-
{
|
|
606
|
-
title: "Claim Outcome",
|
|
607
|
-
description: `Claim an outcome for execution. Requires executor permissions (can_execute=true).
|
|
608
|
-
|
|
609
|
-
Returns:
|
|
610
|
-
{
|
|
611
|
-
"id": "<uuid>",
|
|
612
|
-
"executor_actor_id": "<uuid>",
|
|
613
|
-
"state": "running",
|
|
614
|
-
"status": { ... }
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
Error Codes:
|
|
618
|
-
- NOT_FOUND: Outcome not found
|
|
619
|
-
- FORBIDDEN: Actor lacks execute permissions
|
|
620
|
-
- CONFLICT: Outcome already claimed by another executor
|
|
621
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
622
|
-
inputSchema: {
|
|
623
|
-
id: z.string().describe("Outcome request ID (UUID) to claim")
|
|
624
|
-
},
|
|
625
|
-
annotations: {
|
|
626
|
-
readOnlyHint: false,
|
|
627
|
-
destructiveHint: false,
|
|
628
|
-
idempotentHint: false,
|
|
629
|
-
openWorldHint: false
|
|
630
|
-
}
|
|
631
|
-
},
|
|
632
|
-
async (params) => {
|
|
633
|
-
const { data, error } = await apiRequest(`/outcomes/${params.id}/claim`, {
|
|
634
|
-
method: "POST"
|
|
635
|
-
});
|
|
636
|
-
if (error) {
|
|
637
|
-
return {
|
|
638
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
639
|
-
isError: true
|
|
640
|
-
};
|
|
641
|
-
}
|
|
642
|
-
return {
|
|
643
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
|
-
);
|
|
647
|
-
server.registerTool(
|
|
648
|
-
"update_status",
|
|
649
|
-
{
|
|
650
|
-
title: "Update Outcome Status",
|
|
651
|
-
description: `Update the status of an outcome you are executing. Use this to report progress, request inputs, or change state.
|
|
652
|
-
|
|
653
|
-
Returns:
|
|
654
|
-
{
|
|
655
|
-
"id": "<uuid>",
|
|
656
|
-
"state": "...",
|
|
657
|
-
"status": { ... }
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
Error Codes:
|
|
661
|
-
- NOT_FOUND: Outcome not found
|
|
662
|
-
- FORBIDDEN: Not the executor of this outcome
|
|
663
|
-
- CONFLICT: Outcome not claimed
|
|
664
|
-
- VALIDATION_ERROR: Invalid status update
|
|
665
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
666
|
-
inputSchema: {
|
|
667
|
-
id: z.string().describe("Outcome request ID (UUID)"),
|
|
668
|
-
state: OutcomeState.optional().describe("New state"),
|
|
669
|
-
message: z.string().optional().describe("Status message"),
|
|
670
|
-
progress: StatusProgress.optional().describe("Progress information"),
|
|
671
|
-
requested_inputs: RequestedInputs.optional().describe("Request for additional inputs from requester")
|
|
672
|
-
},
|
|
673
|
-
annotations: {
|
|
674
|
-
readOnlyHint: false,
|
|
675
|
-
destructiveHint: false,
|
|
676
|
-
idempotentHint: false,
|
|
677
|
-
openWorldHint: false
|
|
678
|
-
}
|
|
679
|
-
},
|
|
680
|
-
async (params) => {
|
|
681
|
-
const { id, ...statusUpdate } = params;
|
|
682
|
-
if (!statusUpdate.state && !statusUpdate.message && !statusUpdate.progress && !statusUpdate.requested_inputs) {
|
|
683
|
-
return {
|
|
684
|
-
content: [
|
|
685
|
-
{
|
|
686
|
-
type: "text",
|
|
687
|
-
text: formatError({
|
|
688
|
-
code: "VALIDATION_ERROR",
|
|
689
|
-
message: "At least one status field (state, message, progress, or requested_inputs) is required"
|
|
690
|
-
})
|
|
691
|
-
}
|
|
692
|
-
],
|
|
693
|
-
isError: true
|
|
694
|
-
};
|
|
695
|
-
}
|
|
696
|
-
const { data, error } = await apiRequest(`/outcomes/${id}/submit`, {
|
|
697
|
-
method: "POST",
|
|
698
|
-
body: { status: statusUpdate }
|
|
699
|
-
});
|
|
700
|
-
if (error) {
|
|
701
|
-
return {
|
|
702
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
703
|
-
isError: true
|
|
704
|
-
};
|
|
705
|
-
}
|
|
706
|
-
return {
|
|
707
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
);
|
|
711
|
-
server.registerTool(
|
|
712
|
-
"submit_result",
|
|
713
|
-
{
|
|
714
|
-
title: "Submit Outcome Result",
|
|
715
|
-
description: `Submit the final result for an outcome you are executing. This completes the outcome.
|
|
716
|
-
|
|
717
|
-
The result must include:
|
|
718
|
-
- resolution: Whether the outcome was achieved, when, and by whom
|
|
719
|
-
- artifacts: Evidence/deliverables produced
|
|
720
|
-
- receipt: Verification proof
|
|
721
|
-
|
|
722
|
-
Returns:
|
|
723
|
-
{
|
|
724
|
-
"id": "<uuid>",
|
|
725
|
-
"state": "completed|failed",
|
|
726
|
-
"status": { ... },
|
|
727
|
-
"result": { ... }
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
Error Codes:
|
|
731
|
-
- NOT_FOUND: Outcome not found
|
|
732
|
-
- FORBIDDEN: Not the executor of this outcome
|
|
733
|
-
- CONFLICT: Outcome not claimed or already completed
|
|
734
|
-
- VALIDATION_ERROR: Invalid result format
|
|
735
|
-
- UNAUTHORIZED: Invalid API key`,
|
|
736
|
-
inputSchema: {
|
|
737
|
-
id: z.string().describe("Outcome request ID (UUID)"),
|
|
738
|
-
resolution: Resolution.describe("Resolution details"),
|
|
739
|
-
artifacts: z.array(Artifact).describe("Evidence/deliverables produced"),
|
|
740
|
-
receipt: Receipt.describe("Verification proof")
|
|
741
|
-
},
|
|
742
|
-
annotations: {
|
|
743
|
-
readOnlyHint: false,
|
|
744
|
-
destructiveHint: false,
|
|
745
|
-
idempotentHint: false,
|
|
746
|
-
openWorldHint: false
|
|
747
|
-
}
|
|
748
|
-
},
|
|
749
|
-
async (params) => {
|
|
750
|
-
const { id, ...result } = params;
|
|
751
|
-
const { data, error } = await apiRequest(`/outcomes/${id}/submit`, {
|
|
752
|
-
method: "POST",
|
|
753
|
-
body: { result }
|
|
754
|
-
});
|
|
755
|
-
if (error) {
|
|
756
|
-
return {
|
|
757
|
-
content: [{ type: "text", text: formatError(error) }],
|
|
758
|
-
isError: true
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
return {
|
|
762
|
-
content: [{ type: "text", text: JSON.stringify(data, null, 2) }]
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
);
|
|
766
|
-
server.registerTool(
|
|
767
|
-
"signup",
|
|
768
|
-
{
|
|
769
|
-
title: "Sign Up / Log In",
|
|
770
|
-
description: `Create an account or log into an existing one to get an API key. This is required before using any other tools if no API key was provided at startup.
|
|
771
|
-
|
|
772
|
-
Returns:
|
|
773
|
-
{
|
|
774
|
-
"api_key": "hp_...",
|
|
775
|
-
"actor_id": "<uuid>",
|
|
776
|
-
"key_prefix": "hp_abc",
|
|
777
|
-
"scopes": ["outcomes:read", "outcomes:write"],
|
|
778
|
-
"is_new_account": true|false
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
Error Codes:
|
|
782
|
-
- VALIDATION_ERROR: Missing fields or weak password
|
|
783
|
-
- UNAUTHORIZED: Wrong password for existing account`,
|
|
784
|
-
inputSchema: {
|
|
785
|
-
email: z.string().describe("Email address"),
|
|
786
|
-
password: z.string().describe("Password (min 8 characters)"),
|
|
787
|
-
display_name: z.string().optional().describe("Display name for the account"),
|
|
788
|
-
actor_kind: z.enum(["human", "ai", "org"]).optional().describe("Type of actor (default: ai)")
|
|
789
|
-
},
|
|
790
|
-
annotations: {
|
|
791
|
-
readOnlyHint: false,
|
|
792
|
-
destructiveHint: false,
|
|
793
|
-
idempotentHint: false,
|
|
794
|
-
openWorldHint: true
|
|
795
|
-
}
|
|
796
|
-
},
|
|
797
|
-
async (params) => {
|
|
798
|
-
try {
|
|
799
|
-
const res = await fetch(`${API_URL}/api/v0/auth/signup`, {
|
|
800
|
-
method: "POST",
|
|
801
|
-
headers: { "Content-Type": "application/json" },
|
|
802
|
-
body: JSON.stringify(params)
|
|
803
|
-
});
|
|
804
|
-
const json = await res.json();
|
|
805
|
-
if (!res.ok) {
|
|
806
|
-
return {
|
|
807
|
-
content: [
|
|
808
|
-
{
|
|
809
|
-
type: "text",
|
|
810
|
-
text: formatError({
|
|
811
|
-
code: mapHttpStatusToErrorCode(res.status),
|
|
812
|
-
message: json.error || `HTTP ${res.status}`
|
|
813
|
-
})
|
|
814
|
-
}
|
|
815
|
-
],
|
|
816
|
-
isError: true
|
|
817
|
-
};
|
|
818
|
-
}
|
|
819
|
-
API_KEY = json.api_key;
|
|
820
|
-
return {
|
|
821
|
-
content: [
|
|
822
|
-
{
|
|
823
|
-
type: "text",
|
|
824
|
-
text: JSON.stringify(json, null, 2)
|
|
825
|
-
}
|
|
826
|
-
]
|
|
827
|
-
};
|
|
828
|
-
} catch (err) {
|
|
829
|
-
return {
|
|
830
|
-
content: [
|
|
831
|
-
{
|
|
832
|
-
type: "text",
|
|
833
|
-
text: formatError({
|
|
834
|
-
code: "NETWORK_ERROR",
|
|
835
|
-
message: err instanceof Error ? err.message : "Signup request failed"
|
|
836
|
-
})
|
|
837
|
-
}
|
|
838
|
-
],
|
|
839
|
-
isError: true
|
|
840
|
-
};
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
);
|
|
844
|
-
async function startMcpServer() {
|
|
845
|
-
const transport = new StdioServerTransport();
|
|
846
|
-
await server.connect(transport);
|
|
847
|
-
}
|
|
848
|
-
startMcpServer();
|
|
849
|
-
export {
|
|
850
|
-
startMcpServer
|
|
851
|
-
};
|