fixparser-plugin-mcp 9.1.7-75ded9c1 → 9.1.7-89ae1451

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.
@@ -27,6 +27,103 @@ var import_server = require("@modelcontextprotocol/sdk/server/index.js");
27
27
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
28
28
  var import_fixparser = require("fixparser");
29
29
  var import_zod = require("zod");
30
+ var symbolSchema = import_zod.z.object({
31
+ symbol: import_zod.z.string()
32
+ });
33
+ var fixStringSchema = import_zod.z.object({
34
+ fixString: import_zod.z.string()
35
+ });
36
+ var orderSchema = import_zod.z.object({
37
+ clOrdID: import_zod.z.string(),
38
+ handlInst: import_zod.z.enum(["1", "2", "3"]),
39
+ quantity: import_zod.z.string(),
40
+ price: import_zod.z.string(),
41
+ ordType: import_zod.z.enum([
42
+ "1",
43
+ "2",
44
+ "3",
45
+ "4",
46
+ "5",
47
+ "6",
48
+ "7",
49
+ "8",
50
+ "9",
51
+ "A",
52
+ "B",
53
+ "C",
54
+ "D",
55
+ "E",
56
+ "F",
57
+ "G",
58
+ "H",
59
+ "I",
60
+ "J",
61
+ "K",
62
+ "L",
63
+ "M",
64
+ "P",
65
+ "Q",
66
+ "R",
67
+ "S"
68
+ ]),
69
+ side: import_zod.z.enum(["1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H"]),
70
+ symbol: import_zod.z.string(),
71
+ timeInForce: import_zod.z.enum(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"])
72
+ });
73
+ var marketDataRequestSchema = import_zod.z.object({
74
+ mdUpdateType: import_zod.z.enum(["0", "1"]),
75
+ symbols: import_zod.z.array(import_zod.z.string()),
76
+ mdReqID: import_zod.z.string(),
77
+ subscriptionRequestType: import_zod.z.enum(["0", "1", "2"]),
78
+ mdEntryTypes: import_zod.z.array(
79
+ import_zod.z.enum([
80
+ "0",
81
+ "1",
82
+ "2",
83
+ "3",
84
+ "4",
85
+ "5",
86
+ "6",
87
+ "7",
88
+ "8",
89
+ "9",
90
+ "A",
91
+ "B",
92
+ "C",
93
+ "D",
94
+ "E",
95
+ "F",
96
+ "G",
97
+ "H",
98
+ "J",
99
+ "K",
100
+ "L",
101
+ "M",
102
+ "N",
103
+ "O",
104
+ "P",
105
+ "Q",
106
+ "R",
107
+ "S",
108
+ "T",
109
+ "U",
110
+ "V",
111
+ "W",
112
+ "X",
113
+ "Y",
114
+ "Z",
115
+ "a",
116
+ "b",
117
+ "c",
118
+ "d",
119
+ "e",
120
+ "g",
121
+ "h",
122
+ "i",
123
+ "t"
124
+ ])
125
+ )
126
+ });
30
127
  var MCPLocal = class {
31
128
  parser;
32
129
  server = new import_server.Server(
@@ -185,11 +282,25 @@ var MCPLocal = class {
185
282
  },
186
283
  stockGraph: {
187
284
  description: "Generates a price chart for a given symbol",
188
- uri: "stockGraph/{symbol}"
285
+ uri: "stockGraph",
286
+ parameters: {
287
+ type: "object",
288
+ properties: {
289
+ symbol: { type: "string" }
290
+ },
291
+ required: ["symbol"]
292
+ }
189
293
  },
190
294
  stockPriceHistory: {
191
295
  description: "Returns price history for a given symbol",
192
- uri: "stockPriceHistory/{symbol}"
296
+ uri: "stockPriceHistory",
297
+ parameters: {
298
+ type: "object",
299
+ properties: {
300
+ symbol: { type: "string" }
301
+ },
302
+ required: ["symbol"]
303
+ }
193
304
  }
194
305
  }
195
306
  }
@@ -240,14 +351,6 @@ var MCPLocal = class {
240
351
  level: "info",
241
352
  message: `MCP Server added ${symbol}: ${priceNum}`
242
353
  });
243
- this.server.notification({
244
- method: "priceUpdate",
245
- params: {
246
- symbol: symbolStr,
247
- price: priceNum,
248
- timestamp: Number(timestamp)
249
- }
250
- });
251
354
  }
252
355
  }
253
356
  if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh) {
@@ -291,20 +394,11 @@ var MCPLocal = class {
291
394
  name: "greeting",
292
395
  description: "A simple greeting resource",
293
396
  uri: "greeting-resource"
294
- }
295
- ]
296
- };
297
- }
298
- );
299
- this.server.setRequestHandler(
300
- import_zod.z.object({ method: import_zod.z.literal("resources/templates/list") }),
301
- async (request, extra) => {
302
- return {
303
- resourceTemplates: [
397
+ },
304
398
  {
305
399
  name: "stockGraph",
306
400
  description: "Generates a price chart for a given symbol",
307
- uriTemplate: "stockGraph/{symbol}",
401
+ uri: "stockGraph",
308
402
  parameters: {
309
403
  type: "object",
310
404
  properties: {
@@ -316,7 +410,7 @@ var MCPLocal = class {
316
410
  {
317
411
  name: "stockPriceHistory",
318
412
  description: "Returns price history for a given symbol",
319
- uriTemplate: "stockPriceHistory/{symbol}",
413
+ uri: "stockPriceHistory",
320
414
  parameters: {
321
415
  type: "object",
322
416
  properties: {
@@ -491,176 +585,218 @@ var MCPLocal = class {
491
585
  );
492
586
  this.server.setRequestHandler(
493
587
  import_zod.z.object({
494
- method: import_zod.z.literal("tools/call"),
588
+ method: import_zod.z.literal("resources/read"),
495
589
  params: import_zod.z.object({
496
- name: import_zod.z.string(),
497
- arguments: import_zod.z.any(),
498
- _meta: import_zod.z.object({
499
- progressToken: import_zod.z.number()
500
- }).optional()
590
+ uri: import_zod.z.string()
501
591
  })
502
592
  }),
503
593
  async (request, extra) => {
504
- const { name, arguments: args } = request.params;
505
- switch (name) {
506
- case "parse":
507
- try {
508
- const parsedMessage = this.parser?.parse(args.fixString);
509
- if (!parsedMessage || parsedMessage.length === 0) {
510
- return {
511
- contents: [
512
- {
513
- type: "text",
514
- text: "Error: Failed to parse FIX string",
515
- uri: "parse"
516
- }
517
- ],
518
- isError: true
519
- };
594
+ const { uri } = request.params;
595
+ switch (uri) {
596
+ case "greeting-resource":
597
+ return {
598
+ contents: [
599
+ {
600
+ type: "text",
601
+ text: "Hello, world!"
602
+ }
603
+ ]
604
+ };
605
+ case "stockGraph":
606
+ return {
607
+ contents: [
608
+ {
609
+ type: "text",
610
+ text: "This resource requires a symbol parameter. Please use the stockGraph resource with a symbol parameter."
611
+ }
612
+ ]
613
+ };
614
+ case "stockPriceHistory":
615
+ return {
616
+ contents: [
617
+ {
618
+ type: "text",
619
+ text: "This resource requires a symbol parameter. Please use the stockPriceHistory resource with a symbol parameter."
620
+ }
621
+ ]
622
+ };
623
+ default:
624
+ return {
625
+ contents: [
626
+ {
627
+ type: "text",
628
+ text: `Resource not found: ${uri}`
629
+ }
630
+ ],
631
+ isError: true
632
+ };
633
+ }
634
+ }
635
+ );
636
+ this.server.setRequestHandler(
637
+ import_zod.z.object({
638
+ method: import_zod.z.literal("parse"),
639
+ params: fixStringSchema
640
+ }),
641
+ async (request, extra) => {
642
+ try {
643
+ const args = request.params;
644
+ const parsedMessage = this.parser?.parse(args.fixString);
645
+ if (!parsedMessage || parsedMessage.length === 0) {
646
+ return {
647
+ content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
648
+ isError: true
649
+ };
650
+ }
651
+ return {
652
+ content: [
653
+ {
654
+ type: "text",
655
+ text: `${parsedMessage[0].description}
656
+ ${parsedMessage[0].messageTypeDescription}`
520
657
  }
521
- return {
522
- contents: [
523
- {
524
- type: "text",
525
- text: `${parsedMessage[0].description}
526
- ${parsedMessage[0].messageTypeDescription}`,
527
- uri: "parse"
528
- }
529
- ]
530
- };
531
- } catch (error) {
532
- return {
533
- contents: [
534
- {
535
- type: "text",
536
- text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
537
- uri: "parse"
538
- }
539
- ],
540
- isError: true
541
- };
542
- }
543
- case "parseToJSON":
544
- try {
545
- const parsedMessage = this.parser?.parse(args.fixString);
546
- if (!parsedMessage || parsedMessage.length === 0) {
547
- return {
548
- contents: [
549
- {
550
- type: "text",
551
- text: "Error: Failed to parse FIX string",
552
- uri: "parseToJSON"
553
- }
554
- ],
555
- isError: true
556
- };
658
+ ]
659
+ };
660
+ } catch (error) {
661
+ return {
662
+ content: [
663
+ {
664
+ type: "text",
665
+ text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
557
666
  }
558
- return {
559
- contents: [
560
- {
561
- type: "text",
562
- text: `${parsedMessage[0].toFIXJSON()}`,
563
- uri: "parseToJSON"
564
- }
565
- ]
566
- };
567
- } catch (error) {
568
- return {
569
- contents: [
570
- {
571
- type: "text",
572
- text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
573
- uri: "parseToJSON"
574
- }
575
- ],
576
- isError: true
577
- };
578
- }
579
- case "verifyOrder":
580
- try {
581
- this.verifiedOrders.set(args.clOrdID, {
582
- clOrdID: args.clOrdID,
583
- handlInst: args.handlInst,
584
- quantity: Number.parseFloat(args.quantity),
585
- price: Number.parseFloat(args.price),
586
- ordType: args.ordType,
587
- side: args.side,
588
- symbol: args.symbol,
589
- timeInForce: args.timeInForce
590
- });
591
- const ordTypeNames = {
592
- "1": "Market",
593
- "2": "Limit",
594
- "3": "Stop",
595
- "4": "StopLimit",
596
- "5": "MarketOnClose",
597
- "6": "WithOrWithout",
598
- "7": "LimitOrBetter",
599
- "8": "LimitWithOrWithout",
600
- "9": "OnBasis",
601
- A: "OnClose",
602
- B: "LimitOnClose",
603
- C: "ForexMarket",
604
- D: "PreviouslyQuoted",
605
- E: "PreviouslyIndicated",
606
- F: "ForexLimit",
607
- G: "ForexSwap",
608
- H: "ForexPreviouslyQuoted",
609
- I: "Funari",
610
- J: "MarketIfTouched",
611
- K: "MarketWithLeftOverAsLimit",
612
- L: "PreviousFundValuationPoint",
613
- M: "NextFundValuationPoint",
614
- P: "Pegged",
615
- Q: "CounterOrderSelection",
616
- R: "StopOnBidOrOffer",
617
- S: "StopLimitOnBidOrOffer"
618
- };
619
- const sideNames = {
620
- "1": "Buy",
621
- "2": "Sell",
622
- "3": "BuyMinus",
623
- "4": "SellPlus",
624
- "5": "SellShort",
625
- "6": "SellShortExempt",
626
- "7": "Undisclosed",
627
- "8": "Cross",
628
- "9": "CrossShort",
629
- A: "CrossShortExempt",
630
- B: "AsDefined",
631
- C: "Opposite",
632
- D: "Subscribe",
633
- E: "Redeem",
634
- F: "Lend",
635
- G: "Borrow",
636
- H: "SellUndisclosed"
637
- };
638
- const timeInForceNames = {
639
- "0": "Day",
640
- "1": "GoodTillCancel",
641
- "2": "AtTheOpening",
642
- "3": "ImmediateOrCancel",
643
- "4": "FillOrKill",
644
- "5": "GoodTillCrossing",
645
- "6": "GoodTillDate",
646
- "7": "AtTheClose",
647
- "8": "GoodThroughCrossing",
648
- "9": "AtCrossing",
649
- A: "GoodForTime",
650
- B: "GoodForAuction",
651
- C: "GoodForMonth"
652
- };
653
- const handlInstNames = {
654
- "1": "AutomatedExecutionNoIntervention",
655
- "2": "AutomatedExecutionInterventionOK",
656
- "3": "ManualOrder"
657
- };
658
- return {
659
- contents: [
660
- {
661
- type: "text",
662
- text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
663
-
667
+ ],
668
+ isError: true
669
+ };
670
+ }
671
+ }
672
+ );
673
+ this.server.setRequestHandler(
674
+ import_zod.z.object({
675
+ method: import_zod.z.literal("parseToJSON"),
676
+ params: fixStringSchema
677
+ }),
678
+ async (request, extra) => {
679
+ try {
680
+ const args = request.params;
681
+ const parsedMessage = this.parser?.parse(args.fixString);
682
+ if (!parsedMessage || parsedMessage.length === 0) {
683
+ return {
684
+ content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
685
+ isError: true
686
+ };
687
+ }
688
+ return {
689
+ content: [
690
+ {
691
+ type: "text",
692
+ text: `${parsedMessage[0].toFIXJSON()}`
693
+ }
694
+ ]
695
+ };
696
+ } catch (error) {
697
+ return {
698
+ content: [
699
+ {
700
+ type: "text",
701
+ text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
702
+ }
703
+ ],
704
+ isError: true
705
+ };
706
+ }
707
+ }
708
+ );
709
+ this.server.setRequestHandler(
710
+ import_zod.z.object({
711
+ method: import_zod.z.literal("verifyOrder"),
712
+ params: orderSchema
713
+ }),
714
+ async (request, extra) => {
715
+ try {
716
+ const args = request.params;
717
+ this.verifiedOrders.set(args.clOrdID, {
718
+ clOrdID: args.clOrdID,
719
+ handlInst: args.handlInst,
720
+ quantity: Number.parseFloat(args.quantity),
721
+ price: Number.parseFloat(args.price),
722
+ ordType: args.ordType,
723
+ side: args.side,
724
+ symbol: args.symbol,
725
+ timeInForce: args.timeInForce
726
+ });
727
+ const ordTypeNames = {
728
+ "1": "Market",
729
+ "2": "Limit",
730
+ "3": "Stop",
731
+ "4": "StopLimit",
732
+ "5": "MarketOnClose",
733
+ "6": "WithOrWithout",
734
+ "7": "LimitOrBetter",
735
+ "8": "LimitWithOrWithout",
736
+ "9": "OnBasis",
737
+ A: "OnClose",
738
+ B: "LimitOnClose",
739
+ C: "ForexMarket",
740
+ D: "PreviouslyQuoted",
741
+ E: "PreviouslyIndicated",
742
+ F: "ForexLimit",
743
+ G: "ForexSwap",
744
+ H: "ForexPreviouslyQuoted",
745
+ I: "Funari",
746
+ J: "MarketIfTouched",
747
+ K: "MarketWithLeftOverAsLimit",
748
+ L: "PreviousFundValuationPoint",
749
+ M: "NextFundValuationPoint",
750
+ P: "Pegged",
751
+ Q: "CounterOrderSelection",
752
+ R: "StopOnBidOrOffer",
753
+ S: "StopLimitOnBidOrOffer"
754
+ };
755
+ const sideNames = {
756
+ "1": "Buy",
757
+ "2": "Sell",
758
+ "3": "BuyMinus",
759
+ "4": "SellPlus",
760
+ "5": "SellShort",
761
+ "6": "SellShortExempt",
762
+ "7": "Undisclosed",
763
+ "8": "Cross",
764
+ "9": "CrossShort",
765
+ A: "CrossShortExempt",
766
+ B: "AsDefined",
767
+ C: "Opposite",
768
+ D: "Subscribe",
769
+ E: "Redeem",
770
+ F: "Lend",
771
+ G: "Borrow",
772
+ H: "SellUndisclosed"
773
+ };
774
+ const timeInForceNames = {
775
+ "0": "Day",
776
+ "1": "GoodTillCancel",
777
+ "2": "AtTheOpening",
778
+ "3": "ImmediateOrCancel",
779
+ "4": "FillOrKill",
780
+ "5": "GoodTillCrossing",
781
+ "6": "GoodTillDate",
782
+ "7": "AtTheClose",
783
+ "8": "GoodThroughCrossing",
784
+ "9": "AtCrossing",
785
+ A: "GoodForTime",
786
+ B: "GoodForAuction",
787
+ C: "GoodForMonth"
788
+ };
789
+ const handlInstNames = {
790
+ "1": "AutomatedExecutionNoIntervention",
791
+ "2": "AutomatedExecutionInterventionOK",
792
+ "3": "ManualOrder"
793
+ };
794
+ return {
795
+ content: [
796
+ {
797
+ type: "text",
798
+ text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
799
+
664
800
  Parameters verified:
665
801
  - ClOrdID: ${args.clOrdID}
666
802
  - HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
@@ -671,247 +807,209 @@ Parameters verified:
671
807
  - Symbol: ${args.symbol}
672
808
  - TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
673
809
 
674
- To execute this order, call the executeOrder tool with these exact same parameters.`,
675
- uri: "verifyOrder"
676
- }
677
- ]
678
- };
679
- } catch (error) {
680
- return {
681
- contents: [
682
- {
683
- type: "text",
684
- text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`,
685
- uri: "verifyOrder"
686
- }
687
- ],
688
- isError: true
689
- };
690
- }
691
- case "executeOrder":
692
- try {
693
- const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
694
- if (!verifiedOrder) {
695
- return {
696
- contents: [
697
- {
698
- type: "text",
699
- text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`,
700
- uri: "executeOrder"
701
- }
702
- ],
703
- isError: true
704
- };
705
- }
706
- if (verifiedOrder.handlInst !== args.handlInst || verifiedOrder.quantity !== Number.parseFloat(args.quantity) || verifiedOrder.price !== Number.parseFloat(args.price) || verifiedOrder.ordType !== args.ordType || verifiedOrder.side !== args.side || verifiedOrder.symbol !== args.symbol || verifiedOrder.timeInForce !== args.timeInForce) {
707
- return {
708
- contents: [
709
- {
710
- type: "text",
711
- text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified.",
712
- uri: "executeOrder"
713
- }
714
- ],
715
- isError: true
716
- };
717
- }
718
- const response = new Promise((resolve) => {
719
- this.pendingRequests.set(args.clOrdID, resolve);
720
- });
721
- const order = this.parser?.createMessage(
722
- new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.NewOrderSingle),
723
- new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
724
- new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
725
- new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
726
- new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
727
- new import_fixparser.Field(import_fixparser.Fields.ClOrdID, args.clOrdID),
728
- new import_fixparser.Field(import_fixparser.Fields.Side, args.side),
729
- new import_fixparser.Field(import_fixparser.Fields.Symbol, args.symbol),
730
- new import_fixparser.Field(import_fixparser.Fields.OrderQty, Number.parseFloat(args.quantity)),
731
- new import_fixparser.Field(import_fixparser.Fields.Price, Number.parseFloat(args.price)),
732
- new import_fixparser.Field(import_fixparser.Fields.OrdType, args.ordType),
733
- new import_fixparser.Field(import_fixparser.Fields.HandlInst, args.handlInst),
734
- new import_fixparser.Field(import_fixparser.Fields.TimeInForce, args.timeInForce),
735
- new import_fixparser.Field(import_fixparser.Fields.TransactTime, this.parser?.getTimestamp())
736
- );
737
- if (!this.parser?.connected) {
738
- return {
739
- contents: [
740
- {
741
- type: "text",
742
- text: "Error: Not connected. Ignoring message.",
743
- uri: "executeOrder"
744
- }
745
- ],
746
- isError: true
747
- };
810
+ To execute this order, call the executeOrder tool with these exact same parameters.`
748
811
  }
749
- this.parser?.send(order);
750
- const fixData = await response;
751
- this.verifiedOrders.delete(args.clOrdID);
752
- return {
753
- contents: [
754
- {
755
- type: "text",
756
- text: fixData.messageType === import_fixparser.Messages.Reject ? `Reject message for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}` : `Execution Report for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`,
757
- uri: "executeOrder"
758
- }
759
- ]
760
- };
761
- } catch (error) {
762
- return {
763
- contents: [
764
- {
765
- type: "text",
766
- text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`,
767
- uri: "executeOrder"
768
- }
769
- ],
770
- isError: true
771
- };
772
- }
773
- case "marketDataRequest":
774
- try {
775
- const response = new Promise((resolve) => {
776
- this.pendingRequests.set(args.mdReqID, resolve);
777
- });
778
- const messageFields = [
779
- new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
780
- new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
781
- new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
782
- new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
783
- new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
784
- new import_fixparser.Field(import_fixparser.Fields.MDReqID, args.mdReqID),
785
- new import_fixparser.Field(import_fixparser.Fields.SubscriptionRequestType, args.subscriptionRequestType),
786
- new import_fixparser.Field(import_fixparser.Fields.MarketDepth, 0),
787
- new import_fixparser.Field(import_fixparser.Fields.MDUpdateType, args.mdUpdateType)
788
- ];
789
- messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoRelatedSym, args.symbols.length));
790
- args.symbols.forEach((symbol) => {
791
- messageFields.push(new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol));
792
- });
793
- messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoMDEntryTypes, args.mdEntryTypes.length));
794
- args.mdEntryTypes.forEach((entryType) => {
795
- messageFields.push(new import_fixparser.Field(import_fixparser.Fields.MDEntryType, entryType));
796
- });
797
- const mdr = this.parser?.createMessage(...messageFields);
798
- if (!this.parser?.connected) {
799
- return {
800
- contents: [
801
- {
802
- type: "text",
803
- text: "Error: Not connected. Ignoring message.",
804
- uri: "marketDataRequest"
805
- }
806
- ],
807
- isError: true
808
- };
812
+ ]
813
+ };
814
+ } catch (error) {
815
+ return {
816
+ content: [
817
+ {
818
+ type: "text",
819
+ text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`
809
820
  }
810
- this.parser?.send(mdr);
811
- const fixData = await response;
812
- return {
813
- contents: [
814
- {
815
- type: "text",
816
- text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`,
817
- uri: "marketDataRequest"
818
- }
819
- ]
820
- };
821
- } catch (error) {
822
- return {
823
- contents: [
824
- {
825
- type: "text",
826
- text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`,
827
- uri: "marketDataRequest"
828
- }
829
- ],
830
- isError: true
831
- };
832
- }
833
- default:
834
- return {
835
- contents: [
836
- {
837
- type: "text",
838
- text: `Tool not found: ${name}`,
839
- uri: name
840
- }
841
- ],
842
- isError: true
843
- };
821
+ ],
822
+ isError: true
823
+ };
844
824
  }
845
825
  }
846
826
  );
847
827
  this.server.setRequestHandler(
848
828
  import_zod.z.object({
849
- method: import_zod.z.literal("resources/read"),
850
- params: import_zod.z.object({
851
- uri: import_zod.z.string()
852
- })
829
+ method: import_zod.z.literal("executeOrder"),
830
+ params: orderSchema
853
831
  }),
854
832
  async (request, extra) => {
855
- const { uri } = request.params;
856
- switch (uri) {
857
- case "greeting-resource":
833
+ try {
834
+ const args = request.params;
835
+ const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
836
+ if (!verifiedOrder) {
858
837
  return {
859
- contents: [
838
+ content: [
860
839
  {
861
840
  type: "text",
862
- text: "Hello, world!",
863
- uri: "greeting-resource"
841
+ text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`
864
842
  }
865
- ]
843
+ ],
844
+ isError: true
866
845
  };
867
- case "stockGraph":
846
+ }
847
+ if (verifiedOrder.handlInst !== args.handlInst || verifiedOrder.quantity !== Number.parseFloat(args.quantity) || verifiedOrder.price !== Number.parseFloat(args.price) || verifiedOrder.ordType !== args.ordType || verifiedOrder.side !== args.side || verifiedOrder.symbol !== args.symbol || verifiedOrder.timeInForce !== args.timeInForce) {
868
848
  return {
869
- contents: [
849
+ content: [
870
850
  {
871
851
  type: "text",
872
- text: "This resource requires a symbol parameter. Please use the stockGraph/{symbol} resource.",
873
- uri: "stockGraph"
852
+ text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified."
874
853
  }
875
- ]
854
+ ],
855
+ isError: true
876
856
  };
877
- case "stockPriceHistory":
857
+ }
858
+ const response = new Promise((resolve) => {
859
+ this.pendingRequests.set(args.clOrdID, resolve);
860
+ });
861
+ const order = this.parser?.createMessage(
862
+ new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.NewOrderSingle),
863
+ new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
864
+ new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
865
+ new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
866
+ new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
867
+ new import_fixparser.Field(import_fixparser.Fields.ClOrdID, args.clOrdID),
868
+ new import_fixparser.Field(import_fixparser.Fields.Side, args.side),
869
+ new import_fixparser.Field(import_fixparser.Fields.Symbol, args.symbol),
870
+ new import_fixparser.Field(import_fixparser.Fields.OrderQty, Number.parseFloat(args.quantity)),
871
+ new import_fixparser.Field(import_fixparser.Fields.Price, Number.parseFloat(args.price)),
872
+ new import_fixparser.Field(import_fixparser.Fields.OrdType, args.ordType),
873
+ new import_fixparser.Field(import_fixparser.Fields.HandlInst, args.handlInst),
874
+ new import_fixparser.Field(import_fixparser.Fields.TimeInForce, args.timeInForce),
875
+ new import_fixparser.Field(import_fixparser.Fields.TransactTime, this.parser?.getTimestamp())
876
+ );
877
+ if (!this.parser?.connected) {
878
878
  return {
879
- contents: [
879
+ content: [
880
880
  {
881
881
  type: "text",
882
- text: "This resource requires a symbol parameter. Please use the stockPriceHistory/{symbol} resource.",
883
- uri: "stockPriceHistory"
882
+ text: "Error: Not connected. Ignoring message."
884
883
  }
885
- ]
884
+ ],
885
+ isError: true
886
886
  };
887
- default:
888
- if (uri.startsWith("stockGraph/")) {
889
- const symbol = uri.split("/")[1];
890
- const priceHistory = this.marketDataPrices.get(symbol) || [];
891
- if (priceHistory.length === 0) {
892
- return {
893
- contents: [
894
- {
895
- type: "text",
896
- text: `No price data available for ${symbol}`,
897
- uri
898
- }
899
- ]
900
- };
887
+ }
888
+ this.parser?.send(order);
889
+ const fixData = await response;
890
+ this.verifiedOrders.delete(args.clOrdID);
891
+ return {
892
+ content: [
893
+ {
894
+ type: "text",
895
+ text: fixData.messageType === import_fixparser.Messages.Reject ? `Reject message for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}` : `Execution Report for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`
901
896
  }
902
- const width = 600;
903
- const height = 300;
904
- const padding = 40;
905
- const xScale = (width - 2 * padding) / (priceHistory.length - 1);
906
- const yMin = Math.min(...priceHistory.map((d) => d.price));
907
- const yMax = Math.max(...priceHistory.map((d) => d.price));
908
- const yScale = (height - 2 * padding) / (yMax - yMin);
909
- const points = priceHistory.map((d, i) => {
910
- const x = padding + i * xScale;
911
- const y = height - padding - (d.price - yMin) * yScale;
912
- return `${x},${y}`;
913
- }).join(" L ");
914
- const svg = `<?xml version="1.0" encoding="UTF-8"?>
897
+ ]
898
+ };
899
+ } catch (error) {
900
+ return {
901
+ content: [
902
+ {
903
+ type: "text",
904
+ text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`
905
+ }
906
+ ],
907
+ isError: true
908
+ };
909
+ }
910
+ }
911
+ );
912
+ this.server.setRequestHandler(
913
+ import_zod.z.object({
914
+ method: import_zod.z.literal("marketDataRequest"),
915
+ params: marketDataRequestSchema
916
+ }),
917
+ async (request, extra) => {
918
+ try {
919
+ const args = request.params;
920
+ const response = new Promise((resolve) => {
921
+ this.pendingRequests.set(args.mdReqID, resolve);
922
+ });
923
+ const messageFields = [
924
+ new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
925
+ new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
926
+ new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
927
+ new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
928
+ new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
929
+ new import_fixparser.Field(import_fixparser.Fields.MDReqID, args.mdReqID),
930
+ new import_fixparser.Field(import_fixparser.Fields.SubscriptionRequestType, args.subscriptionRequestType),
931
+ new import_fixparser.Field(import_fixparser.Fields.MarketDepth, 0),
932
+ new import_fixparser.Field(import_fixparser.Fields.MDUpdateType, args.mdUpdateType)
933
+ ];
934
+ messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoRelatedSym, args.symbols.length));
935
+ args.symbols.forEach((symbol) => {
936
+ messageFields.push(new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol));
937
+ });
938
+ messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoMDEntryTypes, args.mdEntryTypes.length));
939
+ args.mdEntryTypes.forEach((entryType) => {
940
+ messageFields.push(new import_fixparser.Field(import_fixparser.Fields.MDEntryType, entryType));
941
+ });
942
+ const mdr = this.parser?.createMessage(...messageFields);
943
+ if (!this.parser?.connected) {
944
+ return {
945
+ content: [
946
+ {
947
+ type: "text",
948
+ text: "Error: Not connected. Ignoring message."
949
+ }
950
+ ],
951
+ isError: true
952
+ };
953
+ }
954
+ this.parser?.send(mdr);
955
+ const fixData = await response;
956
+ return {
957
+ content: [
958
+ {
959
+ type: "text",
960
+ text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`
961
+ }
962
+ ]
963
+ };
964
+ } catch (error) {
965
+ return {
966
+ content: [
967
+ {
968
+ type: "text",
969
+ text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`
970
+ }
971
+ ],
972
+ isError: true
973
+ };
974
+ }
975
+ }
976
+ );
977
+ this.server.setRequestHandler(
978
+ import_zod.z.object({
979
+ method: import_zod.z.literal("stockGraph"),
980
+ params: symbolSchema
981
+ }),
982
+ async (request, extra) => {
983
+ this.parser?.logger.log({
984
+ level: "info",
985
+ message: "MCP Server Resource called: stockGraph"
986
+ });
987
+ const args = request.params;
988
+ const symbol = args.symbol;
989
+ const priceHistory = this.marketDataPrices.get(symbol) || [];
990
+ if (priceHistory.length === 0) {
991
+ return {
992
+ content: [
993
+ {
994
+ type: "text",
995
+ text: `No price data available for ${symbol}`
996
+ }
997
+ ]
998
+ };
999
+ }
1000
+ const width = 600;
1001
+ const height = 300;
1002
+ const padding = 40;
1003
+ const xScale = (width - 2 * padding) / (priceHistory.length - 1);
1004
+ const yMin = Math.min(...priceHistory.map((d) => d.price));
1005
+ const yMax = Math.max(...priceHistory.map((d) => d.price));
1006
+ const yScale = (height - 2 * padding) / (yMax - yMin);
1007
+ const points = priceHistory.map((d, i) => {
1008
+ const x = padding + i * xScale;
1009
+ const y = height - padding - (d.price - yMin) * yScale;
1010
+ return `${x},${y}`;
1011
+ }).join(" L ");
1012
+ const svg = `<?xml version="1.0" encoding="UTF-8"?>
915
1013
  <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
916
1014
  <!-- Background -->
917
1015
  <rect width="100%" height="100%" fill="#f8f9fa"/>
@@ -919,9 +1017,9 @@ To execute this order, call the executeOrder tool with these exact same paramete
919
1017
  <!-- Grid lines -->
920
1018
  <g stroke="#e9ecef" stroke-width="1">
921
1019
  ${Array.from({ length: 5 }, (_, i) => {
922
- const y = padding + (height - 2 * padding) * i / 4;
923
- return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
924
- }).join("\n")}
1020
+ const y = padding + (height - 2 * padding) * i / 4;
1021
+ return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
1022
+ }).join("\n")}
925
1023
  </g>
926
1024
 
927
1025
  <!-- Price line -->
@@ -932,24 +1030,24 @@ To execute this order, call the executeOrder tool with these exact same paramete
932
1030
 
933
1031
  <!-- Data points -->
934
1032
  ${priceHistory.map((d, i) => {
935
- const x = padding + i * xScale;
936
- const y = height - padding - (d.price - yMin) * yScale;
937
- return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
938
- }).join("\n")}
1033
+ const x = padding + i * xScale;
1034
+ const y = height - padding - (d.price - yMin) * yScale;
1035
+ return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
1036
+ }).join("\n")}
939
1037
 
940
1038
  <!-- Labels -->
941
1039
  <g font-family="Arial" font-size="12" fill="#495057">
942
1040
  ${Array.from({ length: 5 }, (_, i) => {
943
- const x = padding + (width - 2 * padding) * i / 4;
944
- const index = Math.floor((priceHistory.length - 1) * i / 4);
945
- const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
946
- return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
947
- }).join("\n")}
1041
+ const x = padding + (width - 2 * padding) * i / 4;
1042
+ const index = Math.floor((priceHistory.length - 1) * i / 4);
1043
+ const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
1044
+ return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
1045
+ }).join("\n")}
948
1046
  ${Array.from({ length: 5 }, (_, i) => {
949
- const y = padding + (height - 2 * padding) * i / 4;
950
- const price = yMax - (yMax - yMin) * i / 4;
951
- return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
952
- }).join("\n")}
1047
+ const y = padding + (height - 2 * padding) * i / 4;
1048
+ const price = yMax - (yMax - yMin) * i / 4;
1049
+ return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
1050
+ }).join("\n")}
953
1051
  </g>
954
1052
 
955
1053
  <!-- Title -->
@@ -959,62 +1057,58 @@ To execute this order, call the executeOrder tool with these exact same paramete
959
1057
  ${symbol} - Price Chart (${priceHistory.length} points)
960
1058
  </text>
961
1059
  </svg>`;
962
- return {
963
- contents: [
964
- {
965
- type: "text",
966
- text: svg,
967
- uri
968
- }
969
- ]
970
- };
1060
+ return {
1061
+ content: [
1062
+ {
1063
+ type: "text",
1064
+ text: svg
971
1065
  }
972
- if (uri.startsWith("stockPriceHistory/")) {
973
- const symbol = uri.split("/")[1];
974
- const priceHistory = this.marketDataPrices.get(symbol) || [];
975
- if (priceHistory.length === 0) {
976
- return {
977
- contents: [
978
- {
979
- type: "text",
980
- text: `No price data available for ${symbol}`,
981
- uri
982
- }
983
- ]
984
- };
1066
+ ]
1067
+ };
1068
+ }
1069
+ );
1070
+ this.server.setRequestHandler(
1071
+ import_zod.z.object({
1072
+ method: import_zod.z.literal("stockPriceHistory"),
1073
+ params: symbolSchema
1074
+ }),
1075
+ async (request, extra) => {
1076
+ this.parser?.logger.log({
1077
+ level: "info",
1078
+ message: "MCP Server Resource called: stockPriceHistory"
1079
+ });
1080
+ const args = request.params;
1081
+ const symbol = args.symbol;
1082
+ const priceHistory = this.marketDataPrices.get(symbol) || [];
1083
+ if (priceHistory.length === 0) {
1084
+ return {
1085
+ content: [
1086
+ {
1087
+ type: "text",
1088
+ text: `No price data available for ${symbol}`
985
1089
  }
986
- return {
987
- contents: [
988
- {
989
- type: "text",
990
- text: JSON.stringify(
991
- {
992
- symbol,
993
- count: priceHistory.length,
994
- prices: priceHistory.map((point) => ({
995
- timestamp: new Date(point.timestamp).toISOString(),
996
- price: point.price
997
- }))
998
- },
999
- null,
1000
- 2
1001
- ),
1002
- uri
1003
- }
1004
- ]
1005
- };
1006
- }
1007
- return {
1008
- contents: [
1009
- {
1010
- type: "text",
1011
- text: `Resource not found: ${uri}`,
1012
- uri
1013
- }
1014
- ],
1015
- isError: true
1016
- };
1090
+ ]
1091
+ };
1017
1092
  }
1093
+ return {
1094
+ content: [
1095
+ {
1096
+ type: "text",
1097
+ text: JSON.stringify(
1098
+ {
1099
+ symbol,
1100
+ count: priceHistory.length,
1101
+ prices: priceHistory.map((point) => ({
1102
+ timestamp: new Date(point.timestamp).toISOString(),
1103
+ price: point.price
1104
+ }))
1105
+ },
1106
+ null,
1107
+ 2
1108
+ )
1109
+ }
1110
+ ]
1111
+ };
1018
1112
  }
1019
1113
  );
1020
1114
  process.on("SIGINT", async () => {