@aetherwealth/mcp 0.2.16 → 0.2.17

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.
Files changed (2) hide show
  1. package/dist/index.js +2 -2
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -107,7 +107,7 @@ data:
107
107
  Skipped: ${W.join("; ")}`:"",Y=`Rendered ${$.pair} ${$.timeframe} — ${X.length} candles, drew ${J.drawn} shape${J.drawn===1?"":"s"}. Price range ${J.priceRange.min}–${J.priceRange.max}.${z}`;return{content:[{type:"image",data:Buffer.from(J.png).toString("base64"),mimeType:"image/png"},{type:"text",text:Y}]}}})];async function yX($){try{return{ok:!0,value:await $}}catch(v){return{ok:!1,error:v instanceof Error?v.message:String(v)}}}function $C($){return/unauthor|forbidden|401|403|sign[ -]?in|token|credential/i.test($)}var vC="The Aether Wealth backend is unreachable right now. Try again shortly.",UC=_5.flatMap(($)=>$.dashboardOnly.map((v)=>`${$.name}: ${v}`)),nX=[E({name:"get_context",description:"Snapshot the user's live Aether Wealth state in one call — accounts, open trades, recent performance stats, and active alerts — plus the map of dashboard-only actions. Call this first to ground suggestions in what the user is actually doing. Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await Promise.all([yX(v.trpc.trading.accounts.list.query()),yX(v.trpc.trading.trades.list.query({status:"OPEN",limit:200})),yX(v.trpc.trading.analytics.stats.query({})),yX(v.trpc.alerts.list.query({}))]);if(D.every((W)=>!W.ok)){let W=D.every((z)=>!z.ok&&$C(z.error));throw Error(W?l5:vC)}let[X,U,G,J]=D,Q=(W)=>W.ok?W.value:{error:W.error};return{accounts:Q(X),openTrades:Q(U),stats:Q(G),alerts:Q(J),dashboard:{url:f4,dashboardOnly:UC,note:`These actions are not available over MCP. When the user needs one, suggest they open the dashboard at ${f4}.`}}}})];function DC(){return{url:process.env.AETHER_APP_PUSH_URL?.trim()||"http://127.0.0.1:9003",secret:process.env.AI_LIVE_PUSH_SECRET?.trim()||void 0}}async function u6($,v,D){let{url:X,secret:U}=DC(),G=`${X.replace(/\/$/,"")}/api/ai/live/push`,J;try{J=await fetch(G,{method:"POST",headers:{"content-type":"application/json",...U?{"x-live-push-secret":U}:{}},body:JSON.stringify({userId:$,tool:v,args:D})})}catch(z){throw Error(`Could not reach the Aether Wealth app at ${G}. Is the dashboard running?`,{cause:z})}if(!J.ok)throw Error(`Live-chart push rejected (HTTP ${J.status}).`);let Q=await J.json().catch(()=>({})),W=typeof Q.delivered==="number"?Q.delivered:0;return{ok:!0,tool:v,delivered:W,message:W>0?`Dispatched to ${W} open chart tab(s).`:"Dispatched, but no open Aether Wealth chart tab is listening — open the dashboard in your browser, then retry."}}function Y7($){let v=JC($);if(!v)return{content:[{type:"text",text:["The backend did not return the required idempotent submission envelope.","Do not re-propose this action. Check trade_intent_status and retry the same keyed submission only if no intent exists."].join(`
108
108
  `)},{type:"text",text:JSON.stringify($,null,2)}],isError:!0};let D=v.proposal,X=XC(v.status,D?.approvalMethod??v.approvalMethod);return{content:[{type:"text",text:[`Broker submission status: ${v.status}. Intent: ${v.intentId}.`,X,"Use trade_intent_status to check whether it was approved, executed, expired, cancelled, or failed."].join(`
109
109
  `)},{type:"text",text:JSON.stringify($,null,2)}],isError:v.status==="execution_pending"||v.status==="execution_unknown"||v.status==="failed"}}function I7($){let v=GC($),D=wH(v?.approvalMethod);return{content:[{type:"text",text:[`Pending broker intent created with approval method: ${v?.approvalMethod??"unknown"}.`,D,"Use trade_intent_status to check whether it was approved, executed, expired, cancelled, or failed."].join(`
110
- `)},{type:"text",text:JSON.stringify($,null,2)}]}}function wH($){if($==="email_otp")return"Ask the user for the emailed confirmation code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="app_otp")return"Ask the user for the current authenticator app code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="push")return"Do NOT ask the user for a code. This action executes only when the user taps Approve in the Aether Wealth app.";if($==="auto")return"No code is required. Call execute_trade with this intentId to execute the action.";return"Inspect the returned proposal before taking the next step."}function XC($,v){if($==="executed")return"The action is already executed. Do not call execute_trade.";if($==="execution_unknown")return"Execution outcome is unknown. Inspect trade_intent_status and do not re-propose this action.";if($==="execution_pending"){if(v==="auto")return"Automatic execution remains pending. Call execute_trade with this intentId for explicit recovery; do not create a replacement proposal.";return"Execution remains pending and may be retried when the executor is available. Inspect trade_intent_status first."}if($==="pending_execution")return"This automatic connection uses the normal two-step flow. Call execute_trade with this intentId; do not create a replacement proposal.";if($==="failed")return"The action failed. Inspect trade_intent_status before proposing a new action.";return wH(v)}function GC($){if(typeof $!=="object"||$===null)return null;let v=$.proposal;if(typeof v!=="object"||v===null)return null;return v}function JC($){if(typeof $!=="object"||$===null)return null;let v=$.submission;if(typeof v!=="object"||v===null)return null;let{status:D,intentId:X,proposal:U,intent:G}=v;if(typeof D!=="string"||typeof X!=="string")return null;let J=typeof G?.approvalMethod==="string"?G.approvalMethod:void 0;return{status:D,intentId:X,...J===void 0?{}:{approvalMethod:J},...U===void 0?{}:{proposal:U}}}var lX=[E({name:"get_broker_account",description:"Get the LIVE broker account balance, equity, currency, and connection status for a connected trading account (distinct from list_accounts which lists journal accounts). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),pZ(v.trpc,$)}}),E({name:"list_broker_positions",description:"List open broker positions with P&L. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),tZ(v.trpc,$)}}),E({name:"get_broker_quote",description:"Get the current broker-native bid and ask for a symbol through the connected execution account. For an MT5 executor connection, this reads the live tick from its VPS and rejects stale ticks. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string",minLength:1,maxLength:10}},required:["accountId","symbol"],additionalProperties:!1},handler:async($,v)=>{return await T(v),oZ(v.trpc,$)}}),E({name:"connect_broker",description:"Attach a live execution provider to a journal account. For MetaApi, provide its account id and region. For `mt5_executor`, provide the expected MT5 login as `providerAccountRef` and its `baseUrl` under `*.aetherwealth.ai`. `providerToken` is sensitive — never expose or log it.",inputSchema:{type:"object",properties:{accountId:{type:"string"},provider:{type:"string",enum:["metaapi","mt5_executor"],description:"Defaults to 'metaapi' when omitted."},region:{type:"string",enum:["new-york","london"]},baseUrl:{type:"string",description:"Required for `mt5_executor`; the executor's HTTPS origin under `*.aetherwealth.ai`."},providerAccountRef:{type:"string",description:"The MetaApi account id, or the expected numeric MT5 login for `mt5_executor`."},providerToken:{type:"string",description:"The provider token. For `mt5_executor`, use its service bearer token. Sensitive — never expose or log it."},approvalMode:{type:"string",enum:["app_otp","email_otp"],description:"Confirmation mode. 'auto' is not selectable here; 'push' is also set later via set_broker_approval_mode because it requires an active app push subscription."}},required:["accountId","providerAccountRef","providerToken"],additionalProperties:!1},handler:async($,v)=>{return await T(v),mZ(v.trpc,$)}}),E({name:"broker_connection_status",description:"Get the current live broker connection status. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),cZ(v.trpc,$)}}),E({name:"disconnect_broker",description:"Disarm broker trading for a connected account instantly. Cancels pending intents, keeps executed history, and does not require confirmation because it is the kill switch. Reconnect later with connect_broker. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),dZ(v.trpc,$)}}),E({name:"set_broker_approval_mode",description:"Change how broker trades are confirmed for a connection between `app_otp` (authenticator), `email_otp`, and `push` (tap-to-approve in the Aether Wealth app; requires an active push subscription). The agent cannot enable `auto` here.",inputSchema:{type:"object",properties:{accountId:{type:"string"},approvalMode:{type:"string",enum:["app_otp","email_otp","push"]}},required:["accountId","approvalMode"],additionalProperties:!1},handler:async($,v)=>{return await T(v),rZ(v.trpc,$)}}),E({name:"propose_trade",description:"Size and propose a broker trade. Supplying idempotencyKey uses the auto-only keyed path; MT5 may place LIVE broker state inline, while MetaApi returns pending execution. Without a key this remains the normal two-step intent flow. Provide exactly one of riskPercent or riskAmount; entryPrice is required for limit/stop. Push-mode intents are sent to the user's device and must not be executed by the agent. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string"},direction:{type:"string",enum:["BUY","SELL"]},orderType:{type:"string",enum:["market","limit","stop"]},entryPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},riskPercent:{type:"number"},riskAmount:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","symbol","direction","stopLoss"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await eZ(v.trpc,$)):I7(await aZ(v.trpc,$))}}),E({name:"execute_trade",description:"Place a previously proposed trade. Email/app modes require the user-provided code; auto mode requires no per-trade code. Push intents cannot be executed by the agent. NEVER guess or fabricate a code.",inputSchema:{type:"object",properties:{intentId:{type:"string"},code:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),sZ(v.trpc,$)}}),E({name:"trade_intent_status",description:"Check whether a proposed trade was approved, executed, expired, cancelled, or failed. Especially useful for push-mode intents, which the user approves on their device.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),vO(v.trpc,$)}}),E({name:"cancel_trade_intent",description:"Cancel a previously proposed broker trade. Requires sign-in.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),$O(v.trpc,$)}}),E({name:"close_position",description:"Propose closing an open broker position fully, or partially by passing `volume`. With idempotencyKey, use the auto-only keyed path; MT5 may close LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. For email_otp/app_otp ask the user for the code and call execute_trade. For push, do not ask for a code — the user taps Approve in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},volume:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await DO(v.trpc,$)):I7(await UO(v.trpc,$))}}),E({name:"modify_position",description:"Propose adjusting stop-loss / take-profit on an open position. Stop-loss changes that INCREASE risk are rejected (use the app). With idempotencyKey, use the auto-only keyed path; MT5 may modify LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. Pass null to clear a level. For email_otp/app_otp ask for the code and call execute_trade, while push is approved in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},stopLoss:{type:["number","null"]},takeProfit:{type:["number","null"]},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await GO(v.trpc,$)):I7(await XO(v.trpc,$))}})],hX=[E({name:"draw_trade_on_chart",description:"Draw a proposed/open trade (entry, stop, target) on the user's OPEN Aether Wealth chart as a position shape. Switch to the intended pair/timeframe first with chart_select_pair.",inputSchema:{type:"object",properties:{direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},label:{type:"string"}},required:["direction","entry","stopLoss"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:[{type:"position",direction:$.direction,entry:$.entry,stop:$.stopLoss,...$.takeProfit===void 0?{}:{target:$.takeProfit},...$.label===void 0?{}:{label:$.label}}]})}})];var QC=["news","press_release"];function WC($){return{pair:$.pair,...$.limit===void 0?{}:{limit:$.limit}}}function zC($){return{currency:$.currency,...$.limit===void 0?{}:{limit:$.limit}}}function YC($){return{currency:$.currency,...$.indicator===void 0?{}:{indicator:$.indicator},...$.limit===void 0?{}:{limit:$.limit}}}function IC($){return{...$.limit===void 0?{}:{limit:$.limit}}}function VC($){return{currency:$.currency,...$.documentType===void 0?{}:{documentType:$.documentType},...$.limit===void 0?{}:{limit:$.limit}}}var mX={type:"integer",minimum:1,maximum:250,description:"Max rows to return (1-250, default 120)."},cX=[E({name:"get_fx_pair_context",description:"Macro context for a currency PAIR (e.g. EURUSD) — the per-currency macro state for both legs (recent releases, surprises, derived trend). Use for a pair-level macro read; for one currency use get_fx_currency_context. Distinct from get_macro_series (raw calendar history) and get_macro_indicator (one curated series).",inputSchema:{type:"object",properties:{pair:{type:"string",description:"6-letter currency pair, e.g. EURUSD (case-insensitive)"},limit:mX},required:["pair"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.pairContext.query(WC($))}}),E({name:"get_fx_currency_context",description:"Macro context for a single CURRENCY (e.g. USD) — recent economic releases, surprises, and the derived macro state for that currency.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.currencyContext.query(zC($))}}),E({name:"get_fx_predictions",description:"Model forecasts / predictions for a currency's upcoming macro releases. Optionally filter to one indicator by release slug (e.g. cpi).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},indicator:{type:"string",description:"Optional release slug filter, e.g. cpi"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.predictions.query(YC($))}}),E({name:"get_fx_risk_sentiment",description:"Cross-market risk-on / risk-off sentiment series (not currency-scoped). Use to gauge broad market risk appetite.",inputSchema:{type:"object",properties:{limit:mX},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.riskSentiment.query(IC($))}}),E({name:"get_fx_documents",description:"Source documents behind the FX macro data for a currency — news items and press releases. Filter by documentType.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},documentType:{type:"string",enum:[...QC],description:"Optional filter: news or press_release"},limit:{type:"integer",minimum:1,maximum:100,description:"Max rows to return (1-100, default 50)."}},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.documents.query(VC($))}}),E({name:"get_fx_sync_state",description:"Ingestion/sync status of the FX macro datasets (per dataset family: status, freshness, last success, next refresh). Diagnostic — check data freshness before relying on the other get_fx_* tools.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.syncState.query()}})];var KC="momentum: rsi, macd, stoch, stochf, stochrsi, cci, cmo, mom, roc, rocr, willr, mfi, ultosc, ppo, apo, bop, aroon, aroonosc; trend: adx, adxr, dx, plus_di, minus_di, plus_dm, minus_dm, sar, supertrend; moving averages: sma, ema, wma, dema, tema, trima, kama, t3ma, mama, vwap, midpoint, midprice; bands: bbands, keltner, percent_b; volatility: atr, natr, trange; volume: obv, ad, adosc; stats: linearreg, linearregslope, linearregangle, linearregintercept, stddev, var, tsf",dX=[E({name:"get_indicators",description:"Compute Twelve Data technical indicators for a pair + timeframe (requires sign-in). "+`Pass up to 8 indicators by name; params are optional and default sensibly. Names — ${KC}. Bad names/params return an error listing valid options. At most 2 distinct pairs per user at a time.`,inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical id, e.g. EURUSD, XAUUSD, BTCUSD (case-insensitive)"},interval:{type:"string",description:"One of 1m, 5m, 15m, 30m, 45m, 1h, 2h, 4h, 1d"},indicators:{type:"array",minItems:1,maxItems:8,description:"Up to 8 indicators to compute on the pair",items:{type:"object",properties:{name:{type:"string",description:"Indicator name, e.g. rsi, macd, ema, bbands, atr, adx"},params:{type:"object",description:'Optional params, e.g. {"time_period": 14} or MACD {"fast_period":12,"slow_period":26}. Defaults applied when omitted.'}},required:["name"],additionalProperties:!1}},outputsize:{type:"integer",minimum:1,maximum:500,description:"Bars to return per indicator (default 30)"}},required:["pair","interval","indicators"],additionalProperties:!1},handler:async($,v)=>{return await T(v),JO(v.trpc,$)}})];var LC=["horizontal_line","horizontal_ray","vertical_line","cross_line","zone","rectangle","trendline","ray","right_ray","left_ray","extended_line","info_line","trend_angle","position"],rX=[E({name:"chart_select_pair",description:"Switch the user's open Aether Wealth chart to a trading pair. Use the canonical id (case-insensitive), e.g. XAUUSD for gold, EURUSD, BTCUSD. Drives the live browser chart, not backend state.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical pair id, e.g. XAUUSD (gold), EURUSD, BTCUSD"}},required:["pair"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"select_pair",{pair:$.pair})}}),E({name:"chart_change_timeframe",description:"Change the timeframe/resolution of the user's open Aether Wealth chart, e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d.",inputSchema:{type:"object",properties:{timeframe:{type:"string",description:"e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d"}},required:["timeframe"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"change_timeframe",{timeframe:$.timeframe})}}),E({name:"chart_draw_shapes",description:"Draw one or more shapes on the user's OPEN Aether Wealth chart. For key levels use multiple `horizontal_line` shapes, each with `price` and a short `label`. For a fair-value-gap / supply-demand zone use a `zone` with `priceHigh` + `priceLow`. For a trade idea use a `position` with `direction` + `entry`/`stop`/`target`. Prices are in the instrument's price units; `time1`/`time2`/`entryTime` are optional epoch seconds. Switch to the intended pair + timeframe first.",inputSchema:{type:"object",properties:{shapes:{type:"array",description:"Shapes to draw in one batch.",items:{type:"object",properties:{type:{type:"string",enum:[...LC]},label:{type:"string"},color:{type:"string",description:"hex, e.g. #22c55e"},price:{type:"number"},priceHigh:{type:"number"},priceLow:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"number",description:"epoch seconds"},time2:{type:"number",description:"epoch seconds"},direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stop:{type:"number"},target:{type:"number"},entryTime:{type:"number",description:"epoch seconds"}},required:["type"],additionalProperties:!1}}},required:["shapes"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:$.shapes})}}),E({name:"chart_remove_drawings",description:"Remove AI-drawn shapes from the user's open Aether Wealth chart. `target` defaults to 'all' (clear everything the assistant drew); also accepts 'last', 'focused', or a specific shape id.",inputSchema:{type:"object",properties:{target:{type:"string",description:"'all' (default), 'last', 'focused', or a shape id"}},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"remove_chart_drawings",{target:$.target??"all"})}})];var pX=[E({name:"get_candles",description:"Get OHLC candles for a pair + timeframe and optional sourceId. Omit sourceId for Twelve Data. Non-default sources return only native timeframes advertised by get_market_config; they are never re-aggregated. Returns up to 200 bars plus nextCursor. Pass nextCursor back as cursor until it is null. The legacy offset remains available for shallow pagination; do not combine offset and cursor.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD (canonical id; case-insensitive)"},timeframe:{type:"string",description:"A base (1m, 5m, 15m, 30m, 1h, 4h, 1d) or an integer multiple of one (e.g. 2h, 3h, 12h, 90m). No fractional (2.5h) or sub-minute values."},sourceId:{type:"string",pattern:"^[a-z][a-z0-9-]{0,62}$",description:"Market-data source from get_market_config, e.g. mt5-primary. Omit for Twelve Data."},limit:{type:"integer",minimum:1,maximum:200,description:"Maximum candles to return (1-200). Omit for the newest 200."},offset:{type:"integer",minimum:0,maximum:4800,description:"Legacy shallow pagination: number of recent candles to skip (0–4800). Use cursor instead for complete history; do not combine offset and cursor."},cursor:{type:"string",minLength:1,maxLength:2048,description:"Opaque nextCursor returned by the previous get_candles page. Omit on the first page; continue until nextCursor is null. Do not combine with offset."}},required:["pair","timeframe"],additionalProperties:!1},handler:async($,v)=>{return await T(v),qX(v.trpc,$)}}),E({name:"list_economic_calendar",description:'List economic calendar events. Filter by currency, date range, release, or minimum impact (1-5). Currency and indicator/release are validated against the catalog: unknown values return an error listing valid options, so an empty result always means "no events" rather than "bad input".',inputSchema:{type:"object",properties:{currency:{type:"array",items:{type:"string"},description:'ISO currency codes, e.g. ["USD","EUR"]'},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},indicator:{type:"string",description:"release slug, e.g. cpi"},impactMin:{type:"integer",minimum:1,maximum:5},view:{type:"string",enum:["minimal","rich"]}},additionalProperties:!1},handler:async($,v)=>{return await T(v),WO(v.trpc,$)}}),E({name:"get_macro_series",description:"Get a macro indicator time series (e.g. USD cpi). Currency and indicator are validated against the catalog: unknown values return an error listing valid options (case-insensitive).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"release slug, e.g. cpi"},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},limit:{type:"integer",minimum:1,maximum:1000}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),zO(v.trpc,$)}}),E({name:"get_macro_indicator",description:"Get a curated macro indicator series from FXMacroData (live + cached) with unit/frequency metadata and the central-bank target where available — use get_macro_series for raw historical calendar values. Range defaults to 12m; returns a null result when the macro data provider is not configured.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"indicator slug in lowercase snake_case, e.g. cpi"},range:{type:"string",enum:[...QO],description:"Lookback window (default 12m)"}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),YO(v.trpc,$)}}),E({name:"get_market_config",description:"Get supported instruments, timeframes, and per-source candle/live capabilities. Use marketDataSources before selecting a non-default sourceId.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),IO(v.trpc)}})];var MH={stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},riskPercent:{type:"number"},tags:{type:"array",items:{type:"string"}}},BC={pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},exitPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},exitTime:{type:"string",description:"ISO 8601 datetime"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},emotionalTag:{type:"string"},fees:{type:"number"},swap:{type:"number"},reportedPnl:{type:"number",description:"Broker-reported P&L to store instead of recalculated P&L"},tags:{type:"array",items:{type:"string"}}},tX=[E({name:"list_trades",description:"List the authenticated user's trades. Filter by account, status, pair, or date range (ISO 8601). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},from:{type:"string",description:"ISO 8601 datetime (inclusive lower bound)"},to:{type:"string",description:"ISO 8601 datetime (inclusive upper bound)"},page:wO,limit:AO},additionalProperties:!1},handler:async($,v)=>{return await T(v),LO(v.trpc,$)}}),E({name:"get_trade",description:"Get a single trade by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),BO(v.trpc,$)}}),E({name:"list_accounts",description:"List the user's trading accounts (use an account's id when creating a trade). Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),kZ(v.trpc)}}),E({name:"create_account",description:'Create a trading account in the journal. Use accountType "demo" for paper/demo accounts and "live" for live accounts. Requires sign-in.',inputSchema:{type:"object",properties:{name:{type:"string"},broker:{type:"string"},accountType:{type:"string",description:"default: personal"},initialBalance:{type:"number",description:"default: 0"},currency:{type:"string",description:"3-letter code, default: USD"},notes:{type:"string"},defaultRiskAmount:{type:"number"}},required:["name"],additionalProperties:!1},handler:async($,v)=>{return await T(v),EZ(v.trpc,$)}}),E({name:"trade_stats",description:"Trading performance statistics (win rate, PnL, etc.). Filter by account, pair, or date range. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},pair:{type:"string"},from:{type:"string",description:"ISO 8601 datetime"},to:{type:"string",description:"ISO 8601 datetime"}},additionalProperties:!1},handler:async($,v)=>{return await T(v),VO(v.trpc,$)}}),E({name:"create_trade",description:"Record a new trade. Requires sign-in. Use list_accounts to find the accountId.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id (see list_accounts)"},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["accountId","pair","direction","entryPrice","entryTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),NO(v.trpc,$)}}),E({name:"update_trade",description:"Update fields on an existing trade. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},pair:{type:"string"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),ZO(v.trpc,$)}}),E({name:"close_trade",description:"Close an open trade with an exit price + time. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},exitPrice:{type:"number"},exitTime:{type:"string",description:"ISO 8601 datetime"},fees:{type:"number"},swap:{type:"number"},notes:{type:"string"}},required:["id","exitPrice","exitTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),OO(v.trpc,$)}}),E({name:"delete_trade",description:"Permanently delete a single trade by id. Only trades on demo accounts can be deleted. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),HO(v.trpc,$)}}),E({name:"delete_trades",description:"Permanently delete trades on a specific demo account. Requires explicit status (OPEN, CLOSED, CANCELLED, or ALL). This cannot be undone. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to delete from"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]}},required:["accountId","status"],additionalProperties:!1},handler:async($,v)=>{return await T(v),FO(v.trpc,$)}}),E({name:"import_trades",description:"Import up to 500 trades into one account. Rows with both exitPrice and exitTime are imported as closed trades. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to import into"},trades:{type:"array",minItems:1,maxItems:500,items:{type:"object",properties:BC,required:["pair","direction","entryPrice","lotSize","entryTime"],additionalProperties:!1}}},required:["accountId","trades"],additionalProperties:!1},handler:async($,v)=>{return await T(v),jO(v.trpc,$)}})];function V7($={}){let v=$.includeAuthTools??!0,D=$.includeLiveChartTools??!1,X=$.includeRenderChartTools??!Y4;return[...v?EX:[],...nX,...tX,...lX,...kX,...pX,...cX,...dX,...X?iX:[],...D?[...rX,...hX]:[]]}var NC="aether-wealth-mcp",ZC="0.2.16";function PH($,v){let D=new A0({name:NC,version:ZC},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return OC(D,$,v??$.toolContext),D}function OC($,v,D){let X=!1,U=(J)=>{let Q=v.clientGate?.nudge;if(Q&&!X)return X=!0,`${Q}
110
+ `)},{type:"text",text:JSON.stringify($,null,2)}]}}function wH($){if($==="email_otp")return"Ask the user for the emailed confirmation code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="app_otp")return"Ask the user for the current authenticator app code, then call execute_trade with this intentId and code. Never guess or fabricate the code.";if($==="push")return"Do NOT ask the user for a code. This action executes only when the user taps Approve in the Aether Wealth app.";if($==="auto")return"No code is required. Call execute_trade with this intentId to execute the action.";return"Inspect the returned proposal before taking the next step."}function XC($,v){if($==="executed")return"The action is already executed. Do not call execute_trade.";if($==="execution_unknown")return"Execution outcome is unknown. Inspect trade_intent_status and do not re-propose this action.";if($==="execution_pending"){if(v==="auto")return"Automatic execution remains pending. Call execute_trade with this intentId for explicit recovery; do not create a replacement proposal.";return"Execution remains pending and may be retried when the executor is available. Inspect trade_intent_status first."}if($==="pending_execution")return"This automatic connection uses the normal two-step flow. Call execute_trade with this intentId; do not create a replacement proposal.";if($==="failed")return"The action failed. Inspect trade_intent_status before proposing a new action.";return wH(v)}function GC($){if(typeof $!=="object"||$===null)return null;let v=$.proposal;if(typeof v!=="object"||v===null)return null;return v}function JC($){if(typeof $!=="object"||$===null)return null;let v=$.submission;if(typeof v!=="object"||v===null)return null;let{status:D,intentId:X,proposal:U,intent:G}=v;if(typeof D!=="string"||typeof X!=="string")return null;let J=typeof G?.approvalMethod==="string"?G.approvalMethod:void 0;return{status:D,intentId:X,...J===void 0?{}:{approvalMethod:J},...U===void 0?{}:{proposal:U}}}var lX=[E({name:"get_broker_account",description:"Get the LIVE broker account balance, equity, currency, and connection status for a connected trading account (distinct from list_accounts which lists journal accounts). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),pZ(v.trpc,$)}}),E({name:"list_broker_positions",description:"List open broker positions with P&L. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),tZ(v.trpc,$)}}),E({name:"get_broker_quote",description:"Get the current broker-native bid and ask for a symbol through the connected execution account. For an MT5 executor connection, this reads the live tick from its VPS and rejects stale ticks. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string",minLength:1,maxLength:10}},required:["accountId","symbol"],additionalProperties:!1},handler:async($,v)=>{return await T(v),oZ(v.trpc,$)}}),E({name:"connect_broker",description:"Attach a live execution provider to a journal account. For MetaApi, provide its account id and region. For `mt5_executor`, provide the expected MT5 login as `providerAccountRef` and its `baseUrl` under `*.aetherwealth.ai`. `providerToken` is sensitive — never expose or log it.",inputSchema:{type:"object",properties:{accountId:{type:"string"},provider:{type:"string",enum:["metaapi","mt5_executor"],description:"Defaults to 'metaapi' when omitted."},region:{type:"string",enum:["new-york","london"]},baseUrl:{type:"string",description:"Required for `mt5_executor`; the executor's HTTPS origin under `*.aetherwealth.ai`."},providerAccountRef:{type:"string",description:"The MetaApi account id, or the expected numeric MT5 login for `mt5_executor`."},providerToken:{type:"string",description:"The provider token. For `mt5_executor`, use its service bearer token. Sensitive — never expose or log it."},approvalMode:{type:"string",enum:["app_otp","email_otp"],description:"Confirmation mode. 'auto' is not selectable here; 'push' is also set later via set_broker_approval_mode because it requires an active app push subscription."}},required:["accountId","providerAccountRef","providerToken"],additionalProperties:!1},handler:async($,v)=>{return await T(v),mZ(v.trpc,$)}}),E({name:"broker_connection_status",description:"Get the current live broker connection status. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),cZ(v.trpc,$)}}),E({name:"disconnect_broker",description:"Disarm broker trading for a connected account instantly. Cancels pending intents, keeps executed history, and does not require confirmation because it is the kill switch. Reconnect later with connect_broker. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"}},required:["accountId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),dZ(v.trpc,$)}}),E({name:"set_broker_approval_mode",description:"Change how broker trades are confirmed for a connection between `app_otp` (authenticator), `email_otp`, and `push` (tap-to-approve in the Aether Wealth app; requires an active push subscription). The agent cannot enable `auto` here.",inputSchema:{type:"object",properties:{accountId:{type:"string"},approvalMode:{type:"string",enum:["app_otp","email_otp","push"]}},required:["accountId","approvalMode"],additionalProperties:!1},handler:async($,v)=>{return await T(v),rZ(v.trpc,$)}}),E({name:"propose_trade",description:"Size and propose a broker trade. Supplying idempotencyKey uses the auto-only keyed path; MT5 may place LIVE broker state inline, while MetaApi returns pending execution. Without a key this remains the normal two-step intent flow. Provide exactly one of riskPercent or riskAmount; entryPrice is required for limit/stop. Push-mode intents are sent to the user's device and must not be executed by the agent. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},symbol:{type:"string"},direction:{type:"string",enum:["BUY","SELL"]},orderType:{type:"string",enum:["market","limit","stop"]},entryPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},riskPercent:{type:"number"},riskAmount:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","symbol","direction","stopLoss"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await eZ(v.trpc,$)):I7(await aZ(v.trpc,$))}}),E({name:"execute_trade",description:"Place a previously proposed trade. Email/app modes require the user-provided code; auto mode requires no per-trade code. Push intents cannot be executed by the agent. NEVER guess or fabricate a code.",inputSchema:{type:"object",properties:{intentId:{type:"string"},code:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),sZ(v.trpc,$)}}),E({name:"trade_intent_status",description:"Check whether a proposed trade was approved, executed, expired, cancelled, or failed. Especially useful for push-mode intents, which the user approves on their device.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),vO(v.trpc,$)}}),E({name:"cancel_trade_intent",description:"Cancel a previously proposed broker trade. Requires sign-in.",inputSchema:{type:"object",properties:{intentId:{type:"string"}},required:["intentId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),$O(v.trpc,$)}}),E({name:"close_position",description:"Propose closing an open broker position fully, or partially by passing `volume`. With idempotencyKey, use the auto-only keyed path; MT5 may close LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. For email_otp/app_otp ask the user for the code and call execute_trade. For push, do not ask for a code — the user taps Approve in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},volume:{type:"number"},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await DO(v.trpc,$)):I7(await UO(v.trpc,$))}}),E({name:"modify_position",description:"Propose adjusting stop-loss / take-profit on an open position. Stop-loss changes that INCREASE risk are rejected (use the app). With idempotencyKey, use the auto-only keyed path; MT5 may modify LIVE broker state inline, while MetaApi returns pending execution. Without a key this is the normal two-step intent flow. Pass null to clear a level. For email_otp/app_otp ask for the code and call execute_trade, while push is approved in the app.",inputSchema:{type:"object",properties:{accountId:{type:"string"},positionId:{type:"string"},stopLoss:{type:["number","null"]},takeProfit:{type:["number","null"]},idempotencyKey:{type:"string",minLength:8,maxLength:80,description:"Stable caller-owned key. Reuse the exact key and inputs only after a transport timeout."}},required:["accountId","positionId"],additionalProperties:!1},handler:async($,v)=>{return await T(v),"idempotencyKey"in $?Y7(await GO(v.trpc,$)):I7(await XO(v.trpc,$))}})],hX=[E({name:"draw_trade_on_chart",description:"Draw a proposed/open trade (entry, stop, target) on the user's OPEN Aether Wealth chart as a position shape. Switch to the intended pair/timeframe first with chart_select_pair.",inputSchema:{type:"object",properties:{direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},label:{type:"string"}},required:["direction","entry","stopLoss"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:[{type:"position",direction:$.direction,entry:$.entry,stop:$.stopLoss,...$.takeProfit===void 0?{}:{target:$.takeProfit},...$.label===void 0?{}:{label:$.label}}]})}})];var QC=["news","press_release"];function WC($){return{pair:$.pair,...$.limit===void 0?{}:{limit:$.limit}}}function zC($){return{currency:$.currency,...$.limit===void 0?{}:{limit:$.limit}}}function YC($){return{currency:$.currency,...$.indicator===void 0?{}:{indicator:$.indicator},...$.limit===void 0?{}:{limit:$.limit}}}function IC($){return{...$.limit===void 0?{}:{limit:$.limit}}}function VC($){return{currency:$.currency,...$.documentType===void 0?{}:{documentType:$.documentType},...$.limit===void 0?{}:{limit:$.limit}}}var mX={type:"integer",minimum:1,maximum:250,description:"Max rows to return (1-250, default 120)."},cX=[E({name:"get_fx_pair_context",description:"Macro context for a currency PAIR (e.g. EURUSD) — the per-currency macro state for both legs (recent releases, surprises, derived trend). Use for a pair-level macro read; for one currency use get_fx_currency_context. Distinct from get_macro_series (raw calendar history) and get_macro_indicator (one curated series).",inputSchema:{type:"object",properties:{pair:{type:"string",description:"6-letter currency pair, e.g. EURUSD (case-insensitive)"},limit:mX},required:["pair"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.pairContext.query(WC($))}}),E({name:"get_fx_currency_context",description:"Macro context for a single CURRENCY (e.g. USD) — recent economic releases, surprises, and the derived macro state for that currency.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.currencyContext.query(zC($))}}),E({name:"get_fx_predictions",description:"Model forecasts / predictions for a currency's upcoming macro releases. Optionally filter to one indicator by release slug (e.g. cpi).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},indicator:{type:"string",description:"Optional release slug filter, e.g. cpi"},limit:mX},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.predictions.query(YC($))}}),E({name:"get_fx_risk_sentiment",description:"Cross-market risk-on / risk-off sentiment series (not currency-scoped). Use to gauge broad market risk appetite.",inputSchema:{type:"object",properties:{limit:mX},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.riskSentiment.query(IC($))}}),E({name:"get_fx_documents",description:"Source documents behind the FX macro data for a currency — news items and press releases. Filter by documentType.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD (case-insensitive)"},documentType:{type:"string",enum:[...QC],description:"Optional filter: news or press_release"},limit:{type:"integer",minimum:1,maximum:100,description:"Max rows to return (1-100, default 50)."}},required:["currency"],additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.documents.query(VC($))}}),E({name:"get_fx_sync_state",description:"Ingestion/sync status of the FX macro datasets (per dataset family: status, freshness, last success, next refresh). Diagnostic — check data freshness before relying on the other get_fx_* tools.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),v.trpc.fxMacroData.syncState.query()}})];var KC="momentum: rsi, macd, stoch, stochf, stochrsi, cci, cmo, mom, roc, rocr, willr, mfi, ultosc, ppo, apo, bop, aroon, aroonosc; trend: adx, adxr, dx, plus_di, minus_di, plus_dm, minus_dm, sar, supertrend; moving averages: sma, ema, wma, dema, tema, trima, kama, t3ma, mama, vwap, midpoint, midprice; bands: bbands, keltner, percent_b; volatility: atr, natr, trange; volume: obv, ad, adosc; stats: linearreg, linearregslope, linearregangle, linearregintercept, stddev, var, tsf",dX=[E({name:"get_indicators",description:"Compute Twelve Data technical indicators for a pair + timeframe (requires sign-in). "+`Pass up to 8 indicators by name; params are optional and default sensibly. Names — ${KC}. Bad names/params return an error listing valid options. At most 2 distinct pairs per user at a time.`,inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical id, e.g. EURUSD, XAUUSD, BTCUSD (case-insensitive)"},interval:{type:"string",description:"One of 1m, 5m, 15m, 30m, 45m, 1h, 2h, 4h, 1d"},indicators:{type:"array",minItems:1,maxItems:8,description:"Up to 8 indicators to compute on the pair",items:{type:"object",properties:{name:{type:"string",description:"Indicator name, e.g. rsi, macd, ema, bbands, atr, adx"},params:{type:"object",description:'Optional params, e.g. {"time_period": 14} or MACD {"fast_period":12,"slow_period":26}. Defaults applied when omitted.'}},required:["name"],additionalProperties:!1}},outputsize:{type:"integer",minimum:1,maximum:500,description:"Bars to return per indicator (default 30)"}},required:["pair","interval","indicators"],additionalProperties:!1},handler:async($,v)=>{return await T(v),JO(v.trpc,$)}})];var LC=["horizontal_line","horizontal_ray","vertical_line","cross_line","zone","rectangle","trendline","ray","right_ray","left_ray","extended_line","info_line","trend_angle","position"],rX=[E({name:"chart_select_pair",description:"Switch the user's open Aether Wealth chart to a trading pair. Use the canonical id (case-insensitive), e.g. XAUUSD for gold, EURUSD, BTCUSD. Drives the live browser chart, not backend state.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"Canonical pair id, e.g. XAUUSD (gold), EURUSD, BTCUSD"}},required:["pair"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"select_pair",{pair:$.pair})}}),E({name:"chart_change_timeframe",description:"Change the timeframe/resolution of the user's open Aether Wealth chart, e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d.",inputSchema:{type:"object",properties:{timeframe:{type:"string",description:"e.g. 1m, 5m, 15m, 30m, 1h, 4h, 1d"}},required:["timeframe"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"change_timeframe",{timeframe:$.timeframe})}}),E({name:"chart_draw_shapes",description:"Draw one or more shapes on the user's OPEN Aether Wealth chart. For key levels use multiple `horizontal_line` shapes, each with `price` and a short `label`. For a fair-value-gap / supply-demand zone use a `zone` with `priceHigh` + `priceLow`. For a trade idea use a `position` with `direction` + `entry`/`stop`/`target`. Prices are in the instrument's price units; `time1`/`time2`/`entryTime` are optional epoch seconds. Switch to the intended pair + timeframe first.",inputSchema:{type:"object",properties:{shapes:{type:"array",description:"Shapes to draw in one batch.",items:{type:"object",properties:{type:{type:"string",enum:[...LC]},label:{type:"string"},color:{type:"string",description:"hex, e.g. #22c55e"},price:{type:"number"},priceHigh:{type:"number"},priceLow:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"number",description:"epoch seconds"},time2:{type:"number",description:"epoch seconds"},direction:{type:"string",enum:["long","short"]},entry:{type:"number"},stop:{type:"number"},target:{type:"number"},entryTime:{type:"number",description:"epoch seconds"}},required:["type"],additionalProperties:!1}}},required:["shapes"],additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"draw_chart_shape",{shapes:$.shapes})}}),E({name:"chart_remove_drawings",description:"Remove AI-drawn shapes from the user's open Aether Wealth chart. `target` defaults to 'all' (clear everything the assistant drew); also accepts 'last', 'focused', or a specific shape id.",inputSchema:{type:"object",properties:{target:{type:"string",description:"'all' (default), 'last', 'focused', or a shape id"}},additionalProperties:!1},handler:async($,v)=>{await T(v);let D=await v.session.getUserId();return u6(D,"remove_chart_drawings",{target:$.target??"all"})}})];var pX=[E({name:"get_candles",description:"Get OHLC candles for a pair + timeframe and optional sourceId. Omit sourceId for Twelve Data. Non-default sources return only native timeframes advertised by get_market_config; they are never re-aggregated. Returns up to 200 bars plus nextCursor. Pass nextCursor back as cursor until it is null. The legacy offset remains available for shallow pagination; do not combine offset and cursor.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD (canonical id; case-insensitive)"},timeframe:{type:"string",description:"A base (1m, 5m, 15m, 30m, 1h, 4h, 1d) or an integer multiple of one (e.g. 2h, 3h, 12h, 90m). No fractional (2.5h) or sub-minute values."},sourceId:{type:"string",pattern:"^[a-z][a-z0-9-]{0,62}$",description:"Market-data source from get_market_config, e.g. mt5-primary. Omit for Twelve Data."},limit:{type:"integer",minimum:1,maximum:200,description:"Maximum candles to return (1-200). Omit for the newest 200."},offset:{type:"integer",minimum:0,maximum:4800,description:"Legacy shallow pagination: number of recent candles to skip (0–4800). Use cursor instead for complete history; do not combine offset and cursor."},cursor:{type:"string",minLength:1,maxLength:2048,description:"Opaque nextCursor returned by the previous get_candles page. Omit on the first page; continue until nextCursor is null. Do not combine with offset."}},required:["pair","timeframe"],additionalProperties:!1},handler:async($,v)=>{return await T(v),qX(v.trpc,$)}}),E({name:"list_economic_calendar",description:'List economic calendar events. Filter by currency, date range, release, or minimum impact (1-5). Currency and indicator/release are validated against the catalog: unknown values return an error listing valid options, so an empty result always means "no events" rather than "bad input".',inputSchema:{type:"object",properties:{currency:{type:"array",items:{type:"string"},description:'ISO currency codes, e.g. ["USD","EUR"]'},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},indicator:{type:"string",description:"release slug, e.g. cpi"},impactMin:{type:"integer",minimum:1,maximum:5},view:{type:"string",enum:["minimal","rich"]}},additionalProperties:!1},handler:async($,v)=>{return await T(v),WO(v.trpc,$)}}),E({name:"get_macro_series",description:"Get a macro indicator time series (e.g. USD cpi). Currency and indicator are validated against the catalog: unknown values return an error listing valid options (case-insensitive).",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"release slug, e.g. cpi"},from:{type:"string",description:"ISO 8601 date or datetime"},to:{type:"string",description:"ISO 8601 date or datetime"},limit:{type:"integer",minimum:1,maximum:1000}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),zO(v.trpc,$)}}),E({name:"get_macro_indicator",description:"Get a curated macro indicator series from FXMacroData (live + cached) with unit/frequency metadata and the central-bank target where available — use get_macro_series for raw historical calendar values. Range defaults to 12m; returns a null result when the macro data provider is not configured.",inputSchema:{type:"object",properties:{currency:{type:"string",description:"ISO currency code, e.g. USD"},indicator:{type:"string",description:"indicator slug in lowercase snake_case, e.g. cpi"},range:{type:"string",enum:[...QO],description:"Lookback window (default 12m)"}},required:["currency","indicator"],additionalProperties:!1},handler:async($,v)=>{return await T(v),YO(v.trpc,$)}}),E({name:"get_market_config",description:"Get supported instruments, timeframes, and per-source candle/live capabilities. Use marketDataSources before selecting a non-default sourceId.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),IO(v.trpc)}})];var MH={stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},riskPercent:{type:"number"},tags:{type:"array",items:{type:"string"}}},BC={pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},exitPrice:{type:"number"},stopLoss:{type:"number"},takeProfit:{type:"number"},lotSize:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},exitTime:{type:"string",description:"ISO 8601 datetime"},notes:{type:"string"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},emotionalTag:{type:"string"},fees:{type:"number"},swap:{type:"number"},reportedPnl:{type:"number",description:"Broker-reported P&L to store instead of recalculated P&L"},tags:{type:"array",items:{type:"string"}}},tX=[E({name:"list_trades",description:"List the authenticated user's trades. Filter by account, status, pair, or date range (ISO 8601). Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},from:{type:"string",description:"ISO 8601 datetime (inclusive lower bound)"},to:{type:"string",description:"ISO 8601 datetime (inclusive upper bound)"},page:wO,limit:AO},additionalProperties:!1},handler:async($,v)=>{return await T(v),LO(v.trpc,$)}}),E({name:"get_trade",description:"Get a single trade by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),BO(v.trpc,$)}}),E({name:"list_accounts",description:"List the user's trading accounts (use an account's id when creating a trade). Requires sign-in.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async($,v)=>{return await T(v),kZ(v.trpc)}}),E({name:"create_account",description:'Create a trading account in the journal. Use accountType "demo" for paper/demo accounts and "live" for live accounts. Requires sign-in.',inputSchema:{type:"object",properties:{name:{type:"string"},broker:{type:"string"},accountType:{type:"string",description:"default: personal"},initialBalance:{type:"number",description:"default: 0"},currency:{type:"string",description:"3-letter code, default: USD"},notes:{type:"string"},defaultRiskAmount:{type:"number"}},required:["name"],additionalProperties:!1},handler:async($,v)=>{return await T(v),EZ(v.trpc,$)}}),E({name:"trade_stats",description:"Trading performance statistics (win rate, PnL, etc.). Filter by account, pair, or date range. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string"},pair:{type:"string"},from:{type:"string",description:"ISO 8601 datetime"},to:{type:"string",description:"ISO 8601 datetime"}},additionalProperties:!1},handler:async($,v)=>{return await T(v),VO(v.trpc,$)}}),E({name:"create_trade",description:"Record a new trade. Requires sign-in. Use list_accounts to find the accountId.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id (see list_accounts)"},pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["accountId","pair","direction","entryPrice","entryTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),NO(v.trpc,$)}}),E({name:"update_trade",description:"Update fields on an existing trade. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},pair:{type:"string"},direction:{type:"string",enum:["LONG","SHORT"]},entryPrice:{type:"number"},entryTime:{type:"string",description:"ISO 8601 datetime"},...MH},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),ZO(v.trpc,$)}}),E({name:"close_trade",description:"Close an open trade with an exit price + time. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},exitPrice:{type:"number"},exitTime:{type:"string",description:"ISO 8601 datetime"},fees:{type:"number"},swap:{type:"number"},notes:{type:"string"}},required:["id","exitPrice","exitTime"],additionalProperties:!1},handler:async($,v)=>{return await T(v),OO(v.trpc,$)}}),E({name:"delete_trade",description:"Permanently delete a single trade by id. Only trades on demo accounts can be deleted. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async($,v)=>{return await T(v),HO(v.trpc,$)}}),E({name:"delete_trades",description:"Permanently delete trades on a specific demo account. Requires explicit status (OPEN, CLOSED, CANCELLED, or ALL). This cannot be undone. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to delete from"},status:{type:"string",enum:["OPEN","CLOSED","CANCELLED","ALL"]}},required:["accountId","status"],additionalProperties:!1},handler:async($,v)=>{return await T(v),FO(v.trpc,$)}}),E({name:"import_trades",description:"Import up to 500 trades into one account. Rows with both exitPrice and exitTime are imported as closed trades. Requires sign-in.",inputSchema:{type:"object",properties:{accountId:{type:"string",description:"Trading account id to import into"},trades:{type:"array",minItems:1,maxItems:500,items:{type:"object",properties:BC,required:["pair","direction","entryPrice","lotSize","entryTime"],additionalProperties:!1}}},required:["accountId","trades"],additionalProperties:!1},handler:async($,v)=>{return await T(v),jO(v.trpc,$)}})];function V7($={}){let v=$.includeAuthTools??!0,D=$.includeLiveChartTools??!1,X=$.includeRenderChartTools??!Y4;return[...v?EX:[],...nX,...tX,...lX,...kX,...pX,...cX,...dX,...X?iX:[],...D?[...rX,...hX]:[]]}var NC="aether-wealth-mcp",ZC="0.2.17";function PH($,v){let D=new A0({name:NC,version:ZC},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return OC(D,$,v??$.toolContext),D}function OC($,v,D){let X=!1,U=(J)=>{let Q=v.clientGate?.nudge;if(Q&&!X)return X=!0,`${Q}
111
111
 
112
112
  ${J}`;return J},G=(J)=>{let Q=v.clientGate?.nudge;if(!Q||X)return J;X=!0;let W=J.find((z)=>z.type==="text");if(W)return J.map((z)=>z===W?{type:"text",text:`${Q}
113
113
 
@@ -116,7 +116,7 @@ ${W.text}`}:z);return[{type:"text",text:Q},...J]};$.setRequestHandler(tv,async()
116
116
  `),A1(500,{error:"internal error"})}finally{await G.close().catch(qH),await U.close().catch(qH)}}function PC($){let{servedPaths:v,bundle:D,log:X,oauth:U,bearerToken:G}=$,J=new Set([qz,...[...v].filter((W)=>W!==K7).map((W)=>`${qz}${W}`)]),Q=async(W)=>{let z=new URL(W.url),Y=bC(W,z,v,U,J);if(Y)return Y;if(U)return qC(W,U,D,X);if(G&&!xC(W,G))return new Response("unauthorized",{status:401});return SH(W,D,void 0,X)};return U?async(W)=>xV(await Q(W)):Q}function bC($,v,D,X,U){if(v.pathname===MC&&$.method==="GET")return A1(200,{ok:!0});if(X){let G=CV($,v,X,U);if(G)return G}if(!D.has(v.pathname))return new Response("not found",{status:404});if($.method==="GET"&&!X)return A1(200,{ok:!0,transport:"streamable-http"});return null}async function qC($,v,D,X){let U=uV($),G=new URL($.url).pathname,J=G===K7?"":G;for(let z of fV($,U)){let Y=v.rateLimiter.check(z);if(!Y.allowed)return new Response(JSON.stringify({error:"rate_limited"}),{status:429,headers:{"content-type":"application/json","retry-after":String(Y.retryAfterSeconds)}})}if(!U)return Sz(v,void 0,J);let Q;try{Q=await v.tokenVerifier.verify(U)}catch(z){return X(`[http] token verification unavailable: ${B7(z)}
117
117
  `),A1(502,{error:"token verification unavailable"})}if(!Q.ok)return Sz(v,"invalid_token",J);let W=iV({token:U,userId:Q.userId,trpcUrl:v.trpcUrl,onUnauthorized:()=>v.tokenVerifier.invalidate(U)});return SH($,D,W,X)}function SC($,v,D){let U=globalThis.Bun.serve({port:v.port,hostname:D,fetch:$});return{port:U.port??v.port,close:async()=>{await U.stop()}}}async function kC($,v,D){let X=AC((J,Q)=>{EC(J,Q,$).catch(()=>{})});return await new Promise((J,Q)=>{X.once("error",Q),X.listen(v.port,D,()=>{X.off("error",Q),J()})}),{port:X.address()?.port??v.port,close:()=>new Promise((J,Q)=>{X.close((W)=>{if(W)Q(W);else J()})})}}async function EC($,v,D){try{let X=await TC($),U=await D(X);await uC(U,v)}catch(X){if(!v.headersSent){v.statusCode=500,v.setHeader("content-type","application/json"),v.end(JSON.stringify({error:B7(X)}));return}v.end()}}async function TC($){let v=RC($),D=gC($),X=$.method??"GET",U={method:X,headers:D},G=await CC($,X);if(G)U.body=G,U.duplex="half";return new Request(v,U)}function RC($){let v=$.socket,D=$.headers["x-forwarded-proto"]??(v?.encrypted?"https":"http"),X=$.headers.host??"localhost";return new URL($.url??"/",`${D}://${X}`).toString()}function gC($){let v=new Headers;for(let[D,X]of Object.entries($.headers)){if(X===void 0)continue;if(Array.isArray(X))for(let U of X)v.append(D,U);else v.append(D,X)}return v}async function CC($,v){if(v==="GET"||v==="HEAD")return;let D=[];for await(let X of $)D.push(Buffer.isBuffer(X)?X:Buffer.from(X));return D.length>0?Buffer.concat(D):void 0}async function uC($,v){if(v.statusCode=$.status,$.headers.forEach((X,U)=>{v.setHeader(U,X)}),!$.body){v.end();return}let D=$.body.getReader();try{while(!0){let{value:X,done:U}=await D.read();if(U)break;if(X)v.write(Buffer.from(X))}}finally{D.releaseLock(),v.end()}}function xC($,v){let D=$.headers.get("authorization");if(!D)return!1;if(!D.startsWith("Bearer "))return!1;let X=D.slice(7),U=Buffer.from(X,"utf8"),G=Buffer.from(v,"utf8");if(U.length!==G.length){let J=Buffer.alloc(G.length);return U.copy(J,0,0,Math.min(U.length,G.length)),bH(J,G),!1}return bH(U,G)}function B7($){if($ instanceof Error)return $.message;return String($)}function qH(){return}var fC="aether-wealth-mcp",_C=[33417,33418,33419],iC="/callback",yC=["trades:read","trades:write","accounts:read","alerts:read","alerts:write"],nC="aether-wealth-mcp",lC="AETHER_MCP_CREDENTIALS_FILE",l1={clientId:fC,redirectPorts:_C,redirectPath:iC,scopes:yC,keychainService:nC,credentialsFilePath:k0("aether-wealth-mcp"),forceFileEnvVar:lC};function kH($={}){let v=$.hosted??!1,D=v?["Authentication is handled by the MCP connection itself — there are no sign-in tools to call. If calls fail with an authorization error, the user must re-authorize this connection in their MCP client settings."]:["Auth tools:"," • login — sign in. Opens the browser; the user authenticates and approves access. Call this first if a data tool reports the user is not signed in."," • logout — sign out on this device."],X=v?" 3. If it fails with an authorization error, tell the user to re-authorize the Aether Wealth connection in their MCP client, then retry.":' 3. If it reports "not signed in", call login, wait for the user to finish in the browser, then retry.';return[{name:"getting_started",description:"Brief the LLM on what the Aether Wealth MCP exposes and the sign-in-first flow.",handler:()=>[{role:"user",content:{type:"text",text:["You are connected to the Aether Wealth MCP — programmatic access to the user's trading journal, alerts, market data, indicators, and charts over an authenticated connection.","",...D,"","Context:"," • get_context — snapshot accounts, open trades, recent stats, and active alerts in one call. Call it first to ground your suggestions.","","Domains (partial — see the aetherwealth://capabilities resource for the full set):"," • Journal: list_trades, get_trade, list_accounts, create_account, create_trade, update_trade, close_trade, delete_trade, delete_trades, import_trades, trade_stats"," • Alerts: list_alerts, create_price_alert, create_trendline_alert, create_indicator_alert, list_indicator_alerts, update_indicator_alert, delete_indicator_alert, update_alert, delete_alert"," • Market data + indicators: get_candles, get_market_config, get_indicators"," • Macro: list_economic_calendar, get_macro_series, get_macro_indicator"," • FX macro context: get_fx_pair_context, get_fx_currency_context, get_fx_predictions, get_fx_risk_sentiment, get_fx_documents, get_fx_sync_state",...Y4?[]:[" • Charts: render_chart"]," • Broker execution: propose_trade, close_position, and modify_position create intents. Email/app modes need a user-provided code; auto mode needs no per-trade code. For push intents, do not ask for a code — use trade_intent_status to check the app approval outcome. disconnect_broker is the instant kill switch and requires no confirmation.","","Deletion rule:"," • delete_trade and delete_trades are permanent and only work on demo accounts. Live-account trade deletes are blocked by the backend.","","Recommended flow:"," 1. Call get_context to load live state."," 2. Try the data tool the user asked for (e.g. list_trades).",X].join(`
118
118
  `)}}]},{name:"dashboard_context",description:"Brief the LLM on the full Aether Wealth surface, grounding in live state, and when to suggest the dashboard.",handler:()=>[{role:"user",content:{type:"text",text:["You are connected to Aether Wealth, a multi-market trading workspace.",PX,"","Before you advise, call get_context to load the user's live state (accounts, open trades, recent stats, active alerts) so your suggestions fit what they are actually doing. The aetherwealth://capabilities resource, if your client attaches it, is the full surface map — which actions are MCP-callable vs dashboard-only.","","When the user needs something that is dashboard-only (the full charting workspace and saved layouts, the Pine editor, the full journal UI, the Aether Intelligence assistant),",`tell them to open the dashboard at ${f4} rather than attempting it over MCP.`].join(`
119
- `)}}]}]}var EH="aether-wealth-mcp",N7="0.2.16";function hC($){return{toolContext:$.toolContext,tools:new Map(($.tools??V7()).map((v)=>[v.name,v])),resources:new Map(($.resources??y5()).map((v)=>[v.uri,v])),prompts:new Map(($.prompts??i5()).map((v)=>[v.name,v])),...$.clientGate?{clientGate:$.clientGate}:{},log:$.log??((v)=>{process.stderr.write(v)})}}function mC($,v){let D=new A0({name:EH,version:N7},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return cC(D,$,v??$.toolContext),D}class oX{server;bundle;constructor($){this.bundle=hC($),this.server=mC(this.bundle),this.server.oninitialized=()=>{this.bundle.log(`${EH} initialized
119
+ `)}}]}]}var EH="aether-wealth-mcp",N7="0.2.17";function hC($){return{toolContext:$.toolContext,tools:new Map(($.tools??V7()).map((v)=>[v.name,v])),resources:new Map(($.resources??y5()).map((v)=>[v.uri,v])),prompts:new Map(($.prompts??i5()).map((v)=>[v.name,v])),...$.clientGate?{clientGate:$.clientGate}:{},log:$.log??((v)=>{process.stderr.write(v)})}}function mC($,v){let D=new A0({name:EH,version:N7},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return cC(D,$,v??$.toolContext),D}class oX{server;bundle;constructor($){this.bundle=hC($),this.server=mC(this.bundle),this.server.oninitialized=()=>{this.bundle.log(`${EH} initialized
120
120
  `)}}async connect($){await this.server.connect($)}async close(){await this.server.close()}isInitialized(){return this.server.getClientVersion()!==void 0}getRegistryBundle(){return this.bundle}}function cC($,v,D){let X=!1,U=(J)=>{let Q=v.clientGate?.nudge;if(Q&&!X)return X=!0,`${Q}
121
121
 
122
122
  ${J}`;return J},G=(J)=>{let Q=v.clientGate?.nudge;if(!Q||X)return J;X=!0;let W=J.find((z)=>z.type==="text");if(W)return J.map((z)=>z===W?{type:"text",text:`${Q}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aetherwealth/mcp",
3
3
  "mcpName": "ai.aetherwealth/mcp",
4
- "version": "0.2.16",
4
+ "version": "0.2.17",
5
5
  "description": "Official Aether Wealth MCP server for journals, alerts, forex and crypto market context, and approval-governed broker actions.",
6
6
  "type": "module",
7
7
  "bin": {