madeonsol_copytrade_create
Create a copy-trade rule to mirror trades from specified Solana wallets, with configurable sizing, delivery mode, and optional trade side filter.
Instructions
Create a copy-trade rule. Returns webhook_secret ONCE on creation when delivery_mode includes 'webhook' — store it to verify HMAC signatures. PRO=5 source_wallets/rule, ULTRA=50.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| source_wallets | Yes | Wallets to mirror (base58) | |
| sizing_amount | Yes | Amount used by the chosen sizing_mode | |
| name | No | Optional human label | |
| min_trade_sol | No | Minimum source-wallet trade size to fire a signal | |
| only_action | No | Filter to one side (default 'both') | |
| sizing_mode | No | How sizing_amount is interpreted | |
| delivery_mode | No | Where to deliver fired signals | |
| webhook_url | No | Required when delivery_mode includes 'webhook' |
Implementation Reference
- src/index.ts:688-709 (handler)The handler logic for madeonsol_copytrade_create tool. It constructs a POST request body from the provided arguments (source_wallets, sizing_amount, and optional fields like name, min_trade_sol, only_action, sizing_mode, delivery_mode, webhook_url) and calls restQuery to POST to '/copytrade/subscriptions'.
server.tool( "madeonsol_copytrade_create", "Create a copy-trade rule. Returns webhook_secret ONCE on creation when delivery_mode includes 'webhook' — store it to verify HMAC signatures. PRO=5 source_wallets/rule, ULTRA=50.", { source_wallets: z.array(z.string()).min(1).max(50).describe("Wallets to mirror (base58)"), sizing_amount: z.number().describe("Amount used by the chosen sizing_mode"), name: z.string().optional().describe("Optional human label"), min_trade_sol: z.number().optional().describe("Minimum source-wallet trade size to fire a signal"), only_action: z.enum(["buy", "sell", "both"]).optional().describe("Filter to one side (default 'both')"), sizing_mode: z.enum(["fixed", "proportional", "percent_source"]).optional().describe("How sizing_amount is interpreted"), delivery_mode: z.enum(["webhook", "websocket", "both"]).optional().describe("Where to deliver fired signals"), webhook_url: z.string().url().optional().describe("Required when delivery_mode includes 'webhook'"), }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async (args) => { const body: Record<string, unknown> = { source_wallets: args.source_wallets, sizing_amount: args.sizing_amount }; for (const k of ["name", "min_trade_sol", "only_action", "sizing_mode", "delivery_mode", "webhook_url"] as const) { if (args[k] !== undefined) body[k] = args[k]; } return { content: [{ type: "text" as const, text: await restQuery("POST", "/copytrade/subscriptions", body) }] }; } ); - src/index.ts:691-700 (schema)Zod schema defining input parameters for madeonsol_copytrade_create: source_wallets (required, array of 1-50 base58 strings), sizing_amount (required number), plus optional fields: name, min_trade_sol, only_action, sizing_mode, delivery_mode, webhook_url.
{ source_wallets: z.array(z.string()).min(1).max(50).describe("Wallets to mirror (base58)"), sizing_amount: z.number().describe("Amount used by the chosen sizing_mode"), name: z.string().optional().describe("Optional human label"), min_trade_sol: z.number().optional().describe("Minimum source-wallet trade size to fire a signal"), only_action: z.enum(["buy", "sell", "both"]).optional().describe("Filter to one side (default 'both')"), sizing_mode: z.enum(["fixed", "proportional", "percent_source"]).optional().describe("How sizing_amount is interpreted"), delivery_mode: z.enum(["webhook", "websocket", "both"]).optional().describe("Where to deliver fired signals"), webhook_url: z.string().url().optional().describe("Required when delivery_mode includes 'webhook'"), }, - src/index.ts:688-709 (registration)The tool is registered via server.tool() call with name 'madeonsol_copytrade_create'. It is conditionally registered only when hasRestAuth (i.e., authMode === 'madeonsol') is true, inside the Webhook & Streaming tools block starting at line 449.
server.tool( "madeonsol_copytrade_create", "Create a copy-trade rule. Returns webhook_secret ONCE on creation when delivery_mode includes 'webhook' — store it to verify HMAC signatures. PRO=5 source_wallets/rule, ULTRA=50.", { source_wallets: z.array(z.string()).min(1).max(50).describe("Wallets to mirror (base58)"), sizing_amount: z.number().describe("Amount used by the chosen sizing_mode"), name: z.string().optional().describe("Optional human label"), min_trade_sol: z.number().optional().describe("Minimum source-wallet trade size to fire a signal"), only_action: z.enum(["buy", "sell", "both"]).optional().describe("Filter to one side (default 'both')"), sizing_mode: z.enum(["fixed", "proportional", "percent_source"]).optional().describe("How sizing_amount is interpreted"), delivery_mode: z.enum(["webhook", "websocket", "both"]).optional().describe("Where to deliver fired signals"), webhook_url: z.string().url().optional().describe("Required when delivery_mode includes 'webhook'"), }, { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true }, async (args) => { const body: Record<string, unknown> = { source_wallets: args.source_wallets, sizing_amount: args.sizing_amount }; for (const k of ["name", "min_trade_sol", "only_action", "sizing_mode", "delivery_mode", "webhook_url"] as const) { if (args[k] !== undefined) body[k] = args[k]; } return { content: [{ type: "text" as const, text: await restQuery("POST", "/copytrade/subscriptions", body) }] }; } ); - src/index.ts:451-466 (helper)The restQuery helper function used by the copy-trade create handler. Sends authenticated HTTP requests (POST/GET/PATCH/DELETE) to the MadeOnSol API at BASE_URL/api/v1/..., serializing JSON bodies and returning parsed JSON or error text.
async function restQuery(method: string, path: string, body?: unknown): Promise<string> { const headers: Record<string, string> = { "Content-Type": "application/json", ...apiKeyHeaders(), }; const res = await fetch(`${BASE_URL}/api/v1${path}`, { method, headers, ...(body ? { body: JSON.stringify(body) } : {}), }); if (!res.ok) { const text = await res.text().catch(() => ""); return `Error ${res.status}: ${text}`; } return JSON.stringify(await res.json(), null, 2); }