@ctxprotocol/sdk 0.6.0 → 0.8.0
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/README.md +136 -23
- package/dist/client/index.cjs +211 -4
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +228 -11
- package/dist/client/index.d.ts +228 -11
- package/dist/client/index.js +211 -5
- package/dist/client/index.js.map +1 -1
- package/dist/index.cjs +211 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +211 -5
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -64,10 +64,41 @@ yarn add @ctxprotocol/sdk
|
|
|
64
64
|
Before using the API, complete setup at [ctxprotocol.com](https://ctxprotocol.com):
|
|
65
65
|
|
|
66
66
|
1. **Sign in** — Creates your embedded wallet
|
|
67
|
-
2. **
|
|
67
|
+
2. **Set spending cap** — Approve USDC spending on the ContextRouter (one-time setup)
|
|
68
68
|
3. **Fund wallet** — Add USDC for tool execution fees
|
|
69
69
|
4. **Generate API key** — In Settings page
|
|
70
70
|
|
|
71
|
+
## Two Modes: Precision vs Intelligence
|
|
72
|
+
|
|
73
|
+
The SDK offers two payment models to serve different use cases:
|
|
74
|
+
|
|
75
|
+
| Mode | Method | Payment Model | Use Case |
|
|
76
|
+
|------|--------|---------------|----------|
|
|
77
|
+
| **Execute** | `client.tools.execute()` | Pay-per-request | Simple data fetches, predictable costs, building custom pipelines |
|
|
78
|
+
| **Query** | `client.query.run()` | Pay-per-response | Complex questions, multi-tool synthesis, curated intelligence |
|
|
79
|
+
|
|
80
|
+
**Execute mode** gives you raw data and full control — one tool, one call, one payment:
|
|
81
|
+
```typescript
|
|
82
|
+
const result = await client.tools.execute({
|
|
83
|
+
toolId: "tool-uuid",
|
|
84
|
+
toolName: "whale_transactions",
|
|
85
|
+
args: { chain: "base", limit: 20 },
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Query mode** gives you curated answers — the server handles tool discovery, multi-tool orchestration (up to 100 MCP calls per tool), self-healing retries, completeness checks, model-aware context budgeting, and AI synthesis for one flat fee:
|
|
90
|
+
```typescript
|
|
91
|
+
const answer = await client.query.run({
|
|
92
|
+
query: "What are the top whale movements on Base?",
|
|
93
|
+
modelId: "glm-model", // optional: choose a supported model
|
|
94
|
+
includeDataUrl: true, // optional: persist full execution data to blob
|
|
95
|
+
});
|
|
96
|
+
console.log(answer.response); // AI-synthesized answer
|
|
97
|
+
console.log(answer.toolsUsed); // Which tools were used
|
|
98
|
+
console.log(answer.cost); // Cost breakdown
|
|
99
|
+
console.log(answer.dataUrl); // Optional blob URL with full data
|
|
100
|
+
```
|
|
101
|
+
|
|
71
102
|
## Quick Start
|
|
72
103
|
|
|
73
104
|
```typescript
|
|
@@ -77,16 +108,17 @@ const client = new ContextClient({
|
|
|
77
108
|
apiKey: "sk_live_...",
|
|
78
109
|
});
|
|
79
110
|
|
|
80
|
-
//
|
|
81
|
-
const
|
|
111
|
+
// Pay-per-response: Ask a question, get a curated answer
|
|
112
|
+
const answer = await client.query.run("What are the top whale movements on Base?");
|
|
113
|
+
console.log(answer.response);
|
|
82
114
|
|
|
83
|
-
// Execute a tool
|
|
115
|
+
// Pay-per-request: Execute a specific tool for raw data
|
|
116
|
+
const tools = await client.discovery.search("gas prices");
|
|
84
117
|
const result = await client.tools.execute({
|
|
85
118
|
toolId: tools[0].id,
|
|
86
119
|
toolName: tools[0].mcpTools[0].name,
|
|
87
120
|
args: { chainId: 1 },
|
|
88
121
|
});
|
|
89
|
-
|
|
90
122
|
console.log(result.result);
|
|
91
123
|
```
|
|
92
124
|
|
|
@@ -143,9 +175,18 @@ const result = await client.tools.execute({
|
|
|
143
175
|
args: llmDecision.args,
|
|
144
176
|
});
|
|
145
177
|
|
|
146
|
-
// Feed
|
|
178
|
+
// Feed a bounded, structured summary back to your LLM for synthesis.
|
|
179
|
+
// Prefer client.query.run() when you want server-managed synthesis.
|
|
180
|
+
const resultPreview = JSON.stringify(result.result, null, 2).slice(0, 50_000);
|
|
181
|
+
const resultKeys =
|
|
182
|
+
result.result && typeof result.result === "object"
|
|
183
|
+
? Object.keys(result.result as Record<string, unknown>)
|
|
184
|
+
: [];
|
|
185
|
+
|
|
147
186
|
const finalAnswer = await myLLM.generate(
|
|
148
|
-
`
|
|
187
|
+
`Tool output keys: ${resultKeys.join(", ") || "(non-object result)"}\n\n` +
|
|
188
|
+
`Tool output preview (truncated):\n${resultPreview}\n\n` +
|
|
189
|
+
"Summarize this for the user and mention if more data may exist beyond the preview."
|
|
149
190
|
);
|
|
150
191
|
```
|
|
151
192
|
|
|
@@ -207,11 +248,17 @@ If you can answer without a tool, just respond normally.`;
|
|
|
207
248
|
args: toolCall.args || {},
|
|
208
249
|
});
|
|
209
250
|
|
|
210
|
-
// 6. Let LLM synthesize
|
|
251
|
+
// 6. Let LLM synthesize a bounded preview (avoid injecting giant JSON blobs)
|
|
252
|
+
const resultPreview = JSON.stringify(result.result, null, 2).slice(0, 50_000);
|
|
253
|
+
const resultKeys =
|
|
254
|
+
result.result && typeof result.result === "object"
|
|
255
|
+
? Object.keys(result.result as Record<string, unknown>)
|
|
256
|
+
: [];
|
|
257
|
+
|
|
211
258
|
return await myLLM.chat(
|
|
212
|
-
`Tool "${toolCall.toolName}" returned: ${
|
|
213
|
-
|
|
214
|
-
Please provide a helpful response to the user's original question: "${userQuery}"`
|
|
259
|
+
`Tool "${toolCall.toolName}" returned keys: ${resultKeys.join(", ") || "(non-object result)"}\n\n` +
|
|
260
|
+
`Preview (truncated):\n${resultPreview}\n\n` +
|
|
261
|
+
`Please provide a helpful response to the user's original question: "${userQuery}"`
|
|
215
262
|
);
|
|
216
263
|
}
|
|
217
264
|
} catch {
|
|
@@ -265,11 +312,11 @@ Get featured/popular tools.
|
|
|
265
312
|
const featured = await client.discovery.getFeatured(5);
|
|
266
313
|
```
|
|
267
314
|
|
|
268
|
-
### Tools
|
|
315
|
+
### Tools (Pay-Per-Request)
|
|
269
316
|
|
|
270
317
|
#### `client.tools.execute(options)`
|
|
271
318
|
|
|
272
|
-
Execute a tool method.
|
|
319
|
+
Execute a single tool method. One call, one payment, raw result.
|
|
273
320
|
|
|
274
321
|
```typescript
|
|
275
322
|
const result = await client.tools.execute({
|
|
@@ -279,6 +326,53 @@ const result = await client.tools.execute({
|
|
|
279
326
|
});
|
|
280
327
|
```
|
|
281
328
|
|
|
329
|
+
### Query (Pay-Per-Response)
|
|
330
|
+
|
|
331
|
+
#### `client.query.run(options)`
|
|
332
|
+
|
|
333
|
+
Run an agentic query. The server discovers tools, executes the full pipeline (up to 100 MCP calls per tool), applies model-aware mediator/data budgeting, and returns an AI-synthesized answer.
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// Simple string
|
|
337
|
+
const answer = await client.query.run("What are the top whale movements on Base?");
|
|
338
|
+
|
|
339
|
+
// With options
|
|
340
|
+
const answer = await client.query.run({
|
|
341
|
+
query: "Analyze whale activity on Base",
|
|
342
|
+
tools: ["tool-uuid-1", "tool-uuid-2"], // optional — auto-discover if omitted
|
|
343
|
+
modelId: "kimi-model-thinking", // optional
|
|
344
|
+
includeData: true, // optional: include execution data inline
|
|
345
|
+
includeDataUrl: true, // optional: include blob URL for full data
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
console.log(answer.response); // AI-synthesized text
|
|
349
|
+
console.log(answer.toolsUsed); // [{ id, name, skillCalls }]
|
|
350
|
+
console.log(answer.cost); // { modelCostUsd, toolCostUsd, totalCostUsd }
|
|
351
|
+
console.log(answer.durationMs); // Total time
|
|
352
|
+
console.log(answer.data); // Optional execution data (when includeData=true)
|
|
353
|
+
console.log(answer.dataUrl); // Optional blob URL (when includeDataUrl=true)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### `client.query.stream(options)`
|
|
357
|
+
|
|
358
|
+
Same as `run()` but streams events in real-time via SSE.
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
for await (const event of client.query.stream("What are the top whale movements?")) {
|
|
362
|
+
switch (event.type) {
|
|
363
|
+
case "tool-status":
|
|
364
|
+
console.log(`Tool ${event.tool.name}: ${event.status}`);
|
|
365
|
+
break;
|
|
366
|
+
case "text-delta":
|
|
367
|
+
process.stdout.write(event.delta);
|
|
368
|
+
break;
|
|
369
|
+
case "done":
|
|
370
|
+
console.log("\nTotal cost:", event.result.cost.totalCostUsd);
|
|
371
|
+
break;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
282
376
|
## Types
|
|
283
377
|
|
|
284
378
|
```typescript
|
|
@@ -296,6 +390,11 @@ import type {
|
|
|
296
390
|
McpTool,
|
|
297
391
|
ExecuteOptions,
|
|
298
392
|
ExecutionResult,
|
|
393
|
+
// Query types (pay-per-response)
|
|
394
|
+
QueryOptions,
|
|
395
|
+
QueryResult,
|
|
396
|
+
QueryCost,
|
|
397
|
+
QueryStreamEvent,
|
|
299
398
|
ContextErrorCode,
|
|
300
399
|
// Auth types (for MCP server contributors)
|
|
301
400
|
VerifyRequestOptions,
|
|
@@ -333,7 +432,7 @@ interface McpTool {
|
|
|
333
432
|
}
|
|
334
433
|
```
|
|
335
434
|
|
|
336
|
-
### ExecutionResult
|
|
435
|
+
### ExecutionResult (Pay-Per-Request)
|
|
337
436
|
|
|
338
437
|
```typescript
|
|
339
438
|
interface ExecutionResult<T = unknown> {
|
|
@@ -343,6 +442,19 @@ interface ExecutionResult<T = unknown> {
|
|
|
343
442
|
}
|
|
344
443
|
```
|
|
345
444
|
|
|
445
|
+
### QueryResult (Pay-Per-Response)
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
interface QueryResult {
|
|
449
|
+
response: string; // AI-synthesized answer
|
|
450
|
+
toolsUsed: QueryToolUsage[]; // Tools used: { id, name, skillCalls }
|
|
451
|
+
cost: QueryCost; // { modelCostUsd, toolCostUsd, totalCostUsd }
|
|
452
|
+
durationMs: number;
|
|
453
|
+
data?: unknown; // Optional execution data (includeData=true)
|
|
454
|
+
dataUrl?: string; // Optional blob URL (includeDataUrl=true)
|
|
455
|
+
}
|
|
456
|
+
```
|
|
457
|
+
|
|
346
458
|
### Context Requirement Types (MCP Server Contributors)
|
|
347
459
|
|
|
348
460
|
```typescript
|
|
@@ -386,8 +498,8 @@ try {
|
|
|
386
498
|
console.log("Setup required:", error.helpUrl);
|
|
387
499
|
break;
|
|
388
500
|
case "insufficient_allowance":
|
|
389
|
-
// User needs to
|
|
390
|
-
console.log("
|
|
501
|
+
// User needs to set a spending cap
|
|
502
|
+
console.log("Set spending cap:", error.helpUrl);
|
|
391
503
|
break;
|
|
392
504
|
case "payment_failed":
|
|
393
505
|
// Insufficient USDC balance
|
|
@@ -407,7 +519,7 @@ try {
|
|
|
407
519
|
| ------------------------ | ---------------------------------------- | ----------------------------------- |
|
|
408
520
|
| `unauthorized` | Invalid API key | Check configuration |
|
|
409
521
|
| `no_wallet` | Wallet not set up | Direct user to `helpUrl` |
|
|
410
|
-
| `insufficient_allowance` |
|
|
522
|
+
| `insufficient_allowance` | Spending cap not set | Direct user to `helpUrl` |
|
|
411
523
|
| `payment_failed` | USDC payment failed | Check balance |
|
|
412
524
|
| `execution_failed` | Tool error | Feed error to LLM for retry |
|
|
413
525
|
|
|
@@ -426,7 +538,7 @@ By adding 1 line of code to verify a JWT, Context saves you from building:
|
|
|
426
538
|
- Refund and dispute logic
|
|
427
539
|
|
|
428
540
|
**The "Stripe Webhook" Analogy:**
|
|
429
|
-
Developers are used to verifying signatures for Stripe Webhooks or GitHub Apps. Context works the same way. When we send a request saying "Execute Tool (
|
|
541
|
+
Developers are used to verifying signatures for Stripe Webhooks or GitHub Apps. Context works the same way. When we send a request saying "Execute Tool (Authorized)", you verify the signature. Without this, anyone could curl your endpoint and drain your resources.
|
|
430
542
|
|
|
431
543
|
### Quick Implementation (1 Line)
|
|
432
544
|
|
|
@@ -707,12 +819,13 @@ pnpm add -D @types/express
|
|
|
707
819
|
|
|
708
820
|
## Payment Flow
|
|
709
821
|
|
|
710
|
-
|
|
822
|
+
Context uses **deferred settlement** — tools execute first, and payment is settled in the background after the result is returned:
|
|
711
823
|
|
|
712
|
-
1. Your
|
|
713
|
-
2.
|
|
714
|
-
3.
|
|
715
|
-
4.
|
|
824
|
+
1. Your USDC spending cap (ERC-20 allowance on ContextRouter) is verified
|
|
825
|
+
2. Tool executes and returns results
|
|
826
|
+
3. Payment is settled server-side via the operator wallet (no client-side transactions)
|
|
827
|
+
4. **90%** goes to the tool developer, **10%** goes to the protocol
|
|
828
|
+
5. If execution fails, tool fees are **waived** — you only pay for successful results
|
|
716
829
|
|
|
717
830
|
## Documentation
|
|
718
831
|
|
package/dist/client/index.cjs
CHANGED
|
@@ -75,8 +75,8 @@ var Tools = class {
|
|
|
75
75
|
* @returns The execution result with the tool's output data
|
|
76
76
|
*
|
|
77
77
|
* @throws {ContextError} With code `no_wallet` if wallet not set up
|
|
78
|
-
* @throws {ContextError} With code `insufficient_allowance` if
|
|
79
|
-
* @throws {ContextError} With code `payment_failed` if
|
|
78
|
+
* @throws {ContextError} With code `insufficient_allowance` if spending cap not set
|
|
79
|
+
* @throws {ContextError} With code `payment_failed` if payment settlement fails
|
|
80
80
|
* @throws {ContextError} With code `execution_failed` if tool execution fails
|
|
81
81
|
*
|
|
82
82
|
* @example
|
|
@@ -97,11 +97,13 @@ var Tools = class {
|
|
|
97
97
|
* ```
|
|
98
98
|
*/
|
|
99
99
|
async execute(options) {
|
|
100
|
-
const { toolId, toolName, args } = options;
|
|
100
|
+
const { toolId, toolName, args, idempotencyKey } = options;
|
|
101
|
+
const headers = idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0;
|
|
101
102
|
const response = await this.client._fetch(
|
|
102
103
|
"/api/v1/tools/execute",
|
|
103
104
|
{
|
|
104
105
|
method: "POST",
|
|
106
|
+
headers,
|
|
105
107
|
body: JSON.stringify({ toolId, toolName, args })
|
|
106
108
|
}
|
|
107
109
|
);
|
|
@@ -125,6 +127,165 @@ var Tools = class {
|
|
|
125
127
|
}
|
|
126
128
|
};
|
|
127
129
|
|
|
130
|
+
// src/client/resources/query.ts
|
|
131
|
+
var Query = class {
|
|
132
|
+
constructor(client) {
|
|
133
|
+
this.client = client;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Run an agentic query and wait for the full response.
|
|
137
|
+
*
|
|
138
|
+
* The server discovers relevant tools (or uses the ones you specify),
|
|
139
|
+
* executes the full agentic pipeline (up to 100 MCP calls per tool),
|
|
140
|
+
* and returns an AI-synthesized answer. Payment is settled after
|
|
141
|
+
* successful execution via deferred settlement.
|
|
142
|
+
*
|
|
143
|
+
* @param options - Query options or a plain string question
|
|
144
|
+
* @returns The complete query result with response text, tools used, and cost
|
|
145
|
+
*
|
|
146
|
+
* @throws {ContextError} With code `no_wallet` if wallet not set up
|
|
147
|
+
* @throws {ContextError} With code `insufficient_allowance` if spending cap not set
|
|
148
|
+
* @throws {ContextError} With code `payment_failed` if payment settlement fails
|
|
149
|
+
* @throws {ContextError} With code `execution_failed` if the agentic pipeline fails
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* // Simple question — server discovers tools automatically
|
|
154
|
+
* const answer = await client.query.run("What are the top whale movements on Base?");
|
|
155
|
+
* console.log(answer.response); // AI-synthesized answer
|
|
156
|
+
* console.log(answer.toolsUsed); // Which tools were used
|
|
157
|
+
* console.log(answer.cost); // Cost breakdown
|
|
158
|
+
*
|
|
159
|
+
* // With specific tools (Manual Mode)
|
|
160
|
+
* const answer = await client.query.run({
|
|
161
|
+
* query: "Analyze whale activity",
|
|
162
|
+
* tools: ["tool-uuid-1", "tool-uuid-2"],
|
|
163
|
+
* });
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
async run(options) {
|
|
167
|
+
const opts = typeof options === "string" ? { query: options } : options;
|
|
168
|
+
const headers = opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0;
|
|
169
|
+
const response = await this.client._fetch(
|
|
170
|
+
"/api/v1/query",
|
|
171
|
+
{
|
|
172
|
+
method: "POST",
|
|
173
|
+
headers,
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
query: opts.query,
|
|
176
|
+
tools: opts.tools,
|
|
177
|
+
modelId: opts.modelId,
|
|
178
|
+
includeData: opts.includeData,
|
|
179
|
+
includeDataUrl: opts.includeDataUrl,
|
|
180
|
+
stream: false
|
|
181
|
+
})
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
if ("error" in response) {
|
|
185
|
+
throw new ContextError(
|
|
186
|
+
response.error,
|
|
187
|
+
response.code,
|
|
188
|
+
void 0,
|
|
189
|
+
response.helpUrl
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
if (response.success) {
|
|
193
|
+
return {
|
|
194
|
+
response: response.response,
|
|
195
|
+
toolsUsed: response.toolsUsed,
|
|
196
|
+
cost: response.cost,
|
|
197
|
+
durationMs: response.durationMs,
|
|
198
|
+
data: response.data,
|
|
199
|
+
dataUrl: response.dataUrl
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
throw new ContextError("Unexpected response format from query API");
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Run an agentic query with streaming. Returns an async iterable that
|
|
206
|
+
* yields events as the server processes the query in real-time.
|
|
207
|
+
*
|
|
208
|
+
* Event types:
|
|
209
|
+
* - `tool-status` — A tool started executing or changed status
|
|
210
|
+
* - `text-delta` — A chunk of the AI response text
|
|
211
|
+
* - `done` — The full response is complete (includes final `QueryResult`)
|
|
212
|
+
*
|
|
213
|
+
* @param options - Query options or a plain string question
|
|
214
|
+
* @returns An async iterable of stream events
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* for await (const event of client.query.stream("What are the top whale movements?")) {
|
|
219
|
+
* switch (event.type) {
|
|
220
|
+
* case "tool-status":
|
|
221
|
+
* console.log(`Tool ${event.tool.name}: ${event.status}`);
|
|
222
|
+
* break;
|
|
223
|
+
* case "text-delta":
|
|
224
|
+
* process.stdout.write(event.delta);
|
|
225
|
+
* break;
|
|
226
|
+
* case "done":
|
|
227
|
+
* console.log("\nCost:", event.result.cost.totalCostUsd);
|
|
228
|
+
* break;
|
|
229
|
+
* }
|
|
230
|
+
* }
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
async *stream(options) {
|
|
234
|
+
const opts = typeof options === "string" ? { query: options } : options;
|
|
235
|
+
const headers = opts.idempotencyKey ? { "Idempotency-Key": opts.idempotencyKey } : void 0;
|
|
236
|
+
const response = await this.client._fetchRaw("/api/v1/query", {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers,
|
|
239
|
+
body: JSON.stringify({
|
|
240
|
+
query: opts.query,
|
|
241
|
+
tools: opts.tools,
|
|
242
|
+
modelId: opts.modelId,
|
|
243
|
+
includeData: opts.includeData,
|
|
244
|
+
includeDataUrl: opts.includeDataUrl,
|
|
245
|
+
stream: true
|
|
246
|
+
})
|
|
247
|
+
});
|
|
248
|
+
const body = response.body;
|
|
249
|
+
if (!body) {
|
|
250
|
+
throw new ContextError("No response body for streaming query");
|
|
251
|
+
}
|
|
252
|
+
const reader = body.getReader();
|
|
253
|
+
const decoder = new TextDecoder();
|
|
254
|
+
let buffer = "";
|
|
255
|
+
try {
|
|
256
|
+
while (true) {
|
|
257
|
+
const { done, value } = await reader.read();
|
|
258
|
+
if (done) break;
|
|
259
|
+
buffer += decoder.decode(value, { stream: true });
|
|
260
|
+
const lines = buffer.split("\n");
|
|
261
|
+
buffer = lines.pop() ?? "";
|
|
262
|
+
for (const line of lines) {
|
|
263
|
+
const trimmed = line.trim();
|
|
264
|
+
if (trimmed.startsWith("data: ")) {
|
|
265
|
+
const data = trimmed.slice(6);
|
|
266
|
+
if (data === "[DONE]") return;
|
|
267
|
+
try {
|
|
268
|
+
yield JSON.parse(data);
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (buffer.trim().startsWith("data: ")) {
|
|
275
|
+
const data = buffer.trim().slice(6);
|
|
276
|
+
if (data !== "[DONE]") {
|
|
277
|
+
try {
|
|
278
|
+
yield JSON.parse(data);
|
|
279
|
+
} catch {
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
} finally {
|
|
284
|
+
reader.releaseLock();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
|
|
128
289
|
// src/client/client.ts
|
|
129
290
|
var ContextClient = class {
|
|
130
291
|
apiKey;
|
|
@@ -135,9 +296,17 @@ var ContextClient = class {
|
|
|
135
296
|
*/
|
|
136
297
|
discovery;
|
|
137
298
|
/**
|
|
138
|
-
* Tools resource for executing tools
|
|
299
|
+
* Tools resource for executing tools (pay-per-request)
|
|
139
300
|
*/
|
|
140
301
|
tools;
|
|
302
|
+
/**
|
|
303
|
+
* Query resource for agentic queries (pay-per-response).
|
|
304
|
+
*
|
|
305
|
+
* Unlike `tools.execute()` which calls a single tool once, `query` sends
|
|
306
|
+
* a natural-language question and lets the server handle tool discovery,
|
|
307
|
+
* multi-tool orchestration, self-healing, and AI synthesis — one flat fee.
|
|
308
|
+
*/
|
|
309
|
+
query;
|
|
141
310
|
/**
|
|
142
311
|
* Creates a new Context Protocol client
|
|
143
312
|
*
|
|
@@ -153,6 +322,7 @@ var ContextClient = class {
|
|
|
153
322
|
this.baseUrl = (options.baseUrl ?? "https://ctxprotocol.com").replace(/\/$/, "");
|
|
154
323
|
this.discovery = new Discovery(this);
|
|
155
324
|
this.tools = new Tools(this);
|
|
325
|
+
this.query = new Query(this);
|
|
156
326
|
}
|
|
157
327
|
/**
|
|
158
328
|
* Close the client and clean up resources.
|
|
@@ -238,11 +408,48 @@ var ContextClient = class {
|
|
|
238
408
|
}
|
|
239
409
|
throw lastError ?? new ContextError("Request failed after retries");
|
|
240
410
|
}
|
|
411
|
+
/**
|
|
412
|
+
* Internal method for making authenticated HTTP requests that returns
|
|
413
|
+
* the raw Response object. Used for streaming endpoints (SSE).
|
|
414
|
+
*
|
|
415
|
+
* @internal
|
|
416
|
+
*/
|
|
417
|
+
async _fetchRaw(endpoint, options = {}) {
|
|
418
|
+
if (this._closed) {
|
|
419
|
+
throw new ContextError("Client has been closed");
|
|
420
|
+
}
|
|
421
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
422
|
+
const response = await fetch(url, {
|
|
423
|
+
...options,
|
|
424
|
+
headers: {
|
|
425
|
+
"Content-Type": "application/json",
|
|
426
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
427
|
+
...options.headers
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
if (!response.ok) {
|
|
431
|
+
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
432
|
+
let errorCode;
|
|
433
|
+
let helpUrl;
|
|
434
|
+
try {
|
|
435
|
+
const errorBody = await response.json();
|
|
436
|
+
if (errorBody.error) {
|
|
437
|
+
errorMessage = errorBody.error;
|
|
438
|
+
errorCode = errorBody.code;
|
|
439
|
+
helpUrl = errorBody.helpUrl;
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
throw new ContextError(errorMessage, errorCode, response.status, helpUrl);
|
|
444
|
+
}
|
|
445
|
+
return response;
|
|
446
|
+
}
|
|
241
447
|
};
|
|
242
448
|
|
|
243
449
|
exports.ContextClient = ContextClient;
|
|
244
450
|
exports.ContextError = ContextError;
|
|
245
451
|
exports.Discovery = Discovery;
|
|
452
|
+
exports.Query = Query;
|
|
246
453
|
exports.Tools = Tools;
|
|
247
454
|
//# sourceMappingURL=index.cjs.map
|
|
248
455
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/client/types.ts","../../src/client/resources/discovery.ts","../../src/client/resources/tools.ts","../../src/client/client.ts"],"names":[],"mappings":";;;AAmMO,IAAM,YAAA,GAAN,MAAM,aAAA,SAAqB,KAAA,CAAM;AAAA,EACtC,WAAA,CACE,OAAA,EACgB,IAAA,EACA,UAAA,EACA,OAAA,EAChB;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AAJG,IAAA,IAAA,CAAA,IAAA,GAAA,IAAA;AACA,IAAA,IAAA,CAAA,UAAA,GAAA,UAAA;AACA,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAGhB,IAAA,IAAA,CAAK,IAAA,GAAO,cAAA;AACZ,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,aAAA,CAAa,SAAS,CAAA;AAAA,EACpD;AACF;;;ACxMO,IAAM,YAAN,MAAgB;AAAA,EACrB,YAAoB,MAAA,EAAuB;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgB5C,MAAM,MAAA,CAAO,KAAA,EAAe,KAAA,EAAiC;AAC3D,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AAEnC,IAAA,IAAI,KAAA,EAAO;AACT,MAAA,MAAA,CAAO,GAAA,CAAI,KAAK,KAAK,CAAA;AAAA,IACvB;AAEA,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,KAAK,CAAC,CAAA;AAAA,IACnC;AAEA,IAAA,MAAM,WAAA,GAAc,OAAO,QAAA,EAAS;AACpC,IAAA,MAAM,WAAW,CAAA,oBAAA,EAAuB,WAAA,GAAc,CAAA,CAAA,EAAI,WAAW,KAAK,EAAE,CAAA,CAAA;AAE5E,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,OAAuB,QAAQ,CAAA;AAElE,IAAA,OAAO,QAAA,CAAS,KAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,YAAY,KAAA,EAAiC;AACjD,IAAA,OAAO,IAAA,CAAK,MAAA,CAAO,EAAA,EAAI,KAAK,CAAA;AAAA,EAC9B;AACF;;;AC7CO,IAAM,QAAN,MAAY;AAAA,EACjB,YAAoB,MAAA,EAAuB;AAAvB,IAAA,IAAA,CAAA,MAAA,GAAA,MAAA;AAAA,EAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiC5C,MAAM,QAAqB,OAAA,EAAsD;AAC/E,IAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,IAAA,EAAK,GAAI,OAAA;AAEnC,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,MAAA,CAAO,MAAA;AAAA,MACjC,uBAAA;AAAA,MACA;AAAA,QACE,MAAA,EAAQ,MAAA;AAAA,QACR,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,MAAA,EAAQ,QAAA,EAAU,MAAM;AAAA;AACjD,KACF;AAGA,IAAA,IAAI,WAAW,QAAA,EAAU;AACvB,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,QAAA,CAAS,KAAA;AAAA,QACT,QAAA,CAAS,IAAA;AAAA,QACT,MAAA;AAAA;AAAA,QACA,QAAA,CAAS;AAAA,OACX;AAAA,IACF;AAGA,IAAA,IAAI,SAAS,OAAA,EAAS;AACpB,MAAA,OAAO;AAAA,QACL,QAAQ,QAAA,CAAS,MAAA;AAAA,QACjB,MAAM,QAAA,CAAS,IAAA;AAAA,QACf,YAAY,QAAA,CAAS;AAAA,OACvB;AAAA,IACF;AAGA,IAAA,MAAM,IAAI,aAAa,qCAAqC,CAAA;AAAA,EAC9D;AACF;;;ACjDO,IAAM,gBAAN,MAAoB;AAAA,EACR,MAAA;AAAA,EACA,OAAA;AAAA,EACT,OAAA,GAAU,KAAA;AAAA;AAAA;AAAA;AAAA,EAKF,SAAA;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAShB,YAAY,OAAA,EAA+B;AACzC,IAAA,IAAI,CAAC,QAAQ,MAAA,EAAQ;AACnB,MAAA,MAAM,IAAI,aAAa,qBAAqB,CAAA;AAAA,IAC9C;AAEA,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,OAAA,IAAW,yBAAA,EAA2B,OAAA,CAAQ,OAAO,EAAE,CAAA;AAG/E,IAAA,IAAA,CAAK,SAAA,GAAY,IAAI,SAAA,CAAU,IAAI,CAAA;AACnC,IAAA,IAAA,CAAK,KAAA,GAAQ,IAAI,KAAA,CAAM,IAAI,CAAA;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AAAA,EACjB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,MAAA,CAAU,QAAA,EAAkB,OAAA,GAAuB,EAAC,EAAe;AACvE,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,MAAM,IAAI,aAAa,wBAAwB,CAAA;AAAA,IACjD;AAEA,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,OAAO,GAAG,QAAQ,CAAA,CAAA;AACtC,IAAA,MAAM,UAAA,GAAa,CAAA;AACnB,IAAA,MAAM,SAAA,GAAY,GAAA;AAElB,IAAA,IAAI,SAAA;AAEJ,IAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,UAAA,EAAY,OAAA,EAAA,EAAW;AACtD,MAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,MAAA,MAAM,UAAU,UAAA,CAAW,MAAM,UAAA,CAAW,KAAA,IAAS,SAAS,CAAA;AAE9D,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,UAChC,GAAG,OAAA;AAAA,UACH,QAAQ,UAAA,CAAW,MAAA;AAAA,UACnB,OAAA,EAAS;AAAA,YACP,cAAA,EAAgB,kBAAA;AAAA,YAChB,aAAA,EAAe,CAAA,OAAA,EAAU,IAAA,CAAK,MAAM,CAAA,CAAA;AAAA,YACpC,GAAG,OAAA,CAAQ;AAAA;AACb,SACD,CAAA;AAED,QAAA,YAAA,CAAa,OAAO,CAAA;AAEpB,QAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAEhB,UAAA,IAAI,QAAA,CAAS,MAAA,IAAU,GAAA,IAAO,OAAA,GAAU,UAAA,EAAY;AAClD,YAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAA,GAAO,CAAA,IAAK,SAAS,GAAM,CAAA;AAClD,YAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,KAAK,CAAC,CAAA;AACzD,YAAA;AAAA,UACF;AAEA,UAAA,IAAI,eAAe,CAAA,KAAA,EAAQ,QAAA,CAAS,MAAM,CAAA,EAAA,EAAK,SAAS,UAAU,CAAA,CAAA;AAClE,UAAA,IAAI,SAAA;AACJ,UAAA,IAAI,OAAA;AAEJ,UAAA,IAAI;AACF,YAAA,MAAM,SAAA,GAAY,MAAM,QAAA,CAAS,IAAA,EAAK;AACtC,YAAA,IAAI,UAAU,KAAA,EAAO;AACnB,cAAA,YAAA,GAAe,SAAA,CAAU,KAAA;AACzB,cAAA,SAAA,GAAY,SAAA,CAAU,IAAA;AACtB,cAAA,OAAA,GAAU,SAAA,CAAU,OAAA;AAAA,YACtB;AAAA,UACF,CAAA,CAAA,MAAQ;AAAA,UAER;AAEA,UAAA,MAAM,IAAI,YAAA,CAAa,YAAA,EAAc,SAAA,EAAW,QAAA,CAAS,QAAQ,OAAO,CAAA;AAAA,QAC1E;AAEA,QAAA,OAAO,SAAS,IAAA,EAAK;AAAA,MACvB,SAAS,KAAA,EAAO;AACd,QAAA,YAAA,CAAa,OAAO,CAAA;AAEpB,QAAA,IAAI,iBAAiB,YAAA,EAAc;AACjC,UAAA,MAAM,KAAA;AAAA,QACR;AAEA,QAAA,SAAA,GAAY,iBAAiB,KAAA,GAAQ,KAAA,GAAQ,IAAI,KAAA,CAAM,MAAA,CAAO,KAAK,CAAC,CAAA;AAGpE,QAAA,MAAM,cACJ,SAAA,CAAU,IAAA,KAAS,YAAA,IACnB,SAAA,CAAU,QAAQ,QAAA,CAAS,cAAc,CAAA,IACzC,SAAA,CAAU,QAAQ,QAAA,CAAS,YAAY,KACvC,SAAA,CAAU,OAAA,CAAQ,SAAS,WAAW,CAAA;AAExC,QAAA,IAAI,WAAA,IAAe,UAAU,UAAA,EAAY;AACvC,UAAA,MAAM,QAAQ,IAAA,CAAK,GAAA,CAAI,GAAA,GAAO,CAAA,IAAK,SAAS,GAAM,CAAA;AAClD,UAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,YAAY,UAAA,CAAW,OAAA,EAAS,KAAK,CAAC,CAAA;AACzD,UAAA;AAAA,QACF;AAEA,QAAA,IAAI,SAAA,CAAU,SAAS,YAAA,EAAc;AACnC,UAAA,MAAM,IAAI,YAAA;AAAA,YACR,CAAA,wBAAA,EAA2B,YAAY,GAAI,CAAA,CAAA,CAAA;AAAA,YAC3C,MAAA;AAAA,YACA;AAAA,WACF;AAAA,QACF;AAEA,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,SAAA,CAAU,OAAA;AAAA,UACV,MAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAAA,IACF;AAEA,IAAA,MAAM,SAAA,IAAa,IAAI,YAAA,CAAa,8BAA8B,CAAA;AAAA,EACpE;AACF","file":"index.cjs","sourcesContent":["/**\n * Configuration options for initializing the ContextClient\n */\nexport interface ContextClientOptions {\n /**\n * Your Context Protocol API key\n * @example \"sk_live_abc123...\"\n */\n apiKey: string;\n\n /**\n * Base URL for the Context Protocol API\n * @default \"https://ctxprotocol.com\"\n */\n baseUrl?: string;\n}\n\n/**\n * An individual MCP tool exposed by a tool listing\n */\nexport interface McpTool {\n /** Name of the MCP tool method */\n name: string;\n\n /** Description of what this method does */\n description: string;\n\n /**\n * JSON Schema for the input arguments this tool accepts.\n * Used by LLMs to generate correct arguments.\n */\n inputSchema?: Record<string, unknown>;\n\n /**\n * JSON Schema for the output this tool returns.\n * Used by LLMs to understand the response structure.\n */\n outputSchema?: Record<string, unknown>;\n}\n\n/**\n * Represents a tool available on the Context Protocol marketplace\n */\nexport interface Tool {\n /** Unique identifier for the tool (UUID) */\n id: string;\n\n /** Human-readable name of the tool */\n name: string;\n\n /** Description of what the tool does */\n description: string;\n\n /** Price per execution in USDC */\n price: string;\n\n /** Tool category (e.g., \"defi\", \"nft\") */\n category?: string;\n\n /** Whether the tool is verified by Context Protocol */\n isVerified?: boolean;\n\n /** Tool type - currently always \"mcp\" */\n kind?: string;\n\n /**\n * Available MCP tool methods\n * Use items from this array as `toolName` when executing\n */\n mcpTools?: McpTool[];\n\n // Trust metrics (Level 2 - Reputation Ledger)\n /** Total number of queries processed */\n totalQueries?: number;\n\n /** Success rate percentage (0-100) */\n successRate?: string;\n\n /** Uptime percentage (0-100) */\n uptimePercent?: string;\n\n /** Total USDC staked by the developer */\n totalStaked?: string;\n\n /** Whether the tool has \"Proven\" status (100+ queries, >95% success, >98% uptime) */\n isProven?: boolean;\n}\n\n/**\n * Response from the tools search endpoint\n */\nexport interface SearchResponse {\n /** Array of matching tools */\n tools: Tool[];\n\n /** The search query that was used */\n query: string;\n\n /** Total number of results */\n count: number;\n}\n\n/**\n * Options for searching tools\n */\nexport interface SearchOptions {\n /** Search query (semantic search) */\n query?: string;\n\n /** Maximum number of results (1-50, default 10) */\n limit?: number;\n}\n\n/**\n * Options for executing a tool\n */\nexport interface ExecuteOptions {\n /** The UUID of the tool to execute (from search results) */\n toolId: string;\n\n /** The specific MCP tool name to call (from tool's mcpTools array) */\n toolName: string;\n\n /** Arguments to pass to the tool */\n args?: Record<string, unknown>;\n}\n\n/**\n * Successful execution response from the API\n */\nexport interface ExecuteApiSuccessResponse {\n success: true;\n\n /** The result data from the tool execution */\n result: unknown;\n\n /** Information about the executed tool */\n tool: {\n id: string;\n name: string;\n };\n\n /** Execution duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Error response from the API\n */\nexport interface ExecuteApiErrorResponse {\n /** Human-readable error message */\n error: string;\n\n /** Error code for programmatic handling */\n code?: ContextErrorCode;\n\n /** URL to help resolve the issue */\n helpUrl?: string;\n}\n\n/**\n * Raw API response from the execute endpoint\n */\nexport type ExecuteApiResponse = ExecuteApiSuccessResponse | ExecuteApiErrorResponse;\n\n/**\n * The resolved result returned to the user after SDK processing\n */\nexport interface ExecutionResult<T = unknown> {\n /** The data returned by the tool */\n result: T;\n\n /** Information about the executed tool */\n tool: {\n id: string;\n name: string;\n };\n\n /** Execution duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Specific error codes returned by the Context Protocol API\n */\nexport type ContextErrorCode =\n | \"unauthorized\"\n | \"no_wallet\"\n | \"insufficient_allowance\"\n | \"payment_failed\"\n | \"execution_failed\";\n\n/**\n * Error thrown by the Context Protocol client\n */\nexport class ContextError extends Error {\n constructor(\n message: string,\n public readonly code?: ContextErrorCode | string,\n public readonly statusCode?: number,\n public readonly helpUrl?: string\n ) {\n super(message);\n this.name = \"ContextError\";\n Object.setPrototypeOf(this, ContextError.prototype);\n }\n}\n","import type { Tool, SearchResponse } from \"../types.js\";\nimport type { ContextClient } from \"../client.js\";\n\n/**\n * Discovery resource for searching and finding tools on the Context Protocol marketplace\n */\nexport class Discovery {\n constructor(private client: ContextClient) {}\n\n /**\n * Search for tools matching a query string\n *\n * @param query - The search query (e.g., \"gas prices\", \"nft metadata\")\n * @param limit - Maximum number of results (1-50, default 10)\n * @returns Array of matching tools\n *\n * @example\n * ```typescript\n * const tools = await client.discovery.search(\"gas prices\");\n * console.log(tools[0].name); // \"Gas Price Oracle\"\n * console.log(tools[0].mcpTools); // Available methods\n * ```\n */\n async search(query: string, limit?: number): Promise<Tool[]> {\n const params = new URLSearchParams();\n\n if (query) {\n params.set(\"q\", query);\n }\n\n if (limit !== undefined) {\n params.set(\"limit\", String(limit));\n }\n\n const queryString = params.toString();\n const endpoint = `/api/v1/tools/search${queryString ? `?${queryString}` : \"\"}`;\n\n const response = await this.client._fetch<SearchResponse>(endpoint);\n\n return response.tools;\n }\n\n /**\n * Get featured/popular tools (empty query search)\n *\n * @param limit - Maximum number of results (1-50, default 10)\n * @returns Array of featured tools\n *\n * @example\n * ```typescript\n * const featured = await client.discovery.getFeatured(5);\n * ```\n */\n async getFeatured(limit?: number): Promise<Tool[]> {\n return this.search(\"\", limit);\n }\n}\n","import type {\n ExecuteOptions,\n ExecuteApiResponse,\n ExecutionResult,\n} from \"../types.js\";\nimport { ContextError } from \"../types.js\";\nimport type { ContextClient } from \"../client.js\";\n\n/**\n * Tools resource for executing tools on the Context Protocol marketplace\n */\nexport class Tools {\n constructor(private client: ContextClient) {}\n\n /**\n * Execute a tool with the provided arguments\n *\n * @param options - Execution options\n * @param options.toolId - The UUID of the tool (from search results)\n * @param options.toolName - The specific MCP tool method to call (from tool's mcpTools array)\n * @param options.args - Arguments to pass to the tool\n * @returns The execution result with the tool's output data\n *\n * @throws {ContextError} With code `no_wallet` if wallet not set up\n * @throws {ContextError} With code `insufficient_allowance` if Auto Pay not enabled\n * @throws {ContextError} With code `payment_failed` if on-chain payment fails\n * @throws {ContextError} With code `execution_failed` if tool execution fails\n *\n * @example\n * ```typescript\n * // First, search for a tool\n * const tools = await client.discovery.search(\"gas prices\");\n * const tool = tools[0];\n *\n * // Execute a specific method from the tool's mcpTools\n * const result = await client.tools.execute({\n * toolId: tool.id,\n * toolName: tool.mcpTools[0].name, // e.g., \"get_gas_prices\"\n * args: { chainId: 1 }\n * });\n *\n * console.log(result.result); // The tool's output\n * console.log(result.durationMs); // Execution time\n * ```\n */\n async execute<T = unknown>(options: ExecuteOptions): Promise<ExecutionResult<T>> {\n const { toolId, toolName, args } = options;\n\n const response = await this.client._fetch<ExecuteApiResponse>(\n \"/api/v1/tools/execute\",\n {\n method: \"POST\",\n body: JSON.stringify({ toolId, toolName, args }),\n }\n );\n\n // Handle error response\n if (\"error\" in response) {\n throw new ContextError(\n response.error,\n response.code,\n undefined, // Don't hardcode - this was a 200 OK with error body\n response.helpUrl\n );\n }\n\n // Handle success response\n if (response.success) {\n return {\n result: response.result as T,\n tool: response.tool,\n durationMs: response.durationMs,\n };\n }\n\n // Fallback - shouldn't reach here with valid API responses\n throw new ContextError(\"Unexpected response format from API\");\n }\n}\n","import type { ContextClientOptions } from \"./types.js\";\nimport { ContextError } from \"./types.js\";\nimport { Discovery } from \"./resources/discovery.js\";\nimport { Tools } from \"./resources/tools.js\";\n\n/**\n * The official TypeScript client for the Context Protocol.\n *\n * Use this client to discover and execute AI tools programmatically.\n *\n * @example\n * ```typescript\n * import { ContextClient } from \"@contextprotocol/client\";\n *\n * const client = new ContextClient({\n * apiKey: \"sk_live_...\"\n * });\n *\n * // Discover tools\n * const tools = await client.discovery.search(\"gas prices\");\n *\n * // Execute a tool method\n * const result = await client.tools.execute({\n * toolId: tools[0].id,\n * toolName: tools[0].mcpTools[0].name,\n * args: { chainId: 1 }\n * });\n * ```\n */\nexport class ContextClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private _closed = false;\n\n /**\n * Discovery resource for searching tools\n */\n public readonly discovery: Discovery;\n\n /**\n * Tools resource for executing tools\n */\n public readonly tools: Tools;\n\n /**\n * Creates a new Context Protocol client\n *\n * @param options - Client configuration options\n * @param options.apiKey - Your Context Protocol API key (format: sk_live_...)\n * @param options.baseUrl - Optional base URL override (defaults to https://ctxprotocol.com)\n */\n constructor(options: ContextClientOptions) {\n if (!options.apiKey) {\n throw new ContextError(\"API key is required\");\n }\n\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? \"https://ctxprotocol.com\").replace(/\\/$/, \"\");\n\n // Initialize resources\n this.discovery = new Discovery(this);\n this.tools = new Tools(this);\n }\n\n /**\n * Close the client and clean up resources.\n * After calling close(), any in-flight requests may be aborted.\n */\n close(): void {\n this._closed = true;\n }\n\n /**\n * Internal method for making authenticated HTTP requests\n * Includes timeout (30s) and retry with exponential backoff for transient errors\n *\n * @internal\n */\n async _fetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n if (this._closed) {\n throw new ContextError(\"Client has been closed\");\n }\n\n const url = `${this.baseUrl}${endpoint}`;\n const maxRetries = 3;\n const timeoutMs = 30_000;\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n clearTimeout(timeout);\n\n if (!response.ok) {\n // Retry on 5xx server errors\n if (response.status >= 500 && attempt < maxRetries) {\n const delay = Math.min(1000 * 2 ** attempt, 10_000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n\n let errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n let errorCode: string | undefined;\n let helpUrl: string | undefined;\n\n try {\n const errorBody = await response.json();\n if (errorBody.error) {\n errorMessage = errorBody.error;\n errorCode = errorBody.code;\n helpUrl = errorBody.helpUrl;\n }\n } catch {\n // Use default error message if JSON parsing fails\n }\n\n throw new ContextError(errorMessage, errorCode, response.status, helpUrl);\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n clearTimeout(timeout);\n\n if (error instanceof ContextError) {\n throw error;\n }\n\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Retry on network errors and timeouts\n const isRetryable =\n lastError.name === \"AbortError\" ||\n lastError.message.includes(\"fetch failed\") ||\n lastError.message.includes(\"ECONNRESET\") ||\n lastError.message.includes(\"ETIMEDOUT\");\n\n if (isRetryable && attempt < maxRetries) {\n const delay = Math.min(1000 * 2 ** attempt, 10_000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n\n if (lastError.name === \"AbortError\") {\n throw new ContextError(\n `Request timed out after ${timeoutMs / 1000}s`,\n undefined,\n 408\n );\n }\n\n throw new ContextError(\n lastError.message,\n undefined,\n undefined\n );\n }\n }\n\n throw lastError ?? new ContextError(\"Request failed after retries\");\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/client/types.ts","../../src/client/resources/discovery.ts","../../src/client/resources/tools.ts","../../src/client/resources/query.ts","../../src/client/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Configuration options for initializing the ContextClient\n */\nexport interface ContextClientOptions {\n /**\n * Your Context Protocol API key\n * @example \"sk_live_abc123...\"\n */\n apiKey: string;\n\n /**\n * Base URL for the Context Protocol API\n * @default \"https://ctxprotocol.com\"\n */\n baseUrl?: string;\n}\n\n/**\n * An individual MCP tool exposed by a tool listing\n */\nexport interface McpTool {\n /** Name of the MCP tool method */\n name: string;\n\n /** Description of what this method does */\n description: string;\n\n /**\n * JSON Schema for the input arguments this tool accepts.\n * Used by LLMs to generate correct arguments.\n */\n inputSchema?: Record<string, unknown>;\n\n /**\n * JSON Schema for the output this tool returns.\n * Used by LLMs to understand the response structure.\n */\n outputSchema?: Record<string, unknown>;\n}\n\n/**\n * Represents a tool available on the Context Protocol marketplace\n */\nexport interface Tool {\n /** Unique identifier for the tool (UUID) */\n id: string;\n\n /** Human-readable name of the tool */\n name: string;\n\n /** Description of what the tool does */\n description: string;\n\n /** Price per execution in USDC */\n price: string;\n\n /** Tool category (e.g., \"defi\", \"nft\") */\n category?: string;\n\n /** Whether the tool is verified by Context Protocol */\n isVerified?: boolean;\n\n /** Tool type - currently always \"mcp\" */\n kind?: string;\n\n /**\n * Available MCP tool methods\n * Use items from this array as `toolName` when executing\n */\n mcpTools?: McpTool[];\n\n // Trust metrics (Level 2 - Reputation Ledger)\n /** Total number of queries processed */\n totalQueries?: number;\n\n /** Success rate percentage (0-100) */\n successRate?: string;\n\n /** Uptime percentage (0-100) */\n uptimePercent?: string;\n\n /** Total USDC staked by the developer */\n totalStaked?: string;\n\n /** Whether the tool has \"Proven\" status (100+ queries, >95% success, >98% uptime) */\n isProven?: boolean;\n}\n\n/**\n * Response from the tools search endpoint\n */\nexport interface SearchResponse {\n /** Array of matching tools */\n tools: Tool[];\n\n /** The search query that was used */\n query: string;\n\n /** Total number of results */\n count: number;\n}\n\n/**\n * Options for searching tools\n */\nexport interface SearchOptions {\n /** Search query (semantic search) */\n query?: string;\n\n /** Maximum number of results (1-50, default 10) */\n limit?: number;\n}\n\n/**\n * Options for executing a tool\n */\nexport interface ExecuteOptions {\n /** The UUID of the tool to execute (from search results) */\n toolId: string;\n\n /** The specific MCP tool name to call (from tool's mcpTools array) */\n toolName: string;\n\n /** Arguments to pass to the tool */\n args?: Record<string, unknown>;\n\n /**\n * Optional idempotency key (UUID recommended).\n * Reuse the same key when retrying the same logical request.\n */\n idempotencyKey?: string;\n}\n\n/**\n * Successful execution response from the API\n */\nexport interface ExecuteApiSuccessResponse {\n success: true;\n\n /** The result data from the tool execution */\n result: unknown;\n\n /** Information about the executed tool */\n tool: {\n id: string;\n name: string;\n };\n\n /** Execution duration in milliseconds */\n durationMs: number;\n}\n\n/**\n * Error response from the API\n */\nexport interface ExecuteApiErrorResponse {\n /** Human-readable error message */\n error: string;\n\n /** Error code for programmatic handling */\n code?: ContextErrorCode;\n\n /** URL to help resolve the issue */\n helpUrl?: string;\n}\n\n/**\n * Raw API response from the execute endpoint\n */\nexport type ExecuteApiResponse = ExecuteApiSuccessResponse | ExecuteApiErrorResponse;\n\n/**\n * The resolved result returned to the user after SDK processing\n */\nexport interface ExecutionResult<T = unknown> {\n /** The data returned by the tool */\n result: T;\n\n /** Information about the executed tool */\n tool: {\n id: string;\n name: string;\n };\n\n /** Execution duration in milliseconds */\n durationMs: number;\n}\n\n// ---------------------------------------------------------------------------\n// Query types (pay-per-response / agentic mode)\n// ---------------------------------------------------------------------------\n\n/**\n * Options for the agentic query endpoint (pay-per-response).\n *\n * Unlike `execute()` which calls a single tool once, `query()` sends a\n * natural-language question and lets the server handle tool discovery,\n * multi-tool orchestration, self-healing retries, and AI synthesis.\n * One flat fee covers up to 100 MCP skill calls per tool.\n */\nexport interface QueryOptions {\n /** The natural-language question to answer */\n query: string;\n\n /**\n * Optional tool IDs to use. When omitted the server discovers tools\n * automatically (Auto Mode). When provided, only these tools are used\n * (Manual Mode).\n */\n tools?: string[];\n\n /**\n * Optional model ID for query orchestration/synthesis.\n * Supported IDs are published by the Context API.\n */\n modelId?: string;\n\n /**\n * Include execution data inline in the query response.\n * Useful for headless agents that need raw structured outputs.\n */\n includeData?: boolean;\n\n /**\n * Persist execution data to Vercel Blob and return a download URL.\n * Useful for large payload workflows where inline JSON is not ideal.\n */\n includeDataUrl?: boolean;\n\n /**\n * Optional idempotency key (UUID recommended).\n * Reuse the same key when retrying the same logical request.\n */\n idempotencyKey?: string;\n}\n\n/**\n * Information about a tool that was used during a query response\n */\nexport interface QueryToolUsage {\n /** Tool ID */\n id: string;\n\n /** Tool name */\n name: string;\n\n /** Number of MCP skill calls made for this tool */\n skillCalls: number;\n}\n\n/**\n * Cost breakdown for a query response.\n * All values are strings representing USD amounts.\n */\nexport interface QueryCost {\n /** AI model inference cost */\n modelCostUsd: string;\n\n /** Sum of all tool fees */\n toolCostUsd: string;\n\n /** Total cost (model + tools) */\n totalCostUsd: string;\n}\n\n/**\n * The resolved result of a pay-per-response query\n */\nexport interface QueryResult {\n /** The AI-synthesized response text */\n response: string;\n\n /** Tools that were used to answer the query */\n toolsUsed: QueryToolUsage[];\n\n /** Cost breakdown */\n cost: QueryCost;\n\n /** Total duration in milliseconds */\n durationMs: number;\n\n /** Optional execution data from tools (when includeData=true) */\n data?: unknown;\n\n /** Optional blob URL for persisted execution data (when includeDataUrl=true) */\n dataUrl?: string;\n}\n\n/**\n * Successful response from the /api/v1/query endpoint\n */\nexport interface QueryApiSuccessResponse {\n success: true;\n response: string;\n toolsUsed: QueryToolUsage[];\n cost: QueryCost;\n durationMs: number;\n data?: unknown;\n dataUrl?: string;\n}\n\n/**\n * Raw API response from the query endpoint\n */\nexport type QueryApiResponse = QueryApiSuccessResponse | ExecuteApiErrorResponse;\n\n// ---------------------------------------------------------------------------\n// Query stream event types\n// ---------------------------------------------------------------------------\n\n/** Emitted when a tool starts or changes execution status */\nexport interface QueryStreamToolStatusEvent {\n type: \"tool-status\";\n tool: { id: string; name: string };\n status: string;\n}\n\n/** Emitted for each chunk of the AI response text */\nexport interface QueryStreamTextDeltaEvent {\n type: \"text-delta\";\n delta: string;\n}\n\n/** Emitted when the full response is complete */\nexport interface QueryStreamDoneEvent {\n type: \"done\";\n result: QueryResult;\n}\n\n/**\n * Union of all events emitted during a streaming query\n */\nexport type QueryStreamEvent =\n | QueryStreamToolStatusEvent\n | QueryStreamTextDeltaEvent\n | QueryStreamDoneEvent;\n\n// ---------------------------------------------------------------------------\n// Error types\n// ---------------------------------------------------------------------------\n\n/**\n * Specific error codes returned by the Context Protocol API\n */\nexport type ContextErrorCode =\n | \"unauthorized\"\n | \"no_wallet\"\n | \"insufficient_allowance\"\n | \"payment_failed\"\n | \"execution_failed\"\n | \"query_failed\";\n\n/**\n * Error thrown by the Context Protocol client\n */\nexport class ContextError extends Error {\n constructor(\n message: string,\n public readonly code?: ContextErrorCode | string,\n public readonly statusCode?: number,\n public readonly helpUrl?: string\n ) {\n super(message);\n this.name = \"ContextError\";\n Object.setPrototypeOf(this, ContextError.prototype);\n }\n}\n","import type { Tool, SearchResponse } from \"../types.js\";\nimport type { ContextClient } from \"../client.js\";\n\n/**\n * Discovery resource for searching and finding tools on the Context Protocol marketplace\n */\nexport class Discovery {\n constructor(private client: ContextClient) {}\n\n /**\n * Search for tools matching a query string\n *\n * @param query - The search query (e.g., \"gas prices\", \"nft metadata\")\n * @param limit - Maximum number of results (1-50, default 10)\n * @returns Array of matching tools\n *\n * @example\n * ```typescript\n * const tools = await client.discovery.search(\"gas prices\");\n * console.log(tools[0].name); // \"Gas Price Oracle\"\n * console.log(tools[0].mcpTools); // Available methods\n * ```\n */\n async search(query: string, limit?: number): Promise<Tool[]> {\n const params = new URLSearchParams();\n\n if (query) {\n params.set(\"q\", query);\n }\n\n if (limit !== undefined) {\n params.set(\"limit\", String(limit));\n }\n\n const queryString = params.toString();\n const endpoint = `/api/v1/tools/search${queryString ? `?${queryString}` : \"\"}`;\n\n const response = await this.client._fetch<SearchResponse>(endpoint);\n\n return response.tools;\n }\n\n /**\n * Get featured/popular tools (empty query search)\n *\n * @param limit - Maximum number of results (1-50, default 10)\n * @returns Array of featured tools\n *\n * @example\n * ```typescript\n * const featured = await client.discovery.getFeatured(5);\n * ```\n */\n async getFeatured(limit?: number): Promise<Tool[]> {\n return this.search(\"\", limit);\n }\n}\n","import type {\n ExecuteOptions,\n ExecuteApiResponse,\n ExecutionResult,\n} from \"../types.js\";\nimport { ContextError } from \"../types.js\";\nimport type { ContextClient } from \"../client.js\";\n\n/**\n * Tools resource for executing tools on the Context Protocol marketplace\n */\nexport class Tools {\n constructor(private client: ContextClient) {}\n\n /**\n * Execute a tool with the provided arguments\n *\n * @param options - Execution options\n * @param options.toolId - The UUID of the tool (from search results)\n * @param options.toolName - The specific MCP tool method to call (from tool's mcpTools array)\n * @param options.args - Arguments to pass to the tool\n * @returns The execution result with the tool's output data\n *\n * @throws {ContextError} With code `no_wallet` if wallet not set up\n * @throws {ContextError} With code `insufficient_allowance` if spending cap not set\n * @throws {ContextError} With code `payment_failed` if payment settlement fails\n * @throws {ContextError} With code `execution_failed` if tool execution fails\n *\n * @example\n * ```typescript\n * // First, search for a tool\n * const tools = await client.discovery.search(\"gas prices\");\n * const tool = tools[0];\n *\n * // Execute a specific method from the tool's mcpTools\n * const result = await client.tools.execute({\n * toolId: tool.id,\n * toolName: tool.mcpTools[0].name, // e.g., \"get_gas_prices\"\n * args: { chainId: 1 }\n * });\n *\n * console.log(result.result); // The tool's output\n * console.log(result.durationMs); // Execution time\n * ```\n */\n async execute<T = unknown>(options: ExecuteOptions): Promise<ExecutionResult<T>> {\n const { toolId, toolName, args, idempotencyKey } = options;\n const headers = idempotencyKey\n ? { \"Idempotency-Key\": idempotencyKey }\n : undefined;\n\n const response = await this.client._fetch<ExecuteApiResponse>(\n \"/api/v1/tools/execute\",\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({ toolId, toolName, args }),\n }\n );\n\n // Handle error response\n if (\"error\" in response) {\n throw new ContextError(\n response.error,\n response.code,\n undefined, // Don't hardcode - this was a 200 OK with error body\n response.helpUrl\n );\n }\n\n // Handle success response\n if (response.success) {\n return {\n result: response.result as T,\n tool: response.tool,\n durationMs: response.durationMs,\n };\n }\n\n // Fallback - shouldn't reach here with valid API responses\n throw new ContextError(\"Unexpected response format from API\");\n }\n}\n","import type {\n QueryOptions,\n QueryApiResponse,\n QueryResult,\n QueryStreamEvent,\n} from \"../types.js\";\nimport { ContextError } from \"../types.js\";\nimport type { ContextClient } from \"../client.js\";\n\n/**\n * Query resource for pay-per-response agentic queries.\n *\n * Unlike `tools.execute()` which calls a single tool once (pay-per-request),\n * the Query resource sends a natural-language question and lets the server\n * handle tool discovery, multi-tool orchestration, self-healing retries,\n * completeness checks, and AI synthesis — all for one flat fee.\n *\n * This is the \"prepared meal\" vs \"raw ingredients\" distinction:\n * - `tools.execute()` = raw data, full control, predictable cost\n * - `query.run()` / `query.stream()` = curated intelligence, one payment\n */\nexport class Query {\n constructor(private client: ContextClient) {}\n\n /**\n * Run an agentic query and wait for the full response.\n *\n * The server discovers relevant tools (or uses the ones you specify),\n * executes the full agentic pipeline (up to 100 MCP calls per tool),\n * and returns an AI-synthesized answer. Payment is settled after\n * successful execution via deferred settlement.\n *\n * @param options - Query options or a plain string question\n * @returns The complete query result with response text, tools used, and cost\n *\n * @throws {ContextError} With code `no_wallet` if wallet not set up\n * @throws {ContextError} With code `insufficient_allowance` if spending cap not set\n * @throws {ContextError} With code `payment_failed` if payment settlement fails\n * @throws {ContextError} With code `execution_failed` if the agentic pipeline fails\n *\n * @example\n * ```typescript\n * // Simple question — server discovers tools automatically\n * const answer = await client.query.run(\"What are the top whale movements on Base?\");\n * console.log(answer.response); // AI-synthesized answer\n * console.log(answer.toolsUsed); // Which tools were used\n * console.log(answer.cost); // Cost breakdown\n *\n * // With specific tools (Manual Mode)\n * const answer = await client.query.run({\n * query: \"Analyze whale activity\",\n * tools: [\"tool-uuid-1\", \"tool-uuid-2\"],\n * });\n * ```\n */\n async run(options: QueryOptions | string): Promise<QueryResult> {\n const opts = typeof options === \"string\" ? { query: options } : options;\n const headers = opts.idempotencyKey\n ? { \"Idempotency-Key\": opts.idempotencyKey }\n : undefined;\n\n const response = await this.client._fetch<QueryApiResponse>(\n \"/api/v1/query\",\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query: opts.query,\n tools: opts.tools,\n modelId: opts.modelId,\n includeData: opts.includeData,\n includeDataUrl: opts.includeDataUrl,\n stream: false,\n }),\n }\n );\n\n // Handle error response\n if (\"error\" in response) {\n throw new ContextError(\n response.error,\n response.code,\n undefined,\n response.helpUrl\n );\n }\n\n // Handle success response\n if (response.success) {\n return {\n response: response.response,\n toolsUsed: response.toolsUsed,\n cost: response.cost,\n durationMs: response.durationMs,\n data: response.data,\n dataUrl: response.dataUrl,\n };\n }\n\n throw new ContextError(\"Unexpected response format from query API\");\n }\n\n /**\n * Run an agentic query with streaming. Returns an async iterable that\n * yields events as the server processes the query in real-time.\n *\n * Event types:\n * - `tool-status` — A tool started executing or changed status\n * - `text-delta` — A chunk of the AI response text\n * - `done` — The full response is complete (includes final `QueryResult`)\n *\n * @param options - Query options or a plain string question\n * @returns An async iterable of stream events\n *\n * @example\n * ```typescript\n * for await (const event of client.query.stream(\"What are the top whale movements?\")) {\n * switch (event.type) {\n * case \"tool-status\":\n * console.log(`Tool ${event.tool.name}: ${event.status}`);\n * break;\n * case \"text-delta\":\n * process.stdout.write(event.delta);\n * break;\n * case \"done\":\n * console.log(\"\\nCost:\", event.result.cost.totalCostUsd);\n * break;\n * }\n * }\n * ```\n */\n async *stream(\n options: QueryOptions | string\n ): AsyncGenerator<QueryStreamEvent> {\n const opts = typeof options === \"string\" ? { query: options } : options;\n const headers = opts.idempotencyKey\n ? { \"Idempotency-Key\": opts.idempotencyKey }\n : undefined;\n\n const response = await this.client._fetchRaw(\"/api/v1/query\", {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n query: opts.query,\n tools: opts.tools,\n modelId: opts.modelId,\n includeData: opts.includeData,\n includeDataUrl: opts.includeDataUrl,\n stream: true,\n }),\n });\n\n const body = response.body;\n if (!body) {\n throw new ContextError(\"No response body for streaming query\");\n }\n\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith(\"data: \")) {\n const data = trimmed.slice(6);\n if (data === \"[DONE]\") return;\n try {\n yield JSON.parse(data) as QueryStreamEvent;\n } catch {\n // Skip malformed SSE events\n }\n }\n }\n }\n\n // Process any remaining buffer\n if (buffer.trim().startsWith(\"data: \")) {\n const data = buffer.trim().slice(6);\n if (data !== \"[DONE]\") {\n try {\n yield JSON.parse(data) as QueryStreamEvent;\n } catch {\n // Skip malformed SSE events\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n","import type { ContextClientOptions } from \"./types.js\";\nimport { ContextError } from \"./types.js\";\nimport { Discovery } from \"./resources/discovery.js\";\nimport { Tools } from \"./resources/tools.js\";\nimport { Query } from \"./resources/query.js\";\n\n/**\n * The official TypeScript client for the Context Protocol.\n *\n * Use this client to discover and execute AI tools programmatically.\n *\n * @example\n * ```typescript\n * import { ContextClient } from \"@contextprotocol/client\";\n *\n * const client = new ContextClient({\n * apiKey: \"sk_live_...\"\n * });\n *\n * // Pay-per-request: Execute a specific tool\n * const result = await client.tools.execute({\n * toolId: \"tool-uuid\",\n * toolName: \"get_gas_prices\",\n * args: { chainId: 1 }\n * });\n *\n * // Pay-per-response: Ask a question, get a curated answer\n * const answer = await client.query.run(\"What are the top whale movements on Base?\");\n * console.log(answer.response);\n * ```\n */\nexport class ContextClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private _closed = false;\n\n /**\n * Discovery resource for searching tools\n */\n public readonly discovery: Discovery;\n\n /**\n * Tools resource for executing tools (pay-per-request)\n */\n public readonly tools: Tools;\n\n /**\n * Query resource for agentic queries (pay-per-response).\n *\n * Unlike `tools.execute()` which calls a single tool once, `query` sends\n * a natural-language question and lets the server handle tool discovery,\n * multi-tool orchestration, self-healing, and AI synthesis — one flat fee.\n */\n public readonly query: Query;\n\n /**\n * Creates a new Context Protocol client\n *\n * @param options - Client configuration options\n * @param options.apiKey - Your Context Protocol API key (format: sk_live_...)\n * @param options.baseUrl - Optional base URL override (defaults to https://ctxprotocol.com)\n */\n constructor(options: ContextClientOptions) {\n if (!options.apiKey) {\n throw new ContextError(\"API key is required\");\n }\n\n this.apiKey = options.apiKey;\n this.baseUrl = (options.baseUrl ?? \"https://ctxprotocol.com\").replace(/\\/$/, \"\");\n\n // Initialize resources\n this.discovery = new Discovery(this);\n this.tools = new Tools(this);\n this.query = new Query(this);\n }\n\n /**\n * Close the client and clean up resources.\n * After calling close(), any in-flight requests may be aborted.\n */\n close(): void {\n this._closed = true;\n }\n\n /**\n * Internal method for making authenticated HTTP requests\n * Includes timeout (30s) and retry with exponential backoff for transient errors\n *\n * @internal\n */\n async _fetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n if (this._closed) {\n throw new ContextError(\"Client has been closed\");\n }\n\n const url = `${this.baseUrl}${endpoint}`;\n const maxRetries = 3;\n const timeoutMs = 30_000;\n\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n\n try {\n const response = await fetch(url, {\n ...options,\n signal: controller.signal,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n clearTimeout(timeout);\n\n if (!response.ok) {\n // Retry on 5xx server errors\n if (response.status >= 500 && attempt < maxRetries) {\n const delay = Math.min(1000 * 2 ** attempt, 10_000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n\n let errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n let errorCode: string | undefined;\n let helpUrl: string | undefined;\n\n try {\n const errorBody = await response.json();\n if (errorBody.error) {\n errorMessage = errorBody.error;\n errorCode = errorBody.code;\n helpUrl = errorBody.helpUrl;\n }\n } catch {\n // Use default error message if JSON parsing fails\n }\n\n throw new ContextError(errorMessage, errorCode, response.status, helpUrl);\n }\n\n return response.json() as Promise<T>;\n } catch (error) {\n clearTimeout(timeout);\n\n if (error instanceof ContextError) {\n throw error;\n }\n\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Retry on network errors and timeouts\n const isRetryable =\n lastError.name === \"AbortError\" ||\n lastError.message.includes(\"fetch failed\") ||\n lastError.message.includes(\"ECONNRESET\") ||\n lastError.message.includes(\"ETIMEDOUT\");\n\n if (isRetryable && attempt < maxRetries) {\n const delay = Math.min(1000 * 2 ** attempt, 10_000);\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue;\n }\n\n if (lastError.name === \"AbortError\") {\n throw new ContextError(\n `Request timed out after ${timeoutMs / 1000}s`,\n undefined,\n 408\n );\n }\n\n throw new ContextError(\n lastError.message,\n undefined,\n undefined\n );\n }\n }\n\n throw lastError ?? new ContextError(\"Request failed after retries\");\n }\n\n /**\n * Internal method for making authenticated HTTP requests that returns\n * the raw Response object. Used for streaming endpoints (SSE).\n *\n * @internal\n */\n async _fetchRaw(endpoint: string, options: RequestInit = {}): Promise<Response> {\n if (this._closed) {\n throw new ContextError(\"Client has been closed\");\n }\n\n const url = `${this.baseUrl}${endpoint}`;\n\n const response = await fetch(url, {\n ...options,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n let errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n let errorCode: string | undefined;\n let helpUrl: string | undefined;\n\n try {\n const errorBody = await response.json();\n if (errorBody.error) {\n errorMessage = errorBody.error;\n errorCode = errorBody.code;\n helpUrl = errorBody.helpUrl;\n }\n } catch {\n // Use default error message if JSON parsing fails\n }\n\n throw new ContextError(errorMessage, errorCode, response.status, helpUrl);\n }\n\n return response;\n }\n}\n"]}
|