ctxprotocol 0.5.5__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
 
@@ -160,17 +160,32 @@ async def verify_context_request(
160
160
  CONTEXT_PLATFORM_PUBLIC_KEY_PEM.encode()
161
161
  )
162
162
 
163
+ # Build decode options - match TypeScript SDK behavior
164
+ decode_options: dict[str, Any] = {
165
+ "verify_signature": True,
166
+ "verify_exp": True,
167
+ "verify_iat": True,
168
+ "require": ["exp", "iat"],
169
+ }
170
+
171
+ # Only verify issuer if we expect it (TypeScript SDK does this)
172
+ # But don't require it in case the platform doesn't always include it
173
+ decode_options["verify_iss"] = True
174
+
175
+ # Only verify audience if explicitly provided
176
+ if audience:
177
+ decode_options["verify_aud"] = True
178
+ else:
179
+ decode_options["verify_aud"] = False
180
+
163
181
  # Verify the JWT
164
182
  payload = jwt.decode(
165
183
  token,
166
184
  public_key,
167
185
  algorithms=["RS256"],
168
186
  issuer="https://ctxprotocol.com",
169
- audience=audience,
170
- options={
171
- "require": ["iss", "sub", "exp", "iat"],
172
- "verify_aud": audience is not None,
173
- },
187
+ audience=audience if audience else None,
188
+ options=decode_options,
174
189
  )
175
190
 
176
191
  return payload
@@ -193,9 +208,21 @@ async def verify_context_request(
193
208
  code="unauthorized",
194
209
  status_code=401,
195
210
  )
196
- except jwt.PyJWTError:
211
+ except jwt.DecodeError as e:
212
+ raise ContextError(
213
+ message=f"JWT decode error: {e}",
214
+ code="unauthorized",
215
+ status_code=401,
216
+ )
217
+ except jwt.InvalidSignatureError:
218
+ raise ContextError(
219
+ message="Invalid JWT signature",
220
+ code="unauthorized",
221
+ status_code=401,
222
+ )
223
+ except jwt.PyJWTError as e:
197
224
  raise ContextError(
198
- message="Invalid Context Protocol signature",
225
+ message=f"JWT verification failed: {e}",
199
226
  code="unauthorized",
200
227
  status_code=401,
201
228
  )
@@ -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.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
  [![Python versions](https://img.shields.io/pypi/pyversions/ctxprotocol.svg)](https://pypi.org/project/ctxprotocol/)
49
49
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
- - **💸 Micro-Billing:** Pay only for what you use (e.g., $0.001/query). No monthly subscriptions for tools you rarely use.
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
 
@@ -263,15 +277,40 @@ if is_protected_mcp_method(body["method"]):
263
277
  raise HTTPException(status_code=401, detail="Unauthorized")
264
278
  ```
265
279
 
266
- ### Security Model
280
+ ### MCP Security Model
281
+
282
+ The SDK implements a **selective authentication** model — discovery is open, execution is protected:
267
283
 
268
- | MCP Method | Auth Required | Reason |
269
- |------------|---------------|--------|
270
- | `tools/list` | ❌ No | Discovery - just returns tool schemas |
271
- | `tools/call` | ✅ Yes | Execution - runs code, may cost money |
284
+ | MCP Method | Auth Required | Why |
285
+ |------------|---------------|-----|
272
286
  | `initialize` | ❌ No | Session setup |
287
+ | `tools/list` | ❌ No | Discovery - agents need to see your schemas |
273
288
  | `resources/list` | ❌ No | Discovery |
274
289
  | `prompts/list` | ❌ No | Discovery |
290
+ | `tools/call` | ✅ **Yes** | **Execution - costs money, runs your code** |
291
+
292
+ **What this means in practice:**
293
+ - ✅ `https://your-mcp.com/mcp` + `initialize` → Works without auth
294
+ - ✅ `https://your-mcp.com/mcp` + `tools/list` → Works without auth
295
+ - ❌ `https://your-mcp.com/mcp` + `tools/call` → **Requires Context Protocol JWT**
296
+
297
+ This matches standard API patterns (OpenAPI schemas are public, GraphQL introspection is open).
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.
275
314
 
276
315
  ## Context Injection (Personalized Tools)
277
316
 
@@ -1,6 +1,6 @@
1
- ctxprotocol/__init__.py,sha256=3nI2BrD1dmzezm1vi8pcKgrErrkX5K_Izyb3LbnXdSo,3613
1
+ ctxprotocol/__init__.py,sha256=m7s0VtVaMwm-cBBXpvBN30M3RfgDgMTIwcpY_vqVHFg,4755
2
2
  ctxprotocol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- ctxprotocol/auth/__init__.py,sha256=6wxgom0k-hQ474zeOAwDddTsayJj2guJdk4CodX4EkI,12272
3
+ ctxprotocol/auth/__init__.py,sha256=J1AWTpN-KKCYfFRMtIW8z8jvBvJHE4cB7Z6t2PjFsVA,13202
4
4
  ctxprotocol/client/__init__.py,sha256=dgtQ9_pzVthsNJazibWHcFkdTjZQlT_gn7SuPCsBOHo,940
5
5
  ctxprotocol/client/client.py,sha256=eEiSTmBY6VHP04LbBEGF-D_9D5oZvDgaVF1ysMVS_zc,4771
6
6
  ctxprotocol/client/types.py,sha256=3FOL5SuzzhiPAGTl-sWasDSxkEKnNjgyoaXc8wjlkOI,8143
@@ -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-0.5.5.dist-info/METADATA,sha256=E8gTfOqYSpCLCOOsngqnnTSWJbLjWQ5vPAnhz01Zf-Y,9885
15
- ctxprotocol-0.5.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- ctxprotocol-0.5.5.dist-info/RECORD,,
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,,