fixparser-plugin-mcp 9.1.7-63c797c5 → 9.1.7-6afc89c5

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.
@@ -1,51 +1,9 @@
1
1
  // src/MCPLocal.ts
2
2
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
3
3
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { Fields as Fields3, Messages as Messages3 } from "fixparser";
4
5
  import { z } from "zod";
5
6
 
6
- // src/MCPBase.ts
7
- var MCPBase = class {
8
- /**
9
- * Optional logger instance for diagnostics and output.
10
- * @protected
11
- */
12
- logger;
13
- /**
14
- * FIXParser instance, set during plugin register().
15
- * @protected
16
- */
17
- parser;
18
- /**
19
- * Called when server is setup and listening.
20
- * @protected
21
- */
22
- onReady = void 0;
23
- /**
24
- * Map to store verified orders before execution
25
- * @protected
26
- */
27
- verifiedOrders = /* @__PURE__ */ new Map();
28
- /**
29
- * Map to store pending market data requests
30
- * @protected
31
- */
32
- pendingRequests = /* @__PURE__ */ new Map();
33
- /**
34
- * Map to store market data prices
35
- * @protected
36
- */
37
- marketDataPrices = /* @__PURE__ */ new Map();
38
- /**
39
- * Maximum number of price history entries to keep per symbol
40
- * @protected
41
- */
42
- MAX_PRICE_HISTORY = 1e5;
43
- constructor({ logger, onReady }) {
44
- this.logger = logger;
45
- this.onReady = onReady;
46
- }
47
- };
48
-
49
7
  // src/schemas/schemas.ts
50
8
  var toolSchemas = {
51
9
  parse: {
@@ -246,12 +204,12 @@ var toolSchemas = {
246
204
  "X",
247
205
  "Y",
248
206
  "Z"
249
- ]
250
- },
251
- description: "Market Data Entry Types: 0=Bid, 1=Offer, 2=Trade, 3=Index Value, 4=Opening Price, 5=Closing Price, 6=Settlement Price, 7=High Price, 8=Low Price, 9=Trade Volume, A=Open Interest, B=Simulated Sell Price, C=Simulated Buy Price, D=Empty Book, E=Session High Bid, F=Session Low Offer, G=Fixing Price, H=Electronic Volume, I=Threshold Limits and Price Band Variation, J=Clearing Price, K=Open Interest Change, L=Last Trade Price, M=Last Trade Volume, N=Last Trade Time, O=Last Trade Tick, P=Last Trade Exchange, Q=Last Trade ID, R=Last Trade Side, S=Last Trade Price Change, T=Last Trade Price Change Percent, U=Last Trade Price Change Basis Points, V=Last Trade Price Change Points, W=Last Trade Price Change Ticks, X=Last Trade Price Change Ticks Percent, Y=Last Trade Price Change Ticks Basis Points, Z=Last Trade Price Change Ticks Points"
207
+ ],
208
+ description: "Market Data Entry Types: 0=Bid, 1=Offer, 2=Trade, 3=Index Value, 4=Opening Price, 5=Closing Price, 6=Settlement Price, 7=High Price, 8=Low Price, 9=Trade Volume, A=Open Interest, B=Simulated Sell Price, C=Simulated Buy Price, D=Empty Book, E=Session High Bid, F=Session Low Offer, G=Fixing Price, H=Electronic Volume, I=Threshold Limits and Price Band Variation, J=Clearing Price, K=Open Interest Change, L=Last Trade Price, M=Last Trade Volume, N=Last Trade Time, O=Last Trade Tick, P=Last Trade Exchange, Q=Last Trade ID, R=Last Trade Side, S=Last Trade Price Change, T=Last Trade Price Change Percent, U=Last Trade Price Change Basis Points, V=Last Trade Price Change Points, W=Last Trade Price Change Ticks, X=Last Trade Price Change Ticks Percent, Y=Last Trade Price Change Ticks Basis Points, Z=Last Trade Price Change Ticks Points"
209
+ }
252
210
  }
253
211
  },
254
- required: ["mdUpdateType", "symbols", "mdReqID", "subscriptionRequestType"]
212
+ required: ["mdUpdateType", "symbols", "mdReqID", "subscriptionRequestType", "mdEntryTypes"]
255
213
  }
256
214
  },
257
215
  getStockGraph: {
@@ -277,7 +235,7 @@ var toolSchemas = {
277
235
  };
278
236
 
279
237
  // src/tools/marketData.ts
280
- import { Field, Fields, MDEntryType, Messages } from "fixparser";
238
+ import { Field, Fields, Messages } from "fixparser";
281
239
  import QuickChart from "quickchart-js";
282
240
  var createMarketDataRequestHandler = (parser, pendingRequests) => {
283
241
  return async (args) => {
@@ -285,7 +243,6 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
285
243
  const response = new Promise((resolve) => {
286
244
  pendingRequests.set(args.mdReqID, resolve);
287
245
  });
288
- const entryTypes = args.mdEntryTypes || [MDEntryType.Bid, MDEntryType.Offer, MDEntryType.TradeVolume];
289
246
  const messageFields = [
290
247
  new Field(Fields.MsgType, Messages.MarketDataRequest),
291
248
  new Field(Fields.SenderCompID, parser.sender),
@@ -301,8 +258,8 @@ var createMarketDataRequestHandler = (parser, pendingRequests) => {
301
258
  args.symbols.forEach((symbol) => {
302
259
  messageFields.push(new Field(Fields.Symbol, symbol));
303
260
  });
304
- messageFields.push(new Field(Fields.NoMDEntryTypes, entryTypes.length));
305
- entryTypes.forEach((entryType) => {
261
+ messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
262
+ args.mdEntryTypes.forEach((entryType) => {
306
263
  messageFields.push(new Field(Fields.MDEntryType, entryType));
307
264
  });
308
265
  const mdr = parser.createMessage(...messageFields);
@@ -360,46 +317,18 @@ var createGetStockGraphHandler = (marketDataPrices) => {
360
317
  };
361
318
  }
362
319
  const chart = new QuickChart();
363
- chart.setWidth(1200);
364
- chart.setHeight(600);
365
- chart.setBackgroundColor("transparent");
320
+ chart.setWidth(600);
321
+ chart.setHeight(300);
366
322
  const labels = priceHistory.map((point) => new Date(point.timestamp).toLocaleTimeString());
367
- const bidData = priceHistory.map((point) => point.bid);
368
- const offerData = priceHistory.map((point) => point.offer);
369
- const spreadData = priceHistory.map((point) => point.spread);
370
- const volumeData = priceHistory.map((point) => point.volume);
371
- const config = {
323
+ const data = priceHistory.map((point) => point.price);
324
+ chart.setConfig({
372
325
  type: "line",
373
326
  data: {
374
327
  labels,
375
328
  datasets: [
376
329
  {
377
- label: "Bid",
378
- data: bidData,
379
- borderColor: "#28a745",
380
- backgroundColor: "rgba(40, 167, 69, 0.1)",
381
- fill: false,
382
- tension: 0.4
383
- },
384
- {
385
- label: "Offer",
386
- data: offerData,
387
- borderColor: "#dc3545",
388
- backgroundColor: "rgba(220, 53, 69, 0.1)",
389
- fill: false,
390
- tension: 0.4
391
- },
392
- {
393
- label: "Spread",
394
- data: spreadData,
395
- borderColor: "#6c757d",
396
- backgroundColor: "rgba(108, 117, 125, 0.1)",
397
- fill: false,
398
- tension: 0.4
399
- },
400
- {
401
- label: "Volume",
402
- data: volumeData,
330
+ label: symbol,
331
+ data,
403
332
  borderColor: "#007bff",
404
333
  backgroundColor: "rgba(0, 123, 255, 0.1)",
405
334
  fill: true,
@@ -408,32 +337,43 @@ var createGetStockGraphHandler = (marketDataPrices) => {
408
337
  ]
409
338
  },
410
339
  options: {
411
- responsive: true,
412
340
  plugins: {
413
341
  title: {
414
342
  display: true,
415
- text: `${symbol} Market Data`
343
+ text: `${symbol} Price History`,
344
+ font: {
345
+ size: 16,
346
+ weight: "bold"
347
+ }
348
+ },
349
+ legend: {
350
+ display: true
416
351
  }
417
352
  },
418
353
  scales: {
419
354
  y: {
420
- beginAtZero: false
355
+ beginAtZero: false,
356
+ grid: {
357
+ color: "#e9ecef"
358
+ }
359
+ },
360
+ x: {
361
+ grid: {
362
+ display: false
363
+ }
421
364
  }
422
365
  }
423
366
  }
424
- };
425
- chart.setConfig(config);
367
+ });
426
368
  const imageBuffer = await chart.toBinary();
427
- const base64 = imageBuffer.toString("base64");
369
+ const base64Image = imageBuffer.toString("base64");
428
370
  return {
429
371
  content: [
430
372
  {
431
- type: "resource",
432
- resource: {
433
- uri: "resource://graph",
434
- mimeType: "image/png",
435
- blob: base64
436
- }
373
+ type: "image",
374
+ text: base64Image,
375
+ uri: "getStockGraph",
376
+ mimeType: "image/png"
437
377
  }
438
378
  ]
439
379
  };
@@ -442,7 +382,7 @@ var createGetStockGraphHandler = (marketDataPrices) => {
442
382
  content: [
443
383
  {
444
384
  type: "text",
445
- text: `Error: ${error instanceof Error ? error.message : "Failed to generate chart"}`,
385
+ text: `Error: ${error instanceof Error ? error.message : "Failed to generate stock graph"}`,
446
386
  uri: "getStockGraph"
447
387
  }
448
388
  ],
@@ -475,12 +415,9 @@ var createGetStockPriceHistoryHandler = (marketDataPrices) => {
475
415
  {
476
416
  symbol,
477
417
  count: priceHistory.length,
478
- data: priceHistory.map((point) => ({
418
+ prices: priceHistory.map((point) => ({
479
419
  timestamp: new Date(point.timestamp).toISOString(),
480
- bid: point.bid,
481
- offer: point.offer,
482
- spread: point.spread,
483
- volume: point.volume
420
+ price: point.price
484
421
  }))
485
422
  },
486
423
  null,
@@ -802,86 +739,9 @@ var createToolHandlers = (parser, verifiedOrders, pendingRequests, marketDataPri
802
739
  getStockPriceHistory: createGetStockPriceHistoryHandler(marketDataPrices)
803
740
  });
804
741
 
805
- // src/utils/messageHandler.ts
806
- import { Fields as Fields3, MDEntryType as MDEntryType2, Messages as Messages3 } from "fixparser";
807
- function handleMessage(message, parser, pendingRequests, marketDataPrices, maxPriceHistory, onPriceUpdate) {
808
- parser.logger.log({
809
- level: "info",
810
- message: `MCP Server received message: ${message.messageType}: ${message.description}`
811
- });
812
- const msgType = message.messageType;
813
- if (msgType === Messages3.MarketDataSnapshotFullRefresh) {
814
- const symbol = message.getField(Fields3.Symbol)?.value;
815
- const entries = message.getField(Fields3.NoMDEntries)?.value;
816
- let bid = 0;
817
- let offer = 0;
818
- let volume = 0;
819
- const entryTypes = message.getFields(Fields3.MDEntryType);
820
- const entryPrices = message.getFields(Fields3.MDEntryPx);
821
- const entrySizes = message.getFields(Fields3.MDEntrySize);
822
- if (entryTypes && entryPrices && entrySizes) {
823
- for (let i = 0; i < entries; i++) {
824
- const entryType = entryTypes[i]?.value;
825
- const entryPrice = Number.parseFloat(entryPrices[i]?.value);
826
- const entrySize = Number.parseFloat(entrySizes[i]?.value);
827
- if (entryType === MDEntryType2.Bid) {
828
- bid = entryPrice;
829
- } else if (entryType === MDEntryType2.Offer) {
830
- offer = entryPrice;
831
- }
832
- volume += entrySize;
833
- }
834
- }
835
- const spread = offer - bid;
836
- const timestamp = Date.now();
837
- const data = {
838
- timestamp,
839
- bid,
840
- offer,
841
- spread,
842
- volume
843
- };
844
- if (!marketDataPrices.has(symbol)) {
845
- marketDataPrices.set(symbol, []);
846
- }
847
- const prices = marketDataPrices.get(symbol);
848
- prices.push(data);
849
- if (prices.length > maxPriceHistory) {
850
- prices.splice(0, prices.length - maxPriceHistory);
851
- }
852
- onPriceUpdate?.(symbol, data);
853
- } else if (msgType === Messages3.ExecutionReport) {
854
- const reqId = message.getField(Fields3.ClOrdID)?.value;
855
- const callback = pendingRequests.get(reqId);
856
- if (callback) {
857
- callback(message);
858
- pendingRequests.delete(reqId);
859
- }
860
- }
861
- }
862
-
863
742
  // src/MCPLocal.ts
864
- var MCPLocal = class extends MCPBase {
865
- /**
866
- * Map to store verified orders before execution
867
- * @private
868
- */
869
- verifiedOrders = /* @__PURE__ */ new Map();
870
- /**
871
- * Map to store pending requests and their callbacks
872
- * @private
873
- */
874
- pendingRequests = /* @__PURE__ */ new Map();
875
- /**
876
- * Map to store market data prices for each symbol
877
- * @private
878
- */
879
- marketDataPrices = /* @__PURE__ */ new Map();
880
- /**
881
- * Maximum number of price points to store per symbol
882
- * @private
883
- */
884
- MAX_PRICE_HISTORY = 1e5;
743
+ var MCPLocal = class {
744
+ parser;
885
745
  server = new Server(
886
746
  {
887
747
  name: "fixparser",
@@ -903,13 +763,77 @@ var MCPLocal = class extends MCPBase {
903
763
  }
904
764
  );
905
765
  transport = new StdioServerTransport();
766
+ onReady = void 0;
767
+ pendingRequests = /* @__PURE__ */ new Map();
768
+ verifiedOrders = /* @__PURE__ */ new Map();
769
+ marketDataPrices = /* @__PURE__ */ new Map();
770
+ MAX_PRICE_HISTORY = 1e5;
771
+ // Maximum number of price points to store per symbol
906
772
  constructor({ logger, onReady }) {
907
- super({ logger, onReady });
773
+ if (onReady) this.onReady = onReady;
908
774
  }
909
775
  async register(parser) {
910
776
  this.parser = parser;
911
777
  this.parser.addOnMessageCallback((message) => {
912
- handleMessage(message, this.parser, this.pendingRequests, this.marketDataPrices, this.MAX_PRICE_HISTORY);
778
+ this.parser?.logger.log({
779
+ level: "info",
780
+ message: `MCP Server received message: ${message.messageType}: ${message.description}`
781
+ });
782
+ const msgType = message.messageType;
783
+ if (msgType === Messages3.MarketDataSnapshotFullRefresh || msgType === Messages3.ExecutionReport || msgType === Messages3.Reject || msgType === Messages3.MarketDataIncrementalRefresh) {
784
+ this.parser?.logger.log({
785
+ level: "info",
786
+ message: `MCP Server handling message type: ${msgType}`
787
+ });
788
+ let id;
789
+ if (msgType === Messages3.MarketDataIncrementalRefresh || msgType === Messages3.MarketDataSnapshotFullRefresh) {
790
+ const symbol = message.getField(Fields3.Symbol);
791
+ const price = message.getField(Fields3.MDEntryPx);
792
+ const timestamp = message.getField(Fields3.MDEntryTime)?.value || Date.now();
793
+ if (symbol?.value && price?.value) {
794
+ const symbolStr = String(symbol.value);
795
+ const priceNum = Number(price.value);
796
+ const priceHistory = this.marketDataPrices.get(symbolStr) || [];
797
+ priceHistory.push({
798
+ timestamp: Number(timestamp),
799
+ price: priceNum
800
+ });
801
+ if (priceHistory.length > this.MAX_PRICE_HISTORY) {
802
+ priceHistory.shift();
803
+ }
804
+ this.marketDataPrices.set(symbolStr, priceHistory);
805
+ this.parser?.logger.log({
806
+ level: "info",
807
+ message: `MCP Server added ${symbol}: ${priceNum}`
808
+ });
809
+ this.server.notification({
810
+ method: "priceUpdate",
811
+ params: {
812
+ symbol: symbolStr,
813
+ price: priceNum,
814
+ timestamp: Number(timestamp)
815
+ }
816
+ });
817
+ }
818
+ }
819
+ if (msgType === Messages3.MarketDataSnapshotFullRefresh) {
820
+ const mdReqID = message.getField(Fields3.MDReqID);
821
+ if (mdReqID) id = String(mdReqID.value);
822
+ } else if (msgType === Messages3.ExecutionReport) {
823
+ const clOrdID = message.getField(Fields3.ClOrdID);
824
+ if (clOrdID) id = String(clOrdID.value);
825
+ } else if (msgType === Messages3.Reject) {
826
+ const refSeqNum = message.getField(Fields3.RefSeqNum);
827
+ if (refSeqNum) id = String(refSeqNum.value);
828
+ }
829
+ if (id) {
830
+ const callback = this.pendingRequests.get(id);
831
+ if (callback) {
832
+ callback(message);
833
+ this.pendingRequests.delete(id);
834
+ }
835
+ }
836
+ }
913
837
  });
914
838
  this.addWorkflows();
915
839
  await this.server.connect(this.transport);
@@ -924,15 +848,18 @@ var MCPLocal = class extends MCPBase {
924
848
  if (!this.server) {
925
849
  return;
926
850
  }
927
- this.server.setRequestHandler(z.object({ method: z.literal("tools/list") }), async () => {
928
- return {
929
- tools: Object.entries(toolSchemas).map(([name, { description, schema }]) => ({
930
- name,
931
- description,
932
- inputSchema: schema
933
- }))
934
- };
935
- });
851
+ this.server.setRequestHandler(
852
+ z.object({ method: z.literal("tools/list") }),
853
+ async (request, extra) => {
854
+ return {
855
+ tools: Object.entries(toolSchemas).map(([name, { description, schema }]) => ({
856
+ name,
857
+ description,
858
+ inputSchema: schema
859
+ }))
860
+ };
861
+ }
862
+ );
936
863
  this.server.setRequestHandler(
937
864
  z.object({
938
865
  method: z.literal("tools/call"),
@@ -944,7 +871,7 @@ var MCPLocal = class extends MCPBase {
944
871
  }).optional()
945
872
  })
946
873
  }),
947
- async (request) => {
874
+ async (request, extra) => {
948
875
  const { name, arguments: args } = request.params;
949
876
  const toolHandlers = createToolHandlers(
950
877
  this.parser,