fixparser-plugin-mcp 9.1.7-3f807208 → 9.1.7-400318ef

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.
@@ -152,9 +152,16 @@ var MCPLocal = class {
152
152
  },
153
153
  required: ["mdUpdateType", "symbols", "mdReqID", "subscriptionRequestType", "mdEntryTypes"]
154
154
  }
155
+ }
156
+ },
157
+ resources: {
158
+ greeting: {
159
+ description: "A simple greeting resource",
160
+ uri: "greeting-resource"
155
161
  },
156
- getStockGraph: {
162
+ stockGraph: {
157
163
  description: "Generates a price chart for a given symbol",
164
+ uri: "stockGraph",
158
165
  parameters: {
159
166
  type: "object",
160
167
  properties: {
@@ -163,8 +170,9 @@ var MCPLocal = class {
163
170
  required: ["symbol"]
164
171
  }
165
172
  },
166
- getStockPriceHistory: {
173
+ stockPriceHistory: {
167
174
  description: "Returns price history for a given symbol",
175
+ uri: "stockPriceHistory",
168
176
  parameters: {
169
177
  type: "object",
170
178
  properties: {
@@ -173,20 +181,6 @@ var MCPLocal = class {
173
181
  required: ["symbol"]
174
182
  }
175
183
  }
176
- },
177
- resources: {
178
- greeting: {
179
- description: "A simple greeting resource",
180
- uri: "greeting-resource"
181
- },
182
- stockGraph: {
183
- description: "Generates a price chart for a given symbol",
184
- uri: "stockGraph/{symbol}"
185
- },
186
- stockPriceHistory: {
187
- description: "Returns price history for a given symbol",
188
- uri: "stockPriceHistory/{symbol}"
189
- }
190
184
  }
191
185
  }
192
186
  }
@@ -236,14 +230,6 @@ var MCPLocal = class {
236
230
  level: "info",
237
231
  message: `MCP Server added ${symbol}: ${priceNum}`
238
232
  });
239
- this.server.notification({
240
- method: "priceUpdate",
241
- params: {
242
- symbol: symbolStr,
243
- price: priceNum,
244
- timestamp: Number(timestamp)
245
- }
246
- });
247
233
  }
248
234
  }
249
235
  if (msgType === Messages.MarketDataSnapshotFullRefresh) {
@@ -278,385 +264,158 @@ var MCPLocal = class {
278
264
  if (!this.server) {
279
265
  return;
280
266
  }
281
- this.server.setRequestHandler(
282
- z.object({ method: z.literal("resources/list") }),
283
- async (request, extra) => {
267
+ this.server.setRequestHandler(z.object({ method: z.literal("parse") }), async (request, extra) => {
268
+ try {
269
+ const args = request.params;
270
+ const parsedMessage = this.parser?.parse(args.fixString);
271
+ if (!parsedMessage || parsedMessage.length === 0) {
272
+ return {
273
+ content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
274
+ isError: true
275
+ };
276
+ }
284
277
  return {
285
- resources: [
278
+ content: [
286
279
  {
287
- name: "greeting",
288
- description: "A simple greeting resource",
289
- uri: "greeting-resource"
280
+ type: "text",
281
+ text: `${parsedMessage[0].description}
282
+ ${parsedMessage[0].messageTypeDescription}`
290
283
  }
291
284
  ]
292
285
  };
293
- }
294
- );
295
- this.server.setRequestHandler(
296
- z.object({ method: z.literal("resources/templates/list") }),
297
- async (request, extra) => {
286
+ } catch (error) {
298
287
  return {
299
- resourceTemplates: [
300
- {
301
- name: "stockGraph",
302
- description: "Generates a price chart for a given symbol",
303
- uriTemplate: "stockGraph/{symbol}",
304
- parameters: {
305
- type: "object",
306
- properties: {
307
- symbol: { type: "string" }
308
- },
309
- required: ["symbol"]
310
- }
311
- },
288
+ content: [
312
289
  {
313
- name: "stockPriceHistory",
314
- description: "Returns price history for a given symbol",
315
- uriTemplate: "stockPriceHistory/{symbol}",
316
- parameters: {
317
- type: "object",
318
- properties: {
319
- symbol: { type: "string" }
320
- },
321
- required: ["symbol"]
322
- }
290
+ type: "text",
291
+ text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
323
292
  }
324
- ]
293
+ ],
294
+ isError: true
325
295
  };
326
296
  }
327
- );
297
+ });
328
298
  this.server.setRequestHandler(
329
- z.object({ method: z.literal("tools/list") }),
299
+ z.object({ method: z.literal("parseToJSON") }),
330
300
  async (request, extra) => {
331
- return {
332
- tools: [
333
- {
334
- name: "parse",
335
- description: "Parses a FIX message and describes it in plain language",
336
- inputSchema: {
337
- type: "object",
338
- properties: {
339
- fixString: { type: "string" }
340
- },
341
- required: ["fixString"]
342
- }
343
- },
344
- {
345
- name: "parseToJSON",
346
- description: "Parses a FIX message into JSON",
347
- inputSchema: {
348
- type: "object",
349
- properties: {
350
- fixString: { type: "string" }
351
- },
352
- required: ["fixString"]
353
- }
354
- },
355
- {
356
- name: "verifyOrder",
357
- description: "Verifies order parameters before execution",
358
- inputSchema: {
359
- type: "object",
360
- properties: {
361
- clOrdID: { type: "string" },
362
- handlInst: { type: "string", enum: ["1", "2", "3"] },
363
- quantity: { type: "string" },
364
- price: { type: "string" },
365
- ordType: {
366
- type: "string",
367
- enum: [
368
- "1",
369
- "2",
370
- "3",
371
- "4",
372
- "5",
373
- "6",
374
- "7",
375
- "8",
376
- "9",
377
- "A",
378
- "B",
379
- "C",
380
- "D",
381
- "E",
382
- "F",
383
- "G",
384
- "H",
385
- "I",
386
- "J",
387
- "K",
388
- "L",
389
- "M",
390
- "P",
391
- "Q",
392
- "R",
393
- "S"
394
- ]
395
- },
396
- side: {
397
- type: "string",
398
- enum: [
399
- "1",
400
- "2",
401
- "3",
402
- "4",
403
- "5",
404
- "6",
405
- "7",
406
- "8",
407
- "9",
408
- "A",
409
- "B",
410
- "C",
411
- "D",
412
- "E",
413
- "F",
414
- "G",
415
- "H"
416
- ]
417
- },
418
- symbol: { type: "string" },
419
- timeInForce: {
420
- type: "string",
421
- enum: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"]
422
- }
423
- },
424
- required: [
425
- "clOrdID",
426
- "handlInst",
427
- "quantity",
428
- "price",
429
- "ordType",
430
- "side",
431
- "symbol",
432
- "timeInForce"
433
- ]
434
- }
435
- },
436
- {
437
- name: "executeOrder",
438
- description: "Executes a verified order",
439
- inputSchema: {
440
- type: "object",
441
- properties: {
442
- clOrdID: { type: "string" },
443
- handlInst: { type: "string", enum: ["1", "2", "3"] },
444
- quantity: { type: "string" },
445
- price: { type: "string" },
446
- ordType: { type: "string" },
447
- side: { type: "string" },
448
- symbol: { type: "string" },
449
- timeInForce: { type: "string" }
450
- },
451
- required: [
452
- "clOrdID",
453
- "handlInst",
454
- "quantity",
455
- "price",
456
- "ordType",
457
- "side",
458
- "symbol",
459
- "timeInForce"
460
- ]
301
+ try {
302
+ const args = request.params;
303
+ const parsedMessage = this.parser?.parse(args.fixString);
304
+ if (!parsedMessage || parsedMessage.length === 0) {
305
+ return {
306
+ content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
307
+ isError: true
308
+ };
309
+ }
310
+ return {
311
+ content: [
312
+ {
313
+ type: "text",
314
+ text: `${parsedMessage[0].toFIXJSON()}`
461
315
  }
462
- },
463
- {
464
- name: "marketDataRequest",
465
- description: "Requests market data for specified symbols",
466
- inputSchema: {
467
- type: "object",
468
- properties: {
469
- mdUpdateType: { type: "string", enum: ["0", "1"] },
470
- symbols: { type: "array", items: { type: "string" } },
471
- mdReqID: { type: "string" },
472
- subscriptionRequestType: { type: "string", enum: ["0", "1", "2"] },
473
- mdEntryTypes: { type: "array", items: { type: "string" } }
474
- },
475
- required: [
476
- "mdUpdateType",
477
- "symbols",
478
- "mdReqID",
479
- "subscriptionRequestType",
480
- "mdEntryTypes"
481
- ]
316
+ ]
317
+ };
318
+ } catch (error) {
319
+ return {
320
+ content: [
321
+ {
322
+ type: "text",
323
+ text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
482
324
  }
483
- }
484
- ]
485
- };
325
+ ],
326
+ isError: true
327
+ };
328
+ }
486
329
  }
487
330
  );
488
331
  this.server.setRequestHandler(
489
- z.object({
490
- method: z.literal("tools/call"),
491
- params: z.object({
492
- name: z.string(),
493
- arguments: z.any(),
494
- _meta: z.object({
495
- progressToken: z.number()
496
- }).optional()
497
- })
498
- }),
332
+ z.object({ method: z.literal("verifyOrder") }),
499
333
  async (request, extra) => {
500
- const { name, arguments: args } = request.params;
501
- switch (name) {
502
- case "parse":
503
- try {
504
- const parsedMessage = this.parser?.parse(args.fixString);
505
- if (!parsedMessage || parsedMessage.length === 0) {
506
- return {
507
- contents: [
508
- {
509
- type: "text",
510
- text: "Error: Failed to parse FIX string",
511
- uri: "parse"
512
- }
513
- ],
514
- isError: true
515
- };
516
- }
517
- return {
518
- contents: [
519
- {
520
- type: "text",
521
- text: `${parsedMessage[0].description}
522
- ${parsedMessage[0].messageTypeDescription}`,
523
- uri: "parse"
524
- }
525
- ]
526
- };
527
- } catch (error) {
528
- return {
529
- contents: [
530
- {
531
- type: "text",
532
- text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
533
- uri: "parse"
534
- }
535
- ],
536
- isError: true
537
- };
538
- }
539
- case "parseToJSON":
540
- try {
541
- const parsedMessage = this.parser?.parse(args.fixString);
542
- if (!parsedMessage || parsedMessage.length === 0) {
543
- return {
544
- contents: [
545
- {
546
- type: "text",
547
- text: "Error: Failed to parse FIX string",
548
- uri: "parseToJSON"
549
- }
550
- ],
551
- isError: true
552
- };
553
- }
554
- return {
555
- contents: [
556
- {
557
- type: "text",
558
- text: `${parsedMessage[0].toFIXJSON()}`,
559
- uri: "parseToJSON"
560
- }
561
- ]
562
- };
563
- } catch (error) {
564
- return {
565
- contents: [
566
- {
567
- type: "text",
568
- text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
569
- uri: "parseToJSON"
570
- }
571
- ],
572
- isError: true
573
- };
574
- }
575
- case "verifyOrder":
576
- try {
577
- this.verifiedOrders.set(args.clOrdID, {
578
- clOrdID: args.clOrdID,
579
- handlInst: args.handlInst,
580
- quantity: Number.parseFloat(args.quantity),
581
- price: Number.parseFloat(args.price),
582
- ordType: args.ordType,
583
- side: args.side,
584
- symbol: args.symbol,
585
- timeInForce: args.timeInForce
586
- });
587
- const ordTypeNames = {
588
- "1": "Market",
589
- "2": "Limit",
590
- "3": "Stop",
591
- "4": "StopLimit",
592
- "5": "MarketOnClose",
593
- "6": "WithOrWithout",
594
- "7": "LimitOrBetter",
595
- "8": "LimitWithOrWithout",
596
- "9": "OnBasis",
597
- A: "OnClose",
598
- B: "LimitOnClose",
599
- C: "ForexMarket",
600
- D: "PreviouslyQuoted",
601
- E: "PreviouslyIndicated",
602
- F: "ForexLimit",
603
- G: "ForexSwap",
604
- H: "ForexPreviouslyQuoted",
605
- I: "Funari",
606
- J: "MarketIfTouched",
607
- K: "MarketWithLeftOverAsLimit",
608
- L: "PreviousFundValuationPoint",
609
- M: "NextFundValuationPoint",
610
- P: "Pegged",
611
- Q: "CounterOrderSelection",
612
- R: "StopOnBidOrOffer",
613
- S: "StopLimitOnBidOrOffer"
614
- };
615
- const sideNames = {
616
- "1": "Buy",
617
- "2": "Sell",
618
- "3": "BuyMinus",
619
- "4": "SellPlus",
620
- "5": "SellShort",
621
- "6": "SellShortExempt",
622
- "7": "Undisclosed",
623
- "8": "Cross",
624
- "9": "CrossShort",
625
- A: "CrossShortExempt",
626
- B: "AsDefined",
627
- C: "Opposite",
628
- D: "Subscribe",
629
- E: "Redeem",
630
- F: "Lend",
631
- G: "Borrow",
632
- H: "SellUndisclosed"
633
- };
634
- const timeInForceNames = {
635
- "0": "Day",
636
- "1": "GoodTillCancel",
637
- "2": "AtTheOpening",
638
- "3": "ImmediateOrCancel",
639
- "4": "FillOrKill",
640
- "5": "GoodTillCrossing",
641
- "6": "GoodTillDate",
642
- "7": "AtTheClose",
643
- "8": "GoodThroughCrossing",
644
- "9": "AtCrossing",
645
- A: "GoodForTime",
646
- B: "GoodForAuction",
647
- C: "GoodForMonth"
648
- };
649
- const handlInstNames = {
650
- "1": "AutomatedExecutionNoIntervention",
651
- "2": "AutomatedExecutionInterventionOK",
652
- "3": "ManualOrder"
653
- };
654
- return {
655
- contents: [
656
- {
657
- type: "text",
658
- text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
659
-
334
+ try {
335
+ const args = request.params;
336
+ this.verifiedOrders.set(args.clOrdID, {
337
+ clOrdID: args.clOrdID,
338
+ handlInst: args.handlInst,
339
+ quantity: Number.parseFloat(args.quantity),
340
+ price: Number.parseFloat(args.price),
341
+ ordType: args.ordType,
342
+ side: args.side,
343
+ symbol: args.symbol,
344
+ timeInForce: args.timeInForce
345
+ });
346
+ const ordTypeNames = {
347
+ "1": "Market",
348
+ "2": "Limit",
349
+ "3": "Stop",
350
+ "4": "StopLimit",
351
+ "5": "MarketOnClose",
352
+ "6": "WithOrWithout",
353
+ "7": "LimitOrBetter",
354
+ "8": "LimitWithOrWithout",
355
+ "9": "OnBasis",
356
+ A: "OnClose",
357
+ B: "LimitOnClose",
358
+ C: "ForexMarket",
359
+ D: "PreviouslyQuoted",
360
+ E: "PreviouslyIndicated",
361
+ F: "ForexLimit",
362
+ G: "ForexSwap",
363
+ H: "ForexPreviouslyQuoted",
364
+ I: "Funari",
365
+ J: "MarketIfTouched",
366
+ K: "MarketWithLeftOverAsLimit",
367
+ L: "PreviousFundValuationPoint",
368
+ M: "NextFundValuationPoint",
369
+ P: "Pegged",
370
+ Q: "CounterOrderSelection",
371
+ R: "StopOnBidOrOffer",
372
+ S: "StopLimitOnBidOrOffer"
373
+ };
374
+ const sideNames = {
375
+ "1": "Buy",
376
+ "2": "Sell",
377
+ "3": "BuyMinus",
378
+ "4": "SellPlus",
379
+ "5": "SellShort",
380
+ "6": "SellShortExempt",
381
+ "7": "Undisclosed",
382
+ "8": "Cross",
383
+ "9": "CrossShort",
384
+ A: "CrossShortExempt",
385
+ B: "AsDefined",
386
+ C: "Opposite",
387
+ D: "Subscribe",
388
+ E: "Redeem",
389
+ F: "Lend",
390
+ G: "Borrow",
391
+ H: "SellUndisclosed"
392
+ };
393
+ const timeInForceNames = {
394
+ "0": "Day",
395
+ "1": "GoodTillCancel",
396
+ "2": "AtTheOpening",
397
+ "3": "ImmediateOrCancel",
398
+ "4": "FillOrKill",
399
+ "5": "GoodTillCrossing",
400
+ "6": "GoodTillDate",
401
+ "7": "AtTheClose",
402
+ "8": "GoodThroughCrossing",
403
+ "9": "AtCrossing",
404
+ A: "GoodForTime",
405
+ B: "GoodForAuction",
406
+ C: "GoodForMonth"
407
+ };
408
+ const handlInstNames = {
409
+ "1": "AutomatedExecutionNoIntervention",
410
+ "2": "AutomatedExecutionInterventionOK",
411
+ "3": "ManualOrder"
412
+ };
413
+ return {
414
+ content: [
415
+ {
416
+ type: "text",
417
+ text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
418
+
660
419
  Parameters verified:
661
420
  - ClOrdID: ${args.clOrdID}
662
421
  - HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
@@ -667,193 +426,217 @@ Parameters verified:
667
426
  - Symbol: ${args.symbol}
668
427
  - TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
669
428
 
670
- To execute this order, call the executeOrder tool with these exact same parameters.`,
671
- uri: "verifyOrder"
672
- }
673
- ]
674
- };
675
- } catch (error) {
676
- return {
677
- contents: [
678
- {
679
- type: "text",
680
- text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`,
681
- uri: "verifyOrder"
682
- }
683
- ],
684
- isError: true
685
- };
686
- }
687
- case "executeOrder":
688
- try {
689
- const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
690
- if (!verifiedOrder) {
691
- return {
692
- contents: [
693
- {
694
- type: "text",
695
- text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`,
696
- uri: "executeOrder"
697
- }
698
- ],
699
- isError: true
700
- };
429
+ To execute this order, call the executeOrder tool with these exact same parameters.`
701
430
  }
702
- 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) {
703
- return {
704
- contents: [
705
- {
706
- type: "text",
707
- text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified.",
708
- uri: "executeOrder"
709
- }
710
- ],
711
- isError: true
712
- };
431
+ ]
432
+ };
433
+ } catch (error) {
434
+ return {
435
+ content: [
436
+ {
437
+ type: "text",
438
+ text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`
713
439
  }
714
- const response = new Promise((resolve) => {
715
- this.pendingRequests.set(args.clOrdID, resolve);
716
- });
717
- const order = this.parser?.createMessage(
718
- new Field(Fields.MsgType, Messages.NewOrderSingle),
719
- new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
720
- new Field(Fields.SenderCompID, this.parser?.sender),
721
- new Field(Fields.TargetCompID, this.parser?.target),
722
- new Field(Fields.SendingTime, this.parser?.getTimestamp()),
723
- new Field(Fields.ClOrdID, args.clOrdID),
724
- new Field(Fields.Side, args.side),
725
- new Field(Fields.Symbol, args.symbol),
726
- new Field(Fields.OrderQty, Number.parseFloat(args.quantity)),
727
- new Field(Fields.Price, Number.parseFloat(args.price)),
728
- new Field(Fields.OrdType, args.ordType),
729
- new Field(Fields.HandlInst, args.handlInst),
730
- new Field(Fields.TimeInForce, args.timeInForce),
731
- new Field(Fields.TransactTime, this.parser?.getTimestamp())
732
- );
733
- if (!this.parser?.connected) {
734
- return {
735
- contents: [
736
- {
737
- type: "text",
738
- text: "Error: Not connected. Ignoring message.",
739
- uri: "executeOrder"
740
- }
741
- ],
742
- isError: true
743
- };
440
+ ],
441
+ isError: true
442
+ };
443
+ }
444
+ }
445
+ );
446
+ this.server.setRequestHandler(
447
+ z.object({ method: z.literal("executeOrder") }),
448
+ async (request, extra) => {
449
+ try {
450
+ const args = request.params;
451
+ const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
452
+ if (!verifiedOrder) {
453
+ return {
454
+ content: [
455
+ {
456
+ type: "text",
457
+ text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`
458
+ }
459
+ ],
460
+ isError: true
461
+ };
462
+ }
463
+ 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) {
464
+ return {
465
+ content: [
466
+ {
467
+ type: "text",
468
+ text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified."
469
+ }
470
+ ],
471
+ isError: true
472
+ };
473
+ }
474
+ const response = new Promise((resolve) => {
475
+ this.pendingRequests.set(args.clOrdID, resolve);
476
+ });
477
+ const order = this.parser?.createMessage(
478
+ new Field(Fields.MsgType, Messages.NewOrderSingle),
479
+ new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
480
+ new Field(Fields.SenderCompID, this.parser?.sender),
481
+ new Field(Fields.TargetCompID, this.parser?.target),
482
+ new Field(Fields.SendingTime, this.parser?.getTimestamp()),
483
+ new Field(Fields.ClOrdID, args.clOrdID),
484
+ new Field(Fields.Side, args.side),
485
+ new Field(Fields.Symbol, args.symbol),
486
+ new Field(Fields.OrderQty, Number.parseFloat(args.quantity)),
487
+ new Field(Fields.Price, Number.parseFloat(args.price)),
488
+ new Field(Fields.OrdType, args.ordType),
489
+ new Field(Fields.HandlInst, args.handlInst),
490
+ new Field(Fields.TimeInForce, args.timeInForce),
491
+ new Field(Fields.TransactTime, this.parser?.getTimestamp())
492
+ );
493
+ if (!this.parser?.connected) {
494
+ return {
495
+ content: [
496
+ {
497
+ type: "text",
498
+ text: "Error: Not connected. Ignoring message."
499
+ }
500
+ ],
501
+ isError: true
502
+ };
503
+ }
504
+ this.parser?.send(order);
505
+ const fixData = await response;
506
+ this.verifiedOrders.delete(args.clOrdID);
507
+ return {
508
+ content: [
509
+ {
510
+ type: "text",
511
+ text: fixData.messageType === Messages.Reject ? `Reject message for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}` : `Execution Report for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`
744
512
  }
745
- this.parser?.send(order);
746
- const fixData = await response;
747
- this.verifiedOrders.delete(args.clOrdID);
748
- return {
749
- contents: [
750
- {
751
- type: "text",
752
- text: fixData.messageType === Messages.Reject ? `Reject message for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}` : `Execution Report for order ${args.clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`,
753
- uri: "executeOrder"
754
- }
755
- ]
756
- };
757
- } catch (error) {
758
- return {
759
- contents: [
760
- {
761
- type: "text",
762
- text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`,
763
- uri: "executeOrder"
764
- }
765
- ],
766
- isError: true
767
- };
768
- }
769
- case "marketDataRequest":
770
- try {
771
- const response = new Promise((resolve) => {
772
- this.pendingRequests.set(args.mdReqID, resolve);
773
- });
774
- const messageFields = [
775
- new Field(Fields.MsgType, Messages.MarketDataRequest),
776
- new Field(Fields.SenderCompID, this.parser?.sender),
777
- new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
778
- new Field(Fields.TargetCompID, this.parser?.target),
779
- new Field(Fields.SendingTime, this.parser?.getTimestamp()),
780
- new Field(Fields.MDReqID, args.mdReqID),
781
- new Field(Fields.SubscriptionRequestType, args.subscriptionRequestType),
782
- new Field(Fields.MarketDepth, 0),
783
- new Field(Fields.MDUpdateType, args.mdUpdateType)
784
- ];
785
- messageFields.push(new Field(Fields.NoRelatedSym, args.symbols.length));
786
- args.symbols.forEach((symbol) => {
787
- messageFields.push(new Field(Fields.Symbol, symbol));
788
- });
789
- messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
790
- args.mdEntryTypes.forEach((entryType) => {
791
- messageFields.push(new Field(Fields.MDEntryType, entryType));
792
- });
793
- const mdr = this.parser?.createMessage(...messageFields);
794
- if (!this.parser?.connected) {
795
- return {
796
- contents: [
797
- {
798
- type: "text",
799
- text: "Error: Not connected. Ignoring message.",
800
- uri: "marketDataRequest"
801
- }
802
- ],
803
- isError: true
804
- };
513
+ ]
514
+ };
515
+ } catch (error) {
516
+ return {
517
+ content: [
518
+ {
519
+ type: "text",
520
+ text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`
805
521
  }
806
- this.parser?.send(mdr);
807
- const fixData = await response;
808
- return {
809
- contents: [
810
- {
811
- type: "text",
812
- text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`,
813
- uri: "marketDataRequest"
814
- }
815
- ]
816
- };
817
- } catch (error) {
818
- return {
819
- contents: [
820
- {
821
- type: "text",
822
- text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`,
823
- uri: "marketDataRequest"
824
- }
825
- ],
826
- isError: true
827
- };
522
+ ],
523
+ isError: true
524
+ };
525
+ }
526
+ }
527
+ );
528
+ this.server.setRequestHandler(
529
+ z.object({ method: z.literal("marketDataRequest") }),
530
+ async (request, extra) => {
531
+ try {
532
+ const args = request.params;
533
+ const response = new Promise((resolve) => {
534
+ this.pendingRequests.set(args.mdReqID, resolve);
535
+ });
536
+ const messageFields = [
537
+ new Field(Fields.MsgType, Messages.MarketDataRequest),
538
+ new Field(Fields.SenderCompID, this.parser?.sender),
539
+ new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
540
+ new Field(Fields.TargetCompID, this.parser?.target),
541
+ new Field(Fields.SendingTime, this.parser?.getTimestamp()),
542
+ new Field(Fields.MDReqID, args.mdReqID),
543
+ new Field(Fields.SubscriptionRequestType, args.subscriptionRequestType),
544
+ new Field(Fields.MarketDepth, 0),
545
+ new Field(Fields.MDUpdateType, args.mdUpdateType)
546
+ ];
547
+ messageFields.push(new Field(Fields.NoRelatedSym, args.symbols.length));
548
+ args.symbols.forEach((symbol) => {
549
+ messageFields.push(new Field(Fields.Symbol, symbol));
550
+ });
551
+ messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
552
+ args.mdEntryTypes.forEach((entryType) => {
553
+ messageFields.push(new Field(Fields.MDEntryType, entryType));
554
+ });
555
+ const mdr = this.parser?.createMessage(...messageFields);
556
+ if (!this.parser?.connected) {
557
+ return {
558
+ content: [
559
+ {
560
+ type: "text",
561
+ text: "Error: Not connected. Ignoring message."
562
+ }
563
+ ],
564
+ isError: true
565
+ };
566
+ }
567
+ this.parser?.send(mdr);
568
+ const fixData = await response;
569
+ return {
570
+ content: [
571
+ {
572
+ type: "text",
573
+ text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`
574
+ }
575
+ ]
576
+ };
577
+ } catch (error) {
578
+ return {
579
+ content: [
580
+ {
581
+ type: "text",
582
+ text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`
583
+ }
584
+ ],
585
+ isError: true
586
+ };
587
+ }
588
+ }
589
+ );
590
+ this.server.setRequestHandler(
591
+ z.object({ method: z.literal("greeting-resource") }),
592
+ async (request, extra) => {
593
+ this.parser?.logger.log({
594
+ level: "info",
595
+ message: "MCP Server Resource called: greeting-resource"
596
+ });
597
+ return {
598
+ content: [
599
+ {
600
+ type: "text",
601
+ text: "Hello, world!"
828
602
  }
829
- case "getStockGraph":
830
- try {
831
- const symbol = args.symbol;
832
- const priceHistory = this.marketDataPrices.get(symbol) || [];
833
- if (priceHistory.length === 0) {
834
- return {
835
- contents: [
836
- {
837
- type: "text",
838
- text: `No price data available for ${symbol}`,
839
- uri: "getStockGraph"
840
- }
841
- ]
842
- };
603
+ ]
604
+ };
605
+ }
606
+ );
607
+ this.server.setRequestHandler(
608
+ z.object({ method: z.literal("stockGraph") }),
609
+ async (request, extra) => {
610
+ this.parser?.logger.log({
611
+ level: "info",
612
+ message: "MCP Server Resource called: stockGraph"
613
+ });
614
+ const args = request.params;
615
+ const symbol = args.symbol;
616
+ const priceHistory = this.marketDataPrices.get(symbol) || [];
617
+ if (priceHistory.length === 0) {
618
+ return {
619
+ content: [
620
+ {
621
+ type: "text",
622
+ text: `No price data available for ${symbol}`
843
623
  }
844
- const width = 600;
845
- const height = 300;
846
- const padding = 40;
847
- const xScale = (width - 2 * padding) / (priceHistory.length - 1);
848
- const yMin = Math.min(...priceHistory.map((d) => d.price));
849
- const yMax = Math.max(...priceHistory.map((d) => d.price));
850
- const yScale = (height - 2 * padding) / (yMax - yMin);
851
- const points = priceHistory.map((d, i) => {
852
- const x = padding + i * xScale;
853
- const y = height - padding - (d.price - yMin) * yScale;
854
- return `${x},${y}`;
855
- }).join(" L ");
856
- const svg = `<?xml version="1.0" encoding="UTF-8"?>
624
+ ]
625
+ };
626
+ }
627
+ const width = 600;
628
+ const height = 300;
629
+ const padding = 40;
630
+ const xScale = (width - 2 * padding) / (priceHistory.length - 1);
631
+ const yMin = Math.min(...priceHistory.map((d) => d.price));
632
+ const yMax = Math.max(...priceHistory.map((d) => d.price));
633
+ const yScale = (height - 2 * padding) / (yMax - yMin);
634
+ const points = priceHistory.map((d, i) => {
635
+ const x = padding + i * xScale;
636
+ const y = height - padding - (d.price - yMin) * yScale;
637
+ return `${x},${y}`;
638
+ }).join(" L ");
639
+ const svg = `<?xml version="1.0" encoding="UTF-8"?>
857
640
  <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
858
641
  <!-- Background -->
859
642
  <rect width="100%" height="100%" fill="#f8f9fa"/>
@@ -861,9 +644,9 @@ To execute this order, call the executeOrder tool with these exact same paramete
861
644
  <!-- Grid lines -->
862
645
  <g stroke="#e9ecef" stroke-width="1">
863
646
  ${Array.from({ length: 5 }, (_, i) => {
864
- const y = padding + (height - 2 * padding) * i / 4;
865
- return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
866
- }).join("\n")}
647
+ const y = padding + (height - 2 * padding) * i / 4;
648
+ return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
649
+ }).join("\n")}
867
650
  </g>
868
651
 
869
652
  <!-- Price line -->
@@ -874,24 +657,24 @@ To execute this order, call the executeOrder tool with these exact same paramete
874
657
 
875
658
  <!-- Data points -->
876
659
  ${priceHistory.map((d, i) => {
877
- const x = padding + i * xScale;
878
- const y = height - padding - (d.price - yMin) * yScale;
879
- return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
880
- }).join("\n")}
660
+ const x = padding + i * xScale;
661
+ const y = height - padding - (d.price - yMin) * yScale;
662
+ return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
663
+ }).join("\n")}
881
664
 
882
665
  <!-- Labels -->
883
666
  <g font-family="Arial" font-size="12" fill="#495057">
884
667
  ${Array.from({ length: 5 }, (_, i) => {
885
- const x = padding + (width - 2 * padding) * i / 4;
886
- const index = Math.floor((priceHistory.length - 1) * i / 4);
887
- const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
888
- return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
889
- }).join("\n")}
668
+ const x = padding + (width - 2 * padding) * i / 4;
669
+ const index = Math.floor((priceHistory.length - 1) * i / 4);
670
+ const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
671
+ return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
672
+ }).join("\n")}
890
673
  ${Array.from({ length: 5 }, (_, i) => {
891
- const y = padding + (height - 2 * padding) * i / 4;
892
- const price = yMax - (yMax - yMin) * i / 4;
893
- return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
894
- }).join("\n")}
674
+ const y = padding + (height - 2 * padding) * i / 4;
675
+ const price = yMax - (yMax - yMin) * i / 4;
676
+ return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
677
+ }).join("\n")}
895
678
  </g>
896
679
 
897
680
  <!-- Title -->
@@ -901,86 +684,45 @@ To execute this order, call the executeOrder tool with these exact same paramete
901
684
  ${symbol} - Price Chart (${priceHistory.length} points)
902
685
  </text>
903
686
  </svg>`;
904
- return {
905
- contents: [
906
- {
907
- type: "text",
908
- text: svg,
909
- uri: "getStockGraph"
910
- }
911
- ]
912
- };
913
- } catch (error) {
914
- return {
915
- contents: [
916
- {
917
- type: "text",
918
- text: `Error: ${error instanceof Error ? error.message : "Failed to generate stock graph"}`,
919
- uri: "getStockGraph"
920
- }
921
- ],
922
- isError: true
923
- };
924
- }
925
- case "getStockPriceHistory":
926
- try {
927
- const symbol = args.symbol;
928
- const priceHistory = this.marketDataPrices.get(symbol) || [];
929
- if (priceHistory.length === 0) {
930
- return {
931
- contents: [
932
- {
933
- type: "text",
934
- text: `No price data available for ${symbol}`,
935
- uri: "getStockPriceHistory"
936
- }
937
- ]
938
- };
939
- }
940
- return {
941
- contents: [
942
- {
943
- type: "text",
944
- text: JSON.stringify(
945
- {
946
- symbol,
947
- count: priceHistory.length,
948
- prices: priceHistory.map((point) => ({
949
- timestamp: new Date(point.timestamp).toISOString(),
950
- price: point.price
951
- }))
952
- },
953
- null,
954
- 2
955
- ),
956
- uri: "getStockPriceHistory"
957
- }
958
- ]
959
- };
960
- } catch (error) {
961
- return {
962
- contents: [
963
- {
964
- type: "text",
965
- text: `Error: ${error instanceof Error ? error.message : "Failed to get stock price history"}`,
966
- uri: "getStockPriceHistory"
967
- }
968
- ],
969
- isError: true
970
- };
687
+ return {
688
+ content: [
689
+ {
690
+ type: "text",
691
+ text: svg
971
692
  }
972
- default:
973
- return {
974
- contents: [
693
+ ]
694
+ };
695
+ }
696
+ );
697
+ this.server.setRequestHandler(
698
+ z.object({ method: z.literal("stockPriceHistory") }),
699
+ async (request, extra) => {
700
+ this.parser?.logger.log({
701
+ level: "info",
702
+ message: "MCP Server Resource called: stockPriceHistory"
703
+ });
704
+ const args = request.params;
705
+ const symbol = args.symbol;
706
+ const priceHistory = this.marketDataPrices.get(symbol) || [];
707
+ return {
708
+ content: [
709
+ {
710
+ type: "text",
711
+ text: JSON.stringify(
975
712
  {
976
- type: "text",
977
- text: `Tool not found: ${name}`,
978
- uri: name
979
- }
980
- ],
981
- isError: true
982
- };
983
- }
713
+ symbol,
714
+ count: priceHistory.length,
715
+ prices: priceHistory.map((point) => ({
716
+ timestamp: new Date(point.timestamp).toISOString(),
717
+ price: point.price
718
+ }))
719
+ },
720
+ null,
721
+ 2
722
+ )
723
+ }
724
+ ]
725
+ };
984
726
  }
985
727
  );
986
728
  process.on("SIGINT", async () => {