@ctxprotocol/sdk 0.1.2 → 0.2.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 +273 -93
- package/dist/client/index.cjs +194 -0
- package/dist/client/index.cjs.map +1 -0
- package/dist/client/index.d.cts +277 -0
- package/dist/client/index.d.ts +277 -0
- package/dist/client/index.js +189 -0
- package/dist/client/index.js.map +1 -0
- package/dist/index.cjs +4 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -269
- package/dist/index.d.ts +1 -269
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
# @ctxprotocol/sdk
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
**The Universal Adapter for AI Agents.**
|
|
4
|
+
|
|
5
|
+
Connect your AI to the real world without managing API keys, hosting servers, or reading documentation.
|
|
6
|
+
|
|
7
|
+
Context Protocol is **npm for AI capabilities**. Just as you install packages to add functionality to your code, use the Context SDK to give your Agent instant access to thousands of live data sources and actions—from DeFi and Gas Oracles to Weather and Search.
|
|
4
8
|
|
|
5
9
|
[](https://www.npmjs.com/package/@ctxprotocol/sdk)
|
|
6
10
|
[](https://opensource.org/licenses/MIT)
|
|
7
11
|
|
|
12
|
+
## Why use Context?
|
|
13
|
+
|
|
14
|
+
- **🔌 One Interface, Everything:** Stop integrating APIs one by one. Use a single SDK to access any tool in the marketplace.
|
|
15
|
+
- **🧠 Zero-Ops:** We're a gateway to the best MCP tools. Just send the JSON and get the result.
|
|
16
|
+
- **⚡️ Agentic Discovery:** Your Agent can search the marketplace at runtime to find tools it didn't know it needed.
|
|
17
|
+
- **💸 Micro-Billing:** Pay only for what you use (e.g., $0.001/query). No monthly subscriptions for tools you rarely use.
|
|
18
|
+
|
|
19
|
+
## Who Is This SDK For?
|
|
20
|
+
|
|
21
|
+
**This SDK is for AI Agent developers** who want to query the Context marketplace and execute tools.
|
|
22
|
+
|
|
23
|
+
| Role | What You Use |
|
|
24
|
+
|------|--------------|
|
|
25
|
+
| **AI Agent Developer** | `@ctxprotocol/sdk` — Query marketplace, execute tools, handle payments |
|
|
26
|
+
| **Tool Contributor (Data Broker)** | `@modelcontextprotocol/sdk` — Standard MCP server + Context extensions |
|
|
27
|
+
|
|
28
|
+
If you're building an MCP server to contribute tools and earn money, you **don't need this SDK**. See [Building MCP Servers](#building-mcp-servers-tool-contributors) for the simple pattern.
|
|
29
|
+
|
|
8
30
|
## Installation
|
|
9
31
|
|
|
10
32
|
```bash
|
|
@@ -21,9 +43,9 @@ yarn add @ctxprotocol/sdk
|
|
|
21
43
|
|
|
22
44
|
## Prerequisites
|
|
23
45
|
|
|
24
|
-
Before using the API,
|
|
46
|
+
Before using the API, complete setup at [ctxprotocol.com](https://ctxprotocol.com):
|
|
25
47
|
|
|
26
|
-
1. **Sign in**
|
|
48
|
+
1. **Sign in** — Creates your embedded wallet
|
|
27
49
|
2. **Enable Auto Pay** — Approve USDC spending for tool payments
|
|
28
50
|
3. **Fund wallet** — Add USDC for tool execution fees
|
|
29
51
|
4. **Generate API key** — In Settings page
|
|
@@ -33,27 +55,156 @@ Before using the API, you must complete setup via the web dashboard:
|
|
|
33
55
|
```typescript
|
|
34
56
|
import { ContextClient } from "@ctxprotocol/sdk";
|
|
35
57
|
|
|
36
|
-
// Initialize the client with your API key
|
|
37
58
|
const client = new ContextClient({
|
|
38
59
|
apiKey: "sk_live_...",
|
|
39
60
|
});
|
|
40
61
|
|
|
41
|
-
//
|
|
62
|
+
// Discover tools
|
|
42
63
|
const tools = await client.discovery.search("gas prices");
|
|
43
|
-
console.log(tools[0].name); // "Gas Price Oracle"
|
|
44
|
-
console.log(tools[0].mcpTools); // Available methods
|
|
45
64
|
|
|
46
|
-
//
|
|
65
|
+
// Execute a tool
|
|
47
66
|
const result = await client.tools.execute({
|
|
48
67
|
toolId: tools[0].id,
|
|
49
|
-
toolName: tools[0].mcpTools[0].name,
|
|
68
|
+
toolName: tools[0].mcpTools[0].name,
|
|
50
69
|
args: { chainId: 1 },
|
|
51
70
|
});
|
|
52
71
|
|
|
53
|
-
console.log(result.result);
|
|
54
|
-
|
|
72
|
+
console.log(result.result);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## The Agentic Pattern: How to Build Autonomous Bots
|
|
78
|
+
|
|
79
|
+
The most powerful way to use this SDK is to let your LLM do the driving. Instead of hardcoding tool calls, follow this **Discovery → Schema → Execution** loop:
|
|
80
|
+
|
|
81
|
+
### 1. Discover
|
|
82
|
+
|
|
83
|
+
Let your Agent search for tools based on the user's intent.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const tools = await client.discovery.search(userQuery);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 2. Inspect Schemas
|
|
90
|
+
|
|
91
|
+
Feed the discovered tool schemas (`inputSchema`) directly to your LLM's system prompt. This allows the LLM to understand exactly how to format the arguments—just like reading a manual.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const systemPrompt = `
|
|
95
|
+
You have access to the following tools:
|
|
96
|
+
|
|
97
|
+
${tools.map(t => `
|
|
98
|
+
Tool: ${t.name} (ID: ${t.id})
|
|
99
|
+
Description: ${t.description}
|
|
100
|
+
Price: ${t.price} USDC
|
|
101
|
+
|
|
102
|
+
Methods:
|
|
103
|
+
${t.mcpTools?.map(m => `
|
|
104
|
+
- ${m.name}: ${m.description}
|
|
105
|
+
Arguments: ${JSON.stringify(m.inputSchema, null, 2)}
|
|
106
|
+
Returns: ${JSON.stringify(m.outputSchema, null, 2)}
|
|
107
|
+
`).join("\n") ?? "No methods available"}
|
|
108
|
+
`).join("\n---\n")}
|
|
109
|
+
|
|
110
|
+
To use a tool, respond with a JSON object: { "toolId": "...", "toolName": "...", "args": {...} }
|
|
111
|
+
`;
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 3. Execute
|
|
115
|
+
|
|
116
|
+
When the LLM generates the arguments, pass them directly to the SDK.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// The LLM generates this object based on the schema you provided
|
|
120
|
+
const llmDecision = await myLLM.generate(userMessage, systemPrompt);
|
|
121
|
+
|
|
122
|
+
const result = await client.tools.execute({
|
|
123
|
+
toolId: llmDecision.toolId,
|
|
124
|
+
toolName: llmDecision.toolName,
|
|
125
|
+
args: llmDecision.args,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Feed the result back to your LLM for synthesis
|
|
129
|
+
const finalAnswer = await myLLM.generate(
|
|
130
|
+
`The tool returned: ${JSON.stringify(result.result)}. Summarize this for the user.`
|
|
131
|
+
);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Handling Data (Outputs)
|
|
135
|
+
|
|
136
|
+
Context Tools return raw, structured JSON data (via `structuredContent`). This allows your Agent to programmatically filter, sort, or analyze results before showing them to the user.
|
|
137
|
+
|
|
138
|
+
> **Note:** For large datasets (like CSVs or PDF analysis), the API may return a reference URL to keep your context window clean.
|
|
139
|
+
|
|
140
|
+
### Full Agentic Loop Example
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { ContextClient, ContextError } from "@ctxprotocol/sdk";
|
|
144
|
+
|
|
145
|
+
const client = new ContextClient({ apiKey: process.env.CONTEXT_API_KEY! });
|
|
146
|
+
|
|
147
|
+
async function agentLoop(userQuery: string) {
|
|
148
|
+
// 1. Discover relevant tools
|
|
149
|
+
const tools = await client.discovery.search(userQuery);
|
|
150
|
+
|
|
151
|
+
if (tools.length === 0) {
|
|
152
|
+
return "I couldn't find any tools to help with that.";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// 2. Build the system prompt with schemas
|
|
156
|
+
const toolDescriptions = tools.slice(0, 5).map(t => ({
|
|
157
|
+
id: t.id,
|
|
158
|
+
name: t.name,
|
|
159
|
+
description: t.description,
|
|
160
|
+
methods: t.mcpTools?.map(m => ({
|
|
161
|
+
name: m.name,
|
|
162
|
+
description: m.description,
|
|
163
|
+
inputSchema: m.inputSchema,
|
|
164
|
+
})),
|
|
165
|
+
}));
|
|
166
|
+
|
|
167
|
+
const systemPrompt = `You are an AI assistant with access to real-time tools.
|
|
168
|
+
|
|
169
|
+
Available tools:
|
|
170
|
+
${JSON.stringify(toolDescriptions, null, 2)}
|
|
171
|
+
|
|
172
|
+
If you need to use a tool, respond ONLY with JSON:
|
|
173
|
+
{ "toolId": "...", "toolName": "...", "args": {...} }
|
|
174
|
+
|
|
175
|
+
If you can answer without a tool, just respond normally.`;
|
|
176
|
+
|
|
177
|
+
// 3. Ask the LLM what to do
|
|
178
|
+
const llmResponse = await myLLM.chat(userQuery, systemPrompt);
|
|
179
|
+
|
|
180
|
+
// 4. Check if LLM wants to use a tool
|
|
181
|
+
try {
|
|
182
|
+
const toolCall = JSON.parse(llmResponse);
|
|
183
|
+
|
|
184
|
+
if (toolCall.toolId && toolCall.toolName) {
|
|
185
|
+
// 5. Execute the tool
|
|
186
|
+
const result = await client.tools.execute({
|
|
187
|
+
toolId: toolCall.toolId,
|
|
188
|
+
toolName: toolCall.toolName,
|
|
189
|
+
args: toolCall.args || {},
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// 6. Let LLM synthesize the result
|
|
193
|
+
return await myLLM.chat(
|
|
194
|
+
`Tool "${toolCall.toolName}" returned: ${JSON.stringify(result.result)}
|
|
195
|
+
|
|
196
|
+
Please provide a helpful response to the user's original question: "${userQuery}"`
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
} catch {
|
|
200
|
+
// LLM responded with text, not JSON - return as-is
|
|
201
|
+
return llmResponse;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
55
204
|
```
|
|
56
205
|
|
|
206
|
+
---
|
|
207
|
+
|
|
57
208
|
## Configuration
|
|
58
209
|
|
|
59
210
|
### Client Options
|
|
@@ -64,13 +215,13 @@ console.log(result.durationMs); // Execution time in ms
|
|
|
64
215
|
| `baseUrl` | `string` | No | `https://ctxprotocol.com`| API base URL (for development) |
|
|
65
216
|
|
|
66
217
|
```typescript
|
|
67
|
-
// Production
|
|
218
|
+
// Production
|
|
68
219
|
const client = new ContextClient({
|
|
69
220
|
apiKey: process.env.CONTEXT_API_KEY!,
|
|
70
221
|
});
|
|
71
222
|
|
|
72
|
-
//
|
|
73
|
-
const
|
|
223
|
+
// Local development
|
|
224
|
+
const client = new ContextClient({
|
|
74
225
|
apiKey: "sk_test_...",
|
|
75
226
|
baseUrl: "http://localhost:3000",
|
|
76
227
|
});
|
|
@@ -85,24 +236,7 @@ const devClient = new ContextClient({
|
|
|
85
236
|
Search for tools matching a query string.
|
|
86
237
|
|
|
87
238
|
```typescript
|
|
88
|
-
const tools = await client.discovery.search("gas
|
|
89
|
-
|
|
90
|
-
// Returns: Tool[]
|
|
91
|
-
// [
|
|
92
|
-
// {
|
|
93
|
-
// id: "uuid-string",
|
|
94
|
-
// name: "Gas Price Oracle",
|
|
95
|
-
// description: "Get current gas prices",
|
|
96
|
-
// price: "0.001",
|
|
97
|
-
// category: "defi",
|
|
98
|
-
// isVerified: true,
|
|
99
|
-
// kind: "mcp",
|
|
100
|
-
// mcpTools: [
|
|
101
|
-
// { name: "get_gas_prices", description: "Get gas prices for a chain" },
|
|
102
|
-
// { name: "get_supported_chains", description: "List supported chains" }
|
|
103
|
-
// ]
|
|
104
|
-
// }
|
|
105
|
-
// ]
|
|
239
|
+
const tools = await client.discovery.search("ethereum gas", 10);
|
|
106
240
|
```
|
|
107
241
|
|
|
108
242
|
#### `client.discovery.getFeatured(limit?)`
|
|
@@ -117,27 +251,18 @@ const featured = await client.discovery.getFeatured(5);
|
|
|
117
251
|
|
|
118
252
|
#### `client.tools.execute(options)`
|
|
119
253
|
|
|
120
|
-
Execute a tool method
|
|
254
|
+
Execute a tool method.
|
|
121
255
|
|
|
122
256
|
```typescript
|
|
123
257
|
const result = await client.tools.execute({
|
|
124
|
-
toolId: "uuid-of-tool",
|
|
125
|
-
toolName: "get_gas_prices",
|
|
126
|
-
args: { chainId: 1 },
|
|
258
|
+
toolId: "uuid-of-tool",
|
|
259
|
+
toolName: "get_gas_prices",
|
|
260
|
+
args: { chainId: 1 },
|
|
127
261
|
});
|
|
128
|
-
|
|
129
|
-
// Returns: ExecutionResult<T>
|
|
130
|
-
// {
|
|
131
|
-
// result: { gasPrice: "25.5", unit: "gwei", ... },
|
|
132
|
-
// tool: { id: "uuid", name: "Gas Price Oracle" },
|
|
133
|
-
// durationMs: 245
|
|
134
|
-
// }
|
|
135
262
|
```
|
|
136
263
|
|
|
137
264
|
## Types
|
|
138
265
|
|
|
139
|
-
All types are exported for full TypeScript autocomplete support:
|
|
140
|
-
|
|
141
266
|
```typescript
|
|
142
267
|
import type {
|
|
143
268
|
ContextClientOptions,
|
|
@@ -159,23 +284,18 @@ interface Tool {
|
|
|
159
284
|
price: string;
|
|
160
285
|
category?: string;
|
|
161
286
|
isVerified?: boolean;
|
|
162
|
-
kind?: string;
|
|
163
287
|
mcpTools?: McpTool[];
|
|
164
288
|
}
|
|
165
|
-
|
|
166
|
-
interface McpTool {
|
|
167
|
-
name: string;
|
|
168
|
-
description: string;
|
|
169
|
-
}
|
|
170
289
|
```
|
|
171
290
|
|
|
172
|
-
###
|
|
291
|
+
### McpTool
|
|
173
292
|
|
|
174
293
|
```typescript
|
|
175
|
-
interface
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
294
|
+
interface McpTool {
|
|
295
|
+
name: string;
|
|
296
|
+
description: string;
|
|
297
|
+
inputSchema?: Record<string, unknown>; // JSON Schema for arguments
|
|
298
|
+
outputSchema?: Record<string, unknown>; // JSON Schema for response
|
|
179
299
|
}
|
|
180
300
|
```
|
|
181
301
|
|
|
@@ -191,36 +311,30 @@ interface ExecutionResult<T = unknown> {
|
|
|
191
311
|
|
|
192
312
|
## Error Handling
|
|
193
313
|
|
|
194
|
-
The SDK throws `ContextError`
|
|
314
|
+
The SDK throws `ContextError` with specific error codes. In an agentic context, you can feed errors back to your LLM so it can self-correct.
|
|
195
315
|
|
|
196
316
|
```typescript
|
|
197
|
-
import {
|
|
317
|
+
import { ContextError } from "@ctxprotocol/sdk";
|
|
198
318
|
|
|
199
319
|
try {
|
|
200
|
-
const result = await client.tools.execute({
|
|
201
|
-
toolId: "...",
|
|
202
|
-
toolName: "...",
|
|
203
|
-
args: {},
|
|
204
|
-
});
|
|
320
|
+
const result = await client.tools.execute({ ... });
|
|
205
321
|
} catch (error) {
|
|
206
322
|
if (error instanceof ContextError) {
|
|
207
|
-
console.error("Error:", error.message);
|
|
208
|
-
console.error("Code:", error.code);
|
|
209
|
-
console.error("HTTP Status:", error.statusCode);
|
|
210
|
-
|
|
211
|
-
// Handle specific error cases
|
|
212
323
|
switch (error.code) {
|
|
213
324
|
case "no_wallet":
|
|
214
|
-
|
|
325
|
+
// User needs to set up wallet
|
|
326
|
+
console.log("Setup required:", error.helpUrl);
|
|
215
327
|
break;
|
|
216
328
|
case "insufficient_allowance":
|
|
217
|
-
|
|
329
|
+
// User needs to enable Auto Pay
|
|
330
|
+
console.log("Enable Auto Pay:", error.helpUrl);
|
|
218
331
|
break;
|
|
219
332
|
case "payment_failed":
|
|
220
|
-
|
|
333
|
+
// Insufficient USDC balance
|
|
221
334
|
break;
|
|
222
335
|
case "execution_failed":
|
|
223
|
-
|
|
336
|
+
// Tool execution error - feed back to LLM to retry with different args
|
|
337
|
+
const retryPrompt = `The tool failed with: ${error.message}. Try different arguments.`;
|
|
224
338
|
break;
|
|
225
339
|
}
|
|
226
340
|
}
|
|
@@ -229,52 +343,118 @@ try {
|
|
|
229
343
|
|
|
230
344
|
### Error Codes
|
|
231
345
|
|
|
232
|
-
| Code | Description |
|
|
233
|
-
| ------------------------ | ---------------------------------------- |
|
|
234
|
-
| `unauthorized` |
|
|
235
|
-
| `no_wallet` |
|
|
236
|
-
| `insufficient_allowance` | Auto Pay not enabled
|
|
237
|
-
| `payment_failed` |
|
|
238
|
-
| `execution_failed` |
|
|
346
|
+
| Code | Description | Agentic Handling |
|
|
347
|
+
| ------------------------ | ---------------------------------------- | ----------------------------------- |
|
|
348
|
+
| `unauthorized` | Invalid API key | Check configuration |
|
|
349
|
+
| `no_wallet` | Wallet not set up | Direct user to `helpUrl` |
|
|
350
|
+
| `insufficient_allowance` | Auto Pay not enabled | Direct user to `helpUrl` |
|
|
351
|
+
| `payment_failed` | USDC payment failed | Check balance |
|
|
352
|
+
| `execution_failed` | Tool error | Feed error to LLM for retry |
|
|
239
353
|
|
|
240
|
-
|
|
354
|
+
---
|
|
241
355
|
|
|
242
|
-
|
|
356
|
+
## Building MCP Servers (Tool Contributors)
|
|
243
357
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
358
|
+
Want to earn money by contributing tools to the Context marketplace? Build a standard MCP server with two Context Protocol extensions:
|
|
359
|
+
|
|
360
|
+
1. **`outputSchema`** in tool definitions — JSON Schema describing your response
|
|
361
|
+
2. **`structuredContent`** in responses — Machine-readable data matching the schema
|
|
362
|
+
|
|
363
|
+
### Why These Matter
|
|
247
364
|
|
|
248
|
-
|
|
365
|
+
| Requirement | Purpose |
|
|
366
|
+
|------------|---------|
|
|
367
|
+
| `outputSchema` | AI agents use this to generate type-safe code. Context uses it for dispute resolution. |
|
|
368
|
+
| `structuredContent` | Agents parse this for programmatic access. Text `content` is for humans. |
|
|
369
|
+
|
|
370
|
+
### Example: Standard MCP Server with Context Extensions
|
|
371
|
+
|
|
372
|
+
Build your server with the standard `@modelcontextprotocol/sdk` — just add the Context Protocol extensions:
|
|
249
373
|
|
|
250
374
|
```typescript
|
|
251
|
-
|
|
252
|
-
|
|
375
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
376
|
+
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
|
|
377
|
+
|
|
378
|
+
// Define tools with outputSchema (Context Protocol extension)
|
|
379
|
+
const TOOLS = [{
|
|
380
|
+
name: "get_gas_price",
|
|
381
|
+
description: "Get current gas prices",
|
|
382
|
+
inputSchema: {
|
|
383
|
+
type: "object",
|
|
384
|
+
properties: {
|
|
385
|
+
chainId: { type: "number", description: "EVM chain ID" },
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
// 👇 Context Protocol extension: define your response structure
|
|
389
|
+
outputSchema: {
|
|
390
|
+
type: "object",
|
|
391
|
+
properties: {
|
|
392
|
+
gasPrice: { type: "number" },
|
|
393
|
+
unit: { type: "string" },
|
|
394
|
+
},
|
|
395
|
+
required: ["gasPrice", "unit"],
|
|
396
|
+
},
|
|
397
|
+
}];
|
|
398
|
+
|
|
399
|
+
// Standard MCP server setup
|
|
400
|
+
const server = new Server(
|
|
401
|
+
{ name: "my-gas-tool", version: "1.0.0" },
|
|
402
|
+
{ capabilities: { tools: {} } }
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
406
|
+
tools: TOOLS, // outputSchema is included automatically
|
|
407
|
+
}));
|
|
408
|
+
|
|
409
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
410
|
+
const data = await fetchGasData(request.params.arguments.chainId);
|
|
411
|
+
|
|
412
|
+
// 👇 Context Protocol extension: include structuredContent
|
|
413
|
+
return {
|
|
414
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
415
|
+
structuredContent: data, // Machine-readable, matches outputSchema
|
|
416
|
+
};
|
|
253
417
|
});
|
|
254
418
|
```
|
|
255
419
|
|
|
420
|
+
### Example Servers
|
|
421
|
+
|
|
422
|
+
See complete working examples in `/examples/server/`:
|
|
423
|
+
|
|
424
|
+
- **[blocknative-contributor](./examples/server/blocknative-contributor)** — Gas price API (3 tools)
|
|
425
|
+
- **[hyperliquid-contributor](./examples/server/hyperliquid-contributor)** — DeFi analytics (16 tools)
|
|
426
|
+
|
|
427
|
+
### Schema Accuracy = Revenue
|
|
428
|
+
|
|
429
|
+
⚠️ **Important**: Your `outputSchema` is a contract. Context's "Robot Judge" validates that your `structuredContent` matches your declared schema. Schema violations result in automatic refunds to users.
|
|
430
|
+
|
|
431
|
+
### Server Dependencies
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
pnpm add @modelcontextprotocol/sdk express
|
|
435
|
+
pnpm add -D @types/express
|
|
436
|
+
```
|
|
437
|
+
|
|
256
438
|
## Payment Flow
|
|
257
439
|
|
|
258
440
|
When you execute a tool:
|
|
259
441
|
|
|
260
|
-
1. Your pre-approved USDC allowance is used
|
|
442
|
+
1. Your pre-approved USDC allowance is used
|
|
261
443
|
2. **90%** goes to the tool developer
|
|
262
444
|
3. **10%** goes to the protocol
|
|
263
445
|
4. Tool executes and returns results
|
|
264
446
|
|
|
265
|
-
Ensure your wallet has sufficient USDC balance before executing paid tools.
|
|
266
|
-
|
|
267
447
|
## Links
|
|
268
448
|
|
|
269
449
|
- [Context Protocol](https://ctxprotocol.com) — Main website
|
|
270
|
-
- [GitHub](https://github.com/ctxprotocol/context) — Main project
|
|
450
|
+
- [GitHub](https://github.com/ctxprotocol/context) — Main project
|
|
271
451
|
- [SDK Repository](https://github.com/ctxprotocol/sdk) — This SDK
|
|
272
452
|
- [NPM Package](https://www.npmjs.com/package/@ctxprotocol/sdk)
|
|
273
453
|
|
|
274
454
|
## Requirements
|
|
275
455
|
|
|
276
|
-
- Node.js 18
|
|
277
|
-
- TypeScript 5
|
|
456
|
+
- Node.js 18+ (for native `fetch`)
|
|
457
|
+
- TypeScript 5+ (recommended)
|
|
278
458
|
|
|
279
459
|
## License
|
|
280
460
|
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/client/types.ts
|
|
4
|
+
var ContextError = class extends Error {
|
|
5
|
+
constructor(message, code, statusCode, helpUrl) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.statusCode = statusCode;
|
|
9
|
+
this.helpUrl = helpUrl;
|
|
10
|
+
this.name = "ContextError";
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// src/client/resources/discovery.ts
|
|
15
|
+
var Discovery = class {
|
|
16
|
+
constructor(client) {
|
|
17
|
+
this.client = client;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Search for tools matching a query string
|
|
21
|
+
*
|
|
22
|
+
* @param query - The search query (e.g., "gas prices", "nft metadata")
|
|
23
|
+
* @param limit - Maximum number of results (1-50, default 10)
|
|
24
|
+
* @returns Array of matching tools
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const tools = await client.discovery.search("gas prices");
|
|
29
|
+
* console.log(tools[0].name); // "Gas Price Oracle"
|
|
30
|
+
* console.log(tools[0].mcpTools); // Available methods
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
async search(query, limit) {
|
|
34
|
+
const params = new URLSearchParams();
|
|
35
|
+
if (query) {
|
|
36
|
+
params.set("q", query);
|
|
37
|
+
}
|
|
38
|
+
if (limit !== void 0) {
|
|
39
|
+
params.set("limit", String(limit));
|
|
40
|
+
}
|
|
41
|
+
const queryString = params.toString();
|
|
42
|
+
const endpoint = `/api/v1/tools/search${queryString ? `?${queryString}` : ""}`;
|
|
43
|
+
const response = await this.client.fetch(endpoint);
|
|
44
|
+
return response.tools;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get featured/popular tools (empty query search)
|
|
48
|
+
*
|
|
49
|
+
* @param limit - Maximum number of results (1-50, default 10)
|
|
50
|
+
* @returns Array of featured tools
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const featured = await client.discovery.getFeatured(5);
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
async getFeatured(limit) {
|
|
58
|
+
return this.search("", limit);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// src/client/resources/tools.ts
|
|
63
|
+
var Tools = class {
|
|
64
|
+
constructor(client) {
|
|
65
|
+
this.client = client;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Execute a tool with the provided arguments
|
|
69
|
+
*
|
|
70
|
+
* @param options - Execution options
|
|
71
|
+
* @param options.toolId - The UUID of the tool (from search results)
|
|
72
|
+
* @param options.toolName - The specific MCP tool method to call (from tool's mcpTools array)
|
|
73
|
+
* @param options.args - Arguments to pass to the tool
|
|
74
|
+
* @returns The execution result with the tool's output data
|
|
75
|
+
*
|
|
76
|
+
* @throws {ContextError} With code `no_wallet` if wallet not set up
|
|
77
|
+
* @throws {ContextError} With code `insufficient_allowance` if Auto Pay not enabled
|
|
78
|
+
* @throws {ContextError} With code `payment_failed` if on-chain payment fails
|
|
79
|
+
* @throws {ContextError} With code `execution_failed` if tool execution fails
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* // First, search for a tool
|
|
84
|
+
* const tools = await client.discovery.search("gas prices");
|
|
85
|
+
* const tool = tools[0];
|
|
86
|
+
*
|
|
87
|
+
* // Execute a specific method from the tool's mcpTools
|
|
88
|
+
* const result = await client.tools.execute({
|
|
89
|
+
* toolId: tool.id,
|
|
90
|
+
* toolName: tool.mcpTools[0].name, // e.g., "get_gas_prices"
|
|
91
|
+
* args: { chainId: 1 }
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* console.log(result.result); // The tool's output
|
|
95
|
+
* console.log(result.durationMs); // Execution time
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
async execute(options) {
|
|
99
|
+
const { toolId, toolName, args } = options;
|
|
100
|
+
const response = await this.client.fetch(
|
|
101
|
+
"/api/v1/tools/execute",
|
|
102
|
+
{
|
|
103
|
+
method: "POST",
|
|
104
|
+
body: JSON.stringify({ toolId, toolName, args })
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
if ("error" in response) {
|
|
108
|
+
throw new ContextError(
|
|
109
|
+
response.error,
|
|
110
|
+
response.code,
|
|
111
|
+
400,
|
|
112
|
+
response.helpUrl
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
if (response.success) {
|
|
116
|
+
return {
|
|
117
|
+
result: response.result,
|
|
118
|
+
tool: response.tool,
|
|
119
|
+
durationMs: response.durationMs
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
throw new ContextError("Unexpected response format from API");
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// src/client/client.ts
|
|
127
|
+
var ContextClient = class {
|
|
128
|
+
apiKey;
|
|
129
|
+
baseUrl;
|
|
130
|
+
/**
|
|
131
|
+
* Discovery resource for searching tools
|
|
132
|
+
*/
|
|
133
|
+
discovery;
|
|
134
|
+
/**
|
|
135
|
+
* Tools resource for executing tools
|
|
136
|
+
*/
|
|
137
|
+
tools;
|
|
138
|
+
/**
|
|
139
|
+
* Creates a new Context Protocol client
|
|
140
|
+
*
|
|
141
|
+
* @param options - Client configuration options
|
|
142
|
+
* @param options.apiKey - Your Context Protocol API key (format: sk_live_...)
|
|
143
|
+
* @param options.baseUrl - Optional base URL override (defaults to https://ctxprotocol.com)
|
|
144
|
+
*/
|
|
145
|
+
constructor(options) {
|
|
146
|
+
if (!options.apiKey) {
|
|
147
|
+
throw new ContextError("API key is required");
|
|
148
|
+
}
|
|
149
|
+
this.apiKey = options.apiKey;
|
|
150
|
+
this.baseUrl = (options.baseUrl ?? "https://ctxprotocol.com").replace(/\/$/, "");
|
|
151
|
+
this.discovery = new Discovery(this);
|
|
152
|
+
this.tools = new Tools(this);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Internal method for making authenticated HTTP requests
|
|
156
|
+
* All requests include the Authorization header with the API key
|
|
157
|
+
*
|
|
158
|
+
* @internal
|
|
159
|
+
*/
|
|
160
|
+
async fetch(endpoint, options = {}) {
|
|
161
|
+
const url = `${this.baseUrl}${endpoint}`;
|
|
162
|
+
const response = await fetch(url, {
|
|
163
|
+
...options,
|
|
164
|
+
headers: {
|
|
165
|
+
"Content-Type": "application/json",
|
|
166
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
167
|
+
...options.headers
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
if (!response.ok) {
|
|
171
|
+
let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
|
|
172
|
+
let errorCode;
|
|
173
|
+
let helpUrl;
|
|
174
|
+
try {
|
|
175
|
+
const errorBody = await response.json();
|
|
176
|
+
if (errorBody.error) {
|
|
177
|
+
errorMessage = errorBody.error;
|
|
178
|
+
errorCode = errorBody.code;
|
|
179
|
+
helpUrl = errorBody.helpUrl;
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
}
|
|
183
|
+
throw new ContextError(errorMessage, errorCode, response.status, helpUrl);
|
|
184
|
+
}
|
|
185
|
+
return response.json();
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
exports.ContextClient = ContextClient;
|
|
190
|
+
exports.ContextError = ContextError;
|
|
191
|
+
exports.Discovery = Discovery;
|
|
192
|
+
exports.Tools = Tools;
|
|
193
|
+
//# sourceMappingURL=index.cjs.map
|
|
194
|
+
//# sourceMappingURL=index.cjs.map
|