tradeblocks-mcp 3.0.2 → 3.3.1

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 (218) hide show
  1. package/README.md +85 -80
  2. package/dist/{chunk-LDKTV7GW.js → chunk-27S67XW3.js} +24 -52
  3. package/dist/chunk-27S67XW3.js.map +1 -0
  4. package/dist/{chunk-FGZH632F.js → chunk-2E63THNI.js} +13 -11
  5. package/dist/chunk-2E63THNI.js.map +1 -0
  6. package/dist/{chunk-QTTR7AAW.js → chunk-2I3S2ZLT.js} +325 -84
  7. package/dist/chunk-2I3S2ZLT.js.map +1 -0
  8. package/dist/{chunk-4BLCXNQ6.js → chunk-4HZLVUOZ.js} +179 -183
  9. package/dist/chunk-4HZLVUOZ.js.map +1 -0
  10. package/dist/{chunk-XXYOUIZY.js → chunk-NWNSKALN.js} +63 -80
  11. package/dist/chunk-NWNSKALN.js.map +1 -0
  12. package/dist/{chunk-PRAYH3RT.js → chunk-WNTYA7ER.js} +29 -12
  13. package/dist/chunk-WNTYA7ER.js.map +1 -0
  14. package/dist/{chunk-BKQ4PM4Y.js → chunk-YZA4RS76.js} +24 -18
  15. package/dist/chunk-YZA4RS76.js.map +1 -0
  16. package/dist/{chunk-W2PP3LEH.js → chunk-ZTJXC3LS.js} +17 -8
  17. package/dist/chunk-ZTJXC3LS.js.map +1 -0
  18. package/dist/daily-log-processor-MZW2XBZY.js +9 -0
  19. package/dist/{daily-logs-store-D3QC2XPV.js → daily-logs-store-3C3OTGPK.js} +2 -2
  20. package/dist/iv-solver-worker.js +9 -1
  21. package/dist/market-provider-6U33TQUT.js +16 -0
  22. package/dist/{proto-3HZTCWK4.js → proto-V23GIZG2.js} +2 -2
  23. package/dist/{sync-PO4IPCYV.js → sync-3UBC37VW.js} +7 -7
  24. package/dist/test-exports.js +876 -483
  25. package/dist/test-exports.js.map +1 -1
  26. package/dist/trade-processor-DKDCWGKD.js +9 -0
  27. package/dist/{trades-store-LOL3FQSF.js → trades-store-R3NL25T2.js} +2 -2
  28. package/package.json +2 -6
  29. package/server/{chunk-CSDVJPBB.js → chunk-536NSFED.js} +29 -12
  30. package/server/chunk-536NSFED.js.map +1 -0
  31. package/server/{chunk-WA5AAPCH.js → chunk-A3UKD64A.js} +17 -8
  32. package/server/chunk-A3UKD64A.js.map +1 -0
  33. package/server/{chunk-PNKG7RY7.js → chunk-OMUDNJBJ.js} +63 -80
  34. package/server/chunk-OMUDNJBJ.js.map +1 -0
  35. package/server/{chunk-72GKJE2U.js → chunk-RJDJHZ5Y.js} +934 -335
  36. package/server/chunk-RJDJHZ5Y.js.map +1 -0
  37. package/server/{chunk-NZO6PT64.js → chunk-RM4B2VKS.js} +167 -177
  38. package/server/chunk-RM4B2VKS.js.map +1 -0
  39. package/server/{chunk-5EBXHT6C.js → chunk-SY6GDNVI.js} +24 -18
  40. package/server/chunk-SY6GDNVI.js.map +1 -0
  41. package/server/{chunk-FBNDMCT5.js → chunk-T66KH2XH.js} +13 -11
  42. package/server/chunk-T66KH2XH.js.map +1 -0
  43. package/server/{chunk-NRFXAJF7.js → chunk-W5E7FHC4.js} +24 -52
  44. package/server/chunk-W5E7FHC4.js.map +1 -0
  45. package/server/{config-6IZXEFEX.js → config-DK7KOMNL.js} +1 -1
  46. package/server/config-DK7KOMNL.js.map +1 -0
  47. package/server/daily-log-processor-B2VPU2FQ.js +10 -0
  48. package/server/{daily-logs-store-NU7M4L5C.js → daily-logs-store-YFNXTB7J.js} +2 -2
  49. package/server/http-server.js +2 -5
  50. package/server/index.js +2497 -2029
  51. package/server/index.js.map +1 -1
  52. package/server/iv-solver-worker.js +9 -1
  53. package/server/market-provider-L56RFHAC.js +17 -0
  54. package/server/{proto-DWRZJO4E.js → proto-YZ4TLX2N.js} +2 -2
  55. package/server/{sync-QFI5L7S7.js → sync-CENKDN53.js} +7 -7
  56. package/server/trade-processor-3PI4LHZI.js +10 -0
  57. package/server/{trades-store-VY26MWER.js → trades-store-RTTEYMXH.js} +2 -2
  58. package/src/auth/clients-store.ts +4 -4
  59. package/src/auth/code-store.ts +1 -1
  60. package/src/auth/config.ts +11 -12
  61. package/src/auth/login-page.ts +8 -10
  62. package/src/auth/provider.ts +35 -30
  63. package/src/auth/token.ts +17 -15
  64. package/src/db/backtest-schemas.ts +38 -8
  65. package/src/db/connection.ts +107 -40
  66. package/src/db/data-root.ts +22 -0
  67. package/src/db/index.ts +37 -5
  68. package/src/db/json-adapters.ts +22 -31
  69. package/src/db/json-migration.ts +11 -15
  70. package/src/db/json-store.ts +5 -3
  71. package/src/db/market-datasets.ts +48 -15
  72. package/src/db/market-schemas.ts +11 -5
  73. package/src/db/market-views.ts +119 -32
  74. package/src/db/parquet-writer.ts +7 -7
  75. package/src/db/profile-schemas.ts +39 -22
  76. package/src/db/schemas.ts +2 -2
  77. package/src/http-server.ts +7 -12
  78. package/src/index.ts +8 -9
  79. package/src/market/ingestor/index.ts +1 -4
  80. package/src/market/ingestor/market-ingestor.ts +140 -67
  81. package/src/market/ingestor/types.ts +1 -3
  82. package/src/market/stores/chain-sql.ts +2 -8
  83. package/src/market/stores/chain-store.ts +1 -4
  84. package/src/market/stores/duckdb-chain-store.ts +3 -14
  85. package/src/market/stores/duckdb-enriched-store.ts +3 -13
  86. package/src/market/stores/duckdb-quote-store.ts +10 -27
  87. package/src/market/stores/duckdb-spot-store.ts +4 -20
  88. package/src/market/stores/enriched-sql.ts +3 -9
  89. package/src/market/stores/enriched-store.ts +2 -2
  90. package/src/market/stores/parquet-chain-store.ts +5 -22
  91. package/src/market/stores/parquet-enriched-store.ts +4 -16
  92. package/src/market/stores/parquet-oi-daily-store.ts +3 -14
  93. package/src/market/stores/parquet-quote-store.ts +9 -29
  94. package/src/market/stores/parquet-spot-store.ts +14 -33
  95. package/src/market/stores/quote-store.ts +2 -10
  96. package/src/market/stores/spot-sql.ts +3 -15
  97. package/src/market/stores/spot-store.ts +1 -1
  98. package/src/market/tickers/defaults.json +6 -6
  99. package/src/market/tickers/loader.ts +2 -7
  100. package/src/market/tickers/registry.ts +5 -15
  101. package/src/market/tickers/schemas.ts +2 -6
  102. package/src/models/strategy-profile.ts +39 -39
  103. package/src/sync/block-sync.ts +44 -65
  104. package/src/sync/index.ts +1 -4
  105. package/src/sync/metadata.ts +14 -20
  106. package/src/test-exports.ts +139 -123
  107. package/src/tools/analysis.ts +180 -159
  108. package/src/tools/batch-exit-analysis.ts +100 -59
  109. package/src/tools/blocks/analysis.ts +116 -101
  110. package/src/tools/blocks/comparison.ts +281 -364
  111. package/src/tools/blocks/core.ts +208 -253
  112. package/src/tools/blocks/health.ts +571 -563
  113. package/src/tools/blocks/index.ts +2 -0
  114. package/src/tools/blocks/paired-comparison.ts +425 -0
  115. package/src/tools/blocks/similarity.ts +316 -283
  116. package/src/tools/edge-decay.ts +124 -197
  117. package/src/tools/exit-analysis.ts +130 -77
  118. package/src/tools/greeks-attribution.ts +84 -35
  119. package/src/tools/guides.ts +4 -6
  120. package/src/tools/imports.ts +11 -14
  121. package/src/tools/market-data.ts +1067 -757
  122. package/src/tools/market-enrichment.ts +3 -3
  123. package/src/tools/market-fetch.ts +148 -67
  124. package/src/tools/market-imports.ts +12 -12
  125. package/src/tools/middleware/sync-middleware.ts +5 -6
  126. package/src/tools/performance.ts +185 -302
  127. package/src/tools/profile-analysis.ts +52 -66
  128. package/src/tools/profiles.ts +106 -69
  129. package/src/tools/regime-advisor.ts +20 -45
  130. package/src/tools/replay.ts +81 -77
  131. package/src/tools/reports/discrepancies.ts +298 -328
  132. package/src/tools/reports/fields.ts +7 -25
  133. package/src/tools/reports/helpers.ts +18 -49
  134. package/src/tools/reports/predictive.ts +27 -70
  135. package/src/tools/reports/slippage-trends.ts +315 -345
  136. package/src/tools/reports/slippage.ts +1 -4
  137. package/src/tools/reports/strategy-matches.ts +399 -441
  138. package/src/tools/schema.ts +43 -40
  139. package/src/tools/shared/filters.ts +3 -9
  140. package/src/tools/snapshot.ts +9 -30
  141. package/src/tools/sql.ts +15 -14
  142. package/src/tools/tickers.ts +1 -4
  143. package/src/utils/batch-exit-analysis.ts +31 -42
  144. package/src/utils/black-scholes.ts +39 -29
  145. package/src/utils/block-loader.ts +69 -83
  146. package/src/utils/calibration-probe.ts +3 -4
  147. package/src/utils/chain-loader.ts +3 -3
  148. package/src/utils/csv-discovery.ts +16 -22
  149. package/src/utils/data-quality.ts +24 -36
  150. package/src/utils/exit-triggers.ts +91 -96
  151. package/src/utils/field-timing.ts +94 -79
  152. package/src/utils/filter-predicates.ts +13 -9
  153. package/src/utils/flatfile-importer.ts +94 -64
  154. package/src/utils/greeks-decomposition.ts +152 -100
  155. package/src/utils/iv-solver-pool.ts +55 -25
  156. package/src/utils/iv-solver-worker.ts +5 -5
  157. package/src/utils/market-enricher.ts +528 -497
  158. package/src/utils/market-importer.ts +31 -12
  159. package/src/utils/market-provider.ts +21 -23
  160. package/src/utils/massive-tier.ts +5 -7
  161. package/src/utils/migrate-option-data-helpers.ts +2 -8
  162. package/src/utils/option-quote-greeks.ts +25 -31
  163. package/src/utils/option-time.ts +4 -8
  164. package/src/utils/output-formatter.ts +1 -4
  165. package/src/utils/provider-capabilities.ts +1 -4
  166. package/src/utils/providers/massive.ts +59 -93
  167. package/src/utils/providers/thetadata/backfill.ts +14 -23
  168. package/src/utils/providers/thetadata/client.ts +12 -8
  169. package/src/utils/providers/thetadata/decode.ts +2 -20
  170. package/src/utils/providers/thetadata/endpoints.ts +32 -40
  171. package/src/utils/providers/thetadata/join.ts +11 -10
  172. package/src/utils/providers/thetadata/proto.ts +12 -10
  173. package/src/utils/providers/thetadata/quote-mid-greeks.ts +5 -5
  174. package/src/utils/providers/thetadata.ts +11 -10
  175. package/src/utils/quote-enricher.ts +4 -4
  176. package/src/utils/quote-parquet-projection.ts +3 -11
  177. package/src/utils/sample-date-selector.ts +3 -5
  178. package/src/utils/schema-metadata.ts +102 -70
  179. package/src/utils/ticker.ts +5 -9
  180. package/src/utils/trade-replay.ts +77 -68
  181. package/dist/chunk-4BLCXNQ6.js.map +0 -1
  182. package/dist/chunk-BKQ4PM4Y.js.map +0 -1
  183. package/dist/chunk-FGZH632F.js.map +0 -1
  184. package/dist/chunk-LDKTV7GW.js.map +0 -1
  185. package/dist/chunk-PRAYH3RT.js.map +0 -1
  186. package/dist/chunk-QTTR7AAW.js.map +0 -1
  187. package/dist/chunk-W2PP3LEH.js.map +0 -1
  188. package/dist/chunk-XXYOUIZY.js.map +0 -1
  189. package/dist/daily-log-processor-6MWJ23JK.js +0 -9
  190. package/dist/market-provider-VDRJUEF2.js +0 -16
  191. package/dist/trade-processor-NHU4VWRX.js +0 -9
  192. package/manifest.json +0 -141
  193. package/server/chunk-5EBXHT6C.js.map +0 -1
  194. package/server/chunk-72GKJE2U.js.map +0 -1
  195. package/server/chunk-CSDVJPBB.js.map +0 -1
  196. package/server/chunk-FBNDMCT5.js.map +0 -1
  197. package/server/chunk-NRFXAJF7.js.map +0 -1
  198. package/server/chunk-NZO6PT64.js.map +0 -1
  199. package/server/chunk-PNKG7RY7.js.map +0 -1
  200. package/server/chunk-WA5AAPCH.js.map +0 -1
  201. package/server/config-6IZXEFEX.js.map +0 -1
  202. package/server/daily-log-processor-Y3PVSVBM.js +0 -10
  203. package/server/market-provider-VOYYVYWT.js +0 -17
  204. package/server/trade-processor-JWVS37KM.js +0 -10
  205. /package/dist/{daily-log-processor-6MWJ23JK.js.map → daily-log-processor-MZW2XBZY.js.map} +0 -0
  206. /package/dist/{daily-logs-store-D3QC2XPV.js.map → daily-logs-store-3C3OTGPK.js.map} +0 -0
  207. /package/dist/{market-provider-VDRJUEF2.js.map → market-provider-6U33TQUT.js.map} +0 -0
  208. /package/dist/{proto-3HZTCWK4.js.map → proto-V23GIZG2.js.map} +0 -0
  209. /package/dist/{sync-PO4IPCYV.js.map → sync-3UBC37VW.js.map} +0 -0
  210. /package/dist/{trade-processor-NHU4VWRX.js.map → trade-processor-DKDCWGKD.js.map} +0 -0
  211. /package/dist/{trades-store-LOL3FQSF.js.map → trades-store-R3NL25T2.js.map} +0 -0
  212. /package/server/{daily-log-processor-Y3PVSVBM.js.map → daily-log-processor-B2VPU2FQ.js.map} +0 -0
  213. /package/server/{daily-logs-store-NU7M4L5C.js.map → daily-logs-store-YFNXTB7J.js.map} +0 -0
  214. /package/server/{market-provider-VOYYVYWT.js.map → market-provider-L56RFHAC.js.map} +0 -0
  215. /package/server/{proto-DWRZJO4E.js.map → proto-YZ4TLX2N.js.map} +0 -0
  216. /package/server/{sync-QFI5L7S7.js.map → sync-CENKDN53.js.map} +0 -0
  217. /package/server/{trade-processor-JWVS37KM.js.map → trade-processor-3PI4LHZI.js.map} +0 -0
  218. /package/server/{trades-store-VY26MWER.js.map → trades-store-RTTEYMXH.js.map} +0 -0
@@ -4,10 +4,7 @@ import { getDataRoot } from "./data-root.ts";
4
4
  import { writeParquetAtomic, writeParquetPartition } from "./parquet-writer.ts";
5
5
 
6
6
  export type CanonicalSingleFileDataset = "daily" | "date_context";
7
- export type CanonicalPartitionedDataset =
8
- | "intraday"
9
- | "option_chain"
10
- | "option_quote_minutes";
7
+ export type CanonicalPartitionedDataset = "intraday" | "option_chain" | "option_quote_minutes";
11
8
  export type CanonicalMarketDataset = CanonicalSingleFileDataset | CanonicalPartitionedDataset;
12
9
 
13
10
  const SINGLE_FILE_DATASETS: Record<CanonicalSingleFileDataset, string> = {
@@ -81,12 +78,24 @@ export interface DatasetDef {
81
78
  }
82
79
 
83
80
  export const DATASETS_V3: Record<string, DatasetDef> = {
84
- spot: { subdir: "spot", partitionKeys: ["ticker", "date"], filename: "data.parquet" },
85
- enriched: { subdir: "enriched", partitionKeys: ["ticker"], filename: "data.parquet" },
86
- enriched_context: { subdir: "enriched/context", partitionKeys: [], filename: "data.parquet" },
87
- option_chain: { subdir: "option_chain", partitionKeys: ["underlying", "date"], filename: "data.parquet" },
88
- option_quote_minutes: { subdir: "option_quote_minutes", partitionKeys: ["underlying", "date"], filename: "data.parquet" },
89
- option_oi_daily: { subdir: "option_oi_daily", partitionKeys: ["underlying", "date"], filename: "data.parquet" },
81
+ spot: { subdir: "spot", partitionKeys: ["ticker", "date"], filename: "data.parquet" },
82
+ enriched: { subdir: "enriched", partitionKeys: ["ticker"], filename: "data.parquet" },
83
+ enriched_context: { subdir: "enriched/context", partitionKeys: [], filename: "data.parquet" },
84
+ option_chain: {
85
+ subdir: "option_chain",
86
+ partitionKeys: ["underlying", "date"],
87
+ filename: "data.parquet",
88
+ },
89
+ option_quote_minutes: {
90
+ subdir: "option_quote_minutes",
91
+ partitionKeys: ["underlying", "date"],
92
+ filename: "data.parquet",
93
+ },
94
+ option_oi_daily: {
95
+ subdir: "option_oi_daily",
96
+ partitionKeys: ["underlying", "date"],
97
+ filename: "data.parquet",
98
+ },
90
99
  };
91
100
 
92
101
  // ------ Per-dataset write helpers ------
@@ -102,12 +111,18 @@ export const DATASETS_V3: Record<string, DatasetDef> = {
102
111
 
103
112
  export async function writeSpotPartition(
104
113
  conn: DuckDBConnection,
105
- args: { dataDir: string; ticker: string; date: string; selectQuery: string; compression?: string },
114
+ args: {
115
+ dataDir: string;
116
+ ticker: string;
117
+ date: string;
118
+ selectQuery: string;
119
+ compression?: string;
120
+ },
106
121
  ): Promise<{ rowCount: number }> {
107
122
  const def = DATASETS_V3.spot;
108
123
  return writeParquetPartition(conn, {
109
124
  baseDir: path.join(resolveMarketDir(args.dataDir), def.subdir),
110
- partitions: { ticker: args.ticker, date: args.date }, // order matches def.partitionKeys
125
+ partitions: { ticker: args.ticker, date: args.date }, // order matches def.partitionKeys
111
126
  selectQuery: args.selectQuery,
112
127
  compression: args.compression,
113
128
  filename: def.filename,
@@ -116,7 +131,13 @@ export async function writeSpotPartition(
116
131
 
117
132
  export async function writeChainPartition(
118
133
  conn: DuckDBConnection,
119
- args: { dataDir: string; underlying: string; date: string; selectQuery: string; compression?: string },
134
+ args: {
135
+ dataDir: string;
136
+ underlying: string;
137
+ date: string;
138
+ selectQuery: string;
139
+ compression?: string;
140
+ },
120
141
  ): Promise<{ rowCount: number }> {
121
142
  const def = DATASETS_V3.option_chain;
122
143
  return writeParquetPartition(conn, {
@@ -130,7 +151,13 @@ export async function writeChainPartition(
130
151
 
131
152
  export async function writeQuoteMinutesPartition(
132
153
  conn: DuckDBConnection,
133
- args: { dataDir: string; underlying: string; date: string; selectQuery: string; compression?: string },
154
+ args: {
155
+ dataDir: string;
156
+ underlying: string;
157
+ date: string;
158
+ selectQuery: string;
159
+ compression?: string;
160
+ },
134
161
  ): Promise<{ rowCount: number }> {
135
162
  const def = DATASETS_V3.option_quote_minutes;
136
163
  // Sort rows by (ticker, time) before writing so DuckDB row groups in the
@@ -162,7 +189,13 @@ export async function writeQuoteMinutesPartition(
162
189
 
163
190
  export async function writeOiDailyPartition(
164
191
  conn: DuckDBConnection,
165
- args: { dataDir: string; underlying: string; date: string; selectQuery: string; compression?: string },
192
+ args: {
193
+ dataDir: string;
194
+ underlying: string;
195
+ date: string;
196
+ selectQuery: string;
197
+ compression?: string;
198
+ },
166
199
  ): Promise<{ rowCount: number }> {
167
200
  const def = DATASETS_V3.option_oi_daily;
168
201
  // Sort rows by ticker before writing so DuckDB row groups carry tight
@@ -37,9 +37,7 @@ import type { DuckDBConnection } from "@duckdb/node-api";
37
37
  *
38
38
  * @param conn - Active DuckDB connection with market catalog attached
39
39
  */
40
- export async function ensureMutableMarketTables(
41
- conn: DuckDBConnection,
42
- ): Promise<void> {
40
+ export async function ensureMutableMarketTables(conn: DuckDBConnection): Promise<void> {
43
41
  // Legacy coverage-tracking table CREATE removed — grep-verified zero readers/writers today.
44
42
  // Coverage is now derived from store.getCoverage() (Parquet: readdirSync; DuckDB: SELECT DISTINCT).
45
43
 
@@ -97,8 +95,16 @@ export async function ensureMarketDataTables(conn: DuckDBConnection): Promise<vo
97
95
  // createMarketParquetViews earlier in the connection sequence). Dropping them
98
96
  // here would destroy legitimate v3.0 views.
99
97
  for (const name of ["daily", "date_context", "intraday", "data_coverage"]) {
100
- try { await conn.run(`DROP VIEW IF EXISTS market.${name}`); } catch { /* wrong type */ }
101
- try { await conn.run(`DROP TABLE IF EXISTS market.${name}`); } catch { /* wrong type */ }
98
+ try {
99
+ await conn.run(`DROP VIEW IF EXISTS market.${name}`);
100
+ } catch {
101
+ /* wrong type */
102
+ }
103
+ try {
104
+ await conn.run(`DROP TABLE IF EXISTS market.${name}`);
105
+ } catch {
106
+ /* wrong type */
107
+ }
102
108
  }
103
109
 
104
110
  // ============================================================================
@@ -27,10 +27,7 @@
27
27
  import type { DuckDBConnection } from "@duckdb/node-api";
28
28
  import { existsSync, readdirSync } from "fs";
29
29
  import * as path from "path";
30
- import {
31
- resolveCanonicalMarketPartitionDir,
32
- resolveMarketDir,
33
- } from "./market-datasets.ts";
30
+ import { resolveCanonicalMarketPartitionDir, resolveMarketDir } from "./market-datasets.ts";
34
31
  import {
35
32
  describeReadParquetColumns,
36
33
  quoteParquetCanonicalProjection,
@@ -51,10 +48,7 @@ export interface ViewCreationResult {
51
48
  * option_chain, option_quote_minutes — all date-only) continue working unchanged.
52
49
  * Pass "ticker" for ticker-first partitioning (the 3.0 spot directory).
53
50
  */
54
- function hasParquetPartitions(
55
- dir: string,
56
- partitionKey: string = "date",
57
- ): boolean {
51
+ function hasParquetPartitions(dir: string, partitionKey: string = "date"): boolean {
58
52
  if (!existsSync(dir)) return false;
59
53
  try {
60
54
  const prefix = `${partitionKey}=`;
@@ -136,7 +130,11 @@ export async function createMarketParquetViews(
136
130
  // (`option_chain/underlying=X/date=Y/...`) — the current store writers
137
131
  // produce the underlying-first layout.
138
132
  const hiveViews: Array<{ name: string; subdir: string; partitionKey: string }> = [
139
- { name: "option_chain", subdir: resolveCanonicalMarketPartitionDir(dataDir, "option_chain"), partitionKey: "underlying" },
133
+ {
134
+ name: "option_chain",
135
+ subdir: resolveCanonicalMarketPartitionDir(dataDir, "option_chain"),
136
+ partitionKey: "underlying",
137
+ },
140
138
  ];
141
139
 
142
140
  for (const { name, subdir, partitionKey } of hiveViews) {
@@ -144,10 +142,19 @@ export async function createMarketParquetViews(
144
142
  // Accept either the dataset's primary partition key (e.g. `underlying` for
145
143
  // Market Data 3.0 option_chain) or the legacy `date` top-level partition.
146
144
  const hasNewLayout = existsSync(dirPath) && hasParquetPartitions(dirPath, partitionKey);
147
- const hasLegacyLayout = partitionKey !== "date" && existsSync(dirPath) && hasParquetPartitions(dirPath, "date");
145
+ const hasLegacyLayout =
146
+ partitionKey !== "date" && existsSync(dirPath) && hasParquetPartitions(dirPath, "date");
148
147
  if (hasNewLayout || hasLegacyLayout) {
149
- try { await conn.run(`DROP VIEW IF EXISTS market.${name}`); } catch { /* wrong type */ }
150
- try { await conn.run(`DROP TABLE IF EXISTS market.${name}`); } catch { /* wrong type */ }
148
+ try {
149
+ await conn.run(`DROP VIEW IF EXISTS market.${name}`);
150
+ } catch {
151
+ /* wrong type */
152
+ }
153
+ try {
154
+ await conn.run(`DROP TABLE IF EXISTS market.${name}`);
155
+ } catch {
156
+ /* wrong type */
157
+ }
151
158
  // Glob on data.parquet (not *.parquet) so DuckDB's in-flight
152
159
  // tmp_data.parquet files — created by concurrent COPY ... TO writers
153
160
  // during atomic parquet replacement — never match. Matching a mid-write
@@ -158,7 +165,11 @@ export async function createMarketParquetViews(
158
165
  );
159
166
  viewsCreated.push(name);
160
167
  } else {
161
- try { await conn.run(`DROP VIEW IF EXISTS market.${name}`); } catch { /* not a view */ }
168
+ try {
169
+ await conn.run(`DROP VIEW IF EXISTS market.${name}`);
170
+ } catch {
171
+ /* not a view */
172
+ }
162
173
  tablesKept.push(name);
163
174
  }
164
175
  }
@@ -175,8 +186,16 @@ export async function createMarketParquetViews(
175
186
  const optionQuoteHasNewLayout = hasParquetPartitions(optionMinuteQuoteDir, "underlying");
176
187
  const optionQuoteHasLegacyLayout = hasParquetPartitions(optionMinuteQuoteDir, "date");
177
188
  if (optionQuoteHasNewLayout || optionQuoteHasLegacyLayout) {
178
- try { await conn.run("DROP VIEW IF EXISTS market.option_quote_minutes"); } catch { /* wrong type */ }
179
- try { await conn.run("DROP TABLE IF EXISTS market.option_quote_minutes"); } catch { /* wrong type */ }
189
+ try {
190
+ await conn.run("DROP VIEW IF EXISTS market.option_quote_minutes");
191
+ } catch {
192
+ /* wrong type */
193
+ }
194
+ try {
195
+ await conn.run("DROP TABLE IF EXISTS market.option_quote_minutes");
196
+ } catch {
197
+ /* wrong type */
198
+ }
180
199
  // See hive-views glob comment: scope to data.parquet so concurrent
181
200
  // COPY ... TO writers' tmp_data.parquet never trips DESCRIBE.
182
201
  const quoteSource = readParquetGlobSql(`${optionMinuteQuoteDir}/**/data.parquet`);
@@ -189,7 +208,11 @@ export async function createMarketParquetViews(
189
208
  );
190
209
  viewsCreated.push("option_quote_minutes");
191
210
  } else {
192
- try { await conn.run("DROP VIEW IF EXISTS market.option_quote_minutes"); } catch { /* not a view */ }
211
+ try {
212
+ await conn.run("DROP VIEW IF EXISTS market.option_quote_minutes");
213
+ } catch {
214
+ /* not a view */
215
+ }
193
216
  tablesKept.push("option_quote_minutes");
194
217
  }
195
218
 
@@ -197,14 +220,30 @@ export async function createMarketParquetViews(
197
220
  // an older run. This keeps the public SQL surface aligned with the current
198
221
  // architecture where quote greeks live inline on option_quote_minutes and
199
222
  // missing values are computed in memory at query time.
200
- try { await conn.run("DROP VIEW IF EXISTS market.option_greeks_minutes"); } catch { /* wrong type */ }
201
- try { await conn.run("DROP TABLE IF EXISTS market.option_greeks_minutes"); } catch { /* wrong type */ }
223
+ try {
224
+ await conn.run("DROP VIEW IF EXISTS market.option_greeks_minutes");
225
+ } catch {
226
+ /* wrong type */
227
+ }
228
+ try {
229
+ await conn.run("DROP TABLE IF EXISTS market.option_greeks_minutes");
230
+ } catch {
231
+ /* wrong type */
232
+ }
202
233
 
203
234
  // Remove the retired delta-index surface from the market schema if it exists
204
235
  // from an older run. Delta selection now reads directly from
205
236
  // market.option_quote_minutes greeks instead of a second persisted dataset.
206
- try { await conn.run("DROP VIEW IF EXISTS market.option_delta_index"); } catch { /* wrong type */ }
207
- try { await conn.run("DROP TABLE IF EXISTS market.option_delta_index"); } catch { /* wrong type */ }
237
+ try {
238
+ await conn.run("DROP VIEW IF EXISTS market.option_delta_index");
239
+ } catch {
240
+ /* wrong type */
241
+ }
242
+ try {
243
+ await conn.run("DROP TABLE IF EXISTS market.option_delta_index");
244
+ } catch {
245
+ /* wrong type */
246
+ }
208
247
 
209
248
  // ============================================================================
210
249
  // Canonical store views (spot, enriched, enriched_context, spot_daily).
@@ -215,8 +254,16 @@ export async function createMarketParquetViews(
215
254
  // market.spot — ticker-first Hive partitioning: spot/ticker=X/date=Y/data.parquet
216
255
  const spotDir = path.join(resolveMarketDir(dataDir), "spot");
217
256
  if (hasParquetPartitions(spotDir, "ticker")) {
218
- try { await conn.run("DROP VIEW IF EXISTS market.spot"); } catch { /* wrong type */ }
219
- try { await conn.run("DROP TABLE IF EXISTS market.spot"); } catch { /* wrong type */ }
257
+ try {
258
+ await conn.run("DROP VIEW IF EXISTS market.spot");
259
+ } catch {
260
+ /* wrong type */
261
+ }
262
+ try {
263
+ await conn.run("DROP TABLE IF EXISTS market.spot");
264
+ } catch {
265
+ /* wrong type */
266
+ }
220
267
  // Scope glob to data.parquet (see tmp_data.parquet race note above).
221
268
  await conn.run(
222
269
  `CREATE OR REPLACE VIEW market.spot AS
@@ -224,36 +271,64 @@ export async function createMarketParquetViews(
224
271
  );
225
272
  viewsCreated.push("spot");
226
273
  } else {
227
- try { await conn.run("DROP VIEW IF EXISTS market.spot"); } catch { /* not a view */ }
274
+ try {
275
+ await conn.run("DROP VIEW IF EXISTS market.spot");
276
+ } catch {
277
+ /* not a view */
278
+ }
228
279
  tablesKept.push("spot");
229
280
  }
230
281
 
231
282
  // market.enriched — per-ticker single file: enriched/ticker=X/data.parquet (no date partition)
232
283
  const enrichedDir = path.join(resolveMarketDir(dataDir), "enriched");
233
284
  if (hasEnrichedTickerFiles(enrichedDir)) {
234
- try { await conn.run("DROP VIEW IF EXISTS market.enriched"); } catch { /* wrong type */ }
235
- try { await conn.run("DROP TABLE IF EXISTS market.enriched"); } catch { /* wrong type */ }
285
+ try {
286
+ await conn.run("DROP VIEW IF EXISTS market.enriched");
287
+ } catch {
288
+ /* wrong type */
289
+ }
290
+ try {
291
+ await conn.run("DROP TABLE IF EXISTS market.enriched");
292
+ } catch {
293
+ /* wrong type */
294
+ }
236
295
  await conn.run(
237
296
  `CREATE OR REPLACE VIEW market.enriched AS
238
297
  SELECT * FROM read_parquet('${enrichedDir}/ticker=*/data.parquet', hive_partitioning=true)`,
239
298
  );
240
299
  viewsCreated.push("enriched");
241
300
  } else {
242
- try { await conn.run("DROP VIEW IF EXISTS market.enriched"); } catch { /* not a view */ }
301
+ try {
302
+ await conn.run("DROP VIEW IF EXISTS market.enriched");
303
+ } catch {
304
+ /* not a view */
305
+ }
243
306
  tablesKept.push("enriched");
244
307
  }
245
308
 
246
309
  // market.enriched_context — global single file: enriched/context/data.parquet (no partition)
247
310
  if (hasEnrichedContextFile(enrichedDir)) {
248
- try { await conn.run("DROP VIEW IF EXISTS market.enriched_context"); } catch { /* wrong type */ }
249
- try { await conn.run("DROP TABLE IF EXISTS market.enriched_context"); } catch { /* wrong type */ }
311
+ try {
312
+ await conn.run("DROP VIEW IF EXISTS market.enriched_context");
313
+ } catch {
314
+ /* wrong type */
315
+ }
316
+ try {
317
+ await conn.run("DROP TABLE IF EXISTS market.enriched_context");
318
+ } catch {
319
+ /* wrong type */
320
+ }
250
321
  await conn.run(
251
322
  `CREATE OR REPLACE VIEW market.enriched_context AS
252
323
  SELECT * FROM read_parquet('${path.join(enrichedDir, "context", "data.parquet")}')`,
253
324
  );
254
325
  viewsCreated.push("enriched_context");
255
326
  } else {
256
- try { await conn.run("DROP VIEW IF EXISTS market.enriched_context"); } catch { /* not a view */ }
327
+ try {
328
+ await conn.run("DROP VIEW IF EXISTS market.enriched_context");
329
+ } catch {
330
+ /* not a view */
331
+ }
257
332
  tablesKept.push("enriched_context");
258
333
  }
259
334
 
@@ -279,8 +354,16 @@ export async function createMarketParquetViews(
279
354
  }
280
355
  })();
281
356
  if (spotExists) {
282
- try { await conn.run("DROP VIEW IF EXISTS market.spot_daily"); } catch { /* wrong type */ }
283
- try { await conn.run("DROP TABLE IF EXISTS market.spot_daily"); } catch { /* wrong type */ }
357
+ try {
358
+ await conn.run("DROP VIEW IF EXISTS market.spot_daily");
359
+ } catch {
360
+ /* wrong type */
361
+ }
362
+ try {
363
+ await conn.run("DROP TABLE IF EXISTS market.spot_daily");
364
+ } catch {
365
+ /* wrong type */
366
+ }
284
367
  await conn.run(`
285
368
  CREATE OR REPLACE VIEW market.spot_daily AS
286
369
  SELECT ticker, date,
@@ -305,7 +388,11 @@ export async function createMarketParquetViews(
305
388
  `);
306
389
  viewsCreated.push("spot_daily");
307
390
  } else {
308
- try { await conn.run("DROP VIEW IF EXISTS market.spot_daily"); } catch { /* not a view */ }
391
+ try {
392
+ await conn.run("DROP VIEW IF EXISTS market.spot_daily");
393
+ } catch {
394
+ /* not a view */
395
+ }
309
396
  tablesKept.push("spot_daily");
310
397
  }
311
398
 
@@ -111,7 +111,11 @@ export async function writeParquetAtomic(
111
111
  return { rowCount };
112
112
  } catch (writeErr) {
113
113
  // Clean up sidecar on failure so the next attempt starts fresh
114
- try { await fs.unlink(tempPath); } catch { /* may not exist */ }
114
+ try {
115
+ await fs.unlink(tempPath);
116
+ } catch {
117
+ /* may not exist */
118
+ }
115
119
  throw writeErr;
116
120
  } finally {
117
121
  // Always clean up staging table
@@ -222,9 +226,7 @@ export async function writeParquetPartition(
222
226
  // composing any path. Reject unsafe input (separators, whitespace, nulls).
223
227
  for (const [k, v] of Object.entries(partitions)) {
224
228
  if (!PARTITION_KEY_RE.test(k)) {
225
- throw new Error(
226
- `writeParquetPartition: unsafe partition key: ${JSON.stringify(k)}`,
227
- );
229
+ throw new Error(`writeParquetPartition: unsafe partition key: ${JSON.stringify(k)}`);
228
230
  }
229
231
  if (!PARTITION_VALUE_RE.test(v)) {
230
232
  throw new Error(
@@ -234,9 +236,7 @@ export async function writeParquetPartition(
234
236
  }
235
237
 
236
238
  // Preserve insertion order (ES2015 guarantees for string keys).
237
- const partitionSegments = Object.entries(partitions).map(
238
- ([k, v]) => `${k}=${v}`,
239
- );
239
+ const partitionSegments = Object.entries(partitions).map(([k, v]) => `${k}=${v}`);
240
240
  const targetPath = path.join(opts.baseDir, ...partitionSegments, filename);
241
241
 
242
242
  // writeParquetAtomic (existing, unchanged) handles: staging table → mkdir -p
@@ -51,13 +51,27 @@ export async function ensureProfilesSchema(conn: DuckDBConnection): Promise<void
51
51
  ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS position_sizing JSON
52
52
  `);
53
53
  // Migration: add schema v2 top-level columns (nullable for backward compat)
54
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS underlying VARCHAR`);
55
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS re_entry BOOLEAN`);
56
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS cap_profits BOOLEAN`);
57
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS cap_losses BOOLEAN`);
58
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS require_two_prices_pt BOOLEAN`);
59
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS close_on_completion BOOLEAN`);
60
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS ignore_margin_req BOOLEAN`);
54
+ await conn.run(
55
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS underlying VARCHAR`,
56
+ );
57
+ await conn.run(
58
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS re_entry BOOLEAN`,
59
+ );
60
+ await conn.run(
61
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS cap_profits BOOLEAN`,
62
+ );
63
+ await conn.run(
64
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS cap_losses BOOLEAN`,
65
+ );
66
+ await conn.run(
67
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS require_two_prices_pt BOOLEAN`,
68
+ );
69
+ await conn.run(
70
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS close_on_completion BOOLEAN`,
71
+ );
72
+ await conn.run(
73
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS ignore_margin_req BOOLEAN`,
74
+ );
61
75
 
62
76
  // Migration: add strategy execution param columns.
63
77
  // block_id stays NOT NULL in the PRIMARY KEY. Template profiles
@@ -76,7 +90,9 @@ export async function ensureProfilesSchema(conn: DuckDBConnection): Promise<void
76
90
  { name: "default_to_date", type: "VARCHAR" },
77
91
  ];
78
92
  for (const col of strategyCols) {
79
- await conn.run(`ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS ${col.name} ${col.type}`);
93
+ await conn.run(
94
+ `ALTER TABLE profiles.strategy_profiles ADD COLUMN IF NOT EXISTS ${col.name} ${col.type}`,
95
+ );
80
96
  }
81
97
 
82
98
  // Migration: normalize expected_regimes to canonical Vol_Regime labels
@@ -111,7 +127,7 @@ async function migrateExpectedRegimes(conn: DuckDBConnection): Promise<void> {
111
127
  const result = await conn.runAndReadAll(
112
128
  `SELECT block_id, strategy_name, expected_regimes
113
129
  FROM profiles.strategy_profiles
114
- WHERE expected_regimes IS NOT NULL AND expected_regimes != '[]'`
130
+ WHERE expected_regimes IS NOT NULL AND expected_regimes != '[]'`,
115
131
  );
116
132
 
117
133
  for (const row of result.getRows()) {
@@ -145,7 +161,7 @@ async function migrateExpectedRegimes(conn: DuckDBConnection): Promise<void> {
145
161
  await conn.run(
146
162
  `UPDATE profiles.strategy_profiles
147
163
  SET expected_regimes = '${escSql(newJson)}', updated_at = current_timestamp
148
- WHERE block_id = '${escSql(blockId)}' AND strategy_name = '${escSql(strategyName)}'`
164
+ WHERE block_id = '${escSql(blockId)}' AND strategy_name = '${escSql(strategyName)}'`,
149
165
  );
150
166
  }
151
167
  }
@@ -220,7 +236,7 @@ function rowToProfile(row: unknown[]): StrategyProfile {
220
236
  const ps = parseJsonObj(row[10]);
221
237
  return ps && Object.keys(ps).length > 0 ? ps : undefined;
222
238
  })(),
223
- underlying: row[11] as string | undefined ?? undefined,
239
+ underlying: (row[11] as string | undefined) ?? undefined,
224
240
  reEntry: toBoolOrUndef(row[12]),
225
241
  capProfits: toBoolOrUndef(row[13]),
226
242
  capLosses: toBoolOrUndef(row[14]),
@@ -252,7 +268,7 @@ const SELECT_COLUMNS = `
252
268
  export async function upsertProfile(
253
269
  conn: DuckDBConnection,
254
270
  profile: Omit<StrategyProfile, "createdAt" | "updatedAt">,
255
- baseDir?: string
271
+ baseDir?: string,
256
272
  ): Promise<StrategyProfile> {
257
273
  if (isParquetMode() && baseDir) {
258
274
  return upsertProfileJson(profile, getBlocksDir(baseDir));
@@ -272,9 +288,12 @@ export async function upsertProfile(
272
288
  const reEntrySql = profile.reEntry === undefined ? "NULL" : String(profile.reEntry);
273
289
  const capProfitsSql = profile.capProfits === undefined ? "NULL" : String(profile.capProfits);
274
290
  const capLossesSql = profile.capLosses === undefined ? "NULL" : String(profile.capLosses);
275
- const requireTwoPricesPTSql = profile.requireTwoPricesPT === undefined ? "NULL" : String(profile.requireTwoPricesPT);
276
- const closeOnCompletionSql = profile.closeOnCompletion === undefined ? "NULL" : String(profile.closeOnCompletion);
277
- const ignoreMarginReqSql = profile.ignoreMarginReq === undefined ? "NULL" : String(profile.ignoreMarginReq);
291
+ const requireTwoPricesPTSql =
292
+ profile.requireTwoPricesPT === undefined ? "NULL" : String(profile.requireTwoPricesPT);
293
+ const closeOnCompletionSql =
294
+ profile.closeOnCompletion === undefined ? "NULL" : String(profile.closeOnCompletion);
295
+ const ignoreMarginReqSql =
296
+ profile.ignoreMarginReq === undefined ? "NULL" : String(profile.ignoreMarginReq);
278
297
 
279
298
  await conn.run(`
280
299
  INSERT INTO profiles.strategy_profiles
@@ -328,7 +347,7 @@ export async function upsertProfile(
328
347
  const stored = await getProfile(conn, profile.blockId, profile.strategyName, baseDir);
329
348
  if (!stored) {
330
349
  throw new Error(
331
- `Failed to retrieve profile after upsert: ${profile.blockId}/${profile.strategyName}`
350
+ `Failed to retrieve profile after upsert: ${profile.blockId}/${profile.strategyName}`,
332
351
  );
333
352
  }
334
353
  return stored;
@@ -346,7 +365,7 @@ export async function getProfile(
346
365
  conn: DuckDBConnection,
347
366
  blockId: string,
348
367
  strategyName: string,
349
- baseDir?: string
368
+ baseDir?: string,
350
369
  ): Promise<StrategyProfile | null> {
351
370
  if (isParquetMode() && baseDir) {
352
371
  return getProfileJson(blockId, strategyName, getBlocksDir(baseDir));
@@ -372,14 +391,12 @@ export async function getProfile(
372
391
  export async function listProfiles(
373
392
  conn: DuckDBConnection,
374
393
  blockId?: string,
375
- baseDir?: string
394
+ baseDir?: string,
376
395
  ): Promise<StrategyProfile[]> {
377
396
  if (isParquetMode() && baseDir) {
378
397
  return listProfilesJson(getBlocksDir(baseDir), blockId);
379
398
  }
380
- const whereClause = blockId
381
- ? `WHERE block_id = '${escSql(blockId)}'`
382
- : "";
399
+ const whereClause = blockId ? `WHERE block_id = '${escSql(blockId)}'` : "";
383
400
 
384
401
  const result = await conn.runAndReadAll(`
385
402
  SELECT ${SELECT_COLUMNS}
@@ -403,7 +420,7 @@ export async function deleteProfile(
403
420
  conn: DuckDBConnection,
404
421
  blockId: string,
405
422
  strategyName: string,
406
- baseDir?: string
423
+ baseDir?: string,
407
424
  ): Promise<boolean> {
408
425
  if (isParquetMode() && baseDir) {
409
426
  return deleteProfileJson(blockId, strategyName, getBlocksDir(baseDir));
package/src/db/schemas.ts CHANGED
@@ -13,7 +13,7 @@ import type { DuckDBConnection } from "@duckdb/node-api";
13
13
  export async function tableExists(
14
14
  conn: DuckDBConnection,
15
15
  schemaName: string,
16
- tableName: string
16
+ tableName: string,
17
17
  ): Promise<boolean> {
18
18
  const result = await conn.runAndReadAll(`
19
19
  SELECT 1
@@ -27,7 +27,7 @@ async function hasColumn(
27
27
  conn: DuckDBConnection,
28
28
  schemaName: string,
29
29
  tableName: string,
30
- columnName: string
30
+ columnName: string,
31
31
  ): Promise<boolean> {
32
32
  const result = await conn.runAndReadAll(`
33
33
  SELECT 1
@@ -34,7 +34,7 @@ export type ServerFactory = () => McpServer;
34
34
  */
35
35
  export async function startHttpServer(
36
36
  serverFactory: ServerFactory,
37
- options: HttpServerOptions
37
+ options: HttpServerOptions,
38
38
  ): Promise<Server> {
39
39
  const { port, host = "0.0.0.0", auth } = options;
40
40
 
@@ -47,18 +47,15 @@ export async function startHttpServer(
47
47
 
48
48
  if (auth && !auth.noAuth) {
49
49
  // SDK auth modules stay dynamic (externalized by esbuild, resolved from node_modules)
50
- const { mcpAuthRouter } = await import(
51
- "@modelcontextprotocol/sdk/server/auth/router.js"
52
- );
53
- const { requireBearerAuth } = await import(
54
- "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js"
55
- );
50
+ const { mcpAuthRouter } = await import("@modelcontextprotocol/sdk/server/auth/router.js");
51
+ const { requireBearerAuth } =
52
+ await import("@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js");
56
53
 
57
54
  const provider = new TradeBlocksAuthProvider(auth);
58
55
 
59
56
  // Determine issuer URL (public URL for OAuth discovery metadata)
60
57
  const issuerUrl = new URL(
61
- auth.issuerUrl || `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`
58
+ auth.issuerUrl || `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`,
62
59
  );
63
60
 
64
61
  // Mount OAuth routes: /.well-known, /authorize, /token, /register
@@ -103,7 +100,7 @@ export async function startHttpServer(
103
100
  console.error(`Authentication enabled. Login at ${issuerUrl}/authorize`);
104
101
  } else if (auth?.noAuth) {
105
102
  console.error(
106
- "WARNING: Authentication disabled (--no-auth). Only use behind an authenticating reverse proxy."
103
+ "WARNING: Authentication disabled (--no-auth). Only use behind an authenticating reverse proxy.",
107
104
  );
108
105
  }
109
106
 
@@ -140,9 +137,7 @@ export async function startHttpServer(
140
137
  return new Promise((resolve, reject) => {
141
138
  httpServer.on("error", reject);
142
139
  httpServer.listen(port, host, () => {
143
- console.error(
144
- `TradeBlocks MCP HTTP server listening on http://${host}:${port}/mcp`
145
- );
140
+ console.error(`TradeBlocks MCP HTTP server listening on http://${host}:${port}/mcp`);
146
141
  console.error(`Health check available at http://${host}:${port}/`);
147
142
  resolve(httpServer);
148
143
  });