Skip to main content
New to MCP? Start with our 5-Minute Quickstart to build and deploy your first server, then come back here for advanced features.

Overview

Want to earn revenue from your data? Turn the insights people pay $500/year for into $0.10/response revenue you keep. Build an MCP server and register it as an MCP Tool on the Context marketplace.

Start With A Product Contract

Before you write code, decide which product you are actually shipping:
SurfaceWhat you are sellingWhat good looks like
QueryA premium answer contractContext can return answer_with_evidence in chat and evidence_only for external agents with the right facts, source refs, freshness, confidence, and view hints
ExecuteA normalized primitive contractAgents get stable typed fields, consistent pagination/filtering, and explicit execute pricing without having to normalize fragmented APIs themselves
BothA normalized primitive that can also power premium answersThe same canonical data can support Query synthesis and direct Execute calls
Every serious builder brief should specify:
  • the exact premium feature or normalized primitive
  • the target paying user
  • 5 must-win prompts or requests
  • the expected evidence fields or normalized schema
  • the ideal output surface (Query, Execute, or both)
  • freshness / latency expectations
  • why free substitutes or direct APIs are insufficient
If your pitch still reads like a category label such as β€œcrypto analytics” or β€œcompany data,” it is too vague. Name the exact feature users would pay for.

Optional Search Helpers For Search-Hard Venues

Most contributors should rely on strong upstream search plus clean tool schemas. If your venue’s native search is too weak to reliably resolve the right market, entity, or series from real user prompts, you may adopt the optional contributor-side pattern described in Optional Contributor Search Helpers. Treat that pattern as a contributor utility, not a marketplace requirement:
  • keep Context discovery generic
  • keep candidate gathering, normalization, provenance, and validation deterministic inside the contributor
  • inject any model judge behind a provider-agnostic boundary
  • keep provider credentials and model spend contributor-owned
  • return honest degraded outcomes when the judge is disabled, malformed, slow, unavailable, or over budget
  • keep the helper optional even after it ships in both SDKs; contributors with strong upstream search should still avoid it
  • save replayable validation artifacts for named regressions, generic overlap, still-ambiguous, and capability-miss cases before you recommend it more broadly
If your upstream venue already exposes reliable search and candidate selection, do not add this helper just because it exists. For the full adoption checklist, parity contract, and rollout gate, see Optional Contributor Search Helpers.

AI-Assisted Builder (TL;DR)

Have an API subscription you want to unbundle? Use Cursor, Claude, or any AI coding agent to build your MCP server automatically.
Prerequisite: You need Context7 MCP configured in your AI coding environment to fetch API documentation automatically.

Two-Step Workflow

1

Build the MCP Server

Use the MCP Builder Template as a system prompt to:
  • Fetch API docs via Context7 and discover all endpoints
  • Define the exact feature, target user, must-win prompts, evidence fields, and ideal surface before writing code
  • Design premium answer products or normalized primitives (not just API passthroughs)
  • Generate complete schemas with outputSchema
  • Implement the full MCP server
Use the mcp-builder-template.md with Context7 for [your-api-library-id].
Fetch the documentation and design tools that answer complex questions.
2

Generate Submission Details

Once your server is built, use the MCP Server Analysis Prompt to:
  • Analyze your MCP server implementation
  • Generate the perfect submission details for the marketplace form
  • Get suggested name, description, category, and pricing
Paste the mcp-server-analysis-prompt.md into your AI chat, then provide your server.ts code or repository URL.

Example Prompt for Cursor/Claude

I want to build an MCP server for the Context Marketplace using the CoinGecko API.

1. Use context7 to fetch the CoinGecko API documentation
2. Follow the mcp-builder-template.md workflow:
   - PHASE 1: Discover all endpoints
   - PHASE 2: Generate the product contract: target user, 5 must-win prompts, evidence fields, and ideal output surface (STOP for my review)
   - PHASE 3: Design and implement tools after I approve

Focus on a premium feature users would actually pay for, not a generic
API wrapper. The end result should either power Query answers with evidence
or provide Execute primitives with a clean normalized schema.
The builder template includes checkpoints where the AI will stop and ask for your approval before proceeding. This ensures you get tools that match your vision.

Earnings Model: You earn 90% of usage fees. Set a listing response price (e.g., $0.01/response) and get paid in USDC when agents use your tool. Want SDK developers to call your methods directly with per-call pricing too? See Enable Execute Pricing after you’re live.

Step 1: Build a Standard MCP Server

Use the official @modelcontextprotocol/sdk to build your server, plus @ctxprotocol/sdk to secure your endpoint.

Install Dependencies

pnpm add @modelcontextprotocol/sdk express
pnpm add @ctxprotocol/sdk
pnpm add -D @types/express

Implement Structured Output

Required for Context: You must implement the MCP structured output standard:
  • outputSchema in your tool definitions (JSON Schema describing your response structure)
  • structuredContent in your responses (the machine-readable data matching your schema)
const TOOLS = [{
  name: "get_gas_price",
  description: "Get current gas prices for any EVM chain",
  inputSchema: {
    type: "object",
    properties: {
      chainId: {
        type: "number",
        description: "EVM chain ID",
        default: 1,
        examples: [1, 10, 8453],
      },
    },
  },
  outputSchema: {
    type: "object",
    properties: {
      gasPrice: { type: "number" },
      unit: { type: "string" },
    },
    required: ["gasPrice", "unit"],
  },
}];

return {
  content: [{ type: "text", text: JSON.stringify(data) }],
  structuredContent: data,
};
Use standard JSON Schema keys in inputSchema properties to guide argument generation:
  • default β€” fallback value the AI uses when the user doesn’t specify one (e.g., "default": "BTC")
  • examples β€” sample values the AI references to understand valid inputs (e.g., "examples": ["BTC", "ETH", "SOL"])
The platform reads these fields during orchestration to generate correct arguments for your methods. In the active unified deep lane, they improve metadata-first planning, clarification grounding, and method selection before execution. Historical traces may still mention deep-light or deep-heavy, but those legacy labels now normalize to the same deep runtime path. Without strong schema hints, the AI must guess valid values from the description alone, leading to more retries and lower first-pass success rates.Do not add custom _meta.inputExamples; _meta should only carry tool metadata such as pricing, context requirements, and rate-limit hints.
Context’s query pipeline uses model-aware data budgets when preparing tool output for synthesis. To maximize answer quality:Best practices for structuredContent:
  • Return structured objects (not one giant serialized string blob)
  • Keep textual analysis/news in dedicated string fields (these are prioritized first)
  • Keep large time-series arrays in separate keys from analysis text
  • Include a compact summary object alongside deep raw data when possible
  • Aim for payloads under ~500K chars for full visibility across all supported models
// βœ… Good: separated and prioritizable
return {
  summary: { direction: "outflow", confidence: 0.82 },
  news: "ETF outflows accelerated this week due to ...",
  chart: { timestamps: [...], values: [...] },
};

// ❌ Bad: giant opaque blob (hard to prioritize/truncate safely)
return JSON.stringify(allData);
Your outputSchema is the single most impactful thing you can do to reduce latency and cost for users.Context’s agentic pipeline uses a planning LLM that reads your outputSchema to generate code that processes your tool’s response. If your schema is vague, the LLM has to guess property names β€” and it will guess wrong. This triggers an automatic self-healing retry loop that:
  • Adds ~30-60 seconds of latency per retry
  • Costs ~$0.02 extra in model inference per retry
  • Wastes retry budget (capped at 8 total iterations)
The #1 cause of these retries? Property name mismatches between what the LLM generates and what your tool actually returns.Self-healing retries target likely code/data-shape issues. Infrastructure failures (rate limits, auth failures, upstream timeouts) are treated as non-healable for that turn.If your tool explicitly reports plan/tier capability limits (for example, β€œlong/short ratio unavailable on current tier”), the platform treats that as a constraint signal and returns best-effort output plus limitations instead of looping.Expose capability flags in structuredContent (for example, supportsLongShortThresholdCheck: false) and include a human-readable limitations field to help the verifier distinguish true parsing bugs from plan constraints.The planning LLM defaults to camelCase (standard JavaScript convention). If your API returns snake_case and your outputSchema doesn’t document the exact property names, the LLM will generate data.exchangeName when your API returns data.exchange_name β€” triggering a retry every time.
// ❌ Bad: Vague schema β€” LLM must guess property names
outputSchema: {
  type: "object",
  properties: {
    data: { type: "array" }
  }
}

// βœ… Good: Detailed schema with exact property names and descriptions
outputSchema: {
  type: "object",
  properties: {
    data: {
      type: "array",
      description: "Array of exchange balance objects, one per exchange",
      items: {
        type: "object",
        properties: {
          exchange_name: { type: "string", description: "Exchange name (e.g. 'Binance')" },
          total_balance: { type: "number", description: "Total balance in the queried coin" },
          balance_change_1d: { type: "number", description: "Balance change in last 24 hours" },
          balance_change_percent_1d: { type: "number", description: "Percent change in last 24 hours" },
        }
      }
    },
    fetchedAt: { type: "string", description: "ISO 8601 timestamp" }
  }
}
Output schema checklist:
  • Document every property name in the response, especially if they’re snake_case
  • Include items.properties for arrays β€” don’t just say { type: "array" }
  • Add descriptions that explain what values mean (units, ranges, interpretation)
  • If the response is an object (not an array), say so β€” { type: "object" } not { type: "array" }
  • Document wrapper properties your handler adds (e.g., fetchedAt, count, symbol)
For third-party APIs with strict quotas, publish pacing hints via _meta.rateLimit. These hints influence both planner pacing and bounded pre-plan probe caps. See Tool Metadata. Reference implementation: Coinglass contributor server.

Secure Your Endpoint

Add Context’s middleware to verify that requests are legitimate:
import express from "express";
import { createContextMiddleware } from "@ctxprotocol/sdk";

const app = express();
app.use(express.json());

app.use("/mcp", createContextMiddleware());
If your tool generates charts, heatmaps, screenshots, or other visual content, you are responsible for hosting the images and returning URLs that the AI can reference.Do not return base64-encoded images in your tool responses. Large base64 strings bloat the response, slow down processing, and may hit token limits. Instead, host your images and return URLs.Recommended: Return Image URLs
return {
  content: [
    { type: "text", text: "Analysis complete. Here's the chart:" }
  ],
  structuredContent: {
    summary: "Market analysis shows bullish signals...",
    chart_url: "https://your-cdn.com/charts/eth-analysis-12345.png",
    chart_alt: "ETH price chart with support levels marked"
  }
};
Image Hosting Options
OptionBest For
Your existing CDNIf you already have infrastructure
Cloud storage (S3, GCS, Azure Blob)Pre-signed URLs for generated content
Vercel Blob / Cloudflare R2Simple, cheap storage for generated images
Imgix / CloudinaryImage transformation and optimization
Output Schema for Image Tools
{
  "type": "object",
  "properties": {
    "summary": {
      "type": "string",
      "description": "Text summary of the analysis"
    },
    "chart_url": {
      "type": "string",
      "format": "uri",
      "description": "URL to the hosted chart image"
    },
    "chart_alt": {
      "type": "string",
      "description": "Accessible description of the image"
    }
  },
  "required": ["summary", "chart_url"]
}
The AI will include your image URL in its response, and users can click to view. For the best experience, use publicly accessible URLs (no auth required) with reasonable cache headers.
Free vs Paid Security Requirements:
Tool TypeSecurity MiddlewareRationale
Free Tools ($0.00)OptionalGreat for distribution and adoption β€” anyone can call your endpoint
Paid Tools ($0.01+)MandatoryWe cannot route payments to insecure endpoints
If you’re building a free tool, you can skip the middleware entirely. However, if you ever want to charge for your tool, you’ll need to add it.

MCP Security Model

Understanding what’s protected: Not all MCP methods require authentication. Discovery methods are open so agents can find your tools, but execution requires payment verification.
MCP MethodAuth RequiredWhy
initialize❌ NoSession setup
tools/list❌ NoDiscovery - agents need to see your schemas
resources/list❌ NoDiscovery
prompts/list❌ NoDiscovery
tools/callβœ… YesExecution - costs money, runs your code
This means:
  • Anyone can call /mcp with initialize or tools/list to discover your tools
  • Only requests with a valid Context Protocol JWT can call tools/call
  • The middleware handles this automatically - you don’t need to implement it yourself

Step 2: Test Your Tool Locally

Before deploying, ensure your server works as expected. You can use the official MCP Inspector or curl to test your tool locally.

Using Curl

curl -X POST http://localhost:3000/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "tools/list",
    "id": 1
  }'

Step 3: Deploy Your Server

Your server needs to be publicly accessible. We support both transport methods:
TransportURL FormatRecommendation
HTTP Streaminghttps://your-server.com/mcpβœ… Recommended
SSE (Server-Sent Events)https://your-server.com/sseSupported
Deploy to any platform: Vercel, Railway, Render, AWS, or your own infrastructure. The only requirement is a publicly accessible HTTPS endpoint.

Step 4: Register in the App

1

Go to /contribute

Navigate to the contribute page in the running Context app
2

Select MCP Tool

Choose β€œMCP Tool” (the default option)
3

Paste Your Endpoint URL

Enter your publicly accessible endpoint URL
4

Auto-Discovery

We’ll auto-discover your skills via listTools()

Step 5: Set a Price

Set a listing response price β€” this is what users pay per response when your tool is used in the Context app or via client.query.run().
Two pricing surfaces: Your listing response price (~$0.10) covers Query mode, where Context is the librarian. If you also want SDK developers calling your methods directly, enable Execute pricing at 1/100 of your response price ($0.001/call). See Enable Execute Pricing below.
PriceWhat happens
$0.00Free β€” great for adoption and getting discovered
$0.01+Paid β€” you earn 90% of each response fee
Response fees are paid once per chat turn. The runtime may call your methods up to 100 times within that turn as a safety-capped orchestration limit.
Security requirement depends on price:
  • Free tools: Security middleware is optional β€” your endpoint works without JWT verification
  • Paid tools: Security middleware is mandatory β€” see Secure Your Endpoint
Compatibility: SDK/API payloads still expose legacy field names like price / pricePerQuery for now. Their billing meaning in Query mode is listing-level price per response turn. A future major release can introduce response-named aliases (for example, pricePerResponse) before deprecating legacy names.

Step 6: Stake USDC

All tools require a minimum USDC stake, enforced on-chain.
Tool TypeMinimum Stake
Free Tools$10 USDC
Paid Tools$10 USDC or 100Γ— response price (whichever is higher)
Stakes are fully refundable with a 7-day withdrawal delay. This creates accountability and enables slashing for fraud.

Step 7: You’re Live! πŸŽ‰

Your MCP Tool is now available on the marketplace. Users can discover and use your tool through the Context app or client.query.run() in the SDK. Want SDK developers to also call your methods directly with per-call pricing? See Enable Execute Pricing below.

Validate Your Tool

Now that your tool is live, validate it works correctly through the marketplace β€” not just on your local server.
1

Manual: Test in the Chat App

Enable Developer Mode in Settings β†’ Developer Settings. In the right sidebar, turn off Auto mode and select your tool. Send one of your must-win prompts and check the Developer Logs preview card at the bottom of the response for errors. See the Debugging Guide for details.
2

Manual: Test via the SDK

Use the TypeScript SDK or Python SDK to test both surfaces:
  • Query mode: client.query.run({ query: "...", tools: ["your-tool-id"], queryDepth: "deep", responseShape: "answer_with_evidence", includeDeveloperTrace: true }) β€” validates your tool can support the same premium answer contract the first-party chat uses
  • Agent-facing Query mode: rerun the same must-win prompt with responseShape: "evidence_only" and confirm the evidence package is still useful without prose synthesis
  • Execute mode (if enabled): client.tools.execute({ toolId: "...", toolName: "...", args: {...} }) β€” validates per-method call responses match your declared schemas
3

Automated: Deep Validation System Prompt

For a comprehensive QA pass, use the Deep Validation System Prompt β€” a reusable prompt you can give to any coding agent (Cursor, Claude, etc.). It runs the full validation workflow:
  • Checks your server against the Context Protocol docs for compliance
  • Tests both Query mode and Execute mode through the SDK with developer traces
  • Scores the server against must-win prompts, expected evidence fields, and intended output surfaces
  • Generates a marketplace listing (name, description, must-win style β€œTry asking” prompts)
  • Provides actionable fixes and re-tests in a loop until all gates pass
The deep validation prompt handles both pre-submission (direct endpoint testing before you list) and post-submission (full SDK-based marketplace testing after you list). Give it your endpoint URL and API key and it figures out the right workflow.
If you are validating a local or preview runtime on your own machine, prefer answerModelId: "glm-turbo-model" for Query regression runs unless you are intentionally comparing models. It is the default non-geo-blocked local validation model for this workstream.

Updating Your Tool

When you add new endpoints, modify schemas, or change your tool’s functionality:
1

Deploy Changes

Push your updated code to your server/hosting
2

Refresh Skills on Context

  1. Go to ctxprotocol.com/developer/tools β†’ Developer Tools (My Tools)
  2. Find your tool and click β€œRefresh Skills”
  3. Context re-calls listTools() to discover changes
3

Update Description (if needed)

If you’ve added significant new tools, update your description:
Don’t forget to Refresh Skills! Deploying new code doesn’t automatically update the marketplace listing. You must click β€œRefresh Skills” for Context to re-discover your tools.

Complete Server Example

Here’s a full working example of an MCP server ready for Context:
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import express from "express";
import { createContextMiddleware } from "@ctxprotocol/sdk";

const app = express();
app.use(express.json());

app.use("/mcp", createContextMiddleware());

const TOOLS = [{
  name: "get_gas_price",
  description: "Get current gas prices",
  inputSchema: {
    type: "object",
    properties: {
      chainId: {
        type: "number",
        description: "EVM chain ID",
        default: 1,
        examples: [1, 10, 8453],
      },
    },
  },
  outputSchema: {
    type: "object",
    properties: {
      gasPrice: { type: "number" },
      unit: { type: "string" },
    },
    required: ["gasPrice", "unit"],
  },
}];

const server = new Server(
  { name: "my-gas-tool", version: "1.0.0" },
  { capabilities: { tools: {} } }
);

server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: TOOLS,
}));

server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const data = await fetchGasData(request.params.arguments.chainId);
  
  return {
    content: [{ type: "text", text: JSON.stringify(data) }],
    structuredContent: data,
  };
});

app.listen(3000, () => {
  console.log("MCP server running on port 3000");
});

Enable Execute Pricing

By default, your tool is available in Query mode β€” users ask questions and Context handles orchestration for a flat per-response fee. If you also want SDK developers to call your methods directly with per-call pricing (Execute mode):
1

Set a default execute price

In the marketplace contribute form, enter a default execute price (e.g., $0.001). This fans out to every method’s _meta.pricing.executeUsd automatically.
2

Or declare pricing per method in code

For fine-grained control, add _meta to each method in your MCP server:
const TOOLS = [{
  name: "get_gas_price",
  description: "Get current gas prices",
  _meta: {
    surface: "both",
    queryEligible: true,
    latencyClass: "instant",
    pricing: {
      executeUsd: "0.001",
    },
    rateLimit: {
      maxRequestsPerMinute: 60,
      cooldownMs: 1000,
      maxConcurrency: 5,
    },
  },
  inputSchema: { /* ... */ },
  outputSchema: { /* ... */ },
}];
Visibility is bidirectional:
  • No execute price β†’ invisible in Execute. Methods without _meta.pricing.executeUsd will not appear in SDK Execute discovery (client.tools.execute()). Set execute pricing to unlock per-call revenue.
  • Execute-only β†’ invisible in Query. If you set surface: "execute" and queryEligible: false, your methods will not appear in the Context app or client.query.run(). They become SDK-only tools.
See Choosing a Mode for the full visibility matrix.
Execute pricing is typically ~1/100 of your listing response price (e.g., $0.10 response β†’ $0.001 per execute call) because a single Query response can invoke up to 100 method calls internally.
See Tool Metadata for the full metadata reference. For production examples, see the Coinglass contributor server (rate limit hints + per-method pricing) and the Normalized Data Provider (Execute-mode data broker with background ingestion).

Advanced Topics

Your outputSchema isn’t just documentation β€” it’s a contract.Context uses automated schema validation as part of our crypto-native dispute resolution system:
  1. Users can dispute tool outputs by providing their transaction_hash (proof of payment)
  2. Robot judge auto-adjudicates by validating your actual output against your declared outputSchema
  3. If schema mismatches, the dispute is resolved against you automatically
  4. Repeated violations (5+ flags) lead to tool deactivation
Example: Schema Compliance
// ❌ BAD: Schema says number, but you return string
outputSchema: { temperature: { type: "number" } }
structuredContent: { temperature: "72" }  // GUILTY - schema mismatch!

// βœ… GOOD: Output matches schema exactly
outputSchema: { temperature: { type: "number" } }
structuredContent: { temperature: 72 }  // Valid
Unlike Web2 β€œstar ratings” that can be gamed by bots, Context disputes require economic proof (you paid for the query). This protects honest developers from spam while ensuring bad actors face consequences.
Critical: MCP tool execution on the Context platform has a ~60 second timeout per call. This applies to both Query and Execute mode. It shapes the marketplace toward high-quality data products β€” but what β€œhigh-quality” means depends on which mode you’re designing for.Where the Timeout Comes FromThe timeout is enforced by the platform infrastructure (and in standard MCP setups like Claude Desktop, by the LLM client itself). When your tool is called, the system waits for a response β€” if it doesn’t arrive in ~60 seconds, execution fails.When a timeout is hit, the runtime propagates cancellation to in-flight MCP calls to avoid post-timeout β€œzombie” traffic. Tool authors should still return explicit failure/degraded states quickly so agents can recover gracefully.This isn’t an MCP protocol limitation β€” SSE connections can stay open indefinitely. The timeout exists at the application layer and serves as a quality forcing function for both modes.Designing for Query ModeIn Query mode, Context is the librarian. Your tool output feeds into a synthesized response that costs the user ~$0.10. The user expects curated intelligence β€” not raw data dumps.The timeout forces you to pre-compute insights rather than running heavy queries at request time:
Raw Access (❌ Bad Query Tool)Curated Intelligence (βœ… Good Query Tool)
β€œRun any SQL on Dune""Smart Money Wallet Tracker"
"Query 4 years of NFT data""NFT Alpha Signals"
"Scan all whale wallets""Whale Alert Feed”
Timeout after 60s ❌Instant response βœ…
The best data businesses don’t sell raw database access. They sell curated, pre-computed insights. This is exactly how Bloomberg, Nansen, Arkham, and Messari work β€” and it’s the right model for Query mode.
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              QUERY MODE: DATA BROKER'S JOB (offline)                β”‚
β”‚                                                                     β”‚
β”‚  1. Run heavy queries on your data source (30 min timeout - OK)    β”‚
β”‚  2. Pre-compute valuable insights ("wallets that sold tops")       β”‚
β”‚  3. Store results in your own database                             β”‚
β”‚  4. Update daily/hourly via cron jobs                              β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    MCP TOOL (instant)                               β”‚
β”‚                                                                     β”‚
β”‚  User: "What are the smart money wallets holding?"                 β”‚
β”‚  Tool: SELECT * FROM my_precomputed_smart_money LIMIT 10           β”‚
β”‚  Response: < 1 second βœ…                                            β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Designing for Execute ModeIn Execute mode, the developer’s agent is the librarian. The agent makes many sequential calls within a session, iterating over structured data to build its own analysis. The user pays ~$0.001 per call.Here, normalized data endpoints ARE the product. The value isn’t in pre-digested insights β€” it’s in clean, typed, consistent data that an agent can iterate over without parsing 15 different exchange APIs.The timeout still applies per-call (a single normalized price lookup shouldn’t take 60 seconds), but the design principles are different:
Inconsistent Data (❌ Bad Execute Tool)Normalized Data (βœ… Good Execute Tool)
Different schema per exchangeUnified { exchange, price, volume } across all exchanges
Untyped JSON blobsStrict outputSchema with typed fields
One method returns everythingFocused methods: get_prices, get_orderbook, get_funding_rates
No pagination or filteringParametric access: symbol, exchange, time range
Inconsistent error formatsStructured error responses with codes
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            EXECUTE MODE: DATA NORMALIZER'S JOB (ongoing)            β”‚
β”‚                                                                     β”‚
β”‚  1. Connect to upstream sources (exchange APIs, data providers)     β”‚
β”‚  2. Normalize schemas into consistent typed formats                β”‚
β”‚  3. Expose focused, single-purpose methods                         β”‚
β”‚  4. Return clean, paginated, parametric data per call              β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    AGENT PIPELINE (iterative)                       β”‚
β”‚                                                                     β”‚
β”‚  Agent: get_prices("BTC/USDT", exchanges=["binance","coinbase"])   β”‚
β”‚  Agent: get_orderbook("BTC/USDT", exchange="binance", depth=20)    β”‚
β”‚  Agent: get_funding_rates("BTC/USDT")                              β”‚
β”‚  Agent: β†’ runs own analysis across all results                     β”‚
β”‚                                                                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
Product Tiers by ModeQuery mode tiers:
  • Real-time curated (< 60s) β€” Direct API calls that return analyzed results quickly (portfolio risk score, gas price recommendation)
  • Pre-computed intelligence (instant) β€” Heavy analysis run offline via cron, insights served instantly (smart money wallets, whale alerts, trending signals)
Execute mode tiers:
  • Normalized data endpoints β€” Clean, typed, paginated data across multiple sources with a consistent schema (cross-exchange prices, historical time-series, order book snapshots). The value is in the normalization and reliability.
  • Specialized computation methods β€” One method does one calculation well (risk score for a wallet, correlation between two assets, signal detection). The developer chains these together.
What if my analysis takes longer than 60 seconds?For Query mode: pre-compute it. Run heavy analysis offline, store results in your database, serve instantly through MCP.For Execute mode: break it into smaller methods. Instead of one method that analyzes all wallets, expose get_wallet_activity per wallet β€” let the agent iterate.
// ❌ BAD (either mode): Long-running analysis at request time
{ name: "analyze_all_wallets", returns: "timeout after 60s" }

// βœ… GOOD (Query): Pre-computed results served instantly
{ name: "get_smart_money_wallets", returns: "instant curated insight from your DB" }

// βœ… GOOD (Execute): Focused method the agent calls per-wallet
{ name: "get_wallet_activity", inputs: "{ address, timeRange }", returns: "normalized activity data" }
Query ModeExecute Mode
Value propositionCurated insights (quality)Normalized data (consistency)
Competes onAnalysis depthSchema reliability, coverage
MoatExpertise in pre-computationNormalization across sources
User expectationOne response = complete answerMany calls = building blocks

Advanced: User Actions (Handshakes)

Need your tool to execute transactions or get user signatures? Use the Handshake Architecture:
import { createSignatureRequest, wrapHandshakeResponse } from "@ctxprotocol/sdk";

if (needsUserSignature) {
  return wrapHandshakeResponse(
    createSignatureRequest({
      domain: { name: "MyProtocol", version: "1", chainId: 1 },
      types: { Order: [{ name: "amount", type: "uint256" }] },
      primaryType: "Order",
      message: { amount: 1000 },
      meta: { description: "Place order", protocol: "MyProtocol" },
    })
  );
}
The Context app will show an approval card, the user signs, and the signature is returned to your tool.

Handshake Architecture Guide

Full guide: signatures, transactions, OAuth flows

Example Servers

Check out these complete working examples:

TypeScript (Express + MCP SDK)

Blocknative

Query mode β€” Gas price API (3 tools), great starting point for your first tool

Hyperliquid

Query mode β€” DeFi analytics (16+ tools, includes handshake example)

Polymarket

Query mode β€” Prediction market intelligence

Normalized Data Provider

Execute mode β€” reference with background ingestion, _meta pricing, and multi-exchange normalization

Python (FastMCP + ctxprotocol)

Hummingbot Market Intel

Query mode β€” Multi-exchange market data (6 tools): prices, order books, funding rates, trade impact analysis
The Python example uses FastMCP which auto-generates outputSchema from Pydantic models and includes structuredContent in responses automatically.