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.
- package/build/cjs/MCPLocal.js +565 -471
- package/build/cjs/MCPLocal.js.map +2 -2
- package/build/esm/MCPLocal.mjs +565 -471
- package/build/esm/MCPLocal.mjs.map +2 -2
- package/build-examples/cjs/example_mcp_local.js +20 -20
- package/build-examples/cjs/example_mcp_local.js.map +3 -3
- package/build-examples/esm/example_mcp_local.mjs +16 -16
- package/build-examples/esm/example_mcp_local.mjs.map +3 -3
- package/package.json +2 -2
package/build/cjs/MCPLocal.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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("
|
|
588
|
+
method: import_zod.z.literal("resources/read"),
|
|
495
589
|
params: import_zod.z.object({
|
|
496
|
-
|
|
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 {
|
|
505
|
-
switch (
|
|
506
|
-
case "
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
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
|
-
|
|
811
|
-
|
|
812
|
-
|
|
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("
|
|
850
|
-
params:
|
|
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
|
-
|
|
856
|
-
|
|
857
|
-
|
|
833
|
+
try {
|
|
834
|
+
const args = request.params;
|
|
835
|
+
const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
|
|
836
|
+
if (!verifiedOrder) {
|
|
858
837
|
return {
|
|
859
|
-
|
|
838
|
+
content: [
|
|
860
839
|
{
|
|
861
840
|
type: "text",
|
|
862
|
-
text:
|
|
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
|
-
|
|
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
|
-
|
|
849
|
+
content: [
|
|
870
850
|
{
|
|
871
851
|
type: "text",
|
|
872
|
-
text: "
|
|
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
|
-
|
|
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
|
-
|
|
879
|
+
content: [
|
|
880
880
|
{
|
|
881
881
|
type: "text",
|
|
882
|
-
text: "
|
|
883
|
-
uri: "stockPriceHistory"
|
|
882
|
+
text: "Error: Not connected. Ignoring message."
|
|
884
883
|
}
|
|
885
|
-
]
|
|
884
|
+
],
|
|
885
|
+
isError: true
|
|
886
886
|
};
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
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
|
-
|
|
923
|
-
|
|
924
|
-
|
|
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
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
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
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
uri
|
|
968
|
-
}
|
|
969
|
-
]
|
|
970
|
-
};
|
|
1060
|
+
return {
|
|
1061
|
+
content: [
|
|
1062
|
+
{
|
|
1063
|
+
type: "text",
|
|
1064
|
+
text: svg
|
|
971
1065
|
}
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
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
|
-
|
|
987
|
-
|
|
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 () => {
|