@aetherwealth/mcp 0.1.18-beta.4 → 0.1.18

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 (3) hide show
  1. package/README.md +124 -121
  2. package/dist/index.js +2 -2
  3. package/package.json +23 -3
package/README.md CHANGED
@@ -1,146 +1,149 @@
1
- # @aetherwealth/mcp
1
+ # Aether Wealth MCP Server
2
2
 
3
- A [Model Context Protocol](https://modelcontextprotocol.io) server that exposes
4
- a user's **Aether Wealth** trading journal to MCP clients such as Claude Desktop.
3
+ Official Model Context Protocol (MCP) server for Aether Wealth. Connect any
4
+ stdio-compatible MCP client to your Aether Wealth trading journal, accounts,
5
+ alerts, market context, macro calendar, and technical indicators.
5
6
 
6
- Authentication is **per-user OAuth 2.1** the user signs in through the browser
7
- and the MCP stores a personal access token locally. No shared secret is
8
- configured on the user's machine. Data calls go to `aether-backend` over a typed
9
- **tRPC** client.
7
+ The package runs locally with `npx`, authenticates with browser-based OAuth, and
8
+ uses your Aether Wealth account permissions for every data tool. It is built for
9
+ traders who want an AI assistant to inspect their journal, summarize performance,
10
+ create alerts, and fetch market context without copying API keys into their MCP
11
+ client.
10
12
 
11
- > Destructive tools (`delete_trade`, `delete_account`) are intentionally not
12
- > exposed — trades are permanent records; delete them in the web app. Market-data
13
- > tools are public (work without sign-in); everything else needs `login`.
13
+ ## Install
14
14
 
15
- ## Tools
16
-
17
- **Auth**
18
-
19
- | Tool | Purpose |
20
- |------|---------|
21
- | `login` | Sign in. Opens the browser; after you approve, the MCP stores a per-user token. |
22
- | `logout` | Sign out on this device (clears the stored token). |
23
-
24
- **Trades** (require sign-in)
25
-
26
- | Tool | Purpose |
27
- |------|---------|
28
- | `list_trades` | List trades; filter by account, status, pair, or date range. |
29
- | `get_trade` | Get a single trade by id. |
30
- | `list_accounts` | List trading accounts (for `create_trade`'s `accountId`). |
31
- | `trade_stats` | Performance statistics (win rate, PnL, …). |
32
- | `create_trade` | Record a new trade. |
33
- | `update_trade` | Update fields on a trade. |
34
- | `close_trade` | Close an open trade with an exit price + time. |
35
-
36
- **Alerts** (require sign-in)
37
-
38
- | Tool | Purpose |
39
- |------|---------|
40
- | `list_alerts` / `list_indicator_alerts` | List price+trendline / indicator alerts. |
41
- | `create_price_alert` | Price-level alert (above/below/crosses). |
42
- | `create_trendline_alert` | Trendline alert from two points. |
43
- | `create_indicator_alert` | Indicator-output alert (e.g. RSI crosses 70). |
44
- | `update_alert` / `delete_alert` | Update / delete a price/trendline alert. |
45
- | `update_indicator_alert` / `delete_indicator_alert` | Update / delete an indicator alert. |
15
+ Use the package in any MCP client that can launch a local stdio server.
46
16
 
47
- **Market data** (public — no sign-in)
17
+ ```bash
18
+ npx -y @aetherwealth/mcp
19
+ ```
48
20
 
49
- | Tool | Purpose |
50
- |------|---------|
51
- | `get_candles` | Recent OHLC candles (up to 500) for a pair + timeframe, read from Redis. |
52
- | `list_economic_calendar` | Economic calendar events; filter by currency, date, impact. |
53
- | `get_macro_series` | Macro indicator time series (e.g. USD cpi). |
54
- | `get_market_config` | Catalog of supported instruments + timeframes. |
21
+ Most MCP clients use a JSON config like this:
55
22
 
56
- A sign-in-required tool called while signed out returns an "authentication
57
- required — call `login`" error, so the assistant prompts you to sign in.
23
+ ```json
24
+ {
25
+ "mcpServers": {
26
+ "aether-wealth": {
27
+ "command": "npx",
28
+ "args": ["-y", "@aetherwealth/mcp"]
29
+ }
30
+ }
31
+ }
32
+ ```
58
33
 
59
- ## Authentication flow
34
+ Restart your MCP client after adding the server. Then ask it to run the
35
+ `login` tool once.
60
36
 
61
- `login` runs an OAuth 2.1 Authorization Code + PKCE flow as a public client:
37
+ ## What It Does
62
38
 
63
- 1. Discover the provider endpoints from `${AETHER_BASE_URL}/auth/api/.well-known/openid-configuration`.
64
- 2. Start a loopback listener on `127.0.0.1` (a fixed port from a small set).
65
- 3. Open the browser to the authorize URL. If you're already signed in to the
66
- Aether Wealth web app it completes immediately; otherwise you sign in there.
67
- 4. The redirect lands on the loopback listener with an authorization code, which
68
- is exchanged (with the PKCE verifier) for an access token.
69
- 5. The token is stored in the **OS keychain** (macOS Keychain / Windows
70
- Credential Manager / Linux libsecret), with a `0600`-file fallback, keyed by
71
- backend host so prod and beta tokens coexist.
39
+ Aether Wealth MCP gives AI assistants a safe tool layer over Aether Wealth:
72
40
 
73
- The token is long-lived; there is no refresh token. When it expires, run `login`
74
- again.
41
+ - Read and summarize your trading journal
42
+ - List accounts and performance stats
43
+ - Create, update, and close trade records
44
+ - Create and manage price, trendline, and indicator alerts
45
+ - Fetch candles, economic calendar events, macro series, and market config
46
+ - Compute technical indicators such as RSI, MACD, EMA, Bollinger Bands, ATR,
47
+ ADX, Supertrend, and more
75
48
 
76
- ## Configuration
49
+ Aether Wealth is trading analysis and journaling software. This MCP server does
50
+ not place broker orders or execute trades.
77
51
 
78
- See [`.env.example`](./.env.example).
52
+ ## Authentication
79
53
 
80
- | Var | Required | Notes |
81
- |-----|----------|-------|
82
- | `AETHER_BASE_URL` | prod only | aether-backend base URL (OAuth + tRPC). Falls back to `http://localhost:9006` in dev. |
83
- | `AETHER_MCP_CREDENTIALS_FILE` | no | Set to force the `0600`-file credential store instead of the OS keychain. |
84
- | `MCP_TRANSPORT` | no | `stdio` (default) or `http`. |
85
- | `MCP_HTTP_PORT` / `MCP_HTTP_HOST` / `MCP_HTTP_PATH` / `MCP_HTTP_BEARER` | no | HTTP transport only. |
54
+ The `login` tool opens the Aether Wealth OAuth flow in your browser. After you
55
+ approve access, the MCP stores a local token in the operating-system keychain
56
+ where available, with a protected file fallback.
86
57
 
87
- ## Running
58
+ No shared API key is configured in your MCP client. All data tools require
59
+ OAuth sign-in, including market-data tools. `logout` clears the local
60
+ credential for this device.
88
61
 
89
- ```bash
90
- # From the repo root
91
- bun run --filter=@aetherwealth/mcp start
92
- # Or directly
93
- cd apps/aether-wealth-mcp && bun run src/index.ts
94
- ```
62
+ ## Tools
95
63
 
96
- ### Claude Desktop config example
64
+ ### Auth
97
65
 
98
- ```json
99
- {
100
- "mcpServers": {
101
- "aether-wealth": {
102
- "command": "bun",
103
- "args": ["run", "/abs/path/to/apps/aether-wealth-mcp/src/index.ts"],
104
- "env": { "AETHER_BASE_URL": "https://api.aetherwealth.ai" }
105
- }
106
- }
107
- }
108
- ```
66
+ | Tool | Purpose |
67
+ | --- | --- |
68
+ | `login` | Sign in with Aether Wealth OAuth. |
69
+ | `logout` | Clear the local OAuth credential on this device. |
109
70
 
110
- Then ask the assistant to sign in (it calls `login`), complete the browser
111
- sign-in, and your trades become available.
71
+ ### Trades, Accounts, And Stats
112
72
 
113
- ## Build & quality
73
+ | Tool | Purpose |
74
+ | --- | --- |
75
+ | `list_trades` | List trades by account, status, pair, or date range. |
76
+ | `get_trade` | Fetch one trade by ID. |
77
+ | `list_accounts` | List your trading accounts. |
78
+ | `trade_stats` | Summarize performance, win rate, PnL, and related stats. |
79
+ | `create_trade` | Record a new trade. |
80
+ | `update_trade` | Update fields on an existing trade. |
81
+ | `close_trade` | Close an open trade with exit price and time. |
114
82
 
115
- ```bash
116
- bun run --filter=@aetherwealth/mcp test # vitest
117
- bun run --filter=@aetherwealth/mcp typecheck # tsc --noEmit
118
- bun run --filter=@aetherwealth/mcp lint # biome
119
- bun run --filter=@aetherwealth/mcp build # bun bundle → dist/
120
- ```
83
+ ### Alerts
121
84
 
122
- > The typecheck imports `AppRouter` from `@opusaether/aether-backend/trpc`, so
123
- > aether-backend's declarations must be built first (the turbo pipeline handles
124
- > this). A stale `node_modules` makes the vitest suite fail with
125
- > `[TSCONFIG_ERROR]` fix with `bun install` and `GITHUB_TOKEN` set.
85
+ | Tool | Purpose |
86
+ | --- | --- |
87
+ | `list_alerts` | List price and trendline alerts. |
88
+ | `create_price_alert` | Create a price-level alert. |
89
+ | `create_trendline_alert` | Create a trendline alert from two chart points. |
90
+ | `update_alert` | Update or archive a price or trendline alert. |
91
+ | `delete_alert` | Delete a price or trendline alert. |
92
+ | `list_indicator_alerts` | List indicator-based alerts. |
93
+ | `create_indicator_alert` | Create an alert on an indicator output condition. |
94
+ | `update_indicator_alert` | Update an indicator alert. |
95
+ | `delete_indicator_alert` | Delete an indicator alert. |
96
+
97
+ ### Market Context
126
98
 
127
- ## Architecture
99
+ | Tool | Purpose |
100
+ | --- | --- |
101
+ | `get_candles` | Fetch recent OHLC candles for a pair and timeframe. |
102
+ | `list_economic_calendar` | List economic calendar events by currency, release, date, or impact. |
103
+ | `get_macro_series` | Fetch macro indicator time series such as CPI or interest-rate data. |
104
+ | `get_market_config` | Fetch supported instruments and timeframes. |
128
105
 
129
- ```
130
- index.ts entry: config → credential store → OAuth session → tRPC client → transport
131
- config.ts env → { baseUrl, trpcUrl, host } (OAuth-only)
132
- trpc-client.ts typed tRPC client (AppRouter); attaches Bearer token per request
133
- tool-registry.ts login / logout / list_trades; McpToolContext = { session, trpc }
134
- server.ts registry → @modelcontextprotocol/sdk request-handler bridge
135
- oauth/
136
- contract.ts client id / redirect ports / scopes (mirrors aether-backend constants)
137
- pkce.ts PKCE verifier + S256 challenge, state
138
- discovery.ts fetch the AS openid-configuration
139
- loopback.ts loopback redirect listener (captures code/state)
140
- browser.ts open the system browser
141
- login.ts runLogin: PKCE → loopback → browser → token exchange
142
- credential-store.ts OS keychain + 0600-file fallback, keyed by host
143
- session.ts OAuthSession: getAccessToken / login / logout
144
- ```
106
+ ### Technical Indicators
145
107
 
146
- See [ADR 0001](../../docs/adr/0001-mcp-oauth.md) for the auth design + trade-offs.
108
+ | Tool | Purpose |
109
+ | --- | --- |
110
+ | `get_indicators` | Compute up to 8 technical indicators for a pair and interval. |
111
+
112
+ ## Example Prompts
113
+
114
+ - "List my open trades and group them by pair. Flag anything without a stop
115
+ loss."
116
+ - "Summarize my last 20 closed trades. What setup is leaking the most R?"
117
+ - "What high-impact USD events are left this week, and which open trades overlap
118
+ them?"
119
+ - "Fetch EUR/USD 1h RSI and MACD, then create an alert if RSI crosses below 40."
120
+ - "Create a price alert if XAU/USD crosses yesterday's high before New York
121
+ open."
122
+
123
+ ## Safety And Limits
124
+
125
+ - OAuth is per user. Your MCP client does not store a shared service secret.
126
+ - Tokens are stored locally and can be cleared with `logout`.
127
+ - Backend rate limits protect market-data reads, MCP Bearer traffic, and
128
+ cost-sensitive indicator calls.
129
+ - Technical indicators include request, active-pair, cache, and upstream credit
130
+ gates.
131
+ - Destructive trade and account deletion tools are intentionally not exposed.
132
+ - The MCP does not execute trades or route broker orders.
133
+
134
+ ## Updating
135
+
136
+ If your MCP client launches the server with `npx -y @aetherwealth/mcp`, restart
137
+ the client to pick up the current published package.
138
+
139
+ The backend can also advertise a minimum supported MCP version. If your local
140
+ server is too old, tool calls return an update-required message instead of
141
+ failing silently.
142
+
143
+ ## Links
144
+
145
+ - Aether Wealth: https://aetherwealth.ai
146
+ - Trading app: https://app.aetherwealth.ai
147
+ - MCP page: https://aetherwealth.ai/mcp
148
+ - Tool reference: https://aetherwealth.ai/mcp-tools.json
149
+ - npm package: https://www.npmjs.com/package/@aetherwealth/mcp
package/dist/index.js CHANGED
@@ -82,7 +82,7 @@ data:
82
82
  `;return G+=`data: ${JSON.stringify(Y)}
83
83
 
84
84
  `,Q.enqueue(X.encode(G)),!0}catch(G){return this.onerror?.(G),!1}}handleUnsupportedRequest(){return this.onerror?.(Error("Method not allowed.")),new Response(JSON.stringify({jsonrpc:"2.0",error:{code:-32000,message:"Method not allowed."},id:null}),{status:405,headers:{Allow:"GET, POST, DELETE","Content-Type":"application/json"}})}async handlePostRequest(Q,X){try{let Y=Q.headers.get("accept");if(!Y?.includes("application/json")||!Y.includes("text/event-stream"))return this.onerror?.(Error("Not Acceptable: Client must accept both application/json and text/event-stream")),this.createJsonErrorResponse(406,-32000,"Not Acceptable: Client must accept both application/json and text/event-stream");let W=Q.headers.get("content-type");if(!W||!W.includes("application/json"))return this.onerror?.(Error("Unsupported Media Type: Content-Type must be application/json")),this.createJsonErrorResponse(415,-32000,"Unsupported Media Type: Content-Type must be application/json");let G={headers:Object.fromEntries(Q.headers.entries()),url:new URL(Q.url)},J;if(X?.parsedBody!==void 0)J=X.parsedBody;else try{J=await Q.json()}catch{return this.onerror?.(Error("Parse error: Invalid JSON")),this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON")}let B;try{if(Array.isArray(J))B=J.map((M)=>q9.parse(M));else B=[q9.parse(J)]}catch{return this.onerror?.(Error("Parse error: Invalid JSON-RPC message")),this.createJsonErrorResponse(400,-32700,"Parse error: Invalid JSON-RPC message")}let H=B.some(uX);if(H){if(this._initialized&&this.sessionId!==void 0)return this.onerror?.(Error("Invalid Request: Server already initialized")),this.createJsonErrorResponse(400,-32600,"Invalid Request: Server already initialized");if(B.length>1)return this.onerror?.(Error("Invalid Request: Only one initialization request is allowed")),this.createJsonErrorResponse(400,-32600,"Invalid Request: Only one initialization request is allowed");if(this.sessionId=this.sessionIdGenerator?.(),this._initialized=!0,this.sessionId&&this._onsessioninitialized)await Promise.resolve(this._onsessioninitialized(this.sessionId))}if(!H){let M=this.validateSession(Q);if(M)return M;let q=this.validateProtocolVersion(Q);if(q)return q}if(!B.some(G1)){for(let M of B)this.onmessage?.(M,{authInfo:X?.authInfo,requestInfo:G});return new Response(null,{status:202})}let z=crypto.randomUUID(),A=B.find((M)=>uX(M)),Z=A?A.params.protocolVersion:Q.headers.get("mcp-protocol-version")??_G;if(this._enableJsonResponse)return new Promise((M)=>{this._streamMapping.set(z,{resolveJson:M,cleanup:()=>{this._streamMapping.delete(z)}});for(let q of B)if(G1(q))this._requestToStreamMapping.set(q.id,z);for(let q of B)this.onmessage?.(q,{authInfo:X?.authInfo,requestInfo:G})});let D=new TextEncoder,F,V=new ReadableStream({start:(M)=>{F=M},cancel:()=>{this._streamMapping.delete(z)}}),L={"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"};if(this.sessionId!==void 0)L["mcp-session-id"]=this.sessionId;for(let M of B)if(G1(M))this._streamMapping.set(z,{controller:F,encoder:D,cleanup:()=>{this._streamMapping.delete(z);try{F.close()}catch{}}}),this._requestToStreamMapping.set(M.id,z);await this.writePrimingEvent(F,D,z,Z);for(let M of B){let q,O;if(G1(M)&&this._eventStore&&Z>="2025-11-25")q=()=>{this.closeSSEStream(M.id)},O=()=>{this.closeStandaloneSSEStream()};this.onmessage?.(M,{authInfo:X?.authInfo,requestInfo:G,closeSSEStream:q,closeStandaloneSSEStream:O})}return new Response(V,{status:200,headers:L})}catch(Y){return this.onerror?.(Y),this.createJsonErrorResponse(400,-32700,"Parse error",{data:String(Y)})}}async handleDeleteRequest(Q){let X=this.validateSession(Q);if(X)return X;let Y=this.validateProtocolVersion(Q);if(Y)return Y;return await Promise.resolve(this._onsessionclosed?.(this.sessionId)),await this.close(),new Response(null,{status:200})}validateSession(Q){if(this.sessionIdGenerator===void 0)return;if(!this._initialized)return this.onerror?.(Error("Bad Request: Server not initialized")),this.createJsonErrorResponse(400,-32000,"Bad Request: Server not initialized");let X=Q.headers.get("mcp-session-id");if(!X)return this.onerror?.(Error("Bad Request: Mcp-Session-Id header is required")),this.createJsonErrorResponse(400,-32000,"Bad Request: Mcp-Session-Id header is required");if(X!==this.sessionId)return this.onerror?.(Error("Session not found")),this.createJsonErrorResponse(404,-32001,"Session not found");return}validateProtocolVersion(Q){let X=Q.headers.get("mcp-protocol-version");if(X!==null&&!v1.includes(X))return this.onerror?.(Error(`Bad Request: Unsupported protocol version: ${X} (supported versions: ${v1.join(", ")})`)),this.createJsonErrorResponse(400,-32000,`Bad Request: Unsupported protocol version: ${X} (supported versions: ${v1.join(", ")})`);return}async close(){this._streamMapping.forEach(({cleanup:Q})=>{Q()}),this._streamMapping.clear(),this._requestResponseMap.clear(),this.onclose?.()}closeSSEStream(Q){let X=this._requestToStreamMapping.get(Q);if(!X)return;let Y=this._streamMapping.get(X);if(Y)Y.cleanup()}closeStandaloneSSEStream(){let Q=this._streamMapping.get(this._standaloneSseStreamId);if(Q)Q.cleanup()}async send(Q,X){let Y=X?.relatedRequestId;if(i0(Q)||x1(Q))Y=Q.id;if(Y===void 0){if(i0(Q)||x1(Q))throw Error("Cannot send a response on a standalone SSE stream unless resuming a previous client request");let J;if(this._eventStore)J=await this._eventStore.storeEvent(this._standaloneSseStreamId,Q);let B=this._streamMapping.get(this._standaloneSseStreamId);if(B===void 0)return;if(B.controller&&B.encoder)this.writeSSEEvent(B.controller,B.encoder,Q,J);return}let W=this._requestToStreamMapping.get(Y);if(!W)throw Error(`No connection established for request ID: ${String(Y)}`);let G=this._streamMapping.get(W);if(!this._enableJsonResponse&&G?.controller&&G?.encoder){let J;if(this._eventStore)J=await this._eventStore.storeEvent(W,Q);this.writeSSEEvent(G.controller,G.encoder,Q,J)}if(i0(Q)||x1(Q)){this._requestResponseMap.set(Y,Q);let J=Array.from(this._requestToStreamMapping.entries()).filter(([H,K])=>K===W).map(([H])=>H);if(J.every((H)=>this._requestResponseMap.has(H))){if(!G)throw Error(`No connection established for request ID: ${String(Y)}`);if(this._enableJsonResponse&&G.resolveJson){let H={"Content-Type":"application/json"};if(this.sessionId!==void 0)H["mcp-session-id"]=this.sessionId;let K=J.map((z)=>this._requestResponseMap.get(z));if(K.length===1)G.resolveJson(new Response(JSON.stringify(K[0]),{status:200,headers:H}));else G.resolveJson(new Response(JSON.stringify(K),{status:200,headers:H}))}else G.cleanup();for(let H of J)this._requestResponseMap.delete(H),this._requestToStreamMapping.delete(H)}}}}function h1(Q){return!!Q._zod}function J1(Q,X){if(h1(Q))return z9(Q,X);return Q.safeParse(X)}function d8(Q){if(!Q)return;let X;if(h1(Q))X=Q._zod?.def?.shape;else X=Q.shape;if(!X)return;if(typeof X==="function")try{return X()}catch{return}return X}function tG(Q){if(h1(Q)){let J=Q._zod?.def;if(J){if(J.value!==void 0)return J.value;if(Array.isArray(J.values)&&J.values.length>0)return J.values[0]}}let Y=Q._def;if(Y){if(Y.value!==void 0)return Y.value;if(Array.isArray(Y.values)&&Y.values.length>0)return Y.values[0]}let W=Q.value;if(W!==void 0)return W;return}function B1(Q){return Q==="completed"||Q==="failed"||Q==="cancelled"}var $F=Symbol("Let zodToJsonSchema decide on which parser to use");var DT=new Set("ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvxyz0123456789");function sX(Q){let Y=d8(Q)?.method;if(!Y)throw Error("Schema is missing a method literal");let W=tG(Y);if(typeof W!=="string")throw Error("Schema method literal must be a string");return W}function eX(Q,X){let Y=J1(Q,X);if(!Y.success)throw Y.error;return Y.data}var jF=60000;class Q6{constructor(Q){if(this._options=Q,this._requestMessageId=0,this._requestHandlers=new Map,this._requestHandlerAbortControllers=new Map,this._notificationHandlers=new Map,this._responseHandlers=new Map,this._progressHandlers=new Map,this._timeoutInfo=new Map,this._pendingDebouncedNotifications=new Set,this._taskProgressTokens=new Map,this._requestResolvers=new Map,this.setNotificationHandler(x8,(X)=>{this._oncancel(X)}),this.setNotificationHandler(g8,(X)=>{this._onprogress(X)}),this.setRequestHandler(y8,(X)=>({})),this._taskStore=Q?.taskStore,this._taskMessageQueue=Q?.taskMessageQueue,this._taskStore)this.setRequestHandler(h8,async(X,Y)=>{let W=await this._taskStore.getTask(X.params.taskId,Y.sessionId);if(!W)throw new T(C.InvalidParams,"Failed to retrieve task: Task not found");return{...W}}),this.setRequestHandler(u8,async(X,Y)=>{let W=async()=>{let G=X.params.taskId;if(this._taskMessageQueue){let B;while(B=await this._taskMessageQueue.dequeue(G,Y.sessionId)){if(B.type==="response"||B.type==="error"){let H=B.message,K=H.id,z=this._requestResolvers.get(K);if(z)if(this._requestResolvers.delete(K),B.type==="response")z(H);else{let A=H,Z=new T(A.error.code,A.error.message,A.error.data);z(Z)}else{let A=B.type==="response"?"Response":"Error";this._onerror(Error(`${A} handler missing for request ${K}`))}continue}await this._transport?.send(B.message,{relatedRequestId:Y.requestId})}}let J=await this._taskStore.getTask(G,Y.sessionId);if(!J)throw new T(C.InvalidParams,`Task not found: ${G}`);if(!B1(J.status))return await this._waitForTaskUpdate(G,Y.signal),await W();if(B1(J.status)){let B=await this._taskStore.getTaskResult(G,Y.sessionId);return this._clearTaskQueue(G),{...B,_meta:{...B._meta,[W1]:{taskId:G}}}}return await W()};return await W()}),this.setRequestHandler(l8,async(X,Y)=>{try{let{tasks:W,nextCursor:G}=await this._taskStore.listTasks(X.params?.cursor,Y.sessionId);return{tasks:W,nextCursor:G,_meta:{}}}catch(W){throw new T(C.InvalidParams,`Failed to list tasks: ${W instanceof Error?W.message:String(W)}`)}}),this.setRequestHandler(c8,async(X,Y)=>{try{let W=await this._taskStore.getTask(X.params.taskId,Y.sessionId);if(!W)throw new T(C.InvalidParams,`Task not found: ${X.params.taskId}`);if(B1(W.status))throw new T(C.InvalidParams,`Cannot cancel task in terminal status: ${W.status}`);await this._taskStore.updateTaskStatus(X.params.taskId,"cancelled","Client cancelled task execution.",Y.sessionId),this._clearTaskQueue(X.params.taskId);let G=await this._taskStore.getTask(X.params.taskId,Y.sessionId);if(!G)throw new T(C.InvalidParams,`Task not found after cancellation: ${X.params.taskId}`);return{_meta:{},...G}}catch(W){if(W instanceof T)throw W;throw new T(C.InvalidRequest,`Failed to cancel task: ${W instanceof Error?W.message:String(W)}`)}})}async _oncancel(Q){if(!Q.params.requestId)return;this._requestHandlerAbortControllers.get(Q.params.requestId)?.abort(Q.params.reason)}_setupTimeout(Q,X,Y,W,G=!1){this._timeoutInfo.set(Q,{timeoutId:setTimeout(W,X),startTime:Date.now(),timeout:X,maxTotalTimeout:Y,resetTimeoutOnProgress:G,onTimeout:W})}_resetTimeout(Q){let X=this._timeoutInfo.get(Q);if(!X)return!1;let Y=Date.now()-X.startTime;if(X.maxTotalTimeout&&Y>=X.maxTotalTimeout)throw this._timeoutInfo.delete(Q),T.fromError(C.RequestTimeout,"Maximum total timeout exceeded",{maxTotalTimeout:X.maxTotalTimeout,totalElapsed:Y});return clearTimeout(X.timeoutId),X.timeoutId=setTimeout(X.onTimeout,X.timeout),!0}_cleanupTimeout(Q){let X=this._timeoutInfo.get(Q);if(X)clearTimeout(X.timeoutId),this._timeoutInfo.delete(Q)}async connect(Q){if(this._transport)throw Error("Already connected to a transport. Call close() before connecting to a new transport, or use a separate Protocol instance per connection.");this._transport=Q;let X=this.transport?.onclose;this._transport.onclose=()=>{X?.(),this._onclose()};let Y=this.transport?.onerror;this._transport.onerror=(G)=>{Y?.(G),this._onerror(G)};let W=this._transport?.onmessage;this._transport.onmessage=(G,J)=>{if(W?.(G,J),i0(G)||x1(G))this._onresponse(G);else if(G1(G))this._onrequest(G,J);else if(lG(G))this._onnotification(G);else this._onerror(Error(`Unknown message type: ${JSON.stringify(G)}`))},await this._transport.start()}_onclose(){let Q=this._responseHandlers;this._responseHandlers=new Map,this._progressHandlers.clear(),this._taskProgressTokens.clear(),this._pendingDebouncedNotifications.clear();for(let Y of this._timeoutInfo.values())clearTimeout(Y.timeoutId);this._timeoutInfo.clear();for(let Y of this._requestHandlerAbortControllers.values())Y.abort();this._requestHandlerAbortControllers.clear();let X=T.fromError(C.ConnectionClosed,"Connection closed");this._transport=void 0,this.onclose?.();for(let Y of Q.values())Y(X)}_onerror(Q){this.onerror?.(Q)}_onnotification(Q){let X=this._notificationHandlers.get(Q.method)??this.fallbackNotificationHandler;if(X===void 0)return;Promise.resolve().then(()=>X(Q)).catch((Y)=>this._onerror(Error(`Uncaught error in notification handler: ${Y}`)))}_onrequest(Q,X){let Y=this._requestHandlers.get(Q.method)??this.fallbackRequestHandler,W=this._transport,G=Q.params?._meta?.[W1]?.taskId;if(Y===void 0){let z={jsonrpc:"2.0",id:Q.id,error:{code:C.MethodNotFound,message:"Method not found"}};if(G&&this._taskMessageQueue)this._enqueueTaskMessage(G,{type:"error",message:z,timestamp:Date.now()},W?.sessionId).catch((A)=>this._onerror(Error(`Failed to enqueue error response: ${A}`)));else W?.send(z).catch((A)=>this._onerror(Error(`Failed to send an error response: ${A}`)));return}let J=new AbortController;this._requestHandlerAbortControllers.set(Q.id,J);let B=hG(Q.params)?Q.params.task:void 0,H=this._taskStore?this.requestTaskStore(Q,W?.sessionId):void 0,K={signal:J.signal,sessionId:W?.sessionId,_meta:Q.params?._meta,sendNotification:async(z)=>{if(J.signal.aborted)return;let A={relatedRequestId:Q.id};if(G)A.relatedTask={taskId:G};await this.notification(z,A)},sendRequest:async(z,A,Z)=>{if(J.signal.aborted)throw new T(C.ConnectionClosed,"Request was cancelled");let D={...Z,relatedRequestId:Q.id};if(G&&!D.relatedTask)D.relatedTask={taskId:G};let F=D.relatedTask?.taskId??G;if(F&&H)await H.updateTaskStatus(F,"input_required");return await this.request(z,A,D)},authInfo:X?.authInfo,requestId:Q.id,requestInfo:X?.requestInfo,taskId:G,taskStore:H,taskRequestedTtl:B?.ttl,closeSSEStream:X?.closeSSEStream,closeStandaloneSSEStream:X?.closeStandaloneSSEStream};Promise.resolve().then(()=>{if(B)this.assertTaskHandlerCapability(Q.method)}).then(()=>Y(Q,K)).then(async(z)=>{if(J.signal.aborted)return;let A={result:z,jsonrpc:"2.0",id:Q.id};if(G&&this._taskMessageQueue)await this._enqueueTaskMessage(G,{type:"response",message:A,timestamp:Date.now()},W?.sessionId);else await W?.send(A)},async(z)=>{if(J.signal.aborted)return;let A={jsonrpc:"2.0",id:Q.id,error:{code:Number.isSafeInteger(z.code)?z.code:C.InternalError,message:z.message??"Internal error",...z.data!==void 0&&{data:z.data}}};if(G&&this._taskMessageQueue)await this._enqueueTaskMessage(G,{type:"error",message:A,timestamp:Date.now()},W?.sessionId);else await W?.send(A)}).catch((z)=>this._onerror(Error(`Failed to send response: ${z}`))).finally(()=>{if(this._requestHandlerAbortControllers.get(Q.id)===J)this._requestHandlerAbortControllers.delete(Q.id)})}_onprogress(Q){let{progressToken:X,...Y}=Q.params,W=Number(X),G=this._progressHandlers.get(W);if(!G){this._onerror(Error(`Received a progress notification for an unknown token: ${JSON.stringify(Q)}`));return}let J=this._responseHandlers.get(W),B=this._timeoutInfo.get(W);if(B&&J&&B.resetTimeoutOnProgress)try{this._resetTimeout(W)}catch(H){this._responseHandlers.delete(W),this._progressHandlers.delete(W),this._cleanupTimeout(W),J(H);return}G(Y)}_onresponse(Q){let X=Number(Q.id),Y=this._requestResolvers.get(X);if(Y){if(this._requestResolvers.delete(X),i0(Q))Y(Q);else{let J=new T(Q.error.code,Q.error.message,Q.error.data);Y(J)}return}let W=this._responseHandlers.get(X);if(W===void 0){this._onerror(Error(`Received a response for an unknown message ID: ${JSON.stringify(Q)}`));return}this._responseHandlers.delete(X),this._cleanupTimeout(X);let G=!1;if(i0(Q)&&Q.result&&typeof Q.result==="object"){let J=Q.result;if(J.task&&typeof J.task==="object"){let B=J.task;if(typeof B.taskId==="string")G=!0,this._taskProgressTokens.set(B.taskId,X)}}if(!G)this._progressHandlers.delete(X);if(i0(Q))W(Q);else{let J=T.fromError(Q.error.code,Q.error.message,Q.error.data);W(J)}}get transport(){return this._transport}async close(){await this._transport?.close()}async*requestStream(Q,X,Y){let{task:W}=Y??{};if(!W){try{yield{type:"result",result:await this.request(Q,X,Y)}}catch(J){yield{type:"error",error:J instanceof T?J:new T(C.InternalError,String(J))}}return}let G;try{let J=await this.request(Q,_1,Y);if(J.task)G=J.task.taskId,yield{type:"taskCreated",task:J.task};else throw new T(C.InternalError,"Task creation did not return a task");while(!0){let B=await this.getTask({taskId:G},Y);if(yield{type:"taskStatus",task:B},B1(B.status)){if(B.status==="completed")yield{type:"result",result:await this.getTaskResult({taskId:G},X,Y)};else if(B.status==="failed")yield{type:"error",error:new T(C.InternalError,`Task ${G} failed`)};else if(B.status==="cancelled")yield{type:"error",error:new T(C.InternalError,`Task ${G} was cancelled`)};return}if(B.status==="input_required"){yield{type:"result",result:await this.getTaskResult({taskId:G},X,Y)};return}let H=B.pollInterval??this._options?.defaultTaskPollInterval??1000;await new Promise((K)=>setTimeout(K,H)),Y?.signal?.throwIfAborted()}}catch(J){yield{type:"error",error:J instanceof T?J:new T(C.InternalError,String(J))}}}request(Q,X,Y){let{relatedRequestId:W,resumptionToken:G,onresumptiontoken:J,task:B,relatedTask:H}=Y??{};return new Promise((K,z)=>{let A=(q)=>{z(q)};if(!this._transport){A(Error("Not connected"));return}if(this._options?.enforceStrictCapabilities===!0)try{if(this.assertCapabilityForMethod(Q.method),B)this.assertTaskCapability(Q.method)}catch(q){A(q);return}Y?.signal?.throwIfAborted();let Z=this._requestMessageId++,D={...Q,jsonrpc:"2.0",id:Z};if(Y?.onprogress)this._progressHandlers.set(Z,Y.onprogress),D.params={...Q.params,_meta:{...Q.params?._meta||{},progressToken:Z}};if(B)D.params={...D.params,task:B};if(H)D.params={...D.params,_meta:{...D.params?._meta||{},[W1]:H}};let F=(q)=>{this._responseHandlers.delete(Z),this._progressHandlers.delete(Z),this._cleanupTimeout(Z),this._transport?.send({jsonrpc:"2.0",method:"notifications/cancelled",params:{requestId:Z,reason:String(q)}},{relatedRequestId:W,resumptionToken:G,onresumptiontoken:J}).catch((j)=>this._onerror(Error(`Failed to send cancellation: ${j}`)));let O=q instanceof T?q:new T(C.RequestTimeout,String(q));z(O)};this._responseHandlers.set(Z,(q)=>{if(Y?.signal?.aborted)return;if(q instanceof Error)return z(q);try{let O=J1(X,q.result);if(!O.success)z(O.error);else K(O.data)}catch(O){z(O)}}),Y?.signal?.addEventListener("abort",()=>{F(Y?.signal?.reason)});let V=Y?.timeout??jF,L=()=>F(T.fromError(C.RequestTimeout,"Request timed out",{timeout:V}));this._setupTimeout(Z,V,Y?.maxTotalTimeout,L,Y?.resetTimeoutOnProgress??!1);let M=H?.taskId;if(M){let q=(O)=>{let j=this._responseHandlers.get(Z);if(j)j(O);else this._onerror(Error(`Response handler missing for side-channeled request ${Z}`))};this._requestResolvers.set(Z,q),this._enqueueTaskMessage(M,{type:"request",message:D,timestamp:Date.now()}).catch((O)=>{this._cleanupTimeout(Z),z(O)})}else this._transport.send(D,{relatedRequestId:W,resumptionToken:G,onresumptiontoken:J}).catch((q)=>{this._cleanupTimeout(Z),z(q)})})}async getTask(Q,X){return this.request({method:"tasks/get",params:Q},f8,X)}async getTaskResult(Q,X,Y){return this.request({method:"tasks/result",params:Q},X,Y)}async listTasks(Q,X){return this.request({method:"tasks/list",params:Q},m8,X)}async cancelTask(Q,X){return this.request({method:"tasks/cancel",params:Q},cG,X)}async notification(Q,X){if(!this._transport)throw Error("Not connected");this.assertNotificationCapability(Q.method);let Y=X?.relatedTask?.taskId;if(Y){let B={...Q,jsonrpc:"2.0",params:{...Q.params,_meta:{...Q.params?._meta||{},[W1]:X.relatedTask}}};await this._enqueueTaskMessage(Y,{type:"notification",message:B,timestamp:Date.now()});return}if((this._options?.debouncedNotificationMethods??[]).includes(Q.method)&&!Q.params&&!X?.relatedRequestId&&!X?.relatedTask){if(this._pendingDebouncedNotifications.has(Q.method))return;this._pendingDebouncedNotifications.add(Q.method),Promise.resolve().then(()=>{if(this._pendingDebouncedNotifications.delete(Q.method),!this._transport)return;let B={...Q,jsonrpc:"2.0"};if(X?.relatedTask)B={...B,params:{...B.params,_meta:{...B.params?._meta||{},[W1]:X.relatedTask}}};this._transport?.send(B,X).catch((H)=>this._onerror(H))});return}let J={...Q,jsonrpc:"2.0"};if(X?.relatedTask)J={...J,params:{...J.params,_meta:{...J.params?._meta||{},[W1]:X.relatedTask}}};await this._transport.send(J,X)}setRequestHandler(Q,X){let Y=sX(Q);this.assertRequestHandlerCapability(Y),this._requestHandlers.set(Y,(W,G)=>{let J=eX(Q,W);return Promise.resolve(X(J,G))})}removeRequestHandler(Q){this._requestHandlers.delete(Q)}assertCanSetRequestHandler(Q){if(this._requestHandlers.has(Q))throw Error(`A request handler for ${Q} already exists, which would be overridden`)}setNotificationHandler(Q,X){let Y=sX(Q);this._notificationHandlers.set(Y,(W)=>{let G=eX(Q,W);return Promise.resolve(X(G))})}removeNotificationHandler(Q){this._notificationHandlers.delete(Q)}_cleanupTaskProgressHandler(Q){let X=this._taskProgressTokens.get(Q);if(X!==void 0)this._progressHandlers.delete(X),this._taskProgressTokens.delete(Q)}async _enqueueTaskMessage(Q,X,Y){if(!this._taskStore||!this._taskMessageQueue)throw Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");let W=this._options?.maxTaskQueueSize;await this._taskMessageQueue.enqueue(Q,X,Y,W)}async _clearTaskQueue(Q,X){if(this._taskMessageQueue){let Y=await this._taskMessageQueue.dequeueAll(Q,X);for(let W of Y)if(W.type==="request"&&G1(W.message)){let G=W.message.id,J=this._requestResolvers.get(G);if(J)J(new T(C.InternalError,"Task cancelled or completed")),this._requestResolvers.delete(G);else this._onerror(Error(`Resolver missing for request ${G} during task ${Q} cleanup`))}}}async _waitForTaskUpdate(Q,X){let Y=this._options?.defaultTaskPollInterval??1000;try{let W=await this._taskStore?.getTask(Q);if(W?.pollInterval)Y=W.pollInterval}catch{}return new Promise((W,G)=>{if(X.aborted){G(new T(C.InvalidRequest,"Request cancelled"));return}let J=setTimeout(W,Y);X.addEventListener("abort",()=>{clearTimeout(J),G(new T(C.InvalidRequest,"Request cancelled"))},{once:!0})})}requestTaskStore(Q,X){let Y=this._taskStore;if(!Y)throw Error("No task store configured");return{createTask:async(W)=>{if(!Q)throw Error("No request provided");return await Y.createTask(W,Q.id,{method:Q.method,params:Q.params},X)},getTask:async(W)=>{let G=await Y.getTask(W,X);if(!G)throw new T(C.InvalidParams,"Failed to retrieve task: Task not found");return G},storeTaskResult:async(W,G,J)=>{await Y.storeTaskResult(W,G,J,X);let B=await Y.getTask(W,X);if(B){let H=P9.parse({method:"notifications/tasks/status",params:B});if(await this.notification(H),B1(B.status))this._cleanupTaskProgressHandler(W)}},getTaskResult:(W)=>{return Y.getTaskResult(W,X)},updateTaskStatus:async(W,G,J)=>{let B=await Y.getTask(W,X);if(!B)throw new T(C.InvalidParams,`Task "${W}" not found - it may have been cleaned up`);if(B1(B.status))throw new T(C.InvalidParams,`Cannot update task "${W}" from terminal status "${B.status}" to "${G}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);await Y.updateTaskStatus(W,G,J,X);let H=await Y.getTask(W,X);if(H){let K=P9.parse({method:"notifications/tasks/status",params:H});if(await this.notification(K),B1(H.status))this._cleanupTaskProgressHandler(W)}},listTasks:(W)=>{return Y.listTasks(W,X)}}}}function aG(Q){return Q!==null&&typeof Q==="object"&&!Array.isArray(Q)}function sG(Q,X){let Y={...Q};for(let W in X){let G=W,J=X[G];if(J===void 0)continue;let B=Y[G];if(aG(B)&&aG(J))Y[G]={...B,...J};else Y[G]=J}return Y}var xK=x4(r6(),1),_K=x4(vK(),1);function rw(){let Q=new xK.default({strict:!1,validateFormats:!0,validateSchema:!1,allErrors:!0});return _K.default(Q),Q}class J4{constructor(Q){this._ajv=Q??rw()}getValidator(Q){let X="$id"in Q&&typeof Q.$id==="string"?this._ajv.getSchema(Q.$id)??this._ajv.compile(Q):this._ajv.compile(Q);return(Y)=>{if(X(Y))return{valid:!0,data:Y,errorMessage:void 0};else return{valid:!1,data:void 0,errorMessage:this._ajv.errorsText(X.errors)}}}}class B4{constructor(Q){this._server=Q}requestStream(Q,X,Y){return this._server.requestStream(Q,X,Y)}createMessageStream(Q,X){let Y=this._server.getClientCapabilities();if((Q.tools||Q.toolChoice)&&!Y?.sampling?.tools)throw Error("Client does not support sampling tools capability.");if(Q.messages.length>0){let W=Q.messages[Q.messages.length-1],G=Array.isArray(W.content)?W.content:[W.content],J=G.some((z)=>z.type==="tool_result"),B=Q.messages.length>1?Q.messages[Q.messages.length-2]:void 0,H=B?Array.isArray(B.content)?B.content:[B.content]:[],K=H.some((z)=>z.type==="tool_use");if(J){if(G.some((z)=>z.type!=="tool_result"))throw Error("The last message must contain only tool_result content if any is present");if(!K)throw Error("tool_result blocks are not matching any tool_use from the previous message")}if(K){let z=new Set(H.filter((Z)=>Z.type==="tool_use").map((Z)=>Z.id)),A=new Set(G.filter((Z)=>Z.type==="tool_result").map((Z)=>Z.toolUseId));if(z.size!==A.size||![...z].every((Z)=>A.has(Z)))throw Error("ids of tool_result blocks and tool_use blocks from previous message do not match")}}return this.requestStream({method:"sampling/createMessage",params:Q},k9,X)}elicitInputStream(Q,X){let Y=this._server.getClientCapabilities(),W=Q.mode??"form";switch(W){case"url":{if(!Y?.elicitation?.url)throw Error("Client does not support url elicitation.");break}case"form":{if(!Y?.elicitation?.form)throw Error("Client does not support form elicitation.");break}}let G=W==="form"&&Q.mode===void 0?{...Q,mode:"form"}:Q;return this.requestStream({method:"elicitation/create",params:G},g1,X)}async getTask(Q,X){return this._server.getTask({taskId:Q},X)}async getTaskResult(Q,X,Y){return this._server.getTaskResult({taskId:Q},X,Y)}async listTasks(Q,X){return this._server.listTasks(Q?{cursor:Q}:void 0,X)}async cancelTask(Q,X){return this._server.cancelTask({taskId:Q},X)}}function yK(Q,X,Y){if(!Q)throw Error(`${Y} does not support task creation (required for ${X})`);switch(X){case"tools/call":if(!Q.tools?.call)throw Error(`${Y} does not support task creation for tools/call (required for ${X})`);break;default:break}}function gK(Q,X,Y){if(!Q)throw Error(`${Y} does not support task creation (required for ${X})`);switch(X){case"sampling/createMessage":if(!Q.sampling?.createMessage)throw Error(`${Y} does not support task creation for sampling/createMessage (required for ${X})`);break;case"elicitation/create":if(!Q.elicitation?.create)throw Error(`${Y} does not support task creation for elicitation/create (required for ${X})`);break;default:break}}class W8 extends Q6{constructor(Q,X){super(X);if(this._serverInfo=Q,this._loggingLevels=new Map,this.LOG_LEVEL_SEVERITY=new Map(b9.options.map((Y,W)=>[Y,W])),this.isMessageIgnored=(Y,W)=>{let G=this._loggingLevels.get(W);return G?this.LOG_LEVEL_SEVERITY.get(Y)<this.LOG_LEVEL_SEVERITY.get(G):!1},this._capabilities=X?.capabilities??{},this._instructions=X?.instructions,this._jsonSchemaValidator=X?.jsonSchemaValidator??new J4,this.setRequestHandler(_8,(Y)=>this._oninitialize(Y)),this.setNotificationHandler(lX,()=>this.oninitialized?.()),this._capabilities.logging)this.setRequestHandler(oX,async(Y,W)=>{let G=W.sessionId||W.requestInfo?.headers["mcp-session-id"]||void 0,{level:J}=Y.params,B=b9.safeParse(J);if(B.success)this._loggingLevels.set(G,B.data);return{}})}get experimental(){if(!this._experimental)this._experimental={tasks:new B4(this)};return this._experimental}registerCapabilities(Q){if(this.transport)throw Error("Cannot register capabilities after connecting to transport");this._capabilities=sG(this._capabilities,Q)}setRequestHandler(Q,X){let W=d8(Q)?.method;if(!W)throw Error("Schema is missing a method literal");let G;if(h1(W)){let B=W;G=B._zod?.def?.value??B.value}else{let B=W;G=B._def?.value??B.value}if(typeof G!=="string")throw Error("Schema method literal must be a string");if(G==="tools/call"){let B=async(H,K)=>{let z=J1(q1,H);if(!z.success){let F=z.error instanceof Error?z.error.message:String(z.error);throw new T(C.InvalidParams,`Invalid tools/call request: ${F}`)}let{params:A}=z.data,Z=await Promise.resolve(X(H,K));if(A.task){let F=J1(_1,Z);if(!F.success){let V=F.error instanceof Error?F.error.message:String(F.error);throw new T(C.InvalidParams,`Invalid task creation result: ${V}`)}return F.data}let D=J1(p8,Z);if(!D.success){let F=D.error instanceof Error?D.error.message:String(D.error);throw new T(C.InvalidParams,`Invalid tools/call result: ${F}`)}return D.data};return super.setRequestHandler(Q,B)}return super.setRequestHandler(Q,X)}assertCapabilityForMethod(Q){switch(Q){case"sampling/createMessage":if(!this._clientCapabilities?.sampling)throw Error(`Client does not support sampling (required for ${Q})`);break;case"elicitation/create":if(!this._clientCapabilities?.elicitation)throw Error(`Client does not support elicitation (required for ${Q})`);break;case"roots/list":if(!this._clientCapabilities?.roots)throw Error(`Client does not support listing roots (required for ${Q})`);break;case"ping":break}}assertNotificationCapability(Q){switch(Q){case"notifications/message":if(!this._capabilities.logging)throw Error(`Server does not support logging (required for ${Q})`);break;case"notifications/resources/updated":case"notifications/resources/list_changed":if(!this._capabilities.resources)throw Error(`Server does not support notifying about resources (required for ${Q})`);break;case"notifications/tools/list_changed":if(!this._capabilities.tools)throw Error(`Server does not support notifying of tool list changes (required for ${Q})`);break;case"notifications/prompts/list_changed":if(!this._capabilities.prompts)throw Error(`Server does not support notifying of prompt list changes (required for ${Q})`);break;case"notifications/elicitation/complete":if(!this._clientCapabilities?.elicitation?.url)throw Error(`Client does not support URL elicitation (required for ${Q})`);break;case"notifications/cancelled":break;case"notifications/progress":break}}assertRequestHandlerCapability(Q){if(!this._capabilities)return;switch(Q){case"completion/complete":if(!this._capabilities.completions)throw Error(`Server does not support completions (required for ${Q})`);break;case"logging/setLevel":if(!this._capabilities.logging)throw Error(`Server does not support logging (required for ${Q})`);break;case"prompts/get":case"prompts/list":if(!this._capabilities.prompts)throw Error(`Server does not support prompts (required for ${Q})`);break;case"resources/list":case"resources/templates/list":case"resources/read":if(!this._capabilities.resources)throw Error(`Server does not support resources (required for ${Q})`);break;case"tools/call":case"tools/list":if(!this._capabilities.tools)throw Error(`Server does not support tools (required for ${Q})`);break;case"tasks/get":case"tasks/list":case"tasks/result":case"tasks/cancel":if(!this._capabilities.tasks)throw Error(`Server does not support tasks capability (required for ${Q})`);break;case"ping":case"initialize":break}}assertTaskCapability(Q){gK(this._clientCapabilities?.tasks?.requests,Q,"Client")}assertTaskHandlerCapability(Q){if(!this._capabilities)return;yK(this._capabilities.tasks?.requests,Q,"Server")}async _oninitialize(Q){let X=Q.params.protocolVersion;return this._clientCapabilities=Q.params.capabilities,this._clientVersion=Q.params.clientInfo,{protocolVersion:v1.includes(X)?X:yX,capabilities:this.getCapabilities(),serverInfo:this._serverInfo,...this._instructions&&{instructions:this._instructions}}}getClientCapabilities(){return this._clientCapabilities}getClientVersion(){return this._clientVersion}getCapabilities(){return this._capabilities}async ping(){return this.request({method:"ping"},v8)}async createMessage(Q,X){if(Q.tools||Q.toolChoice){if(!this._clientCapabilities?.sampling?.tools)throw Error("Client does not support sampling tools capability.")}if(Q.messages.length>0){let Y=Q.messages[Q.messages.length-1],W=Array.isArray(Y.content)?Y.content:[Y.content],G=W.some((K)=>K.type==="tool_result"),J=Q.messages.length>1?Q.messages[Q.messages.length-2]:void 0,B=J?Array.isArray(J.content)?J.content:[J.content]:[],H=B.some((K)=>K.type==="tool_use");if(G){if(W.some((K)=>K.type!=="tool_result"))throw Error("The last message must contain only tool_result content if any is present");if(!H)throw Error("tool_result blocks are not matching any tool_use from the previous message")}if(H){let K=new Set(B.filter((A)=>A.type==="tool_use").map((A)=>A.id)),z=new Set(W.filter((A)=>A.type==="tool_result").map((A)=>A.toolUseId));if(K.size!==z.size||![...K].every((A)=>z.has(A)))throw Error("ids of tool_result blocks and tool_use blocks from previous message do not match")}}if(Q.tools)return this.request({method:"sampling/createMessage",params:Q},rX,X);return this.request({method:"sampling/createMessage",params:Q},k9,X)}async elicitInput(Q,X){switch(Q.mode??"form"){case"url":{if(!this._clientCapabilities?.elicitation?.url)throw Error("Client does not support url elicitation.");let W=Q;return this.request({method:"elicitation/create",params:W},g1,X)}case"form":{if(!this._clientCapabilities?.elicitation?.form)throw Error("Client does not support form elicitation.");let W=Q.mode==="form"?Q:{...Q,mode:"form"},G=await this.request({method:"elicitation/create",params:W},g1,X);if(G.action==="accept"&&G.content&&W.requestedSchema)try{let B=this._jsonSchemaValidator.getValidator(W.requestedSchema)(G.content);if(!B.valid)throw new T(C.InvalidParams,`Elicitation response content does not match requested schema: ${B.errorMessage}`)}catch(J){if(J instanceof T)throw J;throw new T(C.InternalError,`Error validating elicitation response: ${J instanceof Error?J.message:String(J)}`)}return G}}}createElicitationCompletionNotifier(Q,X){if(!this._clientCapabilities?.elicitation?.url)throw Error("Client does not support URL elicitation (required for notifications/elicitation/complete)");return()=>this.notification({method:"notifications/elicitation/complete",params:{elicitationId:Q}},X)}async listRoots(Q,X){return this.request({method:"roots/list",params:Q},tX,X)}async sendLoggingMessage(Q,X){if(this._capabilities.logging){if(!this.isMessageIgnored(Q.level,X))return this.notification({method:"notifications/message",params:Q})}}async sendResourceUpdated(Q){return this.notification({method:"notifications/resources/updated",params:Q})}async sendResourceListChanged(){return this.notification({method:"notifications/resources/list_changed"})}async sendToolListChanged(){return this.notification({method:"notifications/tools/list_changed"})}async sendPromptListChanged(){return this.notification({method:"notifications/prompts/list_changed"})}}function H4(){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, which reads the user's trading journal over an authenticated connection.","","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.","","Data tools:"," • list_trades — list the user's trades, filtered by account, status, pair, or date range.","","Recommended flow:"," 1. Try the data tool the user asked for (e.g. list_trades).",' 2. If it reports "not signed in", call login, wait for the user to finish in the browser, then retry.',"","More read and write tools are being added; for now the journal is read-only over this connection."].join(`
85
- `)}}]}]}function K4(){return[]}function l(Q){return Q}var tw='Not signed in to Aether Wealth. Call the "login" tool to authenticate, then retry.';async function r(Q){if(!await Q.session.getAccessToken())throw Error(tw)}var hK={type:"integer",minimum:1,maximum:200,description:"Max rows to return"},fK={type:"integer",minimum:1,description:"Page number (1-indexed)"};function SQ(Q,X){if(X===null||X===void 0){if(Q.required&&Q.required.length>0)return{ok:!1,reason:`missing required: ${Q.required.join(", ")}`};return{ok:!0,args:{}}}if(typeof X!=="object"||Array.isArray(X))return{ok:!1,reason:"args must be an object"};let Y=X,W=aw(Q,Y)??sw(Q,Y);return W?{ok:!1,reason:W}:{ok:!0,args:Y}}function aw(Q,X){for(let Y of Q.required??[])if(!(Y in X)||X[Y]===void 0||X[Y]===null)return`missing required: ${Y}`;return null}function sw(Q,X){if(Q.additionalProperties!==!1)return null;for(let Y of Object.keys(X))if(!(Y in Q.properties))return`unknown property: ${Y}`;return null}function z4(Q){return{triggerType:Q.triggerType??"close",isPersistent:Q.isPersistent??!1,notifyEmail:Q.notifyEmail??!0,notifyPush:Q.notifyPush??!0,...Q.message===void 0?{}:{message:Q.message},...Q.expiresAt===void 0?{}:{expiresAt:Q.expiresAt}}}function uK(Q){return{...Q.includeArchived===void 0?{}:{includeArchived:Q.includeArchived},...Q.pair===void 0?{}:{pair:Q.pair}}}function ew(Q){return{id:Q.id,...Q.price===void 0?{}:{price:Q.price},...Q.price1===void 0?{}:{price1:Q.price1},...Q.price2===void 0?{}:{price2:Q.price2},...Q.time1===void 0?{}:{time1:Q.time1},...Q.time2===void 0?{}:{time2:Q.time2},...Q.condition===void 0?{}:{condition:Q.condition},...Q.triggerType===void 0?{}:{triggerType:Q.triggerType},...Q.timeframe===void 0?{}:{timeframe:Q.timeframe},...Q.isActive===void 0?{}:{isActive:Q.isActive},...Q.isArchived===void 0?{}:{isArchived:Q.isArchived},...Q.isPersistent===void 0?{}:{isPersistent:Q.isPersistent},...Q.notifyEmail===void 0?{}:{notifyEmail:Q.notifyEmail},...Q.notifyPush===void 0?{}:{notifyPush:Q.notifyPush},...Q.message===void 0?{}:{message:Q.message},...Q.expiresAt===void 0?{}:{expiresAt:Q.expiresAt}}}function Q$(Q){return{id:Q.id,...Q.condition===void 0?{}:{condition:Q.condition},...Q.dedupMode===void 0?{}:{dedupMode:Q.dedupMode},...Q.triggerType===void 0?{}:{triggerType:Q.triggerType},...Q.isActive===void 0?{}:{isActive:Q.isActive},...Q.isArchived===void 0?{}:{isArchived:Q.isArchived},...Q.isPersistent===void 0?{}:{isPersistent:Q.isPersistent},...Q.notifyEmail===void 0?{}:{notifyEmail:Q.notifyEmail},...Q.notifyPush===void 0?{}:{notifyPush:Q.notifyPush},...Q.message===void 0?{}:{message:Q.message},...Q.expiresAt===void 0?{}:{expiresAt:Q.expiresAt}}}var A4={type:"string",enum:["above","below","crosses"]},G8={triggerType:{type:"string",enum:["close","wick"],description:"default: close"},isPersistent:{type:"boolean",description:"Re-arm after firing. default: false"},notifyEmail:{type:"boolean",description:"default: true"},notifyPush:{type:"boolean",description:"default: true"},message:{type:"string"},expiresAt:{type:"string",description:"ISO 8601 datetime"}},lK=[l({name:"list_alerts",description:"List your price + trendline alerts. Optionally include archived or filter by pair. Requires sign-in.",inputSchema:{type:"object",properties:{includeArchived:{type:"boolean"},pair:{type:"string"}},additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.list.query(uK(Q))}}),l({name:"create_price_alert",description:"Create a price-level alert (fires when price is above/below/crosses a level). Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},price:{type:"number"},condition:A4,...G8},required:["pair","timeframe","price","condition"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe,price:Q.price,condition:Q.condition,...z4(Q)};return X.trpc.alerts.createPrice.mutate(Y)}}),l({name:"create_trendline_alert",description:"Create a trendline alert (fires when price crosses a line defined by two points). Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string"},timeframe:{type:"string"},price1:{type:"number"},time1:{type:"string",description:"ISO 8601 datetime of point 1"},price2:{type:"number"},time2:{type:"string",description:"ISO 8601 datetime of point 2"},condition:A4,...G8},required:["pair","timeframe","price1","time1","price2","time2","condition"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe,price1:Q.price1,time1:Q.time1,price2:Q.price2,time2:Q.time2,condition:Q.condition,...z4(Q)};return X.trpc.alerts.createTrendline.mutate(Y)}}),l({name:"update_alert",description:"Update a price/trendline alert (e.g. change level, archive, mute). Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},price:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"string"},time2:{type:"string"},condition:A4,timeframe:{type:"string"},isActive:{type:"boolean"},isArchived:{type:"boolean"},...G8},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.update.mutate(ew(Q))}}),l({name:"delete_alert",description:"Delete a price/trendline alert by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.delete.mutate({id:Q.id})}}),l({name:"list_indicator_alerts",description:"List your indicator-based alerts. Optionally include archived or filter by pair. Requires sign-in.",inputSchema:{type:"object",properties:{includeArchived:{type:"boolean"},pair:{type:"string"}},additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y=uK(Q);return X.trpc.alerts.indicator.list.query(Y)}}),l({name:"create_indicator_alert",description:"Create an indicator alert (fires on an indicator output condition, e.g. RSI crosses 70). `condition` is { op, threshold } or { op, other }. Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string"},timeframe:{type:"string"},indicatorType:{type:"string",description:"e.g. rsi, ema, macd"},indicatorParams:{type:"object",description:"Indicator settings, e.g. { length: 14 }"},outputSeries:{type:"string",description:"Which output series to watch"},condition:{type:"object",description:"{ op: gt|gte|lt|lte|eq|crosses_above|crosses_below, threshold } or { op: *_series, other }"},dedupMode:{type:"string",enum:["edge","continuous"],description:"default: edge"},...G8},required:["pair","timeframe","indicatorType","indicatorParams","outputSeries","condition"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe,indicatorType:Q.indicatorType,indicatorParams:Q.indicatorParams,outputSeries:Q.outputSeries,condition:Q.condition,dedupMode:Q.dedupMode??"edge",...z4(Q)};return X.trpc.alerts.indicator.create.mutate(Y)}}),l({name:"update_indicator_alert",description:"Update an indicator alert (condition, dedup, archive, mute). Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},condition:{type:"object"},dedupMode:{type:"string",enum:["edge","continuous"]},isActive:{type:"boolean"},isArchived:{type:"boolean"},...G8},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.indicator.update.mutate(Q$(Q))}}),l({name:"delete_indicator_alert",description:"Delete an indicator alert by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.indicator.delete.mutate({id:Q.id})}})];var mK=[l({name:"login",description:"Sign in to Aether Wealth. Opens your browser to authenticate; once you approve, the MCP stores a per-user token and the data tools become available.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async(Q,X)=>{return await X.session.login(),{status:"signed_in",message:"Signed in to Aether Wealth."}}}),l({name:"logout",description:"Sign out of Aether Wealth on this device. Clears the stored access token.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async(Q,X)=>{return await X.session.logout(),{status:"signed_out",message:"Signed out on this device."}}})];var X$="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";function Y$(Q){return{pair:Q.pair,interval:Q.interval,indicators:Q.indicators.map((X)=>({name:X.name,...X.params===void 0?{}:{params:X.params}})),...Q.outputsize===void 0?{}:{outputsize:Q.outputsize}}}var cK=[l({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 — ${X$}. 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(Q,X)=>{return await r(X),X.trpc.technicalIndicators.get.query(Y$(Q))}})];function W$(Q){return{...Q.currency===void 0?{}:{currency:Q.currency},...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to},...Q.indicator===void 0?{}:{indicator:Q.indicator},...Q.impactMin===void 0?{}:{impactMin:Q.impactMin},...Q.view===void 0?{}:{view:Q.view}}}function G$(Q){return{currency:Q.currency,indicator:Q.indicator,...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to},...Q.limit===void 0?{}:{limit:Q.limit}}}var pK=[l({name:"get_candles",description:"Get the most recent OHLC candles (up to 500) for a pair + timeframe. Timeframe may be a base (1m, 5m, 15m, 30m, 1h, 4h, 1d) or any integer multiple of one (e.g. 2h, 3h, 12h, 90m), aggregated server-side; invalid or unsupported timeframes return an error, not an empty list.",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."}},required:["pair","timeframe"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe};return X.trpc.marketData.candles.query(Y)}}),l({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(Q,X)=>{return await r(X),X.trpc.marketData.listCalendar.query(W$(Q))}}),l({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(Q,X)=>{return await r(X),X.trpc.marketData.listIndicatorSeries.query(G$(Q))}}),l({name:"get_market_config",description:"Get the catalog of supported instruments + timeframes (useful before calling other tools).",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.marketData.getConfig.query()}})];function J$(Q){return{...Q.accountId===void 0?{}:{accountId:Q.accountId},...Q.status===void 0||Q.status==="ALL"?{}:{status:Q.status},...Q.pair===void 0?{}:{pair:Q.pair},...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to},...Q.page===void 0?{}:{page:Q.page},...Q.limit===void 0?{}:{limit:Q.limit}}}function iK(Q){return{...Q.stopLoss===void 0?{}:{stopLoss:Q.stopLoss},...Q.takeProfit===void 0?{}:{takeProfit:Q.takeProfit},...Q.lotSize===void 0?{}:{lotSize:Q.lotSize},...Q.notes===void 0?{}:{notes:Q.notes},...Q.timeframe===void 0?{}:{timeframe:Q.timeframe},...Q.riskPercent===void 0?{}:{riskPercent:Q.riskPercent},...Q.tags===void 0?{}:{tags:Q.tags}}}function B$(Q){return{accountId:Q.accountId,pair:Q.pair,direction:Q.direction,entryPrice:Q.entryPrice,entryTime:Q.entryTime,...iK(Q)}}function H$(Q){return{id:Q.id,...Q.pair===void 0?{}:{pair:Q.pair},...Q.direction===void 0?{}:{direction:Q.direction},...Q.entryPrice===void 0?{}:{entryPrice:Q.entryPrice},...Q.entryTime===void 0?{}:{entryTime:Q.entryTime},...iK(Q)}}function K$(Q){return{id:Q.id,exitPrice:Q.exitPrice,exitTime:Q.exitTime,...Q.fees===void 0?{}:{fees:Q.fees},...Q.swap===void 0?{}:{swap:Q.swap},...Q.notes===void 0?{}:{notes:Q.notes}}}function z$(Q){return{...Q.accountId===void 0?{}:{accountId:Q.accountId},...Q.pair===void 0?{}:{pair:Q.pair},...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to}}}var dK={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"}}},nK=[l({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:fK,limit:hK},additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.trading.trades.list.query(J$(Q))}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.trades.get.query({id:Q.id})}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.accounts.list.query()}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.analytics.stats.query(z$(Q))}}),l({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"},...dK},required:["accountId","pair","direction","entryPrice","entryTime"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.trading.trades.create.mutate(B$(Q))}}),l({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"},...dK},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.trading.trades.update.mutate(H$(Q))}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.trades.close.mutate(K$(Q))}})];function Z4(){return[...mK,...nK,...lK,...pK,...cK]}var A$="aether-wealth-mcp",Z$="0.1.18-beta.4";function oK(Q){let X=new W8({name:A$,version:Z$},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return F$(X,Q),X}function F$(Q,X){let Y=!1,W=(G)=>{let J=X.clientGate?.nudge;if(J&&!Y)return Y=!0,`${J}
85
+ `)}}]}]}function K4(){return[]}function l(Q){return Q}var tw='Not signed in to Aether Wealth. Call the "login" tool to authenticate, then retry.';async function r(Q){if(!await Q.session.getAccessToken())throw Error(tw)}var hK={type:"integer",minimum:1,maximum:200,description:"Max rows to return"},fK={type:"integer",minimum:1,description:"Page number (1-indexed)"};function SQ(Q,X){if(X===null||X===void 0){if(Q.required&&Q.required.length>0)return{ok:!1,reason:`missing required: ${Q.required.join(", ")}`};return{ok:!0,args:{}}}if(typeof X!=="object"||Array.isArray(X))return{ok:!1,reason:"args must be an object"};let Y=X,W=aw(Q,Y)??sw(Q,Y);return W?{ok:!1,reason:W}:{ok:!0,args:Y}}function aw(Q,X){for(let Y of Q.required??[])if(!(Y in X)||X[Y]===void 0||X[Y]===null)return`missing required: ${Y}`;return null}function sw(Q,X){if(Q.additionalProperties!==!1)return null;for(let Y of Object.keys(X))if(!(Y in Q.properties))return`unknown property: ${Y}`;return null}function z4(Q){return{triggerType:Q.triggerType??"close",isPersistent:Q.isPersistent??!1,notifyEmail:Q.notifyEmail??!0,notifyPush:Q.notifyPush??!0,...Q.message===void 0?{}:{message:Q.message},...Q.expiresAt===void 0?{}:{expiresAt:Q.expiresAt}}}function uK(Q){return{...Q.includeArchived===void 0?{}:{includeArchived:Q.includeArchived},...Q.pair===void 0?{}:{pair:Q.pair}}}function ew(Q){return{id:Q.id,...Q.price===void 0?{}:{price:Q.price},...Q.price1===void 0?{}:{price1:Q.price1},...Q.price2===void 0?{}:{price2:Q.price2},...Q.time1===void 0?{}:{time1:Q.time1},...Q.time2===void 0?{}:{time2:Q.time2},...Q.condition===void 0?{}:{condition:Q.condition},...Q.triggerType===void 0?{}:{triggerType:Q.triggerType},...Q.timeframe===void 0?{}:{timeframe:Q.timeframe},...Q.isActive===void 0?{}:{isActive:Q.isActive},...Q.isArchived===void 0?{}:{isArchived:Q.isArchived},...Q.isPersistent===void 0?{}:{isPersistent:Q.isPersistent},...Q.notifyEmail===void 0?{}:{notifyEmail:Q.notifyEmail},...Q.notifyPush===void 0?{}:{notifyPush:Q.notifyPush},...Q.message===void 0?{}:{message:Q.message},...Q.expiresAt===void 0?{}:{expiresAt:Q.expiresAt}}}function Q$(Q){return{id:Q.id,...Q.condition===void 0?{}:{condition:Q.condition},...Q.dedupMode===void 0?{}:{dedupMode:Q.dedupMode},...Q.triggerType===void 0?{}:{triggerType:Q.triggerType},...Q.isActive===void 0?{}:{isActive:Q.isActive},...Q.isArchived===void 0?{}:{isArchived:Q.isArchived},...Q.isPersistent===void 0?{}:{isPersistent:Q.isPersistent},...Q.notifyEmail===void 0?{}:{notifyEmail:Q.notifyEmail},...Q.notifyPush===void 0?{}:{notifyPush:Q.notifyPush},...Q.message===void 0?{}:{message:Q.message},...Q.expiresAt===void 0?{}:{expiresAt:Q.expiresAt}}}var A4={type:"string",enum:["above","below","crosses"]},G8={triggerType:{type:"string",enum:["close","wick"],description:"default: close"},isPersistent:{type:"boolean",description:"Re-arm after firing. default: false"},notifyEmail:{type:"boolean",description:"default: true"},notifyPush:{type:"boolean",description:"default: true"},message:{type:"string"},expiresAt:{type:"string",description:"ISO 8601 datetime"}},lK=[l({name:"list_alerts",description:"List your price + trendline alerts. Optionally include archived or filter by pair. Requires sign-in.",inputSchema:{type:"object",properties:{includeArchived:{type:"boolean"},pair:{type:"string"}},additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.list.query(uK(Q))}}),l({name:"create_price_alert",description:"Create a price-level alert (fires when price is above/below/crosses a level). Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string",description:"e.g. EURUSD, BTCUSD"},timeframe:{type:"string",description:"e.g. 1h, 4h, 1d"},price:{type:"number"},condition:A4,...G8},required:["pair","timeframe","price","condition"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe,price:Q.price,condition:Q.condition,...z4(Q)};return X.trpc.alerts.createPrice.mutate(Y)}}),l({name:"create_trendline_alert",description:"Create a trendline alert (fires when price crosses a line defined by two points). Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string"},timeframe:{type:"string"},price1:{type:"number"},time1:{type:"string",description:"ISO 8601 datetime of point 1"},price2:{type:"number"},time2:{type:"string",description:"ISO 8601 datetime of point 2"},condition:A4,...G8},required:["pair","timeframe","price1","time1","price2","time2","condition"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe,price1:Q.price1,time1:Q.time1,price2:Q.price2,time2:Q.time2,condition:Q.condition,...z4(Q)};return X.trpc.alerts.createTrendline.mutate(Y)}}),l({name:"update_alert",description:"Update a price/trendline alert (e.g. change level, archive, mute). Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},price:{type:"number"},price1:{type:"number"},price2:{type:"number"},time1:{type:"string"},time2:{type:"string"},condition:A4,timeframe:{type:"string"},isActive:{type:"boolean"},isArchived:{type:"boolean"},...G8},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.update.mutate(ew(Q))}}),l({name:"delete_alert",description:"Delete a price/trendline alert by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.delete.mutate({id:Q.id})}}),l({name:"list_indicator_alerts",description:"List your indicator-based alerts. Optionally include archived or filter by pair. Requires sign-in.",inputSchema:{type:"object",properties:{includeArchived:{type:"boolean"},pair:{type:"string"}},additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y=uK(Q);return X.trpc.alerts.indicator.list.query(Y)}}),l({name:"create_indicator_alert",description:"Create an indicator alert (fires on an indicator output condition, e.g. RSI crosses 70). `condition` is { op, threshold } or { op, other }. Requires sign-in.",inputSchema:{type:"object",properties:{pair:{type:"string"},timeframe:{type:"string"},indicatorType:{type:"string",description:"e.g. rsi, ema, macd"},indicatorParams:{type:"object",description:"Indicator settings, e.g. { length: 14 }"},outputSeries:{type:"string",description:"Which output series to watch"},condition:{type:"object",description:"{ op: gt|gte|lt|lte|eq|crosses_above|crosses_below, threshold } or { op: *_series, other }"},dedupMode:{type:"string",enum:["edge","continuous"],description:"default: edge"},...G8},required:["pair","timeframe","indicatorType","indicatorParams","outputSeries","condition"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe,indicatorType:Q.indicatorType,indicatorParams:Q.indicatorParams,outputSeries:Q.outputSeries,condition:Q.condition,dedupMode:Q.dedupMode??"edge",...z4(Q)};return X.trpc.alerts.indicator.create.mutate(Y)}}),l({name:"update_indicator_alert",description:"Update an indicator alert (condition, dedup, archive, mute). Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"},condition:{type:"object"},dedupMode:{type:"string",enum:["edge","continuous"]},isActive:{type:"boolean"},isArchived:{type:"boolean"},...G8},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.indicator.update.mutate(Q$(Q))}}),l({name:"delete_indicator_alert",description:"Delete an indicator alert by id. Requires sign-in.",inputSchema:{type:"object",properties:{id:{type:"string"}},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.alerts.indicator.delete.mutate({id:Q.id})}})];var mK=[l({name:"login",description:"Sign in to Aether Wealth. Opens your browser to authenticate; once you approve, the MCP stores a per-user token and the data tools become available.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async(Q,X)=>{return await X.session.login(),{status:"signed_in",message:"Signed in to Aether Wealth."}}}),l({name:"logout",description:"Sign out of Aether Wealth on this device. Clears the stored access token.",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async(Q,X)=>{return await X.session.logout(),{status:"signed_out",message:"Signed out on this device."}}})];var X$="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";function Y$(Q){return{pair:Q.pair,interval:Q.interval,indicators:Q.indicators.map((X)=>({name:X.name,...X.params===void 0?{}:{params:X.params}})),...Q.outputsize===void 0?{}:{outputsize:Q.outputsize}}}var cK=[l({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 — ${X$}. 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(Q,X)=>{return await r(X),X.trpc.technicalIndicators.get.query(Y$(Q))}})];function W$(Q){return{...Q.currency===void 0?{}:{currency:Q.currency},...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to},...Q.indicator===void 0?{}:{indicator:Q.indicator},...Q.impactMin===void 0?{}:{impactMin:Q.impactMin},...Q.view===void 0?{}:{view:Q.view}}}function G$(Q){return{currency:Q.currency,indicator:Q.indicator,...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to},...Q.limit===void 0?{}:{limit:Q.limit}}}var pK=[l({name:"get_candles",description:"Get the most recent OHLC candles (up to 500) for a pair + timeframe. Timeframe may be a base (1m, 5m, 15m, 30m, 1h, 4h, 1d) or any integer multiple of one (e.g. 2h, 3h, 12h, 90m), aggregated server-side; invalid or unsupported timeframes return an error, not an empty list.",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."}},required:["pair","timeframe"],additionalProperties:!1},handler:async(Q,X)=>{await r(X);let Y={pair:Q.pair,timeframe:Q.timeframe};return X.trpc.marketData.candles.query(Y)}}),l({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(Q,X)=>{return await r(X),X.trpc.marketData.listCalendar.query(W$(Q))}}),l({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(Q,X)=>{return await r(X),X.trpc.marketData.listIndicatorSeries.query(G$(Q))}}),l({name:"get_market_config",description:"Get the catalog of supported instruments + timeframes (useful before calling other tools).",inputSchema:{type:"object",properties:{},additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.marketData.getConfig.query()}})];function J$(Q){return{...Q.accountId===void 0?{}:{accountId:Q.accountId},...Q.status===void 0||Q.status==="ALL"?{}:{status:Q.status},...Q.pair===void 0?{}:{pair:Q.pair},...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to},...Q.page===void 0?{}:{page:Q.page},...Q.limit===void 0?{}:{limit:Q.limit}}}function iK(Q){return{...Q.stopLoss===void 0?{}:{stopLoss:Q.stopLoss},...Q.takeProfit===void 0?{}:{takeProfit:Q.takeProfit},...Q.lotSize===void 0?{}:{lotSize:Q.lotSize},...Q.notes===void 0?{}:{notes:Q.notes},...Q.timeframe===void 0?{}:{timeframe:Q.timeframe},...Q.riskPercent===void 0?{}:{riskPercent:Q.riskPercent},...Q.tags===void 0?{}:{tags:Q.tags}}}function B$(Q){return{accountId:Q.accountId,pair:Q.pair,direction:Q.direction,entryPrice:Q.entryPrice,entryTime:Q.entryTime,...iK(Q)}}function H$(Q){return{id:Q.id,...Q.pair===void 0?{}:{pair:Q.pair},...Q.direction===void 0?{}:{direction:Q.direction},...Q.entryPrice===void 0?{}:{entryPrice:Q.entryPrice},...Q.entryTime===void 0?{}:{entryTime:Q.entryTime},...iK(Q)}}function K$(Q){return{id:Q.id,exitPrice:Q.exitPrice,exitTime:Q.exitTime,...Q.fees===void 0?{}:{fees:Q.fees},...Q.swap===void 0?{}:{swap:Q.swap},...Q.notes===void 0?{}:{notes:Q.notes}}}function z$(Q){return{...Q.accountId===void 0?{}:{accountId:Q.accountId},...Q.pair===void 0?{}:{pair:Q.pair},...Q.from===void 0?{}:{from:Q.from},...Q.to===void 0?{}:{to:Q.to}}}var dK={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"}}},nK=[l({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:fK,limit:hK},additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.trading.trades.list.query(J$(Q))}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.trades.get.query({id:Q.id})}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.accounts.list.query()}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.analytics.stats.query(z$(Q))}}),l({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"},...dK},required:["accountId","pair","direction","entryPrice","entryTime"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.trading.trades.create.mutate(B$(Q))}}),l({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"},...dK},required:["id"],additionalProperties:!1},handler:async(Q,X)=>{return await r(X),X.trpc.trading.trades.update.mutate(H$(Q))}}),l({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(Q,X)=>{return await r(X),X.trpc.trading.trades.close.mutate(K$(Q))}})];function Z4(){return[...mK,...nK,...lK,...pK,...cK]}var A$="aether-wealth-mcp",Z$="0.1.18";function oK(Q){let X=new W8({name:A$,version:Z$},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return F$(X,Q),X}function F$(Q,X){let Y=!1,W=(G)=>{let J=X.clientGate?.nudge;if(J&&!Y)return Y=!0,`${J}
86
86
 
87
87
  ${G}`;return G};Q.setRequestHandler(C9,async()=>({tools:Array.from(X.tools.values()).map((G)=>({name:G.name,description:G.description,inputSchema:G.inputSchema}))})),Q.setRequestHandler(q1,async(G)=>{if(X.clientGate?.blocked)return{content:[{type:"text",text:X.clientGate.blockedMessage??"Update required."}],isError:!0};let{name:J,arguments:B}=G.params,H=X.tools.get(J);if(!H)throw new T(C.MethodNotFound,`tool not found: ${J}`);let K=SQ(H.inputSchema,B);if(!K.ok)throw new T(C.InvalidParams,`invalid arguments for ${J}: ${K.reason}`);try{let z=await H.handler(K.args,X.toolContext);return{content:[{type:"text",text:W(D$(z))}],isError:!1}}catch(z){return{content:[{type:"text",text:V$(z)}],isError:!0}}}),Q.setRequestHandler(T9,async()=>({resources:Array.from(X.resources.values()).map((G)=>({uri:G.uri,name:G.name,description:G.description,mimeType:G.mimeType}))})),Q.setRequestHandler(j9,async(G)=>{let{uri:J}=G.params,B=X.resources.get(J);if(!B)throw new T(C.MethodNotFound,`resource not found: ${J}`);let H=await B.handler(X.toolContext);return{contents:[{uri:J,mimeType:B.mimeType,text:H.text}]}}),Q.setRequestHandler(S9,async()=>({prompts:Array.from(X.prompts.values()).map((G)=>({name:G.name,description:G.description,...G.arguments?{arguments:G.arguments}:{}}))})),Q.setRequestHandler(R9,async(G)=>{let{name:J,arguments:B}=G.params,H=X.prompts.get(J);if(!H)throw new T(C.MethodNotFound,`prompt not found: ${J}`);let K={};for(let[A,Z]of Object.entries(B??{}))if(typeof Z==="string")K[A]=Z;for(let A of H.arguments??[])if(A.required&&!(A.name in K))throw new T(C.InvalidParams,`prompts/get: missing required argument: ${A.name}`);let z=H.handler(K);return{description:H.description,messages:z}})}function D$(Q){if(typeof Q==="string")return Q;return JSON.stringify(Q,null,2)}function V$(Q){if(!(Q instanceof Error))return String(Q);let X=Q,Y=[`${X.name??"Error"}: ${X.message}`];if(typeof X.status==="number")Y.push(`status=${X.status}`);if(X.code)Y.push(`code=${X.code}`);if(X.method&&X.path)Y.push(`request=${X.method} ${X.path}`);return Y.join(" | ")}var U$="/mcp";async function aK(Q,X){let Y=X.path??U$,W=X.hostname??U1,G=X.log??((K)=>{process.stderr.write(K)});if(!X.bearerToken)G(`[http] WARNING: no bearer token configured; binding to ${W}:${X.port}${Y} unauthenticated
88
88
  `);let J=Q.getRegistryBundle(),B=async(K)=>{if(new URL(K.url).pathname!==Y)return new Response("not found",{status:404});if(K.method==="GET")return new Response(JSON.stringify({ok:!0,transport:"streamable-http"}),{status:200,headers:{"content-type":"application/json"}});if(X.bearerToken&&!E$(K,X.bearerToken))return new Response("unauthorized",{status:401});let A=oK(J),Z=new aX({enableJsonResponse:!0});try{return await A.connect(Z),await Z.handleRequest(K)}catch(D){return G(`[http] dispatch error: ${sK(D)}
@@ -102,7 +102,7 @@ p{margin:0;color:hsl(var(--muted));font-size:.95rem}
102
102
  <body><main class="card"><div class="badge">${Y}</div><h1>${Q.heading}</h1><p>${Q.detail}</p><div class="brand">Aether Wealth</div></main></body></html>`}var f$=Zz({title:"Signed in · Aether Wealth",heading:"Signed in to Aether Wealth",detail:"You can close this tab and return to your assistant.",ok:!0}),Az=Zz({title:"Sign-in failed · Aether Wealth",heading:"Sign-in failed",detail:"You can close this tab and try again from your assistant.",ok:!1});function u$(Q,X){return new Promise((Y)=>{let W=(G)=>{Q.removeListener("error",W),Y({ok:!1,...G.code==="EADDRINUSE"?{}:{error:G}})};Q.once("error",W),Q.listen(X,U1,()=>{Q.removeListener("error",W),Y({ok:!0})})})}async function l$(Q,X){let Y;for(let G of X){let J=await u$(Q,G);if(J.ok)return G;if(J.error)Y=J.error}let W=Y?`: ${Y.message}`:"";throw Error(`could not bind any loopback port (${X.join(", ")}) for OAuth redirect${W}`)}async function Fz(Q,X){let Y=null,W=null,G=null,J=(K)=>{if(Y)return;if(Y=K,K?.ok)W?.(K.value);else if(K)G?.(K.error)},B=h$((K,z)=>{let A=new URL(K.url??"/",`http://${U1}`);if(A.pathname!==X){z.writeHead(404).end("not found");return}let Z=A.searchParams.get("error"),D=A.searchParams.get("code"),F=A.searchParams.get("state");if(Z){z.writeHead(400,{"content-type":"text/html"}).end(Az),J({ok:!1,error:Error(`authorization denied: ${Z}`)});return}if(!D||!F){z.writeHead(400,{"content-type":"text/html"}).end(Az),J({ok:!1,error:Error("callback missing code or state")});return}z.writeHead(200,{"content-type":"text/html"}).end(f$),J({ok:!0,value:{code:D,state:F}})}),H=await l$(B,Q);return{redirectUri:Hz(H),waitForCode(K){return new Promise((z,A)=>{let Z=setTimeout(()=>A(Error("timed out waiting for the browser sign-in to complete")),K);if(Z.unref(),W=(D)=>{clearTimeout(Z),z(D)},G=(D)=>{clearTimeout(Z),A(D)},Y?.ok)W(Y.value);else if(Y)G(Y.error)})},close(){return new Promise((K)=>B.close(()=>K()))}}}import{createHash as m$,randomBytes as Dz}from"node:crypto";function U4(Q){return Q.toString("base64").replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function Vz(Q={}){let X=U4((Q.randomBytesImpl??Dz)(32)),Y=Q.hashImpl?Q.hashImpl(X):m$("sha256").update(X).digest();return{verifier:X,challenge:U4(Y),method:"S256"}}function Lz(Q=Dz){return U4(Q(16))}var c$=300000;async function Uz(Q){let X=Q.fetchImpl??fetch,Y=Q.log??(()=>{}),W=Q.now??Date.now,G=await zz(Q.authBaseUrl,X),J=Vz(),B=Lz(),H=await Fz(Jz,L4);try{let K=d$(Q.authBaseUrl),z=p$(G.authorizationEndpoint,{clientId:V4,redirectUri:H.redirectUri,scope:Bz.join(" "),state:B,codeChallenge:J.challenge,...K?{prompt:"consent"}:{}});if(Y(`Opening your browser to sign in. If it doesn't open, visit:
103
103
  ${z}
104
104
  `),!await(Q.openBrowserImpl??Gz)(z))Y(`Could not open a browser automatically — open the URL above manually.
105
- `);let{code:Z,state:D}=await H.waitForCode(c$);if(D!==B)throw Error("OAuth state mismatch — aborting login (possible CSRF).");return await i$(G.tokenEndpoint,{code:Z,codeVerifier:J.verifier,redirectUri:H.redirectUri,clientId:V4},X,W)}finally{await H.close()}}function p$(Q,X){let Y=new URL(Q);if(Y.searchParams.set("response_type","code"),Y.searchParams.set("client_id",X.clientId),Y.searchParams.set("redirect_uri",X.redirectUri),Y.searchParams.set("scope",X.scope),Y.searchParams.set("state",X.state),Y.searchParams.set("code_challenge",X.codeChallenge),Y.searchParams.set("code_challenge_method","S256"),X.prompt)Y.searchParams.set("prompt",X.prompt);return Y.toString()}function d$(Q){try{return Wz(new URL(Q).hostname)}catch{return!1}}async function i$(Q,X,Y=fetch,W=Date.now){let G=new URLSearchParams({grant_type:"authorization_code",code:X.code,redirect_uri:X.redirectUri,client_id:X.clientId,code_verifier:X.codeVerifier}),J=await Y(Q,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded",accept:"application/json"},body:G.toString()}),B=await RQ(J),H=B.access_token,K=B.expires_in;if(!J.ok||typeof H!=="string")throw Error(`OAuth token exchange failed: ${n$(B,J.status)}`);if(typeof K!=="number"||K<=0)throw Error("OAuth token exchange returned no valid expires_in");return{accessToken:H,expiresAt:W()+K*1000}}function n$(Q,X){if(typeof Q.error_description==="string")return Q.error_description;if(typeof Q.error==="string")return Q.error;return`HTTP ${X}`}class N4{config;store;deps;constructor(Q,X,Y={}){this.config=Q;this.store=X;this.deps=Y}async getAccessToken(){let Q=await this.store.get(this.config.host);if(!Q)return null;let X=(this.deps.now??Date.now)();if(Q.expiresAt<=X)return await this.store.clear(this.config.host),null;return Q.accessToken}async login(){let Q=await(this.deps.runLoginImpl??Uz)({authBaseUrl:this.config.baseUrl,...this.deps.log?{log:this.deps.log}:{},...this.deps.now?{now:this.deps.now}:{}});await this.store.set(this.config.host,{accessToken:Q.accessToken,expiresAt:Q.expiresAt})}async logout(){await this.store.clear(this.config.host)}}var Nz="aether-wealth-mcp",M4="0.1.18-beta.4";function o$(Q){return{toolContext:Q.toolContext,tools:new Map((Q.tools??Z4()).map((X)=>[X.name,X])),resources:new Map((Q.resources??K4()).map((X)=>[X.uri,X])),prompts:new Map((Q.prompts??H4()).map((X)=>[X.name,X])),...Q.clientGate?{clientGate:Q.clientGate}:{},log:Q.log??((X)=>{process.stderr.write(X)})}}function r$(Q){let X=new W8({name:Nz,version:M4},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return t$(X,Q),X}class q4{server;bundle;constructor(Q){this.bundle=o$(Q),this.server=r$(this.bundle),this.server.oninitialized=()=>{this.bundle.log(`${Nz} initialized
105
+ `);let{code:Z,state:D}=await H.waitForCode(c$);if(D!==B)throw Error("OAuth state mismatch — aborting login (possible CSRF).");return await i$(G.tokenEndpoint,{code:Z,codeVerifier:J.verifier,redirectUri:H.redirectUri,clientId:V4},X,W)}finally{await H.close()}}function p$(Q,X){let Y=new URL(Q);if(Y.searchParams.set("response_type","code"),Y.searchParams.set("client_id",X.clientId),Y.searchParams.set("redirect_uri",X.redirectUri),Y.searchParams.set("scope",X.scope),Y.searchParams.set("state",X.state),Y.searchParams.set("code_challenge",X.codeChallenge),Y.searchParams.set("code_challenge_method","S256"),X.prompt)Y.searchParams.set("prompt",X.prompt);return Y.toString()}function d$(Q){try{return Wz(new URL(Q).hostname)}catch{return!1}}async function i$(Q,X,Y=fetch,W=Date.now){let G=new URLSearchParams({grant_type:"authorization_code",code:X.code,redirect_uri:X.redirectUri,client_id:X.clientId,code_verifier:X.codeVerifier}),J=await Y(Q,{method:"POST",headers:{"content-type":"application/x-www-form-urlencoded",accept:"application/json"},body:G.toString()}),B=await RQ(J),H=B.access_token,K=B.expires_in;if(!J.ok||typeof H!=="string")throw Error(`OAuth token exchange failed: ${n$(B,J.status)}`);if(typeof K!=="number"||K<=0)throw Error("OAuth token exchange returned no valid expires_in");return{accessToken:H,expiresAt:W()+K*1000}}function n$(Q,X){if(typeof Q.error_description==="string")return Q.error_description;if(typeof Q.error==="string")return Q.error;return`HTTP ${X}`}class N4{config;store;deps;constructor(Q,X,Y={}){this.config=Q;this.store=X;this.deps=Y}async getAccessToken(){let Q=await this.store.get(this.config.host);if(!Q)return null;let X=(this.deps.now??Date.now)();if(Q.expiresAt<=X)return await this.store.clear(this.config.host),null;return Q.accessToken}async login(){let Q=await(this.deps.runLoginImpl??Uz)({authBaseUrl:this.config.baseUrl,...this.deps.log?{log:this.deps.log}:{},...this.deps.now?{now:this.deps.now}:{}});await this.store.set(this.config.host,{accessToken:Q.accessToken,expiresAt:Q.expiresAt})}async logout(){await this.store.clear(this.config.host)}}var Nz="aether-wealth-mcp",M4="0.1.18";function o$(Q){return{toolContext:Q.toolContext,tools:new Map((Q.tools??Z4()).map((X)=>[X.name,X])),resources:new Map((Q.resources??K4()).map((X)=>[X.uri,X])),prompts:new Map((Q.prompts??H4()).map((X)=>[X.name,X])),...Q.clientGate?{clientGate:Q.clientGate}:{},log:Q.log??((X)=>{process.stderr.write(X)})}}function r$(Q){let X=new W8({name:Nz,version:M4},{capabilities:{tools:{listChanged:!1},resources:{listChanged:!1,subscribe:!1},prompts:{listChanged:!1}}});return t$(X,Q),X}class q4{server;bundle;constructor(Q){this.bundle=o$(Q),this.server=r$(this.bundle),this.server.oninitialized=()=>{this.bundle.log(`${Nz} initialized
106
106
  `)}}async connect(Q){await this.server.connect(Q)}async close(){await this.server.close()}isInitialized(){return this.server.getClientVersion()!==void 0}getRegistryBundle(){return this.bundle}}function t$(Q,X){let Y=!1,W=(G)=>{let J=X.clientGate?.nudge;if(J&&!Y)return Y=!0,`${J}
107
107
 
108
108
  ${G}`;return G};Q.setRequestHandler(C9,async()=>({tools:Array.from(X.tools.values()).map((G)=>({name:G.name,description:G.description,inputSchema:G.inputSchema}))})),Q.setRequestHandler(q1,async(G)=>{if(X.clientGate?.blocked)return{content:[{type:"text",text:X.clientGate.blockedMessage??"Update required."}],isError:!0};let{name:J,arguments:B}=G.params,H=X.tools.get(J);if(!H)throw new T(C.MethodNotFound,`tool not found: ${J}`);let K=SQ(H.inputSchema,B);if(!K.ok)throw new T(C.InvalidParams,`invalid arguments for ${J}: ${K.reason}`);try{let z=await H.handler(K.args,X.toolContext);return{content:[{type:"text",text:W(a$(z))}],isError:!1}}catch(z){return{content:[{type:"text",text:s$(z)}],isError:!0}}}),Q.setRequestHandler(T9,async()=>({resources:Array.from(X.resources.values()).map((G)=>({uri:G.uri,name:G.name,description:G.description,mimeType:G.mimeType}))})),Q.setRequestHandler(j9,async(G)=>{let{uri:J}=G.params,B=X.resources.get(J);if(!B)throw new T(C.MethodNotFound,`resource not found: ${J}`);let H=await B.handler(X.toolContext);return{contents:[{uri:J,mimeType:B.mimeType,text:H.text}]}}),Q.setRequestHandler(S9,async()=>({prompts:Array.from(X.prompts.values()).map((G)=>({name:G.name,description:G.description,...G.arguments?{arguments:G.arguments}:{}}))})),Q.setRequestHandler(R9,async(G)=>{let{name:J,arguments:B}=G.params,H=X.prompts.get(J);if(!H)throw new T(C.MethodNotFound,`prompt not found: ${J}`);let K={};for(let[A,Z]of Object.entries(B??{}))if(typeof Z==="string")K[A]=Z;for(let A of H.arguments??[])if(A.required&&!(A.name in K))throw new T(C.InvalidParams,`prompts/get: missing required argument: ${A.name}`);let z=H.handler(K);return{description:H.description,messages:z}})}function a$(Q){if(typeof Q==="string")return Q;return JSON.stringify(Q,null,2)}function s$(Q){if(!(Q instanceof Error))return String(Q);let X=Q,Y=[`${X.name??"Error"}: ${X.message}`];if(typeof X.status==="number")Y.push(`status=${X.status}`);if(X.code)Y.push(`code=${X.code}`);if(X.method&&X.path)Y.push(`request=${X.method} ${X.path}`);return Y.join(" | ")}import qz from"node:process";class w4{append(Q){this._buffer=this._buffer?Buffer.concat([this._buffer,Q]):Q}readMessage(){if(!this._buffer)return null;let Q=this._buffer.indexOf(`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@aetherwealth/mcp",
3
- "version": "0.1.18-beta.4",
4
- "description": "Aether Wealth MCP server per-user OAuth access to your trading journal, alerts, and market data from Claude, Codex, and other MCP clients.",
3
+ "version": "0.1.18",
4
+ "description": "Official Aether Wealth MCP server for AI trading assistants: OAuth access to your trading journal, alerts, market data, macro calendar, and technical indicators.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "aether-wealth-mcp": "dist/index.js"
@@ -18,6 +18,26 @@
18
18
  "publishConfig": {
19
19
  "access": "public"
20
20
  },
21
+ "keywords": [
22
+ "aether wealth",
23
+ "mcp",
24
+ "model context protocol",
25
+ "trading mcp",
26
+ "ai trading assistant",
27
+ "trading journal",
28
+ "forex",
29
+ "market data",
30
+ "technical indicators",
31
+ "oauth"
32
+ ],
21
33
  "license": "UNLICENSED",
22
- "homepage": "https://aetherwealth.ai"
34
+ "homepage": "https://aetherwealth.ai/mcp",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/Opus-Aether-AI/webapp.git",
38
+ "directory": "apps/aether-wealth-mcp"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/Opus-Aether-AI/webapp/issues"
42
+ }
23
43
  }