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.
| What | Value |
|---|---|
| Address | https://api.vortexdeployer.com/api/v1/mcp |
| How it talks | Streamable HTTP, stateless. Each request is a POST and gets a plain JSON answer. |
| Sign-in | Authorization: Bearer vd_…, the same API key as REST. An OAuth token (vdo_…) works too. |
| Tools | 93, one per REST call. |
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:
readto look,tradeto trade and run tasks,launchto 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.
| Tools | REST calls | Scope |
|---|---|---|
me | GET /me | any |
buy, sell, trade_batch | POST /buy, /sell, /trade/batch | trade |
conversion_assets, conversion_quote | POST /convert/assets, /convert/quote | read |
convert | POST /convert | trade |
burn_tokens, lock_tokens | POST /tokens/burn, /tokens/lock | trade |
creator_fees / claim_creator_fees | GET /tokens/creator-fees / POST …/claim | read / trade |
launch | POST /launch | launch |
upload_metadata | POST /launch/metadata | launch |
list_launchpads, launch_quotes, launch_readiness, next_mint_address, clone_token_look, launch_affordability, supply_preview, pons_launch_terms, otcdesks_rewards | GET /launchpads… and the /launch/… helpers | read |
list_supply_plans, mount_supply_plan / save_supply_plan, delete_supply_plan | /supply-plans… | read / trade |
list_wallets, wallet_balances, token_balances, wallet_profile | GET /wallets… | read |
create_wallets, update_wallet, delete_wallet, delete_wallets, move_wallets | POST, PATCH, DELETE /wallets… | trade |
list_wallet_groups / create_wallet_group, rename_wallet_group, delete_wallet_group | /wallets/groups… | read / trade |
list_cashback / claim_cashback | GET /wallets/cashback / POST …/claim | read / trade |
portfolio, holdings, portfolio_pnl, daily_pnl, pnl, wallet_totals | GET /portfolio…, GET /pnl, POST /pnl/wallet-totals | read |
list_projects, project_by_id, project_by_mint, project_wallets | GET /projects… | read |
create_project, create_cto_project, update_project, delete_project, assign_project_wallets, remove_project_wallets, unlink_project_wallet, closeout_project | POST, PATCH, DELETE /projects… | trade |
list_tasks, get_task, task_executions, task_postmortem, task_playbook, task_mixer_orders, task_disperse, preview_task | GET /tasks…, POST /tasks/:id/preview | read |
create_task, update_task, delete_task, start_task, pause_task, resume_task, stop_task, stop_all_tasks, execute_mixer_recipient | POST, PATCH, DELETE /tasks… | trade |
list_presets / save_preset, delete_preset | /tasks/presets… | read / trade |
list_events, warmup_candidates | GET /events, /warmup/candidates | read |
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_settings | GET / PATCH /settings/trading | read / 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 likeid, anidempotencyKeyon 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 sameidempotencyKey.
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.
amountSolis a plain JSON number in the chain's coin: SOL on Solana, ETH on an EVM chain. Forburn_tokensandlock_tokens,amountwithamountType: "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": falsewith the reason inerror. 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
GETtohttps://api.vortexdeployer.com/api/v1/mcpgets405. OnlyPOSTworks.