@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 CHANGED
@@ -1,10 +1,32 @@
1
1
  # @ctxprotocol/sdk
2
2
 
3
- The official TypeScript SDK for the [Context Protocol](https://ctxprotocol.com) — the monetization layer for MCP. Discover and execute AI tools programmatically.
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
  [![npm version](https://img.shields.io/npm/v/@ctxprotocol/sdk.svg)](https://www.npmjs.com/package/@ctxprotocol/sdk)
6
10
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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, you must complete setup via the web dashboard:
46
+ Before using the API, complete setup at [ctxprotocol.com](https://ctxprotocol.com):
25
47
 
26
- 1. **Sign in** at [ctxprotocol.com](https://ctxprotocol.com) — Creates your embedded wallet
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
- // 1. Discover tools
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
- // 2. Execute a tool method
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, // e.g., "get_gas_prices"
68
+ toolName: tools[0].mcpTools[0].name,
50
69
  args: { chainId: 1 },
51
70
  });
52
71
 
53
- console.log(result.result); // Tool output data
54
- console.log(result.durationMs); // Execution time in ms
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 usage
218
+ // Production
68
219
  const client = new ContextClient({
69
220
  apiKey: process.env.CONTEXT_API_KEY!,
70
221
  });
71
222
 
72
- // Development/testing with local server
73
- const devClient = new ContextClient({
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 prices", 10);
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 with the provided arguments.
254
+ Execute a tool method.
121
255
 
122
256
  ```typescript
123
257
  const result = await client.tools.execute({
124
- toolId: "uuid-of-tool", // From search results
125
- toolName: "get_gas_prices", // From tool's mcpTools array
126
- args: { chainId: 1 }, // Tool-specific arguments
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
- ### ExecuteOptions
291
+ ### McpTool
173
292
 
174
293
  ```typescript
175
- interface ExecuteOptions {
176
- toolId: string; // UUID of the tool
177
- toolName: string; // MCP method name from mcpTools array
178
- args?: Record<string, unknown>;
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` for all API errors with specific error codes:
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 { ContextClient, ContextError } from "@ctxprotocol/sdk";
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
- console.log("Please set up your wallet at", error.helpUrl);
325
+ // User needs to set up wallet
326
+ console.log("Setup required:", error.helpUrl);
215
327
  break;
216
328
  case "insufficient_allowance":
217
- console.log("Please enable Auto Pay at", error.helpUrl);
329
+ // User needs to enable Auto Pay
330
+ console.log("Enable Auto Pay:", error.helpUrl);
218
331
  break;
219
332
  case "payment_failed":
220
- console.log("Payment transaction failed");
333
+ // Insufficient USDC balance
221
334
  break;
222
335
  case "execution_failed":
223
- console.log("Tool execution failed");
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` | Missing or invalid API key |
235
- | `no_wallet` | User hasn't set up wallet via dashboard |
236
- | `insufficient_allowance` | Auto Pay not enabled or allowance too low|
237
- | `payment_failed` | On-chain payment transaction failed |
238
- | `execution_failed` | MCP tool execution error |
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
- ## Authentication
354
+ ---
241
355
 
242
- All requests are automatically authenticated using the Bearer token scheme:
356
+ ## Building MCP Servers (Tool Contributors)
243
357
 
244
- ```
245
- Authorization: Bearer sk_live_...
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
- Your API key should be kept secret. Use environment variables in production:
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
- const client = new ContextClient({
252
- apiKey: process.env.CONTEXT_API_KEY!,
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 for payment
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 repository
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.0.0 or later (for native `fetch` support)
277
- - TypeScript 5.0+ (recommended)
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