ctxprotocol 0.5.6__py3-none-any.whl → 0.5.7__py3-none-any.whl
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.
ctxprotocol/__init__.py
CHANGED
|
@@ -91,6 +91,31 @@ from ctxprotocol.auth import (
|
|
|
91
91
|
CreateContextMiddlewareOptions,
|
|
92
92
|
)
|
|
93
93
|
|
|
94
|
+
# Handshake types and helpers for tools that need user interaction
|
|
95
|
+
# (signatures, transactions, OAuth)
|
|
96
|
+
from ctxprotocol.handshake import (
|
|
97
|
+
# Types
|
|
98
|
+
HandshakeMeta,
|
|
99
|
+
EIP712Domain,
|
|
100
|
+
EIP712TypeField,
|
|
101
|
+
SignatureRequest,
|
|
102
|
+
TransactionProposalMeta,
|
|
103
|
+
TransactionProposal,
|
|
104
|
+
AuthRequiredMeta,
|
|
105
|
+
AuthRequired,
|
|
106
|
+
HandshakeAction,
|
|
107
|
+
# Type guards
|
|
108
|
+
is_handshake_action,
|
|
109
|
+
is_signature_request,
|
|
110
|
+
is_transaction_proposal,
|
|
111
|
+
is_auth_required,
|
|
112
|
+
# Helper functions
|
|
113
|
+
create_signature_request,
|
|
114
|
+
create_transaction_proposal,
|
|
115
|
+
create_auth_required,
|
|
116
|
+
wrap_handshake_response,
|
|
117
|
+
)
|
|
118
|
+
|
|
94
119
|
__all__ = [
|
|
95
120
|
# Version
|
|
96
121
|
"__version__",
|
|
@@ -144,5 +169,25 @@ __all__ = [
|
|
|
144
169
|
"ContextMiddleware",
|
|
145
170
|
"VerifyRequestOptions",
|
|
146
171
|
"CreateContextMiddlewareOptions",
|
|
172
|
+
# Handshake types
|
|
173
|
+
"HandshakeMeta",
|
|
174
|
+
"EIP712Domain",
|
|
175
|
+
"EIP712TypeField",
|
|
176
|
+
"SignatureRequest",
|
|
177
|
+
"TransactionProposalMeta",
|
|
178
|
+
"TransactionProposal",
|
|
179
|
+
"AuthRequiredMeta",
|
|
180
|
+
"AuthRequired",
|
|
181
|
+
"HandshakeAction",
|
|
182
|
+
# Handshake type guards
|
|
183
|
+
"is_handshake_action",
|
|
184
|
+
"is_signature_request",
|
|
185
|
+
"is_transaction_proposal",
|
|
186
|
+
"is_auth_required",
|
|
187
|
+
# Handshake helper functions
|
|
188
|
+
"create_signature_request",
|
|
189
|
+
"create_transaction_proposal",
|
|
190
|
+
"create_auth_required",
|
|
191
|
+
"wrap_handshake_response",
|
|
147
192
|
]
|
|
148
193
|
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Handshake Module - Types and helpers for MCP tools that need user interaction.
|
|
3
|
+
|
|
4
|
+
Use these types when your tool needs to request user actions:
|
|
5
|
+
- Signatures (EIP-712 typed data for Hyperliquid, Polymarket, dYdX)
|
|
6
|
+
- Transactions (direct on-chain actions for Uniswap, NFT mints)
|
|
7
|
+
- OAuth (authentication flows for Discord, Twitter)
|
|
8
|
+
|
|
9
|
+
Example:
|
|
10
|
+
>>> from ctxprotocol.handshake import create_signature_request, wrap_handshake_response
|
|
11
|
+
>>>
|
|
12
|
+
>>> # In your MCP tool handler:
|
|
13
|
+
>>> def handle_place_order(args):
|
|
14
|
+
... return wrap_handshake_response(
|
|
15
|
+
... create_signature_request(
|
|
16
|
+
... domain={"name": "Hyperliquid", "version": "1", "chainId": 42161},
|
|
17
|
+
... types={"Order": [{"name": "asset", "type": "uint32"}, ...]},
|
|
18
|
+
... primary_type="Order",
|
|
19
|
+
... message={"asset": 4, "isBuy": True, ...},
|
|
20
|
+
... meta={"description": "Place order", "protocol": "Hyperliquid"}
|
|
21
|
+
... )
|
|
22
|
+
... )
|
|
23
|
+
|
|
24
|
+
For more information, see: https://docs.ctxprotocol.com/guides/handshake-architecture
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
from typing import Any, Literal, Optional, TypedDict, Union
|
|
30
|
+
|
|
31
|
+
# =============================================================================
|
|
32
|
+
# Shared Meta Types
|
|
33
|
+
# =============================================================================
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class HandshakeMeta(TypedDict, total=False):
|
|
37
|
+
"""UI metadata for handshake approval cards."""
|
|
38
|
+
|
|
39
|
+
description: str
|
|
40
|
+
"""Human-readable description of the action."""
|
|
41
|
+
|
|
42
|
+
protocol: str
|
|
43
|
+
"""Protocol name (e.g., 'Hyperliquid', 'Polymarket')."""
|
|
44
|
+
|
|
45
|
+
action: str
|
|
46
|
+
"""Action verb (e.g., 'Place Order', 'Place Bid')."""
|
|
47
|
+
|
|
48
|
+
token_symbol: str
|
|
49
|
+
"""Token symbol if relevant."""
|
|
50
|
+
|
|
51
|
+
token_amount: str
|
|
52
|
+
"""Human-readable token amount."""
|
|
53
|
+
|
|
54
|
+
warning_level: Literal["info", "caution", "danger"]
|
|
55
|
+
"""UI warning level."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
# =============================================================================
|
|
59
|
+
# Web3: Signature Requests (EIP-712)
|
|
60
|
+
# =============================================================================
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class EIP712Domain(TypedDict, total=False):
|
|
64
|
+
"""EIP-712 domain separator."""
|
|
65
|
+
|
|
66
|
+
name: str
|
|
67
|
+
"""Domain name (e.g., 'Hyperliquid', 'ClobAuthDomain')."""
|
|
68
|
+
|
|
69
|
+
version: str
|
|
70
|
+
"""Domain version."""
|
|
71
|
+
|
|
72
|
+
chainId: int
|
|
73
|
+
"""Chain ID (informational - signing is chain-agnostic)."""
|
|
74
|
+
|
|
75
|
+
verifyingContract: str
|
|
76
|
+
"""Optional verifying contract address (0x...)."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class EIP712TypeField(TypedDict):
|
|
80
|
+
"""A single field in an EIP-712 type definition."""
|
|
81
|
+
|
|
82
|
+
name: str
|
|
83
|
+
type: str
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class SignatureRequest(TypedDict, total=False):
|
|
87
|
+
"""
|
|
88
|
+
Signature Request for EIP-712 typed data signing.
|
|
89
|
+
|
|
90
|
+
Use this for platforms with proxy wallets (Hyperliquid, Polymarket, dYdX).
|
|
91
|
+
|
|
92
|
+
Benefits:
|
|
93
|
+
- No gas required (user signs a message, not a transaction)
|
|
94
|
+
- No network switching needed (signing is chain-agnostic)
|
|
95
|
+
- Works with Privy embedded wallets on any chain
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
_action: Literal["signature_request"]
|
|
99
|
+
"""Action type identifier (required)."""
|
|
100
|
+
|
|
101
|
+
domain: EIP712Domain
|
|
102
|
+
"""EIP-712 domain separator (required)."""
|
|
103
|
+
|
|
104
|
+
types: dict[str, list[EIP712TypeField]]
|
|
105
|
+
"""EIP-712 type definitions (required)."""
|
|
106
|
+
|
|
107
|
+
primaryType: str
|
|
108
|
+
"""The primary type being signed (required)."""
|
|
109
|
+
|
|
110
|
+
message: dict[str, Any]
|
|
111
|
+
"""The message data to sign (required)."""
|
|
112
|
+
|
|
113
|
+
meta: HandshakeMeta
|
|
114
|
+
"""UI metadata for the approval card."""
|
|
115
|
+
|
|
116
|
+
callbackToolName: str
|
|
117
|
+
"""Optional: Tool name to call with the signature result."""
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# =============================================================================
|
|
121
|
+
# Web3: Transaction Proposals
|
|
122
|
+
# =============================================================================
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class TransactionProposalMeta(HandshakeMeta, total=False):
|
|
126
|
+
"""Extended metadata for transaction proposals."""
|
|
127
|
+
|
|
128
|
+
estimated_gas: str
|
|
129
|
+
"""Estimated gas cost (informational - Context may sponsor)."""
|
|
130
|
+
|
|
131
|
+
explorer_url: str
|
|
132
|
+
"""Link to contract on block explorer."""
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class TransactionProposal(TypedDict, total=False):
|
|
136
|
+
"""
|
|
137
|
+
Transaction Proposal for direct on-chain actions.
|
|
138
|
+
|
|
139
|
+
Use this for protocols without proxy wallets (Uniswap, NFT mints, etc.).
|
|
140
|
+
|
|
141
|
+
Note: May require network switching and gas fees.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
_action: Literal["transaction_proposal"]
|
|
145
|
+
"""Action type identifier (required)."""
|
|
146
|
+
|
|
147
|
+
chainId: int
|
|
148
|
+
"""EVM chain ID (e.g., 137 for Polygon, 8453 for Base) (required)."""
|
|
149
|
+
|
|
150
|
+
to: str
|
|
151
|
+
"""Target contract address (0x...) (required)."""
|
|
152
|
+
|
|
153
|
+
data: str
|
|
154
|
+
"""Encoded calldata (0x...) (required)."""
|
|
155
|
+
|
|
156
|
+
value: str
|
|
157
|
+
"""Wei to send (as string, default '0')."""
|
|
158
|
+
|
|
159
|
+
meta: TransactionProposalMeta
|
|
160
|
+
"""UI metadata for the approval card."""
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# =============================================================================
|
|
164
|
+
# Web2: OAuth Requests
|
|
165
|
+
# =============================================================================
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class AuthRequiredMeta(TypedDict, total=False):
|
|
169
|
+
"""Metadata for OAuth requests."""
|
|
170
|
+
|
|
171
|
+
display_name: str
|
|
172
|
+
"""Human-friendly service name."""
|
|
173
|
+
|
|
174
|
+
scopes: list[str]
|
|
175
|
+
"""Permissions being requested."""
|
|
176
|
+
|
|
177
|
+
description: str
|
|
178
|
+
"""Description of what access is needed."""
|
|
179
|
+
|
|
180
|
+
icon_url: str
|
|
181
|
+
"""Tool's icon URL."""
|
|
182
|
+
|
|
183
|
+
expires_in: str
|
|
184
|
+
"""How long authorization lasts."""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class AuthRequired(TypedDict, total=False):
|
|
188
|
+
"""
|
|
189
|
+
Auth Required for OAuth flows.
|
|
190
|
+
|
|
191
|
+
Use this when your tool needs the user to authenticate with an external service.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
_action: Literal["auth_required"]
|
|
195
|
+
"""Action type identifier (required)."""
|
|
196
|
+
|
|
197
|
+
provider: str
|
|
198
|
+
"""Service identifier (e.g., 'discord', 'slack') (required)."""
|
|
199
|
+
|
|
200
|
+
authUrl: str
|
|
201
|
+
"""Your OAuth initiation endpoint (MUST be HTTPS) (required)."""
|
|
202
|
+
|
|
203
|
+
meta: AuthRequiredMeta
|
|
204
|
+
"""UI metadata for the auth card."""
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# =============================================================================
|
|
208
|
+
# Union Type
|
|
209
|
+
# =============================================================================
|
|
210
|
+
|
|
211
|
+
HandshakeAction = Union[SignatureRequest, TransactionProposal, AuthRequired]
|
|
212
|
+
|
|
213
|
+
# =============================================================================
|
|
214
|
+
# Type Guards
|
|
215
|
+
# =============================================================================
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def is_handshake_action(value: Any) -> bool:
|
|
219
|
+
"""Check if a value is a handshake action."""
|
|
220
|
+
if not isinstance(value, dict):
|
|
221
|
+
return False
|
|
222
|
+
action = value.get("_action")
|
|
223
|
+
return action in ("signature_request", "transaction_proposal", "auth_required")
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def is_signature_request(value: Any) -> bool:
|
|
227
|
+
"""Check if a value is a signature request."""
|
|
228
|
+
return is_handshake_action(value) and value.get("_action") == "signature_request"
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def is_transaction_proposal(value: Any) -> bool:
|
|
232
|
+
"""Check if a value is a transaction proposal."""
|
|
233
|
+
return is_handshake_action(value) and value.get("_action") == "transaction_proposal"
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def is_auth_required(value: Any) -> bool:
|
|
237
|
+
"""Check if a value is an auth required action."""
|
|
238
|
+
return is_handshake_action(value) and value.get("_action") == "auth_required"
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# =============================================================================
|
|
242
|
+
# Helper Functions
|
|
243
|
+
# =============================================================================
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def create_signature_request(
|
|
247
|
+
*,
|
|
248
|
+
domain: EIP712Domain,
|
|
249
|
+
types: dict[str, list[EIP712TypeField]],
|
|
250
|
+
primary_type: str,
|
|
251
|
+
message: dict[str, Any],
|
|
252
|
+
meta: HandshakeMeta | None = None,
|
|
253
|
+
callback_tool_name: str | None = None,
|
|
254
|
+
) -> SignatureRequest:
|
|
255
|
+
"""
|
|
256
|
+
Create a signature request response.
|
|
257
|
+
|
|
258
|
+
Use this for platforms with proxy wallets (Hyperliquid, Polymarket, dYdX).
|
|
259
|
+
Benefits: No gas required, no network switching needed.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
domain: EIP-712 domain separator
|
|
263
|
+
types: EIP-712 type definitions
|
|
264
|
+
primary_type: The primary type being signed
|
|
265
|
+
message: The message data to sign
|
|
266
|
+
meta: Optional UI metadata for the approval card
|
|
267
|
+
callback_tool_name: Optional tool name to call with signature result
|
|
268
|
+
|
|
269
|
+
Returns:
|
|
270
|
+
A SignatureRequest dict ready to be wrapped in a handshake response
|
|
271
|
+
"""
|
|
272
|
+
result: SignatureRequest = {
|
|
273
|
+
"_action": "signature_request",
|
|
274
|
+
"domain": domain,
|
|
275
|
+
"types": types,
|
|
276
|
+
"primaryType": primary_type,
|
|
277
|
+
"message": message,
|
|
278
|
+
}
|
|
279
|
+
if meta:
|
|
280
|
+
result["meta"] = meta
|
|
281
|
+
if callback_tool_name:
|
|
282
|
+
result["callbackToolName"] = callback_tool_name
|
|
283
|
+
return result
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def create_transaction_proposal(
|
|
287
|
+
*,
|
|
288
|
+
chain_id: int,
|
|
289
|
+
to: str,
|
|
290
|
+
data: str,
|
|
291
|
+
value: str = "0",
|
|
292
|
+
meta: TransactionProposalMeta | None = None,
|
|
293
|
+
) -> TransactionProposal:
|
|
294
|
+
"""
|
|
295
|
+
Create a transaction proposal response.
|
|
296
|
+
|
|
297
|
+
Use this for protocols without proxy wallets (Uniswap, NFT mints, etc.).
|
|
298
|
+
Note: May require network switching and gas.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
chain_id: EVM chain ID (e.g., 137 for Polygon, 8453 for Base)
|
|
302
|
+
to: Target contract address (0x...)
|
|
303
|
+
data: Encoded calldata (0x...)
|
|
304
|
+
value: Wei to send (as string, default '0')
|
|
305
|
+
meta: Optional UI metadata for the approval card
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
A TransactionProposal dict ready to be wrapped in a handshake response
|
|
309
|
+
"""
|
|
310
|
+
result: TransactionProposal = {
|
|
311
|
+
"_action": "transaction_proposal",
|
|
312
|
+
"chainId": chain_id,
|
|
313
|
+
"to": to,
|
|
314
|
+
"data": data,
|
|
315
|
+
"value": value,
|
|
316
|
+
}
|
|
317
|
+
if meta:
|
|
318
|
+
result["meta"] = meta
|
|
319
|
+
return result
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def create_auth_required(
|
|
323
|
+
*,
|
|
324
|
+
provider: str,
|
|
325
|
+
auth_url: str,
|
|
326
|
+
meta: AuthRequiredMeta | None = None,
|
|
327
|
+
) -> AuthRequired:
|
|
328
|
+
"""
|
|
329
|
+
Create an auth required response.
|
|
330
|
+
|
|
331
|
+
Use this when your tool needs the user to authenticate via OAuth.
|
|
332
|
+
|
|
333
|
+
Args:
|
|
334
|
+
provider: Service identifier (e.g., 'discord', 'slack')
|
|
335
|
+
auth_url: Your OAuth initiation endpoint (MUST be HTTPS)
|
|
336
|
+
meta: Optional UI metadata for the auth card
|
|
337
|
+
|
|
338
|
+
Returns:
|
|
339
|
+
An AuthRequired dict ready to be wrapped in a handshake response
|
|
340
|
+
"""
|
|
341
|
+
result: AuthRequired = {
|
|
342
|
+
"_action": "auth_required",
|
|
343
|
+
"provider": provider,
|
|
344
|
+
"authUrl": auth_url,
|
|
345
|
+
}
|
|
346
|
+
if meta:
|
|
347
|
+
result["meta"] = meta
|
|
348
|
+
return result
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def wrap_handshake_response(action: HandshakeAction) -> dict[str, Any]:
|
|
352
|
+
"""
|
|
353
|
+
Wrap a handshake action in the proper MCP response format.
|
|
354
|
+
|
|
355
|
+
MCP tools should return handshake actions in `_meta.handshakeAction` to prevent
|
|
356
|
+
the MCP SDK from stripping unknown fields.
|
|
357
|
+
|
|
358
|
+
Example:
|
|
359
|
+
>>> return wrap_handshake_response(create_signature_request(
|
|
360
|
+
... domain={"name": "Hyperliquid", "version": "1", "chainId": 42161},
|
|
361
|
+
... types={"Order": [...]},
|
|
362
|
+
... primary_type="Order",
|
|
363
|
+
... message=order_data,
|
|
364
|
+
... meta={"description": "Place order", "protocol": "Hyperliquid"}
|
|
365
|
+
... ))
|
|
366
|
+
|
|
367
|
+
Args:
|
|
368
|
+
action: The handshake action (SignatureRequest, TransactionProposal, or AuthRequired)
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
A dict with the proper MCP response format including structuredContent._meta.handshakeAction
|
|
372
|
+
"""
|
|
373
|
+
action_type = action.get("_action", "unknown").replace("_", " ")
|
|
374
|
+
description = action.get("meta", {}).get("description", f"{action_type} required")
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
"content": [
|
|
378
|
+
{
|
|
379
|
+
"type": "text",
|
|
380
|
+
"text": f"Handshake required: {action_type}. Please approve in the Context app.",
|
|
381
|
+
}
|
|
382
|
+
],
|
|
383
|
+
"structuredContent": {
|
|
384
|
+
"_meta": {
|
|
385
|
+
"handshakeAction": action,
|
|
386
|
+
},
|
|
387
|
+
"status": "handshake_required",
|
|
388
|
+
"message": description,
|
|
389
|
+
},
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# =============================================================================
|
|
394
|
+
# Exports
|
|
395
|
+
# =============================================================================
|
|
396
|
+
|
|
397
|
+
__all__ = [
|
|
398
|
+
# Types
|
|
399
|
+
"HandshakeMeta",
|
|
400
|
+
"EIP712Domain",
|
|
401
|
+
"EIP712TypeField",
|
|
402
|
+
"SignatureRequest",
|
|
403
|
+
"TransactionProposalMeta",
|
|
404
|
+
"TransactionProposal",
|
|
405
|
+
"AuthRequiredMeta",
|
|
406
|
+
"AuthRequired",
|
|
407
|
+
"HandshakeAction",
|
|
408
|
+
# Type guards
|
|
409
|
+
"is_handshake_action",
|
|
410
|
+
"is_signature_request",
|
|
411
|
+
"is_transaction_proposal",
|
|
412
|
+
"is_auth_required",
|
|
413
|
+
# Helper functions
|
|
414
|
+
"create_signature_request",
|
|
415
|
+
"create_transaction_proposal",
|
|
416
|
+
"create_auth_required",
|
|
417
|
+
"wrap_handshake_response",
|
|
418
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ctxprotocol
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.7
|
|
4
4
|
Summary: Official Python SDK for the Context Protocol - Discover and execute AI tools programmatically
|
|
5
5
|
Project-URL: Homepage, https://ctxprotocol.com
|
|
6
6
|
Project-URL: Documentation, https://docs.ctxprotocol.com
|
|
@@ -48,12 +48,26 @@ Context Protocol is **pip for AI capabilities**. Just as you install packages to
|
|
|
48
48
|
[](https://pypi.org/project/ctxprotocol/)
|
|
49
49
|
[](https://opensource.org/licenses/MIT)
|
|
50
50
|
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
### 💰 $10,000 Developer Grant Program
|
|
54
|
+
|
|
55
|
+
We're funding the initial supply of MCP Tools for the Context Marketplace. **Become a Data Broker.**
|
|
56
|
+
|
|
57
|
+
- **🛠️ Build:** Create an MCP Server using this SDK (Solana data, Trading tools, Scrapers, etc.)
|
|
58
|
+
- **📦 List:** Publish it to the Context Registry
|
|
59
|
+
- **💵 Earn:** Get a **$250–$1,000 Grant** + earn USDC every time an agent queries your tool
|
|
60
|
+
|
|
61
|
+
👉 [**View Open Bounties & Apply Here**](https://docs.ctxprotocol.com/grants)
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
51
65
|
## Why use Context?
|
|
52
66
|
|
|
53
67
|
- **🔌 One Interface, Everything:** Stop integrating APIs one by one. Use a single SDK to access any tool in the marketplace.
|
|
54
68
|
- **🧠 Zero-Ops:** We're a gateway to the best MCP tools. Just send the JSON and get the result.
|
|
55
69
|
- **⚡️ Agentic Discovery:** Your Agent can search the marketplace at runtime to find tools it didn't know it needed.
|
|
56
|
-
- **💸
|
|
70
|
+
- **💸 Pay-Per-Response:** The $500/year subscription? Now $0.01/response. No monthly fees, just results.
|
|
57
71
|
|
|
58
72
|
## Who Is This SDK For?
|
|
59
73
|
|
|
@@ -282,6 +296,22 @@ The SDK implements a **selective authentication** model — discovery is open, e
|
|
|
282
296
|
|
|
283
297
|
This matches standard API patterns (OpenAPI schemas are public, GraphQL introspection is open).
|
|
284
298
|
|
|
299
|
+
## Execution Timeout & Product Design
|
|
300
|
+
|
|
301
|
+
⚠️ **Important**: MCP tool execution has a **~60 second timeout** (enforced at the platform/client level, not by MCP itself). This is intentional—it encourages building pre-computed insight products rather than raw data access.
|
|
302
|
+
|
|
303
|
+
**Best practice**: Run heavy queries offline (via cron jobs), store results in your database, and serve instant results via MCP. This is how Bloomberg, Nansen, and Arkham work.
|
|
304
|
+
|
|
305
|
+
```python
|
|
306
|
+
# ❌ BAD: Raw access (timeout-prone, no moat)
|
|
307
|
+
{"name": "run_sql", "description": "Run any SQL against blockchain data"}
|
|
308
|
+
|
|
309
|
+
# ✅ GOOD: Pre-computed product (instant, defensible)
|
|
310
|
+
{"name": "get_smart_money_wallets", "description": "Top 100 wallets that timed market tops"}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
See the [full documentation](https://docs.ctxprotocol.com/guides/build-tools#execution-limits--product-design) for detailed guidance.
|
|
314
|
+
|
|
285
315
|
## Context Injection (Personalized Tools)
|
|
286
316
|
|
|
287
317
|
For tools that analyze user data, Context automatically injects user context:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ctxprotocol/__init__.py,sha256=
|
|
1
|
+
ctxprotocol/__init__.py,sha256=m7s0VtVaMwm-cBBXpvBN30M3RfgDgMTIwcpY_vqVHFg,4755
|
|
2
2
|
ctxprotocol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
ctxprotocol/auth/__init__.py,sha256=J1AWTpN-KKCYfFRMtIW8z8jvBvJHE4cB7Z6t2PjFsVA,13202
|
|
4
4
|
ctxprotocol/client/__init__.py,sha256=dgtQ9_pzVthsNJazibWHcFkdTjZQlT_gn7SuPCsBOHo,940
|
|
@@ -11,6 +11,7 @@ ctxprotocol/context/__init__.py,sha256=v_auetqp3w0eScRLhWGbFHXGRfgjcNzTEeeMKOAxi
|
|
|
11
11
|
ctxprotocol/context/hyperliquid.py,sha256=M59Ku48yCdfpS9_b5l8SyEFukZwbNb8P1xAh2P0Yh-0,7603
|
|
12
12
|
ctxprotocol/context/polymarket.py,sha256=GcBay3VRKf4MQj49VLmhOPrU172_aNa4i2TPS9-sZuQ,3484
|
|
13
13
|
ctxprotocol/context/wallet.py,sha256=g6pXMY_olLn3-4ULQQKmtpDcZ_u9kgCwIdc0JFEkckE,1742
|
|
14
|
-
ctxprotocol
|
|
15
|
-
ctxprotocol-0.5.
|
|
16
|
-
ctxprotocol-0.5.
|
|
14
|
+
ctxprotocol/handshake/__init__.py,sha256=GUWa1lKacLq463cgVaTu5K2Wrx9E8EhJIVbCUbqhwMs,12300
|
|
15
|
+
ctxprotocol-0.5.7.dist-info/METADATA,sha256=cNNsRcxhzJ4Y-jucq1PxJw11bH1sA-L2QfOpQKjFkjs,11706
|
|
16
|
+
ctxprotocol-0.5.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
17
|
+
ctxprotocol-0.5.7.dist-info/RECORD,,
|
|
File without changes
|