DocsAPI

MCP for AI agents

Connect Claude, Cursor or any MCP client to VortexDeployer so it can trade, launch, run tasks and read PnL with your API key.

MCP (Model Context Protocol) is a standard way for AI assistants to call tools. VortexDeployer runs an MCP server. Each REST call is one tool, with the same scopes and limits.

WhatValue
Addresshttps://api.vortexdeployer.com/api/v1/mcp
How it talksStreamable HTTP, stateless. Each request is a POST and gets a plain JSON answer.
Sign-inAuthorization: Bearer vd_…, the same API key as REST. An OAuth token (vdo_…) works too.
Tools93, one per REST call.
The assistant acts as you, with real money

Every tool runs as your account. buy, sell and trade_batch send real trades unless dryRun is true. Give the assistant its own key with only the scopes it needs. Set a max per buy and a daily cap on it. Start with a read-only key.

Before you start#

  • Make an API key in the app. See API overview & keys.
  • Pick its scopes: read to look, trade to trade and run tasks, launch to launch.

Connect#

Claude Code:

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

Claude Desktop, Cursor and most other clients take a JSON entry:

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

Claude.ai cannot send a fixed key. Add a custom connector with the address https://api.vortexdeployer.com/api/v1/mcp. Claude then signs in through OAuth, and you pick which of your API keys it acts as. See OAuth for connectors.

Your own code can use any MCP SDK. With the 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://api.vortexdeployer.com/api/v1/mcp'), {
  requestInit: { headers: { Authorization: 'Bearer vd_…' } },
}));
const { tools } = await client.listTools();
const me = await client.callTool({ name: 'me', arguments: {} });

Then ask in plain words, like "Which of my wallets hold SOL?" or "Dry-run a 0.1 SOL buy of this token from wallet 812." The assistant picks the tool and fills in the details.

Tools#

A tool's name is the call's operationId in the OpenAPI document at https://api.vortexdeployer.com/api/v1/openapi.json. Its arguments are the call's path and query fields plus its JSON body, all in one object.

For calls that take an Idempotency-Key header, pass it as the idempotencyKey argument. Do that on every trade. See Limits, retries & errors.

ToolsREST callsScope
meGET /meany
buy, sell, trade_batchPOST /buy, /sell, /trade/batchtrade
conversion_assets, conversion_quotePOST /convert/assets, /convert/quoteread
convertPOST /converttrade
burn_tokens, lock_tokensPOST /tokens/burn, /tokens/locktrade
creator_fees / claim_creator_feesGET /tokens/creator-fees / POST …/claimread / trade
launchPOST /launchlaunch
upload_metadataPOST /launch/metadatalaunch
list_launchpads, launch_quotes, launch_readiness, next_mint_address, clone_token_look, launch_affordability, supply_preview, pons_launch_terms, otcdesks_rewardsGET /launchpads… and the /launch/… helpersread
list_supply_plans, mount_supply_plan / save_supply_plan, delete_supply_plan/supply-plans…read / trade
list_wallets, wallet_balances, token_balances, wallet_profileGET /wallets…read
create_wallets, update_wallet, delete_wallet, delete_wallets, move_walletsPOST, PATCH, DELETE /wallets…trade
list_wallet_groups / create_wallet_group, rename_wallet_group, delete_wallet_group/wallets/groups…read / trade
list_cashback / claim_cashbackGET /wallets/cashback / POST …/claimread / trade
portfolio, holdings, portfolio_pnl, daily_pnl, pnl, wallet_totalsGET /portfolio…, GET /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, remove_project_wallets, unlink_project_wallet, closeout_projectPOST, PATCH, DELETE /projects…trade
list_tasks, get_task, task_executions, task_postmortem, task_playbook, task_mixer_orders, task_disperse, preview_taskGET /tasks…, POST /tasks/:id/previewread
create_task, update_task, delete_task, start_task, pause_task, resume_task, stop_task, stop_all_tasks, execute_mixer_recipientPOST, PATCH, DELETE /tasks…trade
list_presets / save_preset, delete_preset/tasks/presets…read / trade
list_events, warmup_candidatesGET /events, /warmup/candidatesread
list_marketplace_inventory, marketplace_inventory_stats, list_marketplace_orders, get_marketplace_order / create_marketplace_order, check_marketplace_order/marketplace…read / trade
trading_settings / update_trading_settingsGET / PATCH /settings/tradingread / trade
list_kols / track_kol, update_kol, untrack_kol/kols…read / trade

Each tool carries hints for your client. GET tools are marked read-only. Every other tool is marked as one that changes things. Your client can use this to ask you before it runs such a tool.

Some tools have a dry run on by default: launch, claim_cashback, closeout_project, burn_tokens, lock_tokens and convert. Others are real by default: buy, sell, trade_batch, claim_creator_fees and every task you start.

What a call returns#

The result is the REST answer, as text and as structuredContent. _meta has the HTTP status and, on reads and order checks, the key's rate-limit window:

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

A refusal has "isError": true and the same body as REST: code, error and traceId. Read the code before you retry. The codes are in Limits, retries & errors.

{ "isError": true, "content": [ { "type": "text", "text": "{ \"code\": \"insufficient_scope\", \"error\": \"this API key does not have the 'trade' scope\", \"traceId\": \"…\" }" } ] }

Two refusals come from the MCP server itself:

  • validation_failed — an unknown tool, a missing path argument like id, an idempotencyKey on a tool that takes none, or an unknown argument on a tool with no body. On a tool with a body, extra arguments go into the body, and the REST route checks them.
  • service_unavailable — the call gave no answer in 3 minutes. The work may still finish. Ask again with the same idempotencyKey.

Good to know#

  • Same rules as REST. Each tool call is made as the REST call with your key. Scopes, key limits, rate limits and idempotency all apply.
  • No session. Every request signs in on its own. A dropped line loses nothing.
  • Amounts. On-chain amounts are in base units unless the field says otherwise. amountSol is a plain JSON number in the chain's coin: SOL on Solana, ETH on an EVM chain. For burn_tokens and lock_tokens, amount with amountType: "tokens" is a whole number of base units, like "3500000000", never "1.5". With "percentage" it is a whole number from 1 to 100. A bad amount comes back as "ok": false with the reason in error. See Burn and lock.
  • Streams are not tools. For live trades, PnL and balances, use Live streams.
  • Usage shows in the app on the API page, Usage tab, under the same key.
  • A GET to https://api.vortexdeployer.com/api/v1/mcp gets 405. Only POST works.

See also#