DocsAPI

MCP for AI agents

Let Claude, Cursor or any MCP client trade, launch, run tasks and read your PnL through your API key — one tool per API operation, nothing to install.

VortexDeployer is an MCP server (Model Context Protocol, the open standard AI assistants use to call tools). Point an MCP client at the endpoint with your API key and it gets one tool per API operation — the same buy, sell, launch, tasks, wallets and PnL calls as the REST API, with the same scopes and limits.

WhatValue
Endpointhttps://evm.vortexdeployer.com/evm/api/v1/mcp
TransportStreamable HTTP, stateless — plain JSON answers, no session to keep
AuthAuthorization: Bearer vd_… on every request, exactly like REST — or a vdo_… OAuth access token bound to one (OAuth for connectors)
Tools84, generated from the OpenAPI document — never out of date
The agent acts as you

Every tool runs as your account with real funds. Give an agent a key with only the scopes it needs, put maxSolPerTrade and dailySolCap on it, and keep launch off unless it launches. Start with a read-only key.

Connect#

Claude Code

claude mcp add --transport http vortex https://evm.vortexdeployer.com/evm/api/v1/mcp \
  --header "Authorization: Bearer vd_…"

Claude Desktop, Cursor, Windsurf and most others take a JSON entry:

{
  "mcpServers": {
    "vortex": {
      "url": "https://evm.vortexdeployer.com/evm/api/v1/mcp",
      "headers": { "Authorization": "Bearer vd_…" }
    }
  }
}

Then ask: *"Which of my wallets hold SOL?"*, *"Dry-run a 0.1 SOL buy of <mint> from wallet 12"*, *"Pause every running task"*. The assistant picks the tool, fills the arguments and shows you the answer.

From your own code — any MCP SDK works; with the official TypeScript one:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(new StreamableHTTPClientTransport(new URL('https://evm.vortexdeployer.com/evm/api/v1/mcp'), {
  requestInit: { headers: { Authorization: 'Bearer vd_…' } },
}));
const { tools } = await client.listTools();
const me = await client.callTool({ name: 'me', arguments: {} });

Claude.ai (custom connector) — claude.ai can't send a static bearer key, so it connects with OAuth instead: add a custom connector with the URL https://evm.vortexdeployer.com/evm/api/v1/mcp, and Claude runs the standard OAuth 2.1 dance against this server — register, consent, token. At consent you sign in here and pick which of your API keys the connector acts as; the access token it receives (vdo_…) then works everywhere a vd_ key does, governed by that key's scopes and caps. Full details: OAuth for connectors.

Tools#

Tool names are the operationIds of the OpenAPI document. A tool's arguments are the operation's path and query parameters plus its JSON body, flattened into one object; a route that takes an Idempotency-Key header takes it as the idempotencyKey argument.

ToolREST callScope
meGET /meany
buy, sell, trade_batchPOST /buy, /sell, /trade/batchtrade
launchPOST /launch (dryRun defaults to true)launch
list_launchpads, launch_quotesGET /launchpads, /launchpads/:launchpad/quotes — which quote tokens and stocks a launch can useread
list_wallets, wallet_balances, token_balances, wallet_profileGET /wallets, /wallets/balances, /wallets/token-balances, /wallets/:id/profileread
create_wallets, update_wallet, delete_walletPOST /wallets, PATCH/DELETE /wallets/:idtrade
list_wallet_groups, create_wallet_group, rename_wallet_group, delete_wallet_group/wallets/groups…read / trade
list_cashback, claim_cashbackGET /wallets/cashback, POST /wallets/cashback/claim (dryRun defaults to true)read / trade
portfolio, holdings, portfolio_pnl, daily_pnl, pnl, wallet_totalsGET /portfolio…, /pnl, POST /pnl/wallet-totalsread
list_projects, project_by_id, project_by_mint, project_walletsGET /projects…read
create_project, create_cto_project, update_project, delete_project, assign_project_wallets, unlink_project_walletPOST/PATCH/DELETE /projects…trade
closeout_projectPOST /projects/:id/closeout (dryRun defaults to true)trade
launch_readiness, next_mint_address, clone_token_look, launch_affordability, supply_preview, pons_launch_terms, otcdesks_rewardsGET/POST /launch/… helpersread
upload_metadataPOST /launch/metadatalaunch
list_supply_plans, save_supply_plan, delete_supply_plan, mount_supply_plan/supply-plans…read / trade
conversion_assets, conversion_quote, convertPOST /convert/assets, /convert/quote, /convertread / trade
burn_tokens, lock_tokensPOST /tokens/burn, /tokens/lock (dryRun defaults to true)trade
creator_fees, claim_creator_feesGET /tokens/creator-fees, POST /tokens/creator-fees/claimread / trade
list_events, warmup_candidatesGET /events, /warmup/candidatesread
trading_settings, update_trading_settingsGET/PATCH /settings/tradingread / trade
list_kols, track_kol, update_kol, untrack_kol/kols…read / trade
list_tasks, get_task, task_executions, task_playbook, task_postmortem, task_mixer_orders, task_disperse, preview_taskGET /tasks…, POST /tasks/:id/previewread
create_task, update_task, delete_task, start_task, resume_task, pause_task, stop_task, stop_all_tasksPOST/PATCH/DELETE /tasks…trade
list_presets, save_preset, delete_preset/tasks/presets…read / trade
execute_mixer_recipientPOST /tasks/:id/mixer-orders/:orderId/recipients/:recipientId/executetrade

Every tool carries MCP annotations: readOnlyHint is true on the GET tools, so a client that asks before any non-read call asks before every trade.

What a call returns#

A tool result is the REST answer, as text and as structuredContent, with the HTTP status and your rate window in _meta:

{
  "content": [{ "type": "text", "text": "{ \"user\": { \"id\": \"1\", … }, \"key\": { … } }" }],
  "structuredContent": { "user": { "id": "1", "username": "yourname" }, "key": { "name": "agent", "scopes": ["read"], "restricted": false } },
  "_meta": { "status": 200, "rateLimit": { "limit": 300, "remaining": 299, "reset": 1758050400 } }
}

A refusal is isError: true with the same body a REST caller gets — a stable code, the human error and a traceId — so the assistant can read insufficient_scope, validation_failed (with details) or rate_limited and act on it:

{ "isError": true, "content": [{ "type": "text", "text": "{ \"code\": \"insufficient_scope\", \"error\": \"this key lacks the trade scope\", \"traceId\": \"…\" }" }] }

Scopes, per-key limits, rate limits, idempotency and the error codes are exactly the REST ones — each tool call is the REST call, made for you.

Good to know#

  • No session. Every request authenticates on its own; there is nothing to resume and a dropped connection loses nothing.
  • Long calls. A launch or a synchronous batch can take a while; a tool call waits up to three minutes for its answer.
  • Streams are not tools. For live trades, PnL and balances use the WebSocket.
  • Usage from an agent shows in the API hub under the same key, per route.