@ctxprotocol/sdk 0.8.0 → 0.8.2

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.
@@ -16,6 +16,66 @@ interface ContextClientOptions {
16
16
  /**
17
17
  * An individual MCP tool exposed by a tool listing
18
18
  */
19
+ interface McpToolRateLimitHints {
20
+ /** Suggested request budget for this method */
21
+ maxRequestsPerMinute?: number;
22
+ /** Suggested parallel call ceiling for this method */
23
+ maxConcurrency?: number;
24
+ /** Suggested minimum delay between sequential calls */
25
+ cooldownMs?: number;
26
+ /** Whether this method already supports bulk/batch retrieval */
27
+ supportsBulk?: boolean;
28
+ /** Preferred batch-oriented methods to call instead of fan-out loops */
29
+ recommendedBatchTools?: string[];
30
+ /** Optional human-readable notes for planning */
31
+ notes?: string;
32
+ }
33
+ type DiscoveryMode = "query" | "execute";
34
+ type McpToolSurface = "answer" | "execute" | "both";
35
+ type McpToolLatencyClass = "instant" | "fast" | "slow" | "streaming";
36
+ interface McpToolPricingMeta {
37
+ executeUsd?: string;
38
+ queryUsd?: string;
39
+ [key: string]: unknown;
40
+ }
41
+ interface McpToolMeta {
42
+ /** Declared method surface */
43
+ surface?: McpToolSurface;
44
+ /** Whether this method can be selected in query mode */
45
+ queryEligible?: boolean;
46
+ /** Declared latency class for planner/runtime gating */
47
+ latencyClass?: McpToolLatencyClass;
48
+ /** Method-level pricing metadata */
49
+ pricing?: McpToolPricingMeta;
50
+ /** Derived discovery flag for execute eligibility */
51
+ executeEligible?: boolean;
52
+ /** Derived discovery field for explicit execute pricing visibility */
53
+ executePriceUsd?: string;
54
+ /** Context injection requirements handled by the Context runtime */
55
+ contextRequirements?: string[];
56
+ /**
57
+ * Optional planner/runtime pacing hints.
58
+ * Tool contributors can publish these to reduce rate-limit failures.
59
+ */
60
+ rateLimit?: McpToolRateLimitHints;
61
+ rateLimitHints?: McpToolRateLimitHints;
62
+ /** Flat aliases accepted for convenience */
63
+ maxRequestsPerMinute?: number;
64
+ maxConcurrency?: number;
65
+ cooldownMs?: number;
66
+ supportsBulk?: boolean;
67
+ recommendedBatchTools?: string[];
68
+ notes?: string;
69
+ [key: string]: unknown;
70
+ }
71
+ interface StructuredMethodGuidanceHints {
72
+ /** Suggested call-order sequence extracted from method descriptions */
73
+ callOrderHints?: string[];
74
+ /** Parameter usage caveats extracted from method descriptions */
75
+ parameterCaveats?: string[];
76
+ /** Edge-case behavior notes extracted from method descriptions */
77
+ edgeCaseNotes?: string[];
78
+ }
19
79
  interface McpTool {
20
80
  /** Name of the MCP tool method */
21
81
  name: string;
@@ -31,6 +91,16 @@ interface McpTool {
31
91
  * Used by LLMs to understand the response structure.
32
92
  */
33
93
  outputSchema?: Record<string, unknown>;
94
+ /** MCP metadata extensions (context injection, rate-limit hints) */
95
+ _meta?: McpToolMeta;
96
+ /** Explicit execute eligibility in discovery responses */
97
+ executeEligible?: boolean;
98
+ /** Explicit execute price visibility in discovery responses */
99
+ executePriceUsd?: string | null;
100
+ /** Whether this method has normalized structured guidance hints */
101
+ hasStructuredGuidance?: boolean;
102
+ /** Optional structured guidance hints derived from the method description */
103
+ structuredGuidance?: StructuredMethodGuidanceHints;
34
104
  }
35
105
  /**
36
106
  * Represents a tool available on the Context Protocol marketplace
@@ -72,6 +142,8 @@ interface Tool {
72
142
  interface SearchResponse {
73
143
  /** Array of matching tools */
74
144
  tools: Tool[];
145
+ /** Discovery mode used by the server */
146
+ mode?: DiscoveryMode;
75
147
  /** The search query that was used */
76
148
  query: string;
77
149
  /** Total number of results */
@@ -85,6 +157,18 @@ interface SearchOptions {
85
157
  query?: string;
86
158
  /** Maximum number of results (1-50, default 10) */
87
159
  limit?: number;
160
+ /** Discovery mode with billing semantics */
161
+ mode?: DiscoveryMode;
162
+ /** Optional explicit method surface filter */
163
+ surface?: McpToolSurface;
164
+ /** Require methods marked query eligible */
165
+ queryEligible?: boolean;
166
+ /** Require explicit method execute pricing */
167
+ requireExecutePricing?: boolean;
168
+ /** Exclude methods by latency class */
169
+ excludeLatencyClasses?: McpToolLatencyClass[];
170
+ /** Convenience switch to exclude slow methods in query mode */
171
+ excludeSlow?: boolean;
88
172
  }
89
173
  /**
90
174
  * Options for executing a tool
@@ -101,12 +185,36 @@ interface ExecuteOptions {
101
185
  * Reuse the same key when retrying the same logical request.
102
186
  */
103
187
  idempotencyKey?: string;
188
+ /** Explicit execute mode label for request clarity */
189
+ mode?: "execute";
190
+ /** Optional execute session identifier */
191
+ sessionId?: string;
192
+ /** Optional per-session spend budget envelope (USD) */
193
+ maxSpendUsd?: string;
194
+ /** Request session closure after this execute call settles */
195
+ closeSession?: boolean;
196
+ }
197
+ type ExecuteSessionStatus = "open" | "closed" | "expired";
198
+ interface ExecuteSessionSpend {
199
+ mode: "execute";
200
+ sessionId: string | null;
201
+ methodPrice: string;
202
+ spent: string;
203
+ remaining: string | null;
204
+ maxSpend: string | null;
205
+ /** Optional lifecycle fields when the API returns session state */
206
+ status?: ExecuteSessionStatus;
207
+ expiresAt?: string;
208
+ closeRequested?: boolean;
209
+ pendingAccruedCount?: number;
210
+ pendingAccruedUsd?: string;
104
211
  }
105
212
  /**
106
213
  * Successful execution response from the API
107
214
  */
108
215
  interface ExecuteApiSuccessResponse {
109
216
  success: true;
217
+ mode: "execute";
110
218
  /** The result data from the tool execution */
111
219
  result: unknown;
112
220
  /** Information about the executed tool */
@@ -114,6 +222,13 @@ interface ExecuteApiSuccessResponse {
114
222
  id: string;
115
223
  name: string;
116
224
  };
225
+ /** Method-level execute pricing used for this call */
226
+ method: {
227
+ name: string;
228
+ executePriceUsd: string;
229
+ };
230
+ /** Spend envelope visibility for execute sessions */
231
+ session: ExecuteSessionSpend;
117
232
  /** Execution duration in milliseconds */
118
233
  durationMs: number;
119
234
  }
@@ -123,19 +238,38 @@ interface ExecuteApiSuccessResponse {
123
238
  interface ExecuteApiErrorResponse {
124
239
  /** Human-readable error message */
125
240
  error: string;
241
+ /** Explicit mode label for clarity */
242
+ mode?: "execute";
126
243
  /** Error code for programmatic handling */
127
244
  code?: ContextErrorCode;
128
245
  /** URL to help resolve the issue */
129
246
  helpUrl?: string;
247
+ /** Optional spend envelope context when available */
248
+ session?: ExecuteSessionSpend;
130
249
  }
131
250
  /**
132
251
  * Raw API response from the execute endpoint
133
252
  */
134
253
  type ExecuteApiResponse = ExecuteApiSuccessResponse | ExecuteApiErrorResponse;
254
+ interface ExecuteSessionStartOptions {
255
+ /** Maximum spend budget for the session (USD string) */
256
+ maxSpendUsd: string;
257
+ }
258
+ interface ExecuteSessionApiSuccessResponse {
259
+ success: true;
260
+ mode: "execute";
261
+ session: ExecuteSessionSpend;
262
+ }
263
+ type ExecuteSessionApiResponse = ExecuteSessionApiSuccessResponse | ExecuteApiErrorResponse;
264
+ interface ExecuteSessionResult {
265
+ mode: "execute";
266
+ session: ExecuteSessionSpend;
267
+ }
135
268
  /**
136
269
  * The resolved result returned to the user after SDK processing
137
270
  */
138
271
  interface ExecutionResult<T = unknown> {
272
+ mode: "execute";
139
273
  /** The data returned by the tool */
140
274
  result: T;
141
275
  /** Information about the executed tool */
@@ -143,6 +277,13 @@ interface ExecutionResult<T = unknown> {
143
277
  id: string;
144
278
  name: string;
145
279
  };
280
+ /** Method-level execute pricing used for this call */
281
+ method: {
282
+ name: string;
283
+ executePriceUsd: string;
284
+ };
285
+ /** Spend envelope visibility for execute calls */
286
+ session: ExecuteSessionSpend;
146
287
  /** Execution duration in milliseconds */
147
288
  durationMs: number;
148
289
  }
@@ -266,7 +407,7 @@ type QueryStreamEvent = QueryStreamToolStatusEvent | QueryStreamTextDeltaEvent |
266
407
  /**
267
408
  * Specific error codes returned by the Context Protocol API
268
409
  */
269
- type ContextErrorCode = "unauthorized" | "no_wallet" | "insufficient_allowance" | "payment_failed" | "execution_failed" | "query_failed";
410
+ type ContextErrorCode = "unauthorized" | "no_wallet" | "insufficient_allowance" | "payment_failed" | "execution_failed" | "query_failed" | "invalid_tool_method" | "method_not_execute_eligible" | "invalid_max_spend" | "session_not_found" | "session_forbidden" | "session_closed" | "session_expired" | "max_spend_mismatch" | "session_budget_exceeded";
270
411
  /**
271
412
  * Error thrown by the Context Protocol client
272
413
  */
@@ -284,20 +425,14 @@ declare class Discovery {
284
425
  private client;
285
426
  constructor(client: ContextClient);
286
427
  /**
287
- * Search for tools matching a query string
288
- *
289
- * @param query - The search query (e.g., "gas prices", "nft metadata")
290
- * @param limit - Maximum number of results (1-50, default 10)
291
- * @returns Array of matching tools
428
+ * Search for tools matching a query string.
292
429
  *
293
- * @example
294
- * ```typescript
295
- * const tools = await client.discovery.search("gas prices");
296
- * console.log(tools[0].name); // "Gas Price Oracle"
297
- * console.log(tools[0].mcpTools); // Available methods
298
- * ```
430
+ * Backward-compatible signatures:
431
+ * - `search("gas prices", 10)`
432
+ * - `search({ query: "gas prices", limit: 10, mode: "execute" })`
299
433
  */
300
434
  search(query: string, limit?: number): Promise<Tool[]>;
435
+ search(options: SearchOptions): Promise<Tool[]>;
301
436
  /**
302
437
  * Get featured/popular tools (empty query search)
303
438
  *
@@ -309,7 +444,7 @@ declare class Discovery {
309
444
  * const featured = await client.discovery.getFeatured(5);
310
445
  * ```
311
446
  */
312
- getFeatured(limit?: number): Promise<Tool[]>;
447
+ getFeatured(limit?: number, options?: Omit<SearchOptions, "query" | "limit">): Promise<Tool[]>;
313
448
  }
314
449
 
315
450
  /**
@@ -350,6 +485,19 @@ declare class Tools {
350
485
  * ```
351
486
  */
352
487
  execute<T = unknown>(options: ExecuteOptions): Promise<ExecutionResult<T>>;
488
+ /**
489
+ * Start an execute session with a max spend budget.
490
+ */
491
+ startSession(options: ExecuteSessionStartOptions): Promise<ExecuteSessionResult>;
492
+ /**
493
+ * Fetch current execute session status by ID.
494
+ */
495
+ getSession(sessionId: string): Promise<ExecuteSessionResult>;
496
+ /**
497
+ * Close an execute session by ID.
498
+ */
499
+ closeSession(sessionId: string): Promise<ExecuteSessionResult>;
500
+ private resolveSessionLifecycleResponse;
353
501
  }
354
502
 
355
503
  /**
@@ -505,4 +653,4 @@ declare class ContextClient {
505
653
  _fetchRaw(endpoint: string, options?: RequestInit): Promise<Response>;
506
654
  }
507
655
 
508
- export { ContextClient, type ContextClientOptions, ContextError, type ContextErrorCode, Discovery, type ExecuteApiErrorResponse, type ExecuteApiResponse, type ExecuteApiSuccessResponse, type ExecuteOptions, type ExecutionResult, type McpTool, Query, type QueryApiResponse, type QueryApiSuccessResponse, type QueryCost, type QueryOptions, type QueryResult, type QueryStreamDoneEvent, type QueryStreamEvent, type QueryStreamTextDeltaEvent, type QueryStreamToolStatusEvent, type QueryToolUsage, type SearchOptions, type SearchResponse, type Tool, Tools };
656
+ export { ContextClient, type ContextClientOptions, ContextError, type ContextErrorCode, Discovery, type ExecuteApiErrorResponse, type ExecuteApiResponse, type ExecuteApiSuccessResponse, type ExecuteOptions, type ExecuteSessionApiResponse, type ExecuteSessionApiSuccessResponse, type ExecuteSessionResult, type ExecuteSessionSpend, type ExecuteSessionStartOptions, type ExecuteSessionStatus, type ExecutionResult, type McpTool, type McpToolMeta, type McpToolRateLimitHints, Query, type QueryApiResponse, type QueryApiSuccessResponse, type QueryCost, type QueryOptions, type QueryResult, type QueryStreamDoneEvent, type QueryStreamEvent, type QueryStreamTextDeltaEvent, type QueryStreamToolStatusEvent, type QueryToolUsage, type SearchOptions, type SearchResponse, type Tool, Tools };
@@ -15,27 +15,36 @@ var Discovery = class {
15
15
  constructor(client) {
16
16
  this.client = client;
17
17
  }
18
- /**
19
- * Search for tools matching a query string
20
- *
21
- * @param query - The search query (e.g., "gas prices", "nft metadata")
22
- * @param limit - Maximum number of results (1-50, default 10)
23
- * @returns Array of matching tools
24
- *
25
- * @example
26
- * ```typescript
27
- * const tools = await client.discovery.search("gas prices");
28
- * console.log(tools[0].name); // "Gas Price Oracle"
29
- * console.log(tools[0].mcpTools); // Available methods
30
- * ```
31
- */
32
- async search(query, limit) {
18
+ async search(queryOrOptions, limit) {
19
+ const options = typeof queryOrOptions === "string" ? { query: queryOrOptions, limit } : queryOrOptions;
33
20
  const params = new URLSearchParams();
21
+ const query = options.query ?? "";
34
22
  if (query) {
35
23
  params.set("q", query);
36
24
  }
37
- if (limit !== void 0) {
38
- params.set("limit", String(limit));
25
+ if (options.limit !== void 0) {
26
+ params.set("limit", String(options.limit));
27
+ }
28
+ if (options.mode) {
29
+ params.set("mode", options.mode);
30
+ }
31
+ if (options.surface) {
32
+ params.set("surface", options.surface);
33
+ }
34
+ if (options.queryEligible !== void 0) {
35
+ params.set("queryEligible", String(options.queryEligible));
36
+ }
37
+ if (options.requireExecutePricing !== void 0) {
38
+ params.set(
39
+ "requireExecutePricing",
40
+ String(options.requireExecutePricing)
41
+ );
42
+ }
43
+ if (options.excludeLatencyClasses && options.excludeLatencyClasses.length > 0) {
44
+ params.set("excludeLatency", options.excludeLatencyClasses.join(","));
45
+ }
46
+ if (options.excludeSlow !== void 0) {
47
+ params.set("excludeSlow", String(options.excludeSlow));
39
48
  }
40
49
  const queryString = params.toString();
41
50
  const endpoint = `/api/v1/tools/search${queryString ? `?${queryString}` : ""}`;
@@ -53,8 +62,12 @@ var Discovery = class {
53
62
  * const featured = await client.discovery.getFeatured(5);
54
63
  * ```
55
64
  */
56
- async getFeatured(limit) {
57
- return this.search("", limit);
65
+ async getFeatured(limit, options) {
66
+ return this.search({
67
+ ...options ?? {},
68
+ query: "",
69
+ ...limit !== void 0 ? { limit } : {}
70
+ });
58
71
  }
59
72
  };
60
73
 
@@ -95,14 +108,31 @@ var Tools = class {
95
108
  * ```
96
109
  */
97
110
  async execute(options) {
98
- const { toolId, toolName, args, idempotencyKey } = options;
111
+ const {
112
+ toolId,
113
+ toolName,
114
+ args,
115
+ idempotencyKey,
116
+ mode,
117
+ sessionId,
118
+ maxSpendUsd,
119
+ closeSession
120
+ } = options;
99
121
  const headers = idempotencyKey ? { "Idempotency-Key": idempotencyKey } : void 0;
100
122
  const response = await this.client._fetch(
101
123
  "/api/v1/tools/execute",
102
124
  {
103
125
  method: "POST",
104
126
  headers,
105
- body: JSON.stringify({ toolId, toolName, args })
127
+ body: JSON.stringify({
128
+ toolId,
129
+ toolName,
130
+ args,
131
+ mode: mode ?? "execute",
132
+ sessionId,
133
+ maxSpendUsd,
134
+ closeSession
135
+ })
106
136
  }
107
137
  );
108
138
  if ("error" in response) {
@@ -116,13 +146,79 @@ var Tools = class {
116
146
  }
117
147
  if (response.success) {
118
148
  return {
149
+ mode: response.mode,
119
150
  result: response.result,
120
151
  tool: response.tool,
152
+ method: response.method,
153
+ session: response.session,
121
154
  durationMs: response.durationMs
122
155
  };
123
156
  }
124
157
  throw new ContextError("Unexpected response format from API");
125
158
  }
159
+ /**
160
+ * Start an execute session with a max spend budget.
161
+ */
162
+ async startSession(options) {
163
+ const response = await this.client._fetch(
164
+ "/api/v1/tools/execute/sessions",
165
+ {
166
+ method: "POST",
167
+ body: JSON.stringify({
168
+ mode: "execute",
169
+ maxSpendUsd: options.maxSpendUsd
170
+ })
171
+ }
172
+ );
173
+ return this.resolveSessionLifecycleResponse(response);
174
+ }
175
+ /**
176
+ * Fetch current execute session status by ID.
177
+ */
178
+ async getSession(sessionId) {
179
+ if (!sessionId) {
180
+ throw new ContextError("sessionId is required");
181
+ }
182
+ const encodedSessionId = encodeURIComponent(sessionId);
183
+ const response = await this.client._fetch(
184
+ `/api/v1/tools/execute/sessions/${encodedSessionId}`
185
+ );
186
+ return this.resolveSessionLifecycleResponse(response);
187
+ }
188
+ /**
189
+ * Close an execute session by ID.
190
+ */
191
+ async closeSession(sessionId) {
192
+ if (!sessionId) {
193
+ throw new ContextError("sessionId is required");
194
+ }
195
+ const encodedSessionId = encodeURIComponent(sessionId);
196
+ const response = await this.client._fetch(
197
+ `/api/v1/tools/execute/sessions/${encodedSessionId}/close`,
198
+ {
199
+ method: "POST",
200
+ body: JSON.stringify({ mode: "execute" })
201
+ }
202
+ );
203
+ return this.resolveSessionLifecycleResponse(response);
204
+ }
205
+ resolveSessionLifecycleResponse(response) {
206
+ if ("error" in response) {
207
+ throw new ContextError(
208
+ response.error,
209
+ response.code,
210
+ void 0,
211
+ response.helpUrl
212
+ );
213
+ }
214
+ if (response.success) {
215
+ return {
216
+ mode: response.mode,
217
+ session: response.session
218
+ };
219
+ }
220
+ throw new ContextError("Unexpected response format from API");
221
+ }
126
222
  };
127
223
 
128
224
  // src/client/resources/query.ts