ctxprotocol 0.5.5__tar.gz
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-0.5.5/PKG-INFO +326 -0
- ctxprotocol-0.5.5/README.md +288 -0
- ctxprotocol-0.5.5/ctxprotocol/__init__.py +148 -0
- ctxprotocol-0.5.5/ctxprotocol/auth/__init__.py +369 -0
- ctxprotocol-0.5.5/ctxprotocol/client/__init__.py +46 -0
- ctxprotocol-0.5.5/ctxprotocol/client/client.py +148 -0
- ctxprotocol-0.5.5/ctxprotocol/client/resources/__init__.py +11 -0
- ctxprotocol-0.5.5/ctxprotocol/client/resources/discovery.py +70 -0
- ctxprotocol-0.5.5/ctxprotocol/client/resources/tools.py +103 -0
- ctxprotocol-0.5.5/ctxprotocol/client/types.py +265 -0
- ctxprotocol-0.5.5/ctxprotocol/context/__init__.py +184 -0
- ctxprotocol-0.5.5/ctxprotocol/context/hyperliquid.py +252 -0
- ctxprotocol-0.5.5/ctxprotocol/context/polymarket.py +103 -0
- ctxprotocol-0.5.5/ctxprotocol/context/wallet.py +59 -0
- ctxprotocol-0.5.5/ctxprotocol/py.typed +0 -0
- ctxprotocol-0.5.5/pyproject.toml +83 -0
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ctxprotocol
|
|
3
|
+
Version: 0.5.5
|
|
4
|
+
Summary: Official Python SDK for the Context Protocol - Discover and execute AI tools programmatically
|
|
5
|
+
Project-URL: Homepage, https://ctxprotocol.com
|
|
6
|
+
Project-URL: Documentation, https://docs.ctxprotocol.com
|
|
7
|
+
Project-URL: Repository, https://github.com/ctxprotocol/sdk-python
|
|
8
|
+
Project-URL: Issues, https://github.com/ctxprotocol/sdk-python/issues
|
|
9
|
+
Author-email: Context Protocol <team@ctxprotocol.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Keywords: ai,api-client,context-protocol,ctxprotocol,mcp,sdk,tools
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.10
|
|
25
|
+
Requires-Dist: cryptography>=42.0.0
|
|
26
|
+
Requires-Dist: httpx>=0.27.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: pyjwt[crypto]>=2.8.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: mypy>=1.10.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
34
|
+
Provides-Extra: fastapi
|
|
35
|
+
Requires-Dist: fastapi>=0.111.0; extra == 'fastapi'
|
|
36
|
+
Requires-Dist: starlette>=0.37.0; extra == 'fastapi'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# ctxprotocol
|
|
40
|
+
|
|
41
|
+
**The Universal Adapter for AI Agents.**
|
|
42
|
+
|
|
43
|
+
Connect your AI to the real world without managing API keys, hosting servers, or reading documentation.
|
|
44
|
+
|
|
45
|
+
Context Protocol is **pip 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.
|
|
46
|
+
|
|
47
|
+
[](https://pypi.org/project/ctxprotocol/)
|
|
48
|
+
[](https://pypi.org/project/ctxprotocol/)
|
|
49
|
+
[](https://opensource.org/licenses/MIT)
|
|
50
|
+
|
|
51
|
+
## Why use Context?
|
|
52
|
+
|
|
53
|
+
- **π One Interface, Everything:** Stop integrating APIs one by one. Use a single SDK to access any tool in the marketplace.
|
|
54
|
+
- **π§ Zero-Ops:** We're a gateway to the best MCP tools. Just send the JSON and get the result.
|
|
55
|
+
- **β‘οΈ 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.
|
|
57
|
+
|
|
58
|
+
## Who Is This SDK For?
|
|
59
|
+
|
|
60
|
+
| Role | What You Use |
|
|
61
|
+
|------|--------------|
|
|
62
|
+
| **AI Agent Developer** | `ctxprotocol` β Query marketplace, execute tools, handle payments |
|
|
63
|
+
| **Tool Contributor (Data Broker)** | `mcp` + `ctxprotocol` β Standard MCP server + security middleware |
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pip install ctxprotocol
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Or with optional FastAPI support:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
pip install ctxprotocol[fastapi]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Prerequisites
|
|
78
|
+
|
|
79
|
+
Before using the API, complete setup at [ctxprotocol.com](https://ctxprotocol.com):
|
|
80
|
+
|
|
81
|
+
1. **Sign in** β Creates your embedded wallet
|
|
82
|
+
2. **Enable Auto Pay** β Approve USDC spending for tool payments
|
|
83
|
+
3. **Fund wallet** β Add USDC for tool execution fees
|
|
84
|
+
4. **Generate API key** β In Settings page
|
|
85
|
+
|
|
86
|
+
## Quick Start
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
import asyncio
|
|
90
|
+
from ctxprotocol import ContextClient
|
|
91
|
+
|
|
92
|
+
async def main():
|
|
93
|
+
async with ContextClient(api_key="sk_live_...") as client:
|
|
94
|
+
# Discover tools
|
|
95
|
+
tools = await client.discovery.search("gas prices")
|
|
96
|
+
|
|
97
|
+
# Execute a tool
|
|
98
|
+
result = await client.tools.execute(
|
|
99
|
+
tool_id=tools[0].id,
|
|
100
|
+
tool_name=tools[0].mcp_tools[0].name,
|
|
101
|
+
args={"chainId": 1},
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
print(result.result)
|
|
105
|
+
|
|
106
|
+
asyncio.run(main())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Configuration
|
|
110
|
+
|
|
111
|
+
### Client Options
|
|
112
|
+
|
|
113
|
+
| Option | Type | Required | Default | Description |
|
|
114
|
+
|--------|------|----------|---------|-------------|
|
|
115
|
+
| `api_key` | `str` | Yes | β | Your Context Protocol API key |
|
|
116
|
+
| `base_url` | `str` | No | `https://ctxprotocol.com` | API base URL (for development) |
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
# Production
|
|
120
|
+
client = ContextClient(api_key=os.environ["CONTEXT_API_KEY"])
|
|
121
|
+
|
|
122
|
+
# Local development
|
|
123
|
+
client = ContextClient(
|
|
124
|
+
api_key="sk_test_...",
|
|
125
|
+
base_url="http://localhost:3000",
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## API Reference
|
|
130
|
+
|
|
131
|
+
### Discovery
|
|
132
|
+
|
|
133
|
+
#### `client.discovery.search(query, limit?)`
|
|
134
|
+
|
|
135
|
+
Search for tools matching a query string.
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
tools = await client.discovery.search("ethereum gas", limit=10)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
#### `client.discovery.get_featured(limit?)`
|
|
142
|
+
|
|
143
|
+
Get featured/popular tools.
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
featured = await client.discovery.get_featured(limit=5)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Tools
|
|
150
|
+
|
|
151
|
+
#### `client.tools.execute(tool_id, tool_name, args?)`
|
|
152
|
+
|
|
153
|
+
Execute a tool method.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
result = await client.tools.execute(
|
|
157
|
+
tool_id="uuid-of-tool",
|
|
158
|
+
tool_name="get_gas_prices",
|
|
159
|
+
args={"chainId": 1},
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Types
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
from ctxprotocol import (
|
|
167
|
+
# Auth utilities for tool contributors
|
|
168
|
+
verify_context_request,
|
|
169
|
+
is_protected_mcp_method,
|
|
170
|
+
is_open_mcp_method,
|
|
171
|
+
|
|
172
|
+
# Client types
|
|
173
|
+
ContextClientOptions,
|
|
174
|
+
Tool,
|
|
175
|
+
McpTool,
|
|
176
|
+
ExecuteOptions,
|
|
177
|
+
ExecutionResult,
|
|
178
|
+
ContextErrorCode,
|
|
179
|
+
|
|
180
|
+
# Auth types (for MCP server contributors)
|
|
181
|
+
VerifyRequestOptions,
|
|
182
|
+
|
|
183
|
+
# Context types (for MCP server contributors receiving injected data)
|
|
184
|
+
ContextRequirementType,
|
|
185
|
+
HyperliquidContext,
|
|
186
|
+
PolymarketContext,
|
|
187
|
+
WalletContext,
|
|
188
|
+
UserContext,
|
|
189
|
+
)
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Error Handling
|
|
193
|
+
|
|
194
|
+
The SDK raises `ContextError` with specific error codes:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from ctxprotocol import ContextClient, ContextError
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
result = await client.tools.execute(...)
|
|
201
|
+
except ContextError as e:
|
|
202
|
+
match e.code:
|
|
203
|
+
case "no_wallet":
|
|
204
|
+
# User needs to set up wallet
|
|
205
|
+
print(f"Setup required: {e.help_url}")
|
|
206
|
+
case "insufficient_allowance":
|
|
207
|
+
# User needs to enable Auto Pay
|
|
208
|
+
print(f"Enable Auto Pay: {e.help_url}")
|
|
209
|
+
case "payment_failed":
|
|
210
|
+
# Insufficient USDC balance
|
|
211
|
+
pass
|
|
212
|
+
case "execution_failed":
|
|
213
|
+
# Tool execution error
|
|
214
|
+
pass
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Error Codes
|
|
218
|
+
|
|
219
|
+
| Code | Description | Handling |
|
|
220
|
+
|------|-------------|----------|
|
|
221
|
+
| `unauthorized` | Invalid API key | Check configuration |
|
|
222
|
+
| `no_wallet` | Wallet not set up | Direct user to `help_url` |
|
|
223
|
+
| `insufficient_allowance` | Auto Pay not enabled | Direct user to `help_url` |
|
|
224
|
+
| `payment_failed` | USDC payment failed | Check balance |
|
|
225
|
+
| `execution_failed` | Tool error | Retry with different args |
|
|
226
|
+
|
|
227
|
+
## π Securing Your Tool (MCP Contributors)
|
|
228
|
+
|
|
229
|
+
If you're building an MCP server (tool contributor), verify incoming requests:
|
|
230
|
+
|
|
231
|
+
### Quick Implementation with FastAPI
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from fastapi import FastAPI, Request, Depends, HTTPException
|
|
235
|
+
from ctxprotocol import create_context_middleware, ContextError
|
|
236
|
+
|
|
237
|
+
app = FastAPI()
|
|
238
|
+
verify_context = create_context_middleware(audience="https://your-tool.com/mcp")
|
|
239
|
+
|
|
240
|
+
@app.post("/mcp")
|
|
241
|
+
async def handle_mcp(request: Request, context: dict = Depends(verify_context)):
|
|
242
|
+
# context contains verified JWT payload (on protected methods)
|
|
243
|
+
# None for open methods like tools/list
|
|
244
|
+
body = await request.json()
|
|
245
|
+
# Handle MCP request...
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Manual Verification
|
|
249
|
+
|
|
250
|
+
```python
|
|
251
|
+
from ctxprotocol import verify_context_request, is_protected_mcp_method, ContextError
|
|
252
|
+
|
|
253
|
+
# Check if a method requires auth
|
|
254
|
+
if is_protected_mcp_method(body["method"]):
|
|
255
|
+
try:
|
|
256
|
+
payload = await verify_context_request(
|
|
257
|
+
authorization_header=request.headers.get("authorization"),
|
|
258
|
+
audience="https://your-tool.com/mcp", # optional
|
|
259
|
+
)
|
|
260
|
+
# payload contains verified JWT claims
|
|
261
|
+
except ContextError as e:
|
|
262
|
+
# Handle authentication error
|
|
263
|
+
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Security Model
|
|
267
|
+
|
|
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 |
|
|
272
|
+
| `initialize` | β No | Session setup |
|
|
273
|
+
| `resources/list` | β No | Discovery |
|
|
274
|
+
| `prompts/list` | β No | Discovery |
|
|
275
|
+
|
|
276
|
+
## Context Injection (Personalized Tools)
|
|
277
|
+
|
|
278
|
+
For tools that analyze user data, Context automatically injects user context:
|
|
279
|
+
|
|
280
|
+
```python
|
|
281
|
+
from ctxprotocol import CONTEXT_REQUIREMENTS_KEY, HyperliquidContext
|
|
282
|
+
|
|
283
|
+
# Define tool with context requirements
|
|
284
|
+
TOOLS = [{
|
|
285
|
+
"name": "analyze_my_positions",
|
|
286
|
+
"description": "Analyze your positions with personalized insights",
|
|
287
|
+
"_meta": {
|
|
288
|
+
"contextRequirements": ["hyperliquid"],
|
|
289
|
+
},
|
|
290
|
+
"inputSchema": {
|
|
291
|
+
"type": "object",
|
|
292
|
+
"properties": {
|
|
293
|
+
"portfolio": {
|
|
294
|
+
"type": "object",
|
|
295
|
+
"description": "Portfolio context (injected by platform)",
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
"required": ["portfolio"],
|
|
299
|
+
},
|
|
300
|
+
}]
|
|
301
|
+
|
|
302
|
+
# Your handler receives typed context
|
|
303
|
+
async def handle_analyze_positions(portfolio: HyperliquidContext):
|
|
304
|
+
positions = portfolio.perp_positions
|
|
305
|
+
account = portfolio.account_summary
|
|
306
|
+
# ... analyze and return insights
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Links
|
|
310
|
+
|
|
311
|
+
- [Context Protocol](https://ctxprotocol.com) β Main website
|
|
312
|
+
- [Documentation](https://docs.ctxprotocol.com)
|
|
313
|
+
- [GitHub](https://github.com/ctxprotocol/sdk-python) β This SDK
|
|
314
|
+
- [TypeScript SDK](https://github.com/ctxprotocol/sdk) β For Node.js
|
|
315
|
+
- [PyPI Package](https://pypi.org/project/ctxprotocol/)
|
|
316
|
+
|
|
317
|
+
## Requirements
|
|
318
|
+
|
|
319
|
+
- Python 3.10+
|
|
320
|
+
- httpx
|
|
321
|
+
- pydantic
|
|
322
|
+
- pyjwt[crypto]
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# ctxprotocol
|
|
2
|
+
|
|
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 **pip 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.
|
|
8
|
+
|
|
9
|
+
[](https://pypi.org/project/ctxprotocol/)
|
|
10
|
+
[](https://pypi.org/project/ctxprotocol/)
|
|
11
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
|
|
13
|
+
## Why use Context?
|
|
14
|
+
|
|
15
|
+
- **π One Interface, Everything:** Stop integrating APIs one by one. Use a single SDK to access any tool in the marketplace.
|
|
16
|
+
- **π§ Zero-Ops:** We're a gateway to the best MCP tools. Just send the JSON and get the result.
|
|
17
|
+
- **β‘οΈ Agentic Discovery:** Your Agent can search the marketplace at runtime to find tools it didn't know it needed.
|
|
18
|
+
- **πΈ Micro-Billing:** Pay only for what you use (e.g., $0.001/query). No monthly subscriptions for tools you rarely use.
|
|
19
|
+
|
|
20
|
+
## Who Is This SDK For?
|
|
21
|
+
|
|
22
|
+
| Role | What You Use |
|
|
23
|
+
|------|--------------|
|
|
24
|
+
| **AI Agent Developer** | `ctxprotocol` β Query marketplace, execute tools, handle payments |
|
|
25
|
+
| **Tool Contributor (Data Broker)** | `mcp` + `ctxprotocol` β Standard MCP server + security middleware |
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install ctxprotocol
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Or with optional FastAPI support:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install ctxprotocol[fastapi]
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Prerequisites
|
|
40
|
+
|
|
41
|
+
Before using the API, complete setup at [ctxprotocol.com](https://ctxprotocol.com):
|
|
42
|
+
|
|
43
|
+
1. **Sign in** β Creates your embedded wallet
|
|
44
|
+
2. **Enable Auto Pay** β Approve USDC spending for tool payments
|
|
45
|
+
3. **Fund wallet** β Add USDC for tool execution fees
|
|
46
|
+
4. **Generate API key** β In Settings page
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
import asyncio
|
|
52
|
+
from ctxprotocol import ContextClient
|
|
53
|
+
|
|
54
|
+
async def main():
|
|
55
|
+
async with ContextClient(api_key="sk_live_...") as client:
|
|
56
|
+
# Discover tools
|
|
57
|
+
tools = await client.discovery.search("gas prices")
|
|
58
|
+
|
|
59
|
+
# Execute a tool
|
|
60
|
+
result = await client.tools.execute(
|
|
61
|
+
tool_id=tools[0].id,
|
|
62
|
+
tool_name=tools[0].mcp_tools[0].name,
|
|
63
|
+
args={"chainId": 1},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
print(result.result)
|
|
67
|
+
|
|
68
|
+
asyncio.run(main())
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Configuration
|
|
72
|
+
|
|
73
|
+
### Client Options
|
|
74
|
+
|
|
75
|
+
| Option | Type | Required | Default | Description |
|
|
76
|
+
|--------|------|----------|---------|-------------|
|
|
77
|
+
| `api_key` | `str` | Yes | β | Your Context Protocol API key |
|
|
78
|
+
| `base_url` | `str` | No | `https://ctxprotocol.com` | API base URL (for development) |
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
# Production
|
|
82
|
+
client = ContextClient(api_key=os.environ["CONTEXT_API_KEY"])
|
|
83
|
+
|
|
84
|
+
# Local development
|
|
85
|
+
client = ContextClient(
|
|
86
|
+
api_key="sk_test_...",
|
|
87
|
+
base_url="http://localhost:3000",
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## API Reference
|
|
92
|
+
|
|
93
|
+
### Discovery
|
|
94
|
+
|
|
95
|
+
#### `client.discovery.search(query, limit?)`
|
|
96
|
+
|
|
97
|
+
Search for tools matching a query string.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
tools = await client.discovery.search("ethereum gas", limit=10)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### `client.discovery.get_featured(limit?)`
|
|
104
|
+
|
|
105
|
+
Get featured/popular tools.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
featured = await client.discovery.get_featured(limit=5)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Tools
|
|
112
|
+
|
|
113
|
+
#### `client.tools.execute(tool_id, tool_name, args?)`
|
|
114
|
+
|
|
115
|
+
Execute a tool method.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
result = await client.tools.execute(
|
|
119
|
+
tool_id="uuid-of-tool",
|
|
120
|
+
tool_name="get_gas_prices",
|
|
121
|
+
args={"chainId": 1},
|
|
122
|
+
)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Types
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
from ctxprotocol import (
|
|
129
|
+
# Auth utilities for tool contributors
|
|
130
|
+
verify_context_request,
|
|
131
|
+
is_protected_mcp_method,
|
|
132
|
+
is_open_mcp_method,
|
|
133
|
+
|
|
134
|
+
# Client types
|
|
135
|
+
ContextClientOptions,
|
|
136
|
+
Tool,
|
|
137
|
+
McpTool,
|
|
138
|
+
ExecuteOptions,
|
|
139
|
+
ExecutionResult,
|
|
140
|
+
ContextErrorCode,
|
|
141
|
+
|
|
142
|
+
# Auth types (for MCP server contributors)
|
|
143
|
+
VerifyRequestOptions,
|
|
144
|
+
|
|
145
|
+
# Context types (for MCP server contributors receiving injected data)
|
|
146
|
+
ContextRequirementType,
|
|
147
|
+
HyperliquidContext,
|
|
148
|
+
PolymarketContext,
|
|
149
|
+
WalletContext,
|
|
150
|
+
UserContext,
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Error Handling
|
|
155
|
+
|
|
156
|
+
The SDK raises `ContextError` with specific error codes:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
from ctxprotocol import ContextClient, ContextError
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
result = await client.tools.execute(...)
|
|
163
|
+
except ContextError as e:
|
|
164
|
+
match e.code:
|
|
165
|
+
case "no_wallet":
|
|
166
|
+
# User needs to set up wallet
|
|
167
|
+
print(f"Setup required: {e.help_url}")
|
|
168
|
+
case "insufficient_allowance":
|
|
169
|
+
# User needs to enable Auto Pay
|
|
170
|
+
print(f"Enable Auto Pay: {e.help_url}")
|
|
171
|
+
case "payment_failed":
|
|
172
|
+
# Insufficient USDC balance
|
|
173
|
+
pass
|
|
174
|
+
case "execution_failed":
|
|
175
|
+
# Tool execution error
|
|
176
|
+
pass
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Error Codes
|
|
180
|
+
|
|
181
|
+
| Code | Description | Handling |
|
|
182
|
+
|------|-------------|----------|
|
|
183
|
+
| `unauthorized` | Invalid API key | Check configuration |
|
|
184
|
+
| `no_wallet` | Wallet not set up | Direct user to `help_url` |
|
|
185
|
+
| `insufficient_allowance` | Auto Pay not enabled | Direct user to `help_url` |
|
|
186
|
+
| `payment_failed` | USDC payment failed | Check balance |
|
|
187
|
+
| `execution_failed` | Tool error | Retry with different args |
|
|
188
|
+
|
|
189
|
+
## π Securing Your Tool (MCP Contributors)
|
|
190
|
+
|
|
191
|
+
If you're building an MCP server (tool contributor), verify incoming requests:
|
|
192
|
+
|
|
193
|
+
### Quick Implementation with FastAPI
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
from fastapi import FastAPI, Request, Depends, HTTPException
|
|
197
|
+
from ctxprotocol import create_context_middleware, ContextError
|
|
198
|
+
|
|
199
|
+
app = FastAPI()
|
|
200
|
+
verify_context = create_context_middleware(audience="https://your-tool.com/mcp")
|
|
201
|
+
|
|
202
|
+
@app.post("/mcp")
|
|
203
|
+
async def handle_mcp(request: Request, context: dict = Depends(verify_context)):
|
|
204
|
+
# context contains verified JWT payload (on protected methods)
|
|
205
|
+
# None for open methods like tools/list
|
|
206
|
+
body = await request.json()
|
|
207
|
+
# Handle MCP request...
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Manual Verification
|
|
211
|
+
|
|
212
|
+
```python
|
|
213
|
+
from ctxprotocol import verify_context_request, is_protected_mcp_method, ContextError
|
|
214
|
+
|
|
215
|
+
# Check if a method requires auth
|
|
216
|
+
if is_protected_mcp_method(body["method"]):
|
|
217
|
+
try:
|
|
218
|
+
payload = await verify_context_request(
|
|
219
|
+
authorization_header=request.headers.get("authorization"),
|
|
220
|
+
audience="https://your-tool.com/mcp", # optional
|
|
221
|
+
)
|
|
222
|
+
# payload contains verified JWT claims
|
|
223
|
+
except ContextError as e:
|
|
224
|
+
# Handle authentication error
|
|
225
|
+
raise HTTPException(status_code=401, detail="Unauthorized")
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Security Model
|
|
229
|
+
|
|
230
|
+
| MCP Method | Auth Required | Reason |
|
|
231
|
+
|------------|---------------|--------|
|
|
232
|
+
| `tools/list` | β No | Discovery - just returns tool schemas |
|
|
233
|
+
| `tools/call` | β
Yes | Execution - runs code, may cost money |
|
|
234
|
+
| `initialize` | β No | Session setup |
|
|
235
|
+
| `resources/list` | β No | Discovery |
|
|
236
|
+
| `prompts/list` | β No | Discovery |
|
|
237
|
+
|
|
238
|
+
## Context Injection (Personalized Tools)
|
|
239
|
+
|
|
240
|
+
For tools that analyze user data, Context automatically injects user context:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
from ctxprotocol import CONTEXT_REQUIREMENTS_KEY, HyperliquidContext
|
|
244
|
+
|
|
245
|
+
# Define tool with context requirements
|
|
246
|
+
TOOLS = [{
|
|
247
|
+
"name": "analyze_my_positions",
|
|
248
|
+
"description": "Analyze your positions with personalized insights",
|
|
249
|
+
"_meta": {
|
|
250
|
+
"contextRequirements": ["hyperliquid"],
|
|
251
|
+
},
|
|
252
|
+
"inputSchema": {
|
|
253
|
+
"type": "object",
|
|
254
|
+
"properties": {
|
|
255
|
+
"portfolio": {
|
|
256
|
+
"type": "object",
|
|
257
|
+
"description": "Portfolio context (injected by platform)",
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
"required": ["portfolio"],
|
|
261
|
+
},
|
|
262
|
+
}]
|
|
263
|
+
|
|
264
|
+
# Your handler receives typed context
|
|
265
|
+
async def handle_analyze_positions(portfolio: HyperliquidContext):
|
|
266
|
+
positions = portfolio.perp_positions
|
|
267
|
+
account = portfolio.account_summary
|
|
268
|
+
# ... analyze and return insights
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Links
|
|
272
|
+
|
|
273
|
+
- [Context Protocol](https://ctxprotocol.com) β Main website
|
|
274
|
+
- [Documentation](https://docs.ctxprotocol.com)
|
|
275
|
+
- [GitHub](https://github.com/ctxprotocol/sdk-python) β This SDK
|
|
276
|
+
- [TypeScript SDK](https://github.com/ctxprotocol/sdk) β For Node.js
|
|
277
|
+
- [PyPI Package](https://pypi.org/project/ctxprotocol/)
|
|
278
|
+
|
|
279
|
+
## Requirements
|
|
280
|
+
|
|
281
|
+
- Python 3.10+
|
|
282
|
+
- httpx
|
|
283
|
+
- pydantic
|
|
284
|
+
- pyjwt[crypto]
|
|
285
|
+
|
|
286
|
+
## License
|
|
287
|
+
|
|
288
|
+
MIT
|