@pgnio/helios-mcp 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -139,19 +139,21 @@ var protectiveOrderKindSchema = import_zod.z.enum(["STOP_LOSS", "TAKE_PROFIT"]);
139
139
  var protectiveOrderSideSchema = import_zod.z.enum(["BUY", "SELL"]);
140
140
  var protectiveOrderPositionSideSchema = import_zod.z.enum(["BOTH", "LONG", "SHORT"]);
141
141
  var protectiveOrderSchema = import_zod.z.object({
142
+ /** Helios's record id: what helios_positions_delete_tp_sl takes (the Hyperliquid order id works there too). */
142
143
  id: import_zod.z.string(),
143
- binanceOrderId: import_zod.z.number().int().nonnegative().optional(),
144
- clientOrderId: import_zod.z.string().optional(),
144
+ /** The Hyperliquid order id (named after the retired Binance field). */
145
+ binanceOrderId: import_zod.z.number().int().nonnegative().nullish(),
146
+ clientOrderId: import_zod.z.string().nullish(),
145
147
  symbol: import_zod.z.string(),
146
148
  side: protectiveOrderSideSchema,
147
- positionSide: protectiveOrderPositionSideSchema,
149
+ positionSide: protectiveOrderPositionSideSchema.nullish().transform((value) => value ?? "BOTH"),
148
150
  status: import_zod.z.string(),
149
151
  kind: protectiveOrderKindSchema,
150
- groupId: import_zod.z.string(),
151
- stopPrice: import_zod.z.string().optional(),
152
- parentClientOrderId: import_zod.z.string().optional(),
153
- assetId: import_zod.z.number().int().nonnegative().optional(),
154
- createdAt: import_zod.z.string().datetime()
152
+ groupId: import_zod.z.string().nullish(),
153
+ stopPrice: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).nullish().transform((value) => value == null ? value : String(value)),
154
+ parentClientOrderId: import_zod.z.string().nullish(),
155
+ assetId: import_zod.z.number().int().nonnegative().nullish(),
156
+ createdAt: import_zod.z.string().nullish()
155
157
  });
156
158
  var hyperliquidPortfolioSnapshotSchema = import_zod.z.object({
157
159
  provider: import_zod.z.literal("hyperliquid"),
@@ -160,6 +162,7 @@ var hyperliquidPortfolioSnapshotSchema = import_zod.z.object({
160
162
  totalMargin: import_zod.z.number(),
161
163
  unrealizedPnl: import_zod.z.number(),
162
164
  positions: import_zod.z.array(openPositionSchema),
165
+ /** Unreadable entries are dropped (see readProtectiveOrders) rather than failing the read. */
163
166
  protectiveOrders: import_zod.z.array(protectiveOrderSchema)
164
167
  });
165
168
  var portfolioInputSchema = import_zod.z.object({
@@ -354,6 +357,8 @@ var closePositionInputSchema = import_zod.z.object({
354
357
  });
355
358
  var closePositionResultSchema = import_zod.z.object({
356
359
  status: import_zod.z.literal("submitted"),
360
+ /** `no_position`: nothing was open on this coin, so no order was sent. Absent from older data-bridges. */
361
+ outcome: import_zod.z.enum(["closed", "no_position"]).optional(),
357
362
  provider: providerSchema.optional(),
358
363
  symbol: import_zod.z.string(),
359
364
  mode: import_zod.z.literal("FULL"),
@@ -544,7 +549,8 @@ var HeliosAgentDataBridgeClient = class {
544
549
  }
545
550
  return portfolioSnapshotSchema.parse({
546
551
  ...result,
547
- provider: parsedInput.provider
552
+ provider: parsedInput.provider,
553
+ protectiveOrders: readProtectiveOrders(result.protectiveOrders)
548
554
  });
549
555
  }
550
556
  async getLedgerFills(accessToken, input = {}) {
@@ -597,7 +603,7 @@ var HeliosAgentDataBridgeClient = class {
597
603
  }
598
604
  async closePosition(accessToken, input) {
599
605
  const parsedInput = closePositionInputSchema.parse(input);
600
- await this.request(
606
+ const payload = await this.request(
601
607
  accessToken,
602
608
  "positions.close",
603
609
  {
@@ -608,8 +614,10 @@ var HeliosAgentDataBridgeClient = class {
608
614
  },
609
615
  "POST"
610
616
  );
617
+ const outcome = this.tryUnwrapResult(payload)?.outcome;
611
618
  return closePositionResultSchema.parse({
612
619
  status: "submitted",
620
+ ...outcome === "closed" || outcome === "no_position" ? { outcome } : {},
613
621
  provider: parsedInput.provider,
614
622
  symbol: parsedInput.symbol,
615
623
  mode: "FULL",
@@ -707,6 +715,14 @@ var HeliosAgentDataBridgeClient = class {
707
715
  });
708
716
  }
709
717
  }
718
+ /** The result of a mutation older data-bridges answer with nothing; null when there is none. */
719
+ tryUnwrapResult(payload) {
720
+ try {
721
+ return this.unwrapResult(payload) ?? null;
722
+ } catch {
723
+ return null;
724
+ }
725
+ }
710
726
  unwrapResult(payload) {
711
727
  if (!payload || typeof payload !== "object") {
712
728
  throw new HeliosAgentApiError({
@@ -767,6 +783,13 @@ var HeliosAgentDataBridgeClient = class {
767
783
  return envelope;
768
784
  }
769
785
  };
786
+ function readProtectiveOrders(value) {
787
+ if (!Array.isArray(value)) return [];
788
+ return value.flatMap((entry) => {
789
+ const parsed = protectiveOrderSchema.safeParse(entry);
790
+ return parsed.success ? [parsed.data] : [];
791
+ });
792
+ }
770
793
  var DEFAULT_AGENT_REFRESH_WINDOW_MS = 60 * 1e3;
771
794
  var agentConnectionStatusSchema = import_zod3.z.enum([
772
795
  "disconnected",
@@ -1220,6 +1243,9 @@ var HeliosAgentMcpDocsService = class {
1220
1243
  }
1221
1244
  async getDocument(input) {
1222
1245
  const documentPath = resolveDocumentPath(this.docsRoot, input.path);
1246
+ if (!(0, import_node_fs.existsSync)(documentPath) || !(0, import_node_fs.statSync)(documentPath).isFile()) {
1247
+ throw new Error(`No document "${input.path}". helios_docs_get_index lists the documents.`);
1248
+ }
1223
1249
  const body = (0, import_node_fs.readFileSync)(documentPath, "utf8");
1224
1250
  const stats = (0, import_node_fs.statSync)(documentPath);
1225
1251
  const relativePath = toPosixPath(import_node_path.default.relative(this.docsRoot, documentPath));
@@ -2522,7 +2548,7 @@ function createHeliosAccountToolDefinitions(service) {
2522
2548
  {
2523
2549
  name: "helios_portfolio_get",
2524
2550
  title: "Get Helios Portfolio",
2525
- description: "Returns a provider-specific portfolio snapshot. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2551
+ description: "Returns balances, open positions and their TP/SL (protectiveOrders, each with the id helios_positions_delete_tp_sl takes). Provider may be omitted if one is already pinned with helios_provider_set_active.",
2526
2552
  inputSchema: providerSelectionInputSchema,
2527
2553
  async execute(input) {
2528
2554
  const result = portfolioToolOutputSchema.parse(await service.getPortfolio(input));
@@ -2618,7 +2644,7 @@ function createHeliosMarketToolDefinitions(service) {
2618
2644
  {
2619
2645
  name: "helios_market_get_snapshot",
2620
2646
  title: "Get Helios Market Snapshot",
2621
- description: "Returns a compact market snapshot for the selected, all, or explicitly requested symbols. By default: the top 50 coins by market cap with the main columns; name symbols to get other coins, pass fields for other columns.",
2647
+ description: "Returns a compact market snapshot for the selected, all, or explicitly requested symbols. By default: the top 50 coins by market cap with the main columns; name symbols to get other coins, pass fields for other columns. What each column means: helios_docs_get_document with path ai-agent-indicators/00_columns.txt (read it once).",
2622
2648
  inputSchema: marketSnapshotInputSchema,
2623
2649
  async execute(input) {
2624
2650
  const result = marketSnapshotToolOutputSchema.parse(await service.getSnapshot(input));
@@ -2759,7 +2785,7 @@ function createHeliosTradingToolDefinitions(service) {
2759
2785
  content: [
2760
2786
  {
2761
2787
  type: "text",
2762
- text: result.status === "connected" ? `Submitted Helios full close for ${result.closure.provider ?? input.provider} ${result.closure.symbol}.` : result.message
2788
+ text: result.status !== "connected" ? result.message : result.closure.outcome === "no_position" ? `No open ${result.closure.symbol} position: nothing was closed and no order was sent.` : `Sent a market order closing the full ${result.closure.provider ?? input.provider ?? "hyperliquid"} ${result.closure.symbol} position. Check helios_positions_get_open to confirm.`
2763
2789
  }
2764
2790
  ],
2765
2791
  structuredContent: result.status === "connected" ? result : {
@@ -2793,7 +2819,7 @@ function createHeliosTradingToolDefinitions(service) {
2793
2819
  {
2794
2820
  name: "helios_positions_delete_tp_sl",
2795
2821
  title: "Delete Helios TP/SL",
2796
- description: "Deletes an existing Helios take-profit or stop-loss order by order id. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2822
+ description: "Deletes a take-profit or stop-loss. orderId is the id from helios_portfolio_get protectiveOrders, or the Hyperliquid order id of the TP/SL. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2797
2823
  inputSchema: deleteTpSlInputSchema,
2798
2824
  async execute(input) {
2799
2825
  const result = deleteTpSlToolOutputSchema.parse(await service.deleteTpSl(input));
@@ -2886,7 +2912,7 @@ async function createHeliosAgentMcpServer(options) {
2886
2912
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
2887
2913
  const server = new McpServer({
2888
2914
  name: "helios",
2889
- version: "0.2.7"
2915
+ version: "0.2.9"
2890
2916
  });
2891
2917
  const sessionStore = options.sessionStore ?? new FileAgentSessionStore(options.sessionPath ?? getDefaultHeliosAgentMcpSessionPath());
2892
2918
  const client = new HeliosAgentAuthClient({
package/dist/cli.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  createHeliosAgentMcpServer
4
- } from "./chunk-V453JYRN.mjs";
4
+ } from "./chunk-7TMDGB6C.mjs";
5
5
 
6
6
  // src/cli.ts
7
7
  async function main() {
package/dist/index.d.mts CHANGED
@@ -94,28 +94,28 @@ declare const portfolioSnapshotSchema: z.ZodObject<{
94
94
  }, z.core.$strip>>;
95
95
  protectiveOrders: z.ZodArray<z.ZodObject<{
96
96
  id: z.ZodString;
97
- binanceOrderId: z.ZodOptional<z.ZodNumber>;
98
- clientOrderId: z.ZodOptional<z.ZodString>;
97
+ binanceOrderId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
98
+ clientOrderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
99
99
  symbol: z.ZodString;
100
100
  side: z.ZodEnum<{
101
101
  BUY: "BUY";
102
102
  SELL: "SELL";
103
103
  }>;
104
- positionSide: z.ZodEnum<{
104
+ positionSide: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
105
105
  LONG: "LONG";
106
106
  SHORT: "SHORT";
107
107
  BOTH: "BOTH";
108
- }>;
108
+ }>>>, z.ZodTransform<"LONG" | "SHORT" | "BOTH", "LONG" | "SHORT" | "BOTH" | null | undefined>>;
109
109
  status: z.ZodString;
110
110
  kind: z.ZodEnum<{
111
111
  STOP_LOSS: "STOP_LOSS";
112
112
  TAKE_PROFIT: "TAKE_PROFIT";
113
113
  }>;
114
- groupId: z.ZodString;
115
- stopPrice: z.ZodOptional<z.ZodString>;
116
- parentClientOrderId: z.ZodOptional<z.ZodString>;
117
- assetId: z.ZodOptional<z.ZodNumber>;
118
- createdAt: z.ZodString;
114
+ groupId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
115
+ stopPrice: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>, z.ZodTransform<string | null | undefined, string | number | null | undefined>>;
116
+ parentClientOrderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
117
+ assetId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
118
+ createdAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
119
119
  }, z.core.$strip>>;
120
120
  }, z.core.$strip>;
121
121
  declare const ledgerFillsInputSchema: z.ZodObject<{
@@ -440,6 +440,10 @@ declare const closePositionInputSchema: z.ZodObject<{
440
440
  }, z.core.$strip>;
441
441
  declare const closePositionResultSchema: z.ZodObject<{
442
442
  status: z.ZodLiteral<"submitted">;
443
+ outcome: z.ZodOptional<z.ZodEnum<{
444
+ closed: "closed";
445
+ no_position: "no_position";
446
+ }>>;
443
447
  provider: z.ZodOptional<z.ZodEnum<{
444
448
  hyperliquid: "hyperliquid";
445
449
  }>>;
@@ -606,6 +610,8 @@ declare class HeliosAgentDataBridgeClient {
606
610
  private request;
607
611
  private buildProcedureUrl;
608
612
  private parseJson;
613
+ /** The result of a mutation older data-bridges answer with nothing; null when there is none. */
614
+ private tryUnwrapResult;
609
615
  private unwrapResult;
610
616
  private extractErrorCode;
611
617
  private extractErrorMessage;
package/dist/index.d.ts CHANGED
@@ -94,28 +94,28 @@ declare const portfolioSnapshotSchema: z.ZodObject<{
94
94
  }, z.core.$strip>>;
95
95
  protectiveOrders: z.ZodArray<z.ZodObject<{
96
96
  id: z.ZodString;
97
- binanceOrderId: z.ZodOptional<z.ZodNumber>;
98
- clientOrderId: z.ZodOptional<z.ZodString>;
97
+ binanceOrderId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
98
+ clientOrderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
99
99
  symbol: z.ZodString;
100
100
  side: z.ZodEnum<{
101
101
  BUY: "BUY";
102
102
  SELL: "SELL";
103
103
  }>;
104
- positionSide: z.ZodEnum<{
104
+ positionSide: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodEnum<{
105
105
  LONG: "LONG";
106
106
  SHORT: "SHORT";
107
107
  BOTH: "BOTH";
108
- }>;
108
+ }>>>, z.ZodTransform<"LONG" | "SHORT" | "BOTH", "LONG" | "SHORT" | "BOTH" | null | undefined>>;
109
109
  status: z.ZodString;
110
110
  kind: z.ZodEnum<{
111
111
  STOP_LOSS: "STOP_LOSS";
112
112
  TAKE_PROFIT: "TAKE_PROFIT";
113
113
  }>;
114
- groupId: z.ZodString;
115
- stopPrice: z.ZodOptional<z.ZodString>;
116
- parentClientOrderId: z.ZodOptional<z.ZodString>;
117
- assetId: z.ZodOptional<z.ZodNumber>;
118
- createdAt: z.ZodString;
114
+ groupId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
115
+ stopPrice: z.ZodPipe<z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>>, z.ZodTransform<string | null | undefined, string | number | null | undefined>>;
116
+ parentClientOrderId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
117
+ assetId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
118
+ createdAt: z.ZodOptional<z.ZodNullable<z.ZodString>>;
119
119
  }, z.core.$strip>>;
120
120
  }, z.core.$strip>;
121
121
  declare const ledgerFillsInputSchema: z.ZodObject<{
@@ -440,6 +440,10 @@ declare const closePositionInputSchema: z.ZodObject<{
440
440
  }, z.core.$strip>;
441
441
  declare const closePositionResultSchema: z.ZodObject<{
442
442
  status: z.ZodLiteral<"submitted">;
443
+ outcome: z.ZodOptional<z.ZodEnum<{
444
+ closed: "closed";
445
+ no_position: "no_position";
446
+ }>>;
443
447
  provider: z.ZodOptional<z.ZodEnum<{
444
448
  hyperliquid: "hyperliquid";
445
449
  }>>;
@@ -606,6 +610,8 @@ declare class HeliosAgentDataBridgeClient {
606
610
  private request;
607
611
  private buildProcedureUrl;
608
612
  private parseJson;
613
+ /** The result of a mutation older data-bridges answer with nothing; null when there is none. */
614
+ private tryUnwrapResult;
609
615
  private unwrapResult;
610
616
  private extractErrorCode;
611
617
  private extractErrorMessage;
package/dist/index.js CHANGED
@@ -179,19 +179,21 @@ var protectiveOrderKindSchema = import_zod.z.enum(["STOP_LOSS", "TAKE_PROFIT"]);
179
179
  var protectiveOrderSideSchema = import_zod.z.enum(["BUY", "SELL"]);
180
180
  var protectiveOrderPositionSideSchema = import_zod.z.enum(["BOTH", "LONG", "SHORT"]);
181
181
  var protectiveOrderSchema = import_zod.z.object({
182
+ /** Helios's record id: what helios_positions_delete_tp_sl takes (the Hyperliquid order id works there too). */
182
183
  id: import_zod.z.string(),
183
- binanceOrderId: import_zod.z.number().int().nonnegative().optional(),
184
- clientOrderId: import_zod.z.string().optional(),
184
+ /** The Hyperliquid order id (named after the retired Binance field). */
185
+ binanceOrderId: import_zod.z.number().int().nonnegative().nullish(),
186
+ clientOrderId: import_zod.z.string().nullish(),
185
187
  symbol: import_zod.z.string(),
186
188
  side: protectiveOrderSideSchema,
187
- positionSide: protectiveOrderPositionSideSchema,
189
+ positionSide: protectiveOrderPositionSideSchema.nullish().transform((value) => value ?? "BOTH"),
188
190
  status: import_zod.z.string(),
189
191
  kind: protectiveOrderKindSchema,
190
- groupId: import_zod.z.string(),
191
- stopPrice: import_zod.z.string().optional(),
192
- parentClientOrderId: import_zod.z.string().optional(),
193
- assetId: import_zod.z.number().int().nonnegative().optional(),
194
- createdAt: import_zod.z.string().datetime()
192
+ groupId: import_zod.z.string().nullish(),
193
+ stopPrice: import_zod.z.union([import_zod.z.string(), import_zod.z.number()]).nullish().transform((value) => value == null ? value : String(value)),
194
+ parentClientOrderId: import_zod.z.string().nullish(),
195
+ assetId: import_zod.z.number().int().nonnegative().nullish(),
196
+ createdAt: import_zod.z.string().nullish()
195
197
  });
196
198
  var hyperliquidPortfolioSnapshotSchema = import_zod.z.object({
197
199
  provider: import_zod.z.literal("hyperliquid"),
@@ -200,6 +202,7 @@ var hyperliquidPortfolioSnapshotSchema = import_zod.z.object({
200
202
  totalMargin: import_zod.z.number(),
201
203
  unrealizedPnl: import_zod.z.number(),
202
204
  positions: import_zod.z.array(openPositionSchema),
205
+ /** Unreadable entries are dropped (see readProtectiveOrders) rather than failing the read. */
203
206
  protectiveOrders: import_zod.z.array(protectiveOrderSchema)
204
207
  });
205
208
  var portfolioInputSchema = import_zod.z.object({
@@ -394,6 +397,8 @@ var closePositionInputSchema = import_zod.z.object({
394
397
  });
395
398
  var closePositionResultSchema = import_zod.z.object({
396
399
  status: import_zod.z.literal("submitted"),
400
+ /** `no_position`: nothing was open on this coin, so no order was sent. Absent from older data-bridges. */
401
+ outcome: import_zod.z.enum(["closed", "no_position"]).optional(),
397
402
  provider: providerSchema.optional(),
398
403
  symbol: import_zod.z.string(),
399
404
  mode: import_zod.z.literal("FULL"),
@@ -584,7 +589,8 @@ var HeliosAgentDataBridgeClient = class {
584
589
  }
585
590
  return portfolioSnapshotSchema.parse({
586
591
  ...result,
587
- provider: parsedInput.provider
592
+ provider: parsedInput.provider,
593
+ protectiveOrders: readProtectiveOrders(result.protectiveOrders)
588
594
  });
589
595
  }
590
596
  async getLedgerFills(accessToken, input = {}) {
@@ -637,7 +643,7 @@ var HeliosAgentDataBridgeClient = class {
637
643
  }
638
644
  async closePosition(accessToken, input) {
639
645
  const parsedInput = closePositionInputSchema.parse(input);
640
- await this.request(
646
+ const payload = await this.request(
641
647
  accessToken,
642
648
  "positions.close",
643
649
  {
@@ -648,8 +654,10 @@ var HeliosAgentDataBridgeClient = class {
648
654
  },
649
655
  "POST"
650
656
  );
657
+ const outcome = this.tryUnwrapResult(payload)?.outcome;
651
658
  return closePositionResultSchema.parse({
652
659
  status: "submitted",
660
+ ...outcome === "closed" || outcome === "no_position" ? { outcome } : {},
653
661
  provider: parsedInput.provider,
654
662
  symbol: parsedInput.symbol,
655
663
  mode: "FULL",
@@ -747,6 +755,14 @@ var HeliosAgentDataBridgeClient = class {
747
755
  });
748
756
  }
749
757
  }
758
+ /** The result of a mutation older data-bridges answer with nothing; null when there is none. */
759
+ tryUnwrapResult(payload) {
760
+ try {
761
+ return this.unwrapResult(payload) ?? null;
762
+ } catch {
763
+ return null;
764
+ }
765
+ }
750
766
  unwrapResult(payload) {
751
767
  if (!payload || typeof payload !== "object") {
752
768
  throw new HeliosAgentApiError({
@@ -807,6 +823,13 @@ var HeliosAgentDataBridgeClient = class {
807
823
  return envelope;
808
824
  }
809
825
  };
826
+ function readProtectiveOrders(value) {
827
+ if (!Array.isArray(value)) return [];
828
+ return value.flatMap((entry) => {
829
+ const parsed = protectiveOrderSchema.safeParse(entry);
830
+ return parsed.success ? [parsed.data] : [];
831
+ });
832
+ }
810
833
  var DEFAULT_AGENT_REFRESH_WINDOW_MS = 60 * 1e3;
811
834
  var agentConnectionStatusSchema = import_zod3.z.enum([
812
835
  "disconnected",
@@ -1260,6 +1283,9 @@ var HeliosAgentMcpDocsService = class {
1260
1283
  }
1261
1284
  async getDocument(input) {
1262
1285
  const documentPath = resolveDocumentPath(this.docsRoot, input.path);
1286
+ if (!(0, import_node_fs.existsSync)(documentPath) || !(0, import_node_fs.statSync)(documentPath).isFile()) {
1287
+ throw new Error(`No document "${input.path}". helios_docs_get_index lists the documents.`);
1288
+ }
1263
1289
  const body = (0, import_node_fs.readFileSync)(documentPath, "utf8");
1264
1290
  const stats = (0, import_node_fs.statSync)(documentPath);
1265
1291
  const relativePath = toPosixPath(import_node_path.default.relative(this.docsRoot, documentPath));
@@ -2596,7 +2622,7 @@ function createHeliosAccountToolDefinitions(service) {
2596
2622
  {
2597
2623
  name: "helios_portfolio_get",
2598
2624
  title: "Get Helios Portfolio",
2599
- description: "Returns a provider-specific portfolio snapshot. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2625
+ description: "Returns balances, open positions and their TP/SL (protectiveOrders, each with the id helios_positions_delete_tp_sl takes). Provider may be omitted if one is already pinned with helios_provider_set_active.",
2600
2626
  inputSchema: providerSelectionInputSchema,
2601
2627
  async execute(input) {
2602
2628
  const result = portfolioToolOutputSchema.parse(await service.getPortfolio(input));
@@ -2692,7 +2718,7 @@ function createHeliosMarketToolDefinitions(service) {
2692
2718
  {
2693
2719
  name: "helios_market_get_snapshot",
2694
2720
  title: "Get Helios Market Snapshot",
2695
- description: "Returns a compact market snapshot for the selected, all, or explicitly requested symbols. By default: the top 50 coins by market cap with the main columns; name symbols to get other coins, pass fields for other columns.",
2721
+ description: "Returns a compact market snapshot for the selected, all, or explicitly requested symbols. By default: the top 50 coins by market cap with the main columns; name symbols to get other coins, pass fields for other columns. What each column means: helios_docs_get_document with path ai-agent-indicators/00_columns.txt (read it once).",
2696
2722
  inputSchema: marketSnapshotInputSchema,
2697
2723
  async execute(input) {
2698
2724
  const result = marketSnapshotToolOutputSchema.parse(await service.getSnapshot(input));
@@ -2833,7 +2859,7 @@ function createHeliosTradingToolDefinitions(service) {
2833
2859
  content: [
2834
2860
  {
2835
2861
  type: "text",
2836
- text: result.status === "connected" ? `Submitted Helios full close for ${result.closure.provider ?? input.provider} ${result.closure.symbol}.` : result.message
2862
+ text: result.status !== "connected" ? result.message : result.closure.outcome === "no_position" ? `No open ${result.closure.symbol} position: nothing was closed and no order was sent.` : `Sent a market order closing the full ${result.closure.provider ?? input.provider ?? "hyperliquid"} ${result.closure.symbol} position. Check helios_positions_get_open to confirm.`
2837
2863
  }
2838
2864
  ],
2839
2865
  structuredContent: result.status === "connected" ? result : {
@@ -2867,7 +2893,7 @@ function createHeliosTradingToolDefinitions(service) {
2867
2893
  {
2868
2894
  name: "helios_positions_delete_tp_sl",
2869
2895
  title: "Delete Helios TP/SL",
2870
- description: "Deletes an existing Helios take-profit or stop-loss order by order id. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2896
+ description: "Deletes a take-profit or stop-loss. orderId is the id from helios_portfolio_get protectiveOrders, or the Hyperliquid order id of the TP/SL. Provider may be omitted if one is already pinned with helios_provider_set_active.",
2871
2897
  inputSchema: deleteTpSlInputSchema,
2872
2898
  async execute(input) {
2873
2899
  const result = deleteTpSlToolOutputSchema.parse(await service.deleteTpSl(input));
@@ -2960,7 +2986,7 @@ async function createHeliosAgentMcpServer(options) {
2960
2986
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
2961
2987
  const server = new McpServer({
2962
2988
  name: "helios",
2963
- version: "0.2.7"
2989
+ version: "0.2.9"
2964
2990
  });
2965
2991
  const sessionStore = options.sessionStore ?? new FileAgentSessionStore(options.sessionPath ?? getDefaultHeliosAgentMcpSessionPath());
2966
2992
  const client = new HeliosAgentAuthClient({
package/dist/index.mjs CHANGED
@@ -29,7 +29,7 @@ import {
29
29
  topOfSnapshot,
30
30
  withDataText,
31
31
  withSnapshotDefaults
32
- } from "./chunk-V453JYRN.mjs";
32
+ } from "./chunk-7TMDGB6C.mjs";
33
33
  export {
34
34
  DEFAULT_SNAPSHOT_FIELDS,
35
35
  DEFAULT_SNAPSHOT_TOP,
@@ -0,0 +1,76 @@
1
+ HELIOS SNAPSHOT COLUMNS: HOW TO READ THEM
2
+ Short guide to the column ids in helios_market_get_snapshot, _chunk and symbol detail `row`.
3
+ All % values are already percent (2.5 = 2.5%). null = unavailable, never zero.
4
+ Longer guides for single indicators are in this folder (see 00_index.txt).
5
+
6
+ PRICE AND SIZE
7
+ rank market cap rank (1 = largest). null = unranked.
8
+ lp last price.
9
+ qv 24h quote volume in USD. Low qv = thin market, wider fills.
10
+ bv 24h base volume (coins).
11
+
12
+ RETURNS (% change over the window)
13
+ r1m r3m r5m r15m r30m r1h r2h r4h r6h r8h r12h r24h r3d r7d
14
+
15
+ DISTANCE FROM RECENT RANGE
16
+ fh24 fh7 % from the 24h / 7d high. Always <= 0. -10 = 10% below the high.
17
+ fl24 fl7 % from the 24h / 7d low. Always >= 0. 2 = 2% above the low.
18
+ Near 0 on fl = sitting on the low; near 0 on fh = at the high.
19
+
20
+ REGIME AND MOMENTUM
21
+ reg regime label from 12 timeframe changes:
22
+ up: CASCADE UP, PARABOLIC, BREAKOUT, TREND UP, GRIND UP, SPIKE UP
23
+ down: CASCADE DOWN, CRASH, BREAKDOWN, TREND DN, BLEED, SPIKE DN
24
+ turning: REVERSAL (down->up), ROLLOVER (up->down), EXHAUST UP, EXHAUST DN
25
+ no trend: CHOP, CONSOL, RANGING (momentum signals are weak here)
26
+ Known quirk: a steady fall can read CRASH even when not accelerating.
27
+ hmi Helios momentum index, -100..100. Cross-timeframe momentum, 1h-4h weigh most.
28
+ > 40 strong up, 15..40 up, -15..15 no direction, < -40 strong down.
29
+ hmiv hmi velocity (rate of change of hmi). > 0 momentum improving, < 0 fading.
30
+ Meaningless when hmi is between -15 and 15.
31
+ tri trend strength label: STRONG TREND, MODERATE TREND, WEAK TREND, RANGING/CHOPPY.
32
+ No direction: use hmi or reg for which way.
33
+ trv trend strength 0..100 (> 60 STRONG, > 40 MODERATE, > 20 WEAK).
34
+ de directional efficiency -1..1. +1 every timeframe moving up cleanly,
35
+ -1 every timeframe moving down cleanly, near 0 choppy.
36
+
37
+ STRETCH
38
+ ob overbought, 0..100 (weighted RSI across timeframes). >= 70 stretched up,
39
+ <= 30 stretched down.
40
+ obosp probability ob is high versus its own last 60 readings, 0..1.
41
+ > 0.9 extreme for this coin right now, < 0.1 extreme low.
42
+ In a strong trend ob can be 95 while obosp is low; read both.
43
+ vwz VWAP z-score vs the 7d VWAP. > +2 far above, -0.5..0.5 at VWAP, < -2 far below.
44
+
45
+ STRUCTURE
46
+ ms market structure: RANGING, CONSOL (compressed), BREAKING↑ / BREAKING↓,
47
+ CONFIRMED-BREAK↑ / ↓, TEST-S (testing support), TEST-R (testing resistance),
48
+ FAILED-BREAK (breakout that reversed).
49
+ srs nearest support price. srr nearest resistance price (null = none found).
50
+
51
+ SCORE
52
+ hs Helios score, a mean-reversion setup score. Positive = long setup
53
+ (needs ob <= 30 and de <= -0.4), negative = short setup (ob >= 60, de >= 0.4),
54
+ 0 = no setup. Size: 5+ notable, 7+ strong, 9+ rare. Short scores have not
55
+ held up in backtests; long scores in a falling market have.
56
+
57
+ OTHER COLUMNS (only with fields)
58
+ hfi Helios flow index (funding, OI, CVD), -100..100. Its OI part saturates; treat as rough.
59
+ ar az activity ratio and z-score (volume now vs baseline; ar < 0.5 = dead).
60
+ vwap poc pocd vap VWAP price, volume point of control, % from it, value-area position.
61
+ macdh macds macdd MACD histogram, signal label, divergence.
62
+ vcc vcp volatility clustering and change-point probability.
63
+ obosd overbought/oversold divergence. trh trend Hurst (> 0.5 trending).
64
+
65
+ MARKET BLOCK (every response)
66
+ os how many of 23 large coins are down more than 7% in 24h (0..23). High = broad selloff.
67
+ mti micro trend: 5m + 1h moves of BTC, ETH, BNB, SOL, scaled. > 0 majors rising now.
68
+ Mti macro trend: weighted 5m/1h/6h/24h momentum of six majors, in %. > 0 market up.
69
+
70
+ WHAT HAS HELD UP IN BACKTESTS (out of sample, last 2 years)
71
+ - Buying a sharp turn up (hmiv > 0, r15m and r1h turning positive) in a coin whose
72
+ reg is BREAKDOWN or CRASH, especially when the whole market is down (os high, Mti < 0).
73
+ - Fading stretched moves beat chasing them.
74
+ - Shorts did not hold up. Edges are small: about +0.1% a trade after a 0.38% round-trip cost,
75
+ on holds of 1-3 days. Frequent in-and-out trading pays fees without that edge.
76
+ Size small, always use a stop.
@@ -1,8 +1,9 @@
1
1
  ================================================================================
2
2
  HELIOS INDICATOR INDEX — AI AGENT REFERENCE
3
3
  ================================================================================
4
- # One entry per indicator. Format: file | data_field | type | range | purpose
5
- # All fields accessed via: indicators.<data_field>
4
+ # START WITH 00_columns.txt: a short guide to every snapshot column id (hmi, reg, ob, hs...).
5
+ # The entries below are longer guides, one per indicator. Format: file | data_field | type | range | purpose
6
+ # data_field names here are the long names; snapshot columns use the short ids in 00_columns.txt.
6
7
 
7
8
  --------------------------------------------------------------------------------
8
9
  MOMENTUM / DIRECTION
@@ -22,10 +23,6 @@ regime.txt
22
23
  19-state market regime classifier (e.g. CASCADE_UP, CHOP, REVERSAL).
23
24
  PRIMARY GATE: invalidates all momentum indicators when CHOP/CONSOL/RANGING.
24
25
 
25
- tf_alignment.txt
26
- indicators.tf_alignment | NUMERIC | 0..100
27
- % of short-TF (1m-2h) movement aligned with macro (4h-24h) direction.
28
- Breadth complement to HMI magnitude.
29
26
 
30
27
  directional_efficiency.txt
31
28
  indicators.directional_efficiency | NUMERIC | -1..1
@@ -135,21 +132,10 @@ hfi_weights.txt
135
132
  Any weight < 0.10 = that data source is degraded.
136
133
 
137
134
  --------------------------------------------------------------------------------
138
- VOLATILITY / SQUEEZE
135
+ VOLATILITY
139
136
  --------------------------------------------------------------------------------
140
- volatility_squeeze.txt
141
- indicators.volatility_squeeze | NUMERIC | [0, ∞); typical [0.1, 1.5]
142
- Short/long GARCH(1,1) ratio. LOWER = MORE compressed (< 0.8 = in squeeze).
143
- Counterintuitive scale: value 0.2 = maximum squeeze, NOT minimum.
144
137
 
145
- squeeze_duration.txt
146
- indicators.squeeze_duration | INTEGER | [0, ∞)
147
- Count of consecutive ticks with volatility_squeeze < 0.8. Resets to 0 on break.
148
138
 
149
- squeeze_energy.txt
150
- indicators.squeeze_energy | NUMERIC | [0, ∞)
151
- Integrated compression: squeeze_duration × (0.8 - volatility_squeeze).
152
- Summary measure of pent-up energy. Zero = no active squeeze.
153
139
 
154
140
  volatility_clustering.txt
155
141
  indicators.volatility_clustering | NUMERIC | [0.7, 1.5] ← NOT [0, 1]
@@ -200,7 +186,6 @@ DEPENDENCY MAP — KEY RELATIONSHIPS
200
186
  macd_signal → macd_histogram (state without magnitude is incomplete)
201
187
  macd_divergence → macd_signal (divergence leads; cross confirms)
202
188
  hfi → hfi_weights (composite without component split is opaque)
203
- squeeze_energy → volatility_squeeze + squeeze_duration (derived from both)
204
189
  vwap_zscore → vwap + volatility_clustering (both required for interpretation)
205
190
  poc_distance → poc (normalized deviation requires knowing the level)
206
191
  value_area_position → regime (same state is opposite signal in different regimes)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgnio/helios-mcp",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "Helios MCP server: connect an AI agent to your Helios account to read markets and your portfolio and to trade on Hyperliquid. Most apps can use the hosted URL https://helios.pgn.io/mcp instead.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://helios.pgn.io/?connect=ai",