@alfe.ai/ctrader-mcp 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server.js +31 -31
- package/package.json +3 -3
package/dist/server.js
CHANGED
|
@@ -1187,7 +1187,7 @@ async function withLiveSubscription(pool, numericAccountId, symbolId, kind, hand
|
|
|
1187
1187
|
* required-when-multiple rule at runtime so the model gets a helpful listing
|
|
1188
1188
|
* instead of a bare validation error.
|
|
1189
1189
|
*/
|
|
1190
|
-
const accountIdField = z.string().optional().describe("ctidTraderAccountId of the account to act on (from get_accounts). Optional when exactly one account is connected; REQUIRED when several are — this tool never defaults to an arbitrary account.");
|
|
1190
|
+
const accountIdField = z.coerce.string().optional().describe("ctidTraderAccountId of the account to act on (from get_accounts). Optional when exactly one account is connected; REQUIRED when several are — this tool never defaults to an arbitrary account.");
|
|
1191
1191
|
function registerTools(server, pool) {
|
|
1192
1192
|
const register = server.registerTool.bind(server);
|
|
1193
1193
|
register("get_accounts", {
|
|
@@ -1323,9 +1323,9 @@ function registerTools(server, pool) {
|
|
|
1323
1323
|
description: "Get recent OHLC candles (trendbars) for a symbol on a connected cTrader account. Specify symbolId (from get_symbols), a period, and how many bars. Prices are returned as real decimal prices. Pass `accountId` when several accounts are connected.",
|
|
1324
1324
|
inputSchema: {
|
|
1325
1325
|
accountId: accountIdField,
|
|
1326
|
-
symbolId: z.number().int().positive().describe("The symbol id from get_symbols"),
|
|
1326
|
+
symbolId: z.coerce.number().int().positive().describe("The symbol id from get_symbols"),
|
|
1327
1327
|
period: z.enum(Object.keys(TRENDBAR_PERIOD)).default("H1").describe("Candle period: M1, M5, M15, M30, H1, H4, D1, W1, MN1, etc."),
|
|
1328
|
-
count: z.number().int().min(1).max(1e3).default(50).describe("Number of most-recent bars to return (1-1000)")
|
|
1328
|
+
count: z.coerce.number().int().min(1).max(1e3).default(50).describe("Number of most-recent bars to return (1-1000)")
|
|
1329
1329
|
}
|
|
1330
1330
|
}, async (args) => {
|
|
1331
1331
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1352,7 +1352,7 @@ function registerTools(server, pool) {
|
|
|
1352
1352
|
description: "Get full trading details for one symbol on a connected cTrader account: price digits, pip position, lot size, min/max/step volume (in lots), swap rates, and short-selling availability. Pass `accountId` when several accounts are connected.",
|
|
1353
1353
|
inputSchema: {
|
|
1354
1354
|
accountId: accountIdField,
|
|
1355
|
-
symbolId: z.number().int().positive().describe("Symbol id from get_symbols")
|
|
1355
|
+
symbolId: z.coerce.number().int().positive().describe("Symbol id from get_symbols")
|
|
1356
1356
|
}
|
|
1357
1357
|
}, async (args) => {
|
|
1358
1358
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1385,8 +1385,8 @@ function registerTools(server, pool) {
|
|
|
1385
1385
|
description: "Get the LIVE bid/ask for a symbol on a connected cTrader account via a brief spot subscription (ticks merge until both sides are seen, up to waitMs). Returns bid, ask, and spread as real decimal prices. Pass `accountId` when several accounts are connected.",
|
|
1386
1386
|
inputSchema: {
|
|
1387
1387
|
accountId: accountIdField,
|
|
1388
|
-
symbolId: z.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1389
|
-
waitMs: z.number().int().min(200).max(1e4).default(3e3).describe("Max milliseconds to wait for both bid and ask ticks (returns early once both are seen)")
|
|
1388
|
+
symbolId: z.coerce.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1389
|
+
waitMs: z.coerce.number().int().min(200).max(1e4).default(3e3).describe("Max milliseconds to wait for both bid and ask ticks (returns early once both are seen)")
|
|
1390
1390
|
}
|
|
1391
1391
|
}, async (args) => {
|
|
1392
1392
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1428,9 +1428,9 @@ function registerTools(server, pool) {
|
|
|
1428
1428
|
description: "Get the LIVE Level 2 order book (depth of market) for a symbol on a connected cTrader account. Subscribes briefly, assembles the bid/ask ladder (sizes aggregated per price level), then unsubscribes. Returns bids (descending) and asks (ascending) with sizes in base-asset units and lots, plus best bid/ask and spread. Not every broker/symbol publishes depth — a NO_DEPTH_DATA error means none arrived. Pass `accountId` when several accounts are connected.",
|
|
1429
1429
|
inputSchema: {
|
|
1430
1430
|
accountId: accountIdField,
|
|
1431
|
-
symbolId: z.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1432
|
-
levels: z.number().int().min(1).max(50).default(10).describe("Max price levels per side to return"),
|
|
1433
|
-
collectMs: z.number().int().min(200).max(5e3).default(1e3).describe("How long to collect depth events before snapshotting (the full book arrives on subscribe; longer windows fold in more updates)")
|
|
1431
|
+
symbolId: z.coerce.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1432
|
+
levels: z.coerce.number().int().min(1).max(50).default(10).describe("Max price levels per side to return"),
|
|
1433
|
+
collectMs: z.coerce.number().int().min(200).max(5e3).default(1e3).describe("How long to collect depth events before snapshotting (the full book arrives on subscribe; longer windows fold in more updates)")
|
|
1434
1434
|
}
|
|
1435
1435
|
}, async (args) => {
|
|
1436
1436
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1503,7 +1503,7 @@ function registerTools(server, pool) {
|
|
|
1503
1503
|
description: "Get historical tick-by-tick prices (BID or ASK side) for a symbol on a connected cTrader account. Defaults to the last 5 minutes — keep ranges short, tick volumes are large; `hasMore` signals truncation. Ticks are returned newest-first with real decimal prices.",
|
|
1504
1504
|
inputSchema: {
|
|
1505
1505
|
accountId: accountIdField,
|
|
1506
|
-
symbolId: z.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1506
|
+
symbolId: z.coerce.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1507
1507
|
type: z.enum(["BID", "ASK"]).default("BID").describe("Which side's ticks to fetch"),
|
|
1508
1508
|
from: z.string().optional().describe("ISO 8601 range start (default: 5 minutes before `to`)"),
|
|
1509
1509
|
to: z.string().optional().describe("ISO 8601 range end (default: now)")
|
|
@@ -1542,7 +1542,7 @@ function registerTools(server, pool) {
|
|
|
1542
1542
|
accountId: accountIdField,
|
|
1543
1543
|
from: z.string().optional().describe("ISO 8601 range start (default: 7 days before `to`)"),
|
|
1544
1544
|
to: z.string().optional().describe("ISO 8601 range end (default: now)"),
|
|
1545
|
-
maxRows: z.number().int().min(1).max(1e3).default(100).describe("Max deals to return")
|
|
1545
|
+
maxRows: z.coerce.number().int().min(1).max(1e3).default(100).describe("Max deals to return")
|
|
1546
1546
|
}
|
|
1547
1547
|
}, async (args) => {
|
|
1548
1548
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1620,8 +1620,8 @@ function registerTools(server, pool) {
|
|
|
1620
1620
|
description: "Get the margin that would be required to open BUY/SELL positions of given sizes (in lots) on a symbol, in the account's deposit currency. Useful before place_order to check affordability. Pass `accountId` when several accounts are connected.",
|
|
1621
1621
|
inputSchema: {
|
|
1622
1622
|
accountId: accountIdField,
|
|
1623
|
-
symbolId: z.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1624
|
-
volumesLots: z.array(z.number().positive()).min(1).max(10).describe("Position sizes in lots to quote margin for, e.g. [0.1, 0.5, 1]")
|
|
1623
|
+
symbolId: z.coerce.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1624
|
+
volumesLots: z.array(z.coerce.number().positive()).min(1).max(10).describe("Position sizes in lots to quote margin for, e.g. [0.1, 0.5, 1]")
|
|
1625
1625
|
}
|
|
1626
1626
|
}, async (args) => {
|
|
1627
1627
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1692,7 +1692,7 @@ function registerTools(server, pool) {
|
|
|
1692
1692
|
description: "Place a MARKET, LIMIT, STOP, or STOP_LIMIT order on a connected cTrader account. Volume is in LOTS and is converted to the symbol's protocol volume (validated against min/max/step). LIMIT requires limitPrice; STOP requires stopPrice; STOP_LIMIT requires stopPrice + slippageInPoints. SL/TP: pass ABSOLUTE prices via stopLoss/takeProfit, or price DISTANCES via stopLossDistance/takeProfitDistance (required for a trailing stop). timeInForce defaults to GOOD_TILL_CANCEL; GOOD_TILL_DATE requires expiresAt. Pass `accountId` when several accounts are connected. WARNING: on a live account this moves real money.",
|
|
1693
1693
|
inputSchema: {
|
|
1694
1694
|
accountId: accountIdField,
|
|
1695
|
-
symbolId: z.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1695
|
+
symbolId: z.coerce.number().int().positive().describe("Symbol id from get_symbols"),
|
|
1696
1696
|
side: z.enum(["BUY", "SELL"]).describe("Trade side"),
|
|
1697
1697
|
orderType: z.enum([
|
|
1698
1698
|
"MARKET",
|
|
@@ -1700,14 +1700,14 @@ function registerTools(server, pool) {
|
|
|
1700
1700
|
"STOP",
|
|
1701
1701
|
"STOP_LIMIT"
|
|
1702
1702
|
]).default("MARKET").describe("MARKET fills now; LIMIT rests at limitPrice; STOP triggers at stopPrice; STOP_LIMIT triggers at stopPrice with a max slippage of slippageInPoints"),
|
|
1703
|
-
volumeLots: z.number().positive().describe("Order size in lots (e.g. 0.1). Converted to protocol volume via the symbol lotSize."),
|
|
1704
|
-
limitPrice: z.number().positive().optional().describe("Required for LIMIT orders — the price to rest the order at"),
|
|
1705
|
-
stopPrice: z.number().positive().optional().describe("Required for STOP and STOP_LIMIT orders — the trigger price"),
|
|
1706
|
-
slippageInPoints: z.number().int().min(0).optional().describe("Required for STOP_LIMIT — max slippage from stopPrice, in points"),
|
|
1707
|
-
stopLoss: z.number().positive().optional().describe("Absolute stop-loss price (mutually exclusive with stopLossDistance)"),
|
|
1708
|
-
takeProfit: z.number().positive().optional().describe("Absolute take-profit price (mutually exclusive with takeProfitDistance)"),
|
|
1709
|
-
stopLossDistance: z.number().positive().optional().describe("Stop-loss as a price DISTANCE from entry (e.g. 0.0010 = 10 pips on a 5-digit FX pair). Required when trailingStopLoss is true."),
|
|
1710
|
-
takeProfitDistance: z.number().positive().optional().describe("Take-profit as a price DISTANCE from entry"),
|
|
1703
|
+
volumeLots: z.coerce.number().positive().describe("Order size in lots (e.g. 0.1). Converted to protocol volume via the symbol lotSize."),
|
|
1704
|
+
limitPrice: z.coerce.number().positive().optional().describe("Required for LIMIT orders — the price to rest the order at"),
|
|
1705
|
+
stopPrice: z.coerce.number().positive().optional().describe("Required for STOP and STOP_LIMIT orders — the trigger price"),
|
|
1706
|
+
slippageInPoints: z.coerce.number().int().min(0).optional().describe("Required for STOP_LIMIT — max slippage from stopPrice, in points"),
|
|
1707
|
+
stopLoss: z.coerce.number().positive().optional().describe("Absolute stop-loss price (mutually exclusive with stopLossDistance)"),
|
|
1708
|
+
takeProfit: z.coerce.number().positive().optional().describe("Absolute take-profit price (mutually exclusive with takeProfitDistance)"),
|
|
1709
|
+
stopLossDistance: z.coerce.number().positive().optional().describe("Stop-loss as a price DISTANCE from entry (e.g. 0.0010 = 10 pips on a 5-digit FX pair). Required when trailingStopLoss is true."),
|
|
1710
|
+
takeProfitDistance: z.coerce.number().positive().optional().describe("Take-profit as a price DISTANCE from entry"),
|
|
1711
1711
|
trailingStopLoss: z.boolean().optional().describe("Trail the stop-loss as price moves in your favour. Requires stopLossDistance (not an absolute stopLoss)."),
|
|
1712
1712
|
timeInForce: z.enum([
|
|
1713
1713
|
"GOOD_TILL_CANCEL",
|
|
@@ -1777,12 +1777,12 @@ function registerTools(server, pool) {
|
|
|
1777
1777
|
description: "Modify an existing order on a connected cTrader account. For a PENDING order (by orderId) you can change limitPrice/stopPrice/SL/TP. For an OPEN position's protection, pass positionId to set stopLoss/takeProfit. Provide exactly one of orderId or positionId. Pass `accountId` when several accounts are connected.",
|
|
1778
1778
|
inputSchema: {
|
|
1779
1779
|
accountId: accountIdField,
|
|
1780
|
-
orderId: z.number().int().positive().optional().describe("Pending order id (from get_orders) to amend"),
|
|
1781
|
-
positionId: z.number().int().positive().optional().describe("Open position id (from get_positions) to set SL/TP on"),
|
|
1782
|
-
limitPrice: z.number().positive().optional().describe("New limit price (pending order only)"),
|
|
1783
|
-
stopPrice: z.number().positive().optional().describe("New stop price (pending order only)"),
|
|
1784
|
-
stopLoss: z.number().positive().optional().describe("New absolute stop-loss price"),
|
|
1785
|
-
takeProfit: z.number().positive().optional().describe("New absolute take-profit price")
|
|
1780
|
+
orderId: z.coerce.number().int().positive().optional().describe("Pending order id (from get_orders) to amend"),
|
|
1781
|
+
positionId: z.coerce.number().int().positive().optional().describe("Open position id (from get_positions) to set SL/TP on"),
|
|
1782
|
+
limitPrice: z.coerce.number().positive().optional().describe("New limit price (pending order only)"),
|
|
1783
|
+
stopPrice: z.coerce.number().positive().optional().describe("New stop price (pending order only)"),
|
|
1784
|
+
stopLoss: z.coerce.number().positive().optional().describe("New absolute stop-loss price"),
|
|
1785
|
+
takeProfit: z.coerce.number().positive().optional().describe("New absolute take-profit price")
|
|
1786
1786
|
}
|
|
1787
1787
|
}, async (args) => {
|
|
1788
1788
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1828,8 +1828,8 @@ function registerTools(server, pool) {
|
|
|
1828
1828
|
description: "Close an open position (fully or partially) on a connected cTrader account. Pass the positionId from get_positions. volumeLots defaults to the full position size when omitted. Pass `accountId` when several accounts are connected.",
|
|
1829
1829
|
inputSchema: {
|
|
1830
1830
|
accountId: accountIdField,
|
|
1831
|
-
positionId: z.number().int().positive().describe("Position id from get_positions"),
|
|
1832
|
-
volumeLots: z.number().positive().optional().describe("Lots to close; omit to close the whole position")
|
|
1831
|
+
positionId: z.coerce.number().int().positive().describe("Position id from get_positions"),
|
|
1832
|
+
volumeLots: z.coerce.number().positive().optional().describe("Lots to close; omit to close the whole position")
|
|
1833
1833
|
}
|
|
1834
1834
|
}, async (args) => {
|
|
1835
1835
|
const resolved = resolveAccount(pool, args.accountId);
|
|
@@ -1869,7 +1869,7 @@ function registerTools(server, pool) {
|
|
|
1869
1869
|
description: "Cancel a pending (not-yet-filled) order on a connected cTrader account. Pass the orderId from get_orders. Pass `accountId` when several accounts are connected.",
|
|
1870
1870
|
inputSchema: {
|
|
1871
1871
|
accountId: accountIdField,
|
|
1872
|
-
orderId: z.number().int().positive().describe("Pending order id from get_orders")
|
|
1872
|
+
orderId: z.coerce.number().int().positive().describe("Pending order id from get_orders")
|
|
1873
1873
|
}
|
|
1874
1874
|
}, async (args) => {
|
|
1875
1875
|
const resolved = resolveAccount(pool, args.accountId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/ctrader-mcp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"description": "cTrader MCP server — full trading (market/limit/stop/stop-limit orders, trailing stops), Level 2 depth, live quotes, trade history, PnL & margin over the cTrader Open API (protobuf/TLS)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/server.js",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
22
22
|
"protobufjs": "^8.7.0",
|
|
23
23
|
"zod": "^4.0.5",
|
|
24
|
-
"@alfe.ai/agent-api-client": "0.
|
|
25
|
-
"@alfe.ai/config": "0.
|
|
24
|
+
"@alfe.ai/agent-api-client": "0.14.0",
|
|
25
|
+
"@alfe.ai/config": "0.4.0"
|
|
26
26
|
},
|
|
27
27
|
"license": "UNLICENSED",
|
|
28
28
|
"homepage": "https://alfe.ai",
|