Skip to main content

Official Python SDK for the Context Protocol - Discover and execute AI tools programmatically

Project description

ctxprotocol

The Universal Adapter for AI Agents.

Connect your AI to the real world without managing API keys, hosting servers, or reading documentation.

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.

PyPI version Python versions License: MIT


💰 $10,000 Developer Grant Program

We're funding the initial supply of MCP Tools for the Context Marketplace. Become a Data Broker.

  • 🛠️ Build: Create an MCP Server using this SDK (Solana data, Trading tools, Scrapers, etc.)
  • 📦 List: Publish it to the Context Registry
  • 💵 Earn: Get a $250–$1,000 Grant + earn USDC every time an agent queries your tool

👉 View Open Bounties & Apply Here


Why use Context?

  • 🔌 One Interface, Everything: Stop integrating APIs one by one. Use a single SDK to access any tool in the marketplace.
  • 🧠 Zero-Ops: We're a gateway to the best MCP tools. Just send the JSON and get the result.
  • ⚡️ Agentic Discovery: Your Agent can search the marketplace at runtime to find tools it didn't know it needed.
  • 💸 Pay-Per-Response: The $500/year subscription? Now $0.01/response. No monthly fees, just results.

Who Is This SDK For?

Role What You Use
AI Agent Developer ctxprotocol — Query marketplace, execute tools, handle payments
Tool Contributor (Data Broker) mcp + ctxprotocol — Standard MCP server + security middleware

Installation

pip install ctxprotocol

Or with optional FastAPI support:

pip install ctxprotocol[fastapi]

Prerequisites

Before using the API, complete setup at ctxprotocol.com:

  1. Sign in — Creates your embedded wallet
  2. Set spending cap — Approve USDC spending on the ContextRouter (one-time setup)
  3. Fund wallet — Add USDC for tool execution fees
  4. Generate API key — In Settings page

Two Modes: Precision vs Intelligence

The SDK offers two payment models to serve different use cases:

Mode Method Payment Model Use Case
Execute client.tools.execute() Pay-per-request Simple data fetches, predictable costs, building custom pipelines
Query client.query.run() Pay-per-response Complex questions, multi-tool synthesis, curated intelligence

Execute mode gives you raw data and full control — one tool, one call, one payment:

result = await client.tools.execute(
    tool_id="tool-uuid",
    tool_name="whale_transactions",
    args={"chain": "base", "limit": 20},
)

Query mode gives you curated answers — the server handles tool discovery, multi-tool orchestration (up to 100 MCP calls per tool), self-healing retries, completeness checks, model-aware context budgeting, and AI synthesis for one flat fee:

answer = await client.query.run(
    query="What are the top whale movements on Base?",
    model_id="glm-model",      # optional: choose a supported model
    include_data_url=True,     # optional: persist full execution data to blob
)
print(answer.response)    # AI-synthesized answer
print(answer.tools_used)  # Which tools were used
print(answer.cost)        # Cost breakdown
print(answer.data_url)    # Optional blob URL with full data

Quick Start

import asyncio
from ctxprotocol import ContextClient

async def main():
    async with ContextClient(api_key="sk_live_...") as client:
        # Pay-per-response: Ask a question, get a curated answer
        answer = await client.query.run("What are the top whale movements on Base?")
        print(answer.response)

        # Pay-per-request: Execute a specific tool for raw data
        tools = await client.discovery.search("gas prices")
        result = await client.tools.execute(
            tool_id=tools[0].id,
            tool_name=tools[0].mcp_tools[0].name,
            args={"chainId": 1},
        )
        print(result.result)

asyncio.run(main())

Configuration

Client Options

Option Type Required Default Description
api_key str Yes Your Context Protocol API key
base_url str No https://ctxprotocol.com API base URL (for development)
# Production
client = ContextClient(api_key=os.environ["CONTEXT_API_KEY"])

# Local development
client = ContextClient(
    api_key="sk_test_...",
    base_url="http://localhost:3000",
)

API Reference

Discovery

client.discovery.search(query, limit?)

Search for tools matching a query string.

tools = await client.discovery.search("ethereum gas", limit=10)

client.discovery.get_featured(limit?)

Get featured/popular tools.

featured = await client.discovery.get_featured(limit=5)

Tools (Pay-Per-Request)

client.tools.execute(tool_id, tool_name, args?)

Execute a single tool method. One call, one payment, raw result.

result = await client.tools.execute(
    tool_id="uuid-of-tool",
    tool_name="get_gas_prices",
    args={"chainId": 1},
)

Query (Pay-Per-Response)

client.query.run(query, tools?, model_id?, include_data?, include_data_url?)

Run an agentic query. The server discovers tools, executes the full pipeline (up to 100 MCP calls per tool), applies model-aware mediator/data budgeting, and returns an AI-synthesized answer.

# Simple string
answer = await client.query.run("What are the top whale movements on Base?")

# With specific tools
answer = await client.query.run(
    query="Analyze whale activity on Base",
    tools=["tool-uuid-1", "tool-uuid-2"],  # optional — auto-discover if omitted
    model_id="kimi-model-thinking",          # optional
    include_data=True,                       # optional: include execution data inline
    include_data_url=True,                   # optional: include blob URL for full data
)

print(answer.response)      # AI-synthesized text
print(answer.tools_used)    # [QueryToolUsage(id, name, skill_calls)]
print(answer.cost)          # QueryCost(model_cost_usd, tool_cost_usd, total_cost_usd)
print(answer.duration_ms)   # Total time
print(answer.data)          # Optional execution data (when include_data=True)
print(answer.data_url)      # Optional blob URL (when include_data_url=True)

client.query.stream(query, tools?, model_id?, include_data?, include_data_url?)

Same as run() but streams events in real-time via SSE.

async for event in client.query.stream("What are the top whale movements?"):
    if event.type == "tool-status":
        print(f"Tool {event.tool.name}: {event.status}")
    elif event.type == "text-delta":
        print(event.delta, end="")
    elif event.type == "done":
        print(f"\nTotal cost: {event.result.cost.total_cost_usd}")

Types

from ctxprotocol import (
    # Auth utilities for tool contributors
    verify_context_request,
    is_protected_mcp_method,
    is_open_mcp_method,
    
    # Client types
    ContextClientOptions,
    Tool,
    McpTool,
    ExecuteOptions,
    ExecutionResult,
    ContextErrorCode,
    
    # Auth types (for MCP server contributors)
    VerifyRequestOptions,
    
    # Context types (for MCP server contributors receiving injected data)
    ContextRequirementType,
    HyperliquidContext,
    PolymarketContext,
    WalletContext,
    UserContext,
)

Error Handling

The SDK raises ContextError with specific error codes:

from ctxprotocol import ContextClient, ContextError

try:
    result = await client.tools.execute(...)
except ContextError as e:
    match e.code:
        case "no_wallet":
            # User needs to set up wallet
            print(f"Setup required: {e.help_url}")
        case "insufficient_allowance":
            # User needs to set a spending cap
            print(f"Set spending cap: {e.help_url}")
        case "payment_failed":
            # Insufficient USDC balance
            pass
        case "execution_failed":
            # Tool execution error
            pass

Error Codes

Code Description Handling
unauthorized Invalid API key Check configuration
no_wallet Wallet not set up Direct user to help_url
insufficient_allowance Spending cap not set Direct user to help_url
payment_failed USDC payment failed Check balance
execution_failed Tool error Retry with different args

🔒 Securing Your Tool (MCP Contributors)

If you're building an MCP server (tool contributor), verify incoming requests:

Quick Implementation with FastAPI

from fastapi import FastAPI, Request, Depends, HTTPException
from ctxprotocol import create_context_middleware, ContextError

app = FastAPI()
verify_context = create_context_middleware(audience="https://your-tool.com/mcp")

@app.post("/mcp")
async def handle_mcp(request: Request, context: dict = Depends(verify_context)):
    # context contains verified JWT payload (on protected methods)
    # None for open methods like tools/list
    body = await request.json()
    # Handle MCP request...

Manual Verification

from ctxprotocol import verify_context_request, is_protected_mcp_method, ContextError

# Check if a method requires auth
if is_protected_mcp_method(body["method"]):
    try:
        payload = await verify_context_request(
            authorization_header=request.headers.get("authorization"),
            audience="https://your-tool.com/mcp",  # optional
        )
        # payload contains verified JWT claims
    except ContextError as e:
        # Handle authentication error
        raise HTTPException(status_code=401, detail="Unauthorized")

MCP Security Model

The SDK implements a selective authentication model — discovery is open, execution is protected:

MCP Method Auth Required Why
initialize ❌ No Session setup
tools/list ❌ No Discovery - agents need to see your schemas
resources/list ❌ No Discovery
prompts/list ❌ No Discovery
tools/call Yes Execution - costs money, runs your code

What this means in practice:

  • https://your-mcp.com/mcp + initialize → Works without auth
  • https://your-mcp.com/mcp + tools/list → Works without auth
  • https://your-mcp.com/mcp + tools/callRequires Context Protocol JWT

This matches standard API patterns (OpenAPI schemas are public, GraphQL introspection is open).

Execution Timeout & Product Design

⚠️ 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.

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.

# ❌ BAD: Raw access (timeout-prone, no moat)
{"name": "run_sql", "description": "Run any SQL against blockchain data"}

# ✅ GOOD: Pre-computed product (instant, defensible)
{"name": "get_smart_money_wallets", "description": "Top 100 wallets that timed market tops"}

See the full documentation for detailed guidance.

Context Injection (Personalized Tools)

For tools that analyze user data, Context automatically injects user context:

from ctxprotocol import CONTEXT_REQUIREMENTS_KEY, HyperliquidContext

# Define tool with context requirements
TOOLS = [{
    "name": "analyze_my_positions",
    "description": "Analyze your positions with personalized insights",
    "_meta": {
        "contextRequirements": ["hyperliquid"],
        "rateLimit": {
            "maxRequestsPerMinute": 30,
            "cooldownMs": 2000,
            "maxConcurrency": 1,
            "supportsBulk": True,
            "recommendedBatchTools": ["get_portfolio_snapshot"],
            "notes": "Hobby tier: use snapshot endpoints before fan-out loops.",
        },
    },
    "inputSchema": {
        "type": "object",
        "properties": {
            "portfolio": {
                "type": "object",
                "description": "Portfolio context (injected by platform)",
            },
        },
        "required": ["portfolio"],
    },
}]

# Your handler receives typed context
async def handle_analyze_positions(portfolio: HyperliquidContext):
    positions = portfolio.perp_positions
    account = portfolio.account_summary
    # ... analyze and return insights

Links

Requirements

  • Python 3.10+
  • httpx
  • pydantic
  • pyjwt[crypto]

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ctxprotocol-0.8.1.tar.gz (40.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ctxprotocol-0.8.1-py3-none-any.whl (34.1 kB view details)

Uploaded Python 3

File details

Details for the file ctxprotocol-0.8.1.tar.gz.

File metadata

  • Download URL: ctxprotocol-0.8.1.tar.gz
  • Upload date:
  • Size: 40.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for ctxprotocol-0.8.1.tar.gz
Algorithm Hash digest
SHA256 d9a9495f3770cebf7d52aa7859bef5b27a37e5c8d523022e859802b1cf287fd1
MD5 4a03483038254722ed1dcece88894b9c
BLAKE2b-256 106503e0fa1633370c95ac8981637e63143e92b09720ab29dd696290eed4ccc7

See more details on using hashes here.

File details

Details for the file ctxprotocol-0.8.1-py3-none-any.whl.

File metadata

  • Download URL: ctxprotocol-0.8.1-py3-none-any.whl
  • Upload date:
  • Size: 34.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for ctxprotocol-0.8.1-py3-none-any.whl
Algorithm Hash digest
SHA256 229a22559b52a466c263173b6abbf1753a38d344d17ae6b026303a7c2b16d047
MD5 b7286022d2508bc0344bea7b3208d0b0
BLAKE2b-256 4bf99091a3293bc330394b63f64bdf29de3fe1a977ae127f0ddcc604ad42e1a2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page