fixparser-plugin-mcp 9.1.7-a9d84740 → 9.1.7-abf6e45c
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 +570 -476
- package/build/cjs/MCPLocal.js.map +2 -2
- package/build/esm/MCPLocal.mjs +570 -476
- 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/esm/MCPLocal.mjs
CHANGED
|
@@ -3,6 +3,103 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { Field, Fields, Messages } from "fixparser";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
var symbolSchema = z.object({
|
|
7
|
+
symbol: z.string()
|
|
8
|
+
});
|
|
9
|
+
var fixStringSchema = z.object({
|
|
10
|
+
fixString: z.string()
|
|
11
|
+
});
|
|
12
|
+
var orderSchema = z.object({
|
|
13
|
+
clOrdID: z.string(),
|
|
14
|
+
handlInst: z.enum(["1", "2", "3"]),
|
|
15
|
+
quantity: z.string(),
|
|
16
|
+
price: z.string(),
|
|
17
|
+
ordType: z.enum([
|
|
18
|
+
"1",
|
|
19
|
+
"2",
|
|
20
|
+
"3",
|
|
21
|
+
"4",
|
|
22
|
+
"5",
|
|
23
|
+
"6",
|
|
24
|
+
"7",
|
|
25
|
+
"8",
|
|
26
|
+
"9",
|
|
27
|
+
"A",
|
|
28
|
+
"B",
|
|
29
|
+
"C",
|
|
30
|
+
"D",
|
|
31
|
+
"E",
|
|
32
|
+
"F",
|
|
33
|
+
"G",
|
|
34
|
+
"H",
|
|
35
|
+
"I",
|
|
36
|
+
"J",
|
|
37
|
+
"K",
|
|
38
|
+
"L",
|
|
39
|
+
"M",
|
|
40
|
+
"P",
|
|
41
|
+
"Q",
|
|
42
|
+
"R",
|
|
43
|
+
"S"
|
|
44
|
+
]),
|
|
45
|
+
side: z.enum(["1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H"]),
|
|
46
|
+
symbol: z.string(),
|
|
47
|
+
timeInForce: z.enum(["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"])
|
|
48
|
+
});
|
|
49
|
+
var marketDataRequestSchema = z.object({
|
|
50
|
+
mdUpdateType: z.enum(["0", "1"]),
|
|
51
|
+
symbols: z.array(z.string()),
|
|
52
|
+
mdReqID: z.string(),
|
|
53
|
+
subscriptionRequestType: z.enum(["0", "1", "2"]),
|
|
54
|
+
mdEntryTypes: z.array(
|
|
55
|
+
z.enum([
|
|
56
|
+
"0",
|
|
57
|
+
"1",
|
|
58
|
+
"2",
|
|
59
|
+
"3",
|
|
60
|
+
"4",
|
|
61
|
+
"5",
|
|
62
|
+
"6",
|
|
63
|
+
"7",
|
|
64
|
+
"8",
|
|
65
|
+
"9",
|
|
66
|
+
"A",
|
|
67
|
+
"B",
|
|
68
|
+
"C",
|
|
69
|
+
"D",
|
|
70
|
+
"E",
|
|
71
|
+
"F",
|
|
72
|
+
"G",
|
|
73
|
+
"H",
|
|
74
|
+
"J",
|
|
75
|
+
"K",
|
|
76
|
+
"L",
|
|
77
|
+
"M",
|
|
78
|
+
"N",
|
|
79
|
+
"O",
|
|
80
|
+
"P",
|
|
81
|
+
"Q",
|
|
82
|
+
"R",
|
|
83
|
+
"S",
|
|
84
|
+
"T",
|
|
85
|
+
"U",
|
|
86
|
+
"V",
|
|
87
|
+
"W",
|
|
88
|
+
"X",
|
|
89
|
+
"Y",
|
|
90
|
+
"Z",
|
|
91
|
+
"a",
|
|
92
|
+
"b",
|
|
93
|
+
"c",
|
|
94
|
+
"d",
|
|
95
|
+
"e",
|
|
96
|
+
"g",
|
|
97
|
+
"h",
|
|
98
|
+
"i",
|
|
99
|
+
"t"
|
|
100
|
+
])
|
|
101
|
+
)
|
|
102
|
+
});
|
|
6
103
|
var MCPLocal = class {
|
|
7
104
|
parser;
|
|
8
105
|
server = new Server(
|
|
@@ -161,11 +258,25 @@ var MCPLocal = class {
|
|
|
161
258
|
},
|
|
162
259
|
stockGraph: {
|
|
163
260
|
description: "Generates a price chart for a given symbol",
|
|
164
|
-
uri: "stockGraph
|
|
261
|
+
uri: "stockGraph",
|
|
262
|
+
parameters: {
|
|
263
|
+
type: "object",
|
|
264
|
+
properties: {
|
|
265
|
+
symbol: { type: "string" }
|
|
266
|
+
},
|
|
267
|
+
required: ["symbol"]
|
|
268
|
+
}
|
|
165
269
|
},
|
|
166
270
|
stockPriceHistory: {
|
|
167
271
|
description: "Returns price history for a given symbol",
|
|
168
|
-
uri: "stockPriceHistory
|
|
272
|
+
uri: "stockPriceHistory",
|
|
273
|
+
parameters: {
|
|
274
|
+
type: "object",
|
|
275
|
+
properties: {
|
|
276
|
+
symbol: { type: "string" }
|
|
277
|
+
},
|
|
278
|
+
required: ["symbol"]
|
|
279
|
+
}
|
|
169
280
|
}
|
|
170
281
|
}
|
|
171
282
|
}
|
|
@@ -216,14 +327,6 @@ var MCPLocal = class {
|
|
|
216
327
|
level: "info",
|
|
217
328
|
message: `MCP Server added ${symbol}: ${priceNum}`
|
|
218
329
|
});
|
|
219
|
-
this.server.notification({
|
|
220
|
-
method: "priceUpdate",
|
|
221
|
-
params: {
|
|
222
|
-
symbol: symbolStr,
|
|
223
|
-
price: priceNum,
|
|
224
|
-
timestamp: Number(timestamp)
|
|
225
|
-
}
|
|
226
|
-
});
|
|
227
330
|
}
|
|
228
331
|
}
|
|
229
332
|
if (msgType === Messages.MarketDataSnapshotFullRefresh) {
|
|
@@ -267,20 +370,11 @@ var MCPLocal = class {
|
|
|
267
370
|
name: "greeting",
|
|
268
371
|
description: "A simple greeting resource",
|
|
269
372
|
uri: "greeting-resource"
|
|
270
|
-
}
|
|
271
|
-
]
|
|
272
|
-
};
|
|
273
|
-
}
|
|
274
|
-
);
|
|
275
|
-
this.server.setRequestHandler(
|
|
276
|
-
z.object({ method: z.literal("resources/templates/list") }),
|
|
277
|
-
async (request, extra) => {
|
|
278
|
-
return {
|
|
279
|
-
resourceTemplates: [
|
|
373
|
+
},
|
|
280
374
|
{
|
|
281
375
|
name: "stockGraph",
|
|
282
376
|
description: "Generates a price chart for a given symbol",
|
|
283
|
-
|
|
377
|
+
uri: "stockGraph",
|
|
284
378
|
parameters: {
|
|
285
379
|
type: "object",
|
|
286
380
|
properties: {
|
|
@@ -292,7 +386,7 @@ var MCPLocal = class {
|
|
|
292
386
|
{
|
|
293
387
|
name: "stockPriceHistory",
|
|
294
388
|
description: "Returns price history for a given symbol",
|
|
295
|
-
|
|
389
|
+
uri: "stockPriceHistory",
|
|
296
390
|
parameters: {
|
|
297
391
|
type: "object",
|
|
298
392
|
properties: {
|
|
@@ -313,7 +407,7 @@ var MCPLocal = class {
|
|
|
313
407
|
{
|
|
314
408
|
name: "parse",
|
|
315
409
|
description: "Parses a FIX message and describes it in plain language",
|
|
316
|
-
|
|
410
|
+
parameters: {
|
|
317
411
|
type: "object",
|
|
318
412
|
properties: {
|
|
319
413
|
fixString: { type: "string" }
|
|
@@ -324,7 +418,7 @@ var MCPLocal = class {
|
|
|
324
418
|
{
|
|
325
419
|
name: "parseToJSON",
|
|
326
420
|
description: "Parses a FIX message into JSON",
|
|
327
|
-
|
|
421
|
+
parameters: {
|
|
328
422
|
type: "object",
|
|
329
423
|
properties: {
|
|
330
424
|
fixString: { type: "string" }
|
|
@@ -335,7 +429,7 @@ var MCPLocal = class {
|
|
|
335
429
|
{
|
|
336
430
|
name: "verifyOrder",
|
|
337
431
|
description: "Verifies order parameters before execution",
|
|
338
|
-
|
|
432
|
+
parameters: {
|
|
339
433
|
type: "object",
|
|
340
434
|
properties: {
|
|
341
435
|
clOrdID: { type: "string" },
|
|
@@ -416,7 +510,7 @@ var MCPLocal = class {
|
|
|
416
510
|
{
|
|
417
511
|
name: "executeOrder",
|
|
418
512
|
description: "Executes a verified order",
|
|
419
|
-
|
|
513
|
+
parameters: {
|
|
420
514
|
type: "object",
|
|
421
515
|
properties: {
|
|
422
516
|
clOrdID: { type: "string" },
|
|
@@ -443,7 +537,7 @@ var MCPLocal = class {
|
|
|
443
537
|
{
|
|
444
538
|
name: "marketDataRequest",
|
|
445
539
|
description: "Requests market data for specified symbols",
|
|
446
|
-
|
|
540
|
+
parameters: {
|
|
447
541
|
type: "object",
|
|
448
542
|
properties: {
|
|
449
543
|
mdUpdateType: { type: "string", enum: ["0", "1"] },
|
|
@@ -467,176 +561,218 @@ var MCPLocal = class {
|
|
|
467
561
|
);
|
|
468
562
|
this.server.setRequestHandler(
|
|
469
563
|
z.object({
|
|
470
|
-
method: z.literal("
|
|
564
|
+
method: z.literal("resources/read"),
|
|
471
565
|
params: z.object({
|
|
472
|
-
|
|
473
|
-
arguments: z.any(),
|
|
474
|
-
_meta: z.object({
|
|
475
|
-
progressToken: z.number()
|
|
476
|
-
})
|
|
566
|
+
uri: z.string()
|
|
477
567
|
})
|
|
478
568
|
}),
|
|
479
569
|
async (request, extra) => {
|
|
480
|
-
const {
|
|
481
|
-
switch (
|
|
482
|
-
case "
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
570
|
+
const { uri } = request.params;
|
|
571
|
+
switch (uri) {
|
|
572
|
+
case "greeting-resource":
|
|
573
|
+
return {
|
|
574
|
+
content: [
|
|
575
|
+
{
|
|
576
|
+
type: "text",
|
|
577
|
+
text: "Hello, world!"
|
|
578
|
+
}
|
|
579
|
+
]
|
|
580
|
+
};
|
|
581
|
+
case "stockGraph":
|
|
582
|
+
return {
|
|
583
|
+
content: [
|
|
584
|
+
{
|
|
585
|
+
type: "text",
|
|
586
|
+
text: "This resource requires a symbol parameter. Please use the stockGraph resource with a symbol parameter."
|
|
587
|
+
}
|
|
588
|
+
]
|
|
589
|
+
};
|
|
590
|
+
case "stockPriceHistory":
|
|
591
|
+
return {
|
|
592
|
+
content: [
|
|
593
|
+
{
|
|
594
|
+
type: "text",
|
|
595
|
+
text: "This resource requires a symbol parameter. Please use the stockPriceHistory resource with a symbol parameter."
|
|
596
|
+
}
|
|
597
|
+
]
|
|
598
|
+
};
|
|
599
|
+
default:
|
|
600
|
+
return {
|
|
601
|
+
content: [
|
|
602
|
+
{
|
|
603
|
+
type: "text",
|
|
604
|
+
text: `Resource not found: ${uri}`
|
|
605
|
+
}
|
|
606
|
+
],
|
|
607
|
+
isError: true
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
);
|
|
612
|
+
this.server.setRequestHandler(
|
|
613
|
+
z.object({
|
|
614
|
+
method: z.literal("parse"),
|
|
615
|
+
params: fixStringSchema
|
|
616
|
+
}),
|
|
617
|
+
async (request, extra) => {
|
|
618
|
+
try {
|
|
619
|
+
const args = request.params;
|
|
620
|
+
const parsedMessage = this.parser?.parse(args.fixString);
|
|
621
|
+
if (!parsedMessage || parsedMessage.length === 0) {
|
|
622
|
+
return {
|
|
623
|
+
content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
|
|
624
|
+
isError: true
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
content: [
|
|
629
|
+
{
|
|
630
|
+
type: "text",
|
|
631
|
+
text: `${parsedMessage[0].description}
|
|
632
|
+
${parsedMessage[0].messageTypeDescription}`
|
|
496
633
|
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
]
|
|
506
|
-
};
|
|
507
|
-
} catch (error) {
|
|
508
|
-
return {
|
|
509
|
-
contents: [
|
|
510
|
-
{
|
|
511
|
-
type: "text",
|
|
512
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`,
|
|
513
|
-
uri: "parse"
|
|
514
|
-
}
|
|
515
|
-
],
|
|
516
|
-
isError: true
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
case "parseToJSON":
|
|
520
|
-
try {
|
|
521
|
-
const parsedMessage = this.parser?.parse(args.fixString);
|
|
522
|
-
if (!parsedMessage || parsedMessage.length === 0) {
|
|
523
|
-
return {
|
|
524
|
-
contents: [
|
|
525
|
-
{
|
|
526
|
-
type: "text",
|
|
527
|
-
text: "Error: Failed to parse FIX string",
|
|
528
|
-
uri: "parseToJSON"
|
|
529
|
-
}
|
|
530
|
-
],
|
|
531
|
-
isError: true
|
|
532
|
-
};
|
|
634
|
+
]
|
|
635
|
+
};
|
|
636
|
+
} catch (error) {
|
|
637
|
+
return {
|
|
638
|
+
content: [
|
|
639
|
+
{
|
|
640
|
+
type: "text",
|
|
641
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
533
642
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
643
|
+
],
|
|
644
|
+
isError: true
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
);
|
|
649
|
+
this.server.setRequestHandler(
|
|
650
|
+
z.object({
|
|
651
|
+
method: z.literal("parseToJSON"),
|
|
652
|
+
params: fixStringSchema
|
|
653
|
+
}),
|
|
654
|
+
async (request, extra) => {
|
|
655
|
+
try {
|
|
656
|
+
const args = request.params;
|
|
657
|
+
const parsedMessage = this.parser?.parse(args.fixString);
|
|
658
|
+
if (!parsedMessage || parsedMessage.length === 0) {
|
|
659
|
+
return {
|
|
660
|
+
content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
|
|
661
|
+
isError: true
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
return {
|
|
665
|
+
content: [
|
|
666
|
+
{
|
|
667
|
+
type: "text",
|
|
668
|
+
text: `${parsedMessage[0].toFIXJSON()}`
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
};
|
|
672
|
+
} catch (error) {
|
|
673
|
+
return {
|
|
674
|
+
content: [
|
|
675
|
+
{
|
|
676
|
+
type: "text",
|
|
677
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
678
|
+
}
|
|
679
|
+
],
|
|
680
|
+
isError: true
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
this.server.setRequestHandler(
|
|
686
|
+
z.object({
|
|
687
|
+
method: z.literal("verifyOrder"),
|
|
688
|
+
params: orderSchema
|
|
689
|
+
}),
|
|
690
|
+
async (request, extra) => {
|
|
691
|
+
try {
|
|
692
|
+
const args = request.params;
|
|
693
|
+
this.verifiedOrders.set(args.clOrdID, {
|
|
694
|
+
clOrdID: args.clOrdID,
|
|
695
|
+
handlInst: args.handlInst,
|
|
696
|
+
quantity: Number.parseFloat(args.quantity),
|
|
697
|
+
price: Number.parseFloat(args.price),
|
|
698
|
+
ordType: args.ordType,
|
|
699
|
+
side: args.side,
|
|
700
|
+
symbol: args.symbol,
|
|
701
|
+
timeInForce: args.timeInForce
|
|
702
|
+
});
|
|
703
|
+
const ordTypeNames = {
|
|
704
|
+
"1": "Market",
|
|
705
|
+
"2": "Limit",
|
|
706
|
+
"3": "Stop",
|
|
707
|
+
"4": "StopLimit",
|
|
708
|
+
"5": "MarketOnClose",
|
|
709
|
+
"6": "WithOrWithout",
|
|
710
|
+
"7": "LimitOrBetter",
|
|
711
|
+
"8": "LimitWithOrWithout",
|
|
712
|
+
"9": "OnBasis",
|
|
713
|
+
A: "OnClose",
|
|
714
|
+
B: "LimitOnClose",
|
|
715
|
+
C: "ForexMarket",
|
|
716
|
+
D: "PreviouslyQuoted",
|
|
717
|
+
E: "PreviouslyIndicated",
|
|
718
|
+
F: "ForexLimit",
|
|
719
|
+
G: "ForexSwap",
|
|
720
|
+
H: "ForexPreviouslyQuoted",
|
|
721
|
+
I: "Funari",
|
|
722
|
+
J: "MarketIfTouched",
|
|
723
|
+
K: "MarketWithLeftOverAsLimit",
|
|
724
|
+
L: "PreviousFundValuationPoint",
|
|
725
|
+
M: "NextFundValuationPoint",
|
|
726
|
+
P: "Pegged",
|
|
727
|
+
Q: "CounterOrderSelection",
|
|
728
|
+
R: "StopOnBidOrOffer",
|
|
729
|
+
S: "StopLimitOnBidOrOffer"
|
|
730
|
+
};
|
|
731
|
+
const sideNames = {
|
|
732
|
+
"1": "Buy",
|
|
733
|
+
"2": "Sell",
|
|
734
|
+
"3": "BuyMinus",
|
|
735
|
+
"4": "SellPlus",
|
|
736
|
+
"5": "SellShort",
|
|
737
|
+
"6": "SellShortExempt",
|
|
738
|
+
"7": "Undisclosed",
|
|
739
|
+
"8": "Cross",
|
|
740
|
+
"9": "CrossShort",
|
|
741
|
+
A: "CrossShortExempt",
|
|
742
|
+
B: "AsDefined",
|
|
743
|
+
C: "Opposite",
|
|
744
|
+
D: "Subscribe",
|
|
745
|
+
E: "Redeem",
|
|
746
|
+
F: "Lend",
|
|
747
|
+
G: "Borrow",
|
|
748
|
+
H: "SellUndisclosed"
|
|
749
|
+
};
|
|
750
|
+
const timeInForceNames = {
|
|
751
|
+
"0": "Day",
|
|
752
|
+
"1": "GoodTillCancel",
|
|
753
|
+
"2": "AtTheOpening",
|
|
754
|
+
"3": "ImmediateOrCancel",
|
|
755
|
+
"4": "FillOrKill",
|
|
756
|
+
"5": "GoodTillCrossing",
|
|
757
|
+
"6": "GoodTillDate",
|
|
758
|
+
"7": "AtTheClose",
|
|
759
|
+
"8": "GoodThroughCrossing",
|
|
760
|
+
"9": "AtCrossing",
|
|
761
|
+
A: "GoodForTime",
|
|
762
|
+
B: "GoodForAuction",
|
|
763
|
+
C: "GoodForMonth"
|
|
764
|
+
};
|
|
765
|
+
const handlInstNames = {
|
|
766
|
+
"1": "AutomatedExecutionNoIntervention",
|
|
767
|
+
"2": "AutomatedExecutionInterventionOK",
|
|
768
|
+
"3": "ManualOrder"
|
|
769
|
+
};
|
|
770
|
+
return {
|
|
771
|
+
content: [
|
|
772
|
+
{
|
|
773
|
+
type: "text",
|
|
774
|
+
text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
|
|
775
|
+
|
|
640
776
|
Parameters verified:
|
|
641
777
|
- ClOrdID: ${args.clOrdID}
|
|
642
778
|
- HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
|
|
@@ -647,247 +783,209 @@ Parameters verified:
|
|
|
647
783
|
- Symbol: ${args.symbol}
|
|
648
784
|
- TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
|
|
649
785
|
|
|
650
|
-
To execute this order, call the executeOrder tool with these exact same parameters
|
|
651
|
-
uri: "verifyOrder"
|
|
652
|
-
}
|
|
653
|
-
]
|
|
654
|
-
};
|
|
655
|
-
} catch (error) {
|
|
656
|
-
return {
|
|
657
|
-
contents: [
|
|
658
|
-
{
|
|
659
|
-
type: "text",
|
|
660
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`,
|
|
661
|
-
uri: "verifyOrder"
|
|
662
|
-
}
|
|
663
|
-
],
|
|
664
|
-
isError: true
|
|
665
|
-
};
|
|
666
|
-
}
|
|
667
|
-
case "executeOrder":
|
|
668
|
-
try {
|
|
669
|
-
const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
|
|
670
|
-
if (!verifiedOrder) {
|
|
671
|
-
return {
|
|
672
|
-
contents: [
|
|
673
|
-
{
|
|
674
|
-
type: "text",
|
|
675
|
-
text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`,
|
|
676
|
-
uri: "executeOrder"
|
|
677
|
-
}
|
|
678
|
-
],
|
|
679
|
-
isError: true
|
|
680
|
-
};
|
|
681
|
-
}
|
|
682
|
-
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) {
|
|
683
|
-
return {
|
|
684
|
-
contents: [
|
|
685
|
-
{
|
|
686
|
-
type: "text",
|
|
687
|
-
text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified.",
|
|
688
|
-
uri: "executeOrder"
|
|
689
|
-
}
|
|
690
|
-
],
|
|
691
|
-
isError: true
|
|
692
|
-
};
|
|
786
|
+
To execute this order, call the executeOrder tool with these exact same parameters.`
|
|
693
787
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
new Field(Fields.SendingTime, this.parser?.getTimestamp()),
|
|
703
|
-
new Field(Fields.ClOrdID, args.clOrdID),
|
|
704
|
-
new Field(Fields.Side, args.side),
|
|
705
|
-
new Field(Fields.Symbol, args.symbol),
|
|
706
|
-
new Field(Fields.OrderQty, Number.parseFloat(args.quantity)),
|
|
707
|
-
new Field(Fields.Price, Number.parseFloat(args.price)),
|
|
708
|
-
new Field(Fields.OrdType, args.ordType),
|
|
709
|
-
new Field(Fields.HandlInst, args.handlInst),
|
|
710
|
-
new Field(Fields.TimeInForce, args.timeInForce),
|
|
711
|
-
new Field(Fields.TransactTime, this.parser?.getTimestamp())
|
|
712
|
-
);
|
|
713
|
-
if (!this.parser?.connected) {
|
|
714
|
-
return {
|
|
715
|
-
contents: [
|
|
716
|
-
{
|
|
717
|
-
type: "text",
|
|
718
|
-
text: "Error: Not connected. Ignoring message.",
|
|
719
|
-
uri: "executeOrder"
|
|
720
|
-
}
|
|
721
|
-
],
|
|
722
|
-
isError: true
|
|
723
|
-
};
|
|
788
|
+
]
|
|
789
|
+
};
|
|
790
|
+
} catch (error) {
|
|
791
|
+
return {
|
|
792
|
+
content: [
|
|
793
|
+
{
|
|
794
|
+
type: "text",
|
|
795
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`
|
|
724
796
|
}
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
return {
|
|
729
|
-
contents: [
|
|
730
|
-
{
|
|
731
|
-
type: "text",
|
|
732
|
-
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())}`,
|
|
733
|
-
uri: "executeOrder"
|
|
734
|
-
}
|
|
735
|
-
]
|
|
736
|
-
};
|
|
737
|
-
} catch (error) {
|
|
738
|
-
return {
|
|
739
|
-
contents: [
|
|
740
|
-
{
|
|
741
|
-
type: "text",
|
|
742
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`,
|
|
743
|
-
uri: "executeOrder"
|
|
744
|
-
}
|
|
745
|
-
],
|
|
746
|
-
isError: true
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
case "marketDataRequest":
|
|
750
|
-
try {
|
|
751
|
-
const response = new Promise((resolve) => {
|
|
752
|
-
this.pendingRequests.set(args.mdReqID, resolve);
|
|
753
|
-
});
|
|
754
|
-
const messageFields = [
|
|
755
|
-
new Field(Fields.MsgType, Messages.MarketDataRequest),
|
|
756
|
-
new Field(Fields.SenderCompID, this.parser?.sender),
|
|
757
|
-
new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
758
|
-
new Field(Fields.TargetCompID, this.parser?.target),
|
|
759
|
-
new Field(Fields.SendingTime, this.parser?.getTimestamp()),
|
|
760
|
-
new Field(Fields.MDReqID, args.mdReqID),
|
|
761
|
-
new Field(Fields.SubscriptionRequestType, args.subscriptionRequestType),
|
|
762
|
-
new Field(Fields.MarketDepth, 0),
|
|
763
|
-
new Field(Fields.MDUpdateType, args.mdUpdateType)
|
|
764
|
-
];
|
|
765
|
-
messageFields.push(new Field(Fields.NoRelatedSym, args.symbols.length));
|
|
766
|
-
args.symbols.forEach((symbol) => {
|
|
767
|
-
messageFields.push(new Field(Fields.Symbol, symbol));
|
|
768
|
-
});
|
|
769
|
-
messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
|
|
770
|
-
args.mdEntryTypes.forEach((entryType) => {
|
|
771
|
-
messageFields.push(new Field(Fields.MDEntryType, entryType));
|
|
772
|
-
});
|
|
773
|
-
const mdr = this.parser?.createMessage(...messageFields);
|
|
774
|
-
if (!this.parser?.connected) {
|
|
775
|
-
return {
|
|
776
|
-
contents: [
|
|
777
|
-
{
|
|
778
|
-
type: "text",
|
|
779
|
-
text: "Error: Not connected. Ignoring message.",
|
|
780
|
-
uri: "marketDataRequest"
|
|
781
|
-
}
|
|
782
|
-
],
|
|
783
|
-
isError: true
|
|
784
|
-
};
|
|
785
|
-
}
|
|
786
|
-
this.parser?.send(mdr);
|
|
787
|
-
const fixData = await response;
|
|
788
|
-
return {
|
|
789
|
-
contents: [
|
|
790
|
-
{
|
|
791
|
-
type: "text",
|
|
792
|
-
text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`,
|
|
793
|
-
uri: "marketDataRequest"
|
|
794
|
-
}
|
|
795
|
-
]
|
|
796
|
-
};
|
|
797
|
-
} catch (error) {
|
|
798
|
-
return {
|
|
799
|
-
contents: [
|
|
800
|
-
{
|
|
801
|
-
type: "text",
|
|
802
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`,
|
|
803
|
-
uri: "marketDataRequest"
|
|
804
|
-
}
|
|
805
|
-
],
|
|
806
|
-
isError: true
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
default:
|
|
810
|
-
return {
|
|
811
|
-
contents: [
|
|
812
|
-
{
|
|
813
|
-
type: "text",
|
|
814
|
-
text: `Tool not found: ${name}`,
|
|
815
|
-
uri: name
|
|
816
|
-
}
|
|
817
|
-
],
|
|
818
|
-
isError: true
|
|
819
|
-
};
|
|
797
|
+
],
|
|
798
|
+
isError: true
|
|
799
|
+
};
|
|
820
800
|
}
|
|
821
801
|
}
|
|
822
802
|
);
|
|
823
803
|
this.server.setRequestHandler(
|
|
824
804
|
z.object({
|
|
825
|
-
method: z.literal("
|
|
826
|
-
params:
|
|
827
|
-
uri: z.string()
|
|
828
|
-
})
|
|
805
|
+
method: z.literal("executeOrder"),
|
|
806
|
+
params: orderSchema
|
|
829
807
|
}),
|
|
830
808
|
async (request, extra) => {
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
809
|
+
try {
|
|
810
|
+
const args = request.params;
|
|
811
|
+
const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
|
|
812
|
+
if (!verifiedOrder) {
|
|
834
813
|
return {
|
|
835
|
-
|
|
814
|
+
content: [
|
|
836
815
|
{
|
|
837
816
|
type: "text",
|
|
838
|
-
text:
|
|
839
|
-
uri: "greeting-resource"
|
|
817
|
+
text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`
|
|
840
818
|
}
|
|
841
|
-
]
|
|
819
|
+
],
|
|
820
|
+
isError: true
|
|
842
821
|
};
|
|
843
|
-
|
|
822
|
+
}
|
|
823
|
+
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) {
|
|
844
824
|
return {
|
|
845
|
-
|
|
825
|
+
content: [
|
|
846
826
|
{
|
|
847
827
|
type: "text",
|
|
848
|
-
text: "
|
|
849
|
-
uri: "stockGraph"
|
|
828
|
+
text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified."
|
|
850
829
|
}
|
|
851
|
-
]
|
|
830
|
+
],
|
|
831
|
+
isError: true
|
|
852
832
|
};
|
|
853
|
-
|
|
833
|
+
}
|
|
834
|
+
const response = new Promise((resolve) => {
|
|
835
|
+
this.pendingRequests.set(args.clOrdID, resolve);
|
|
836
|
+
});
|
|
837
|
+
const order = this.parser?.createMessage(
|
|
838
|
+
new Field(Fields.MsgType, Messages.NewOrderSingle),
|
|
839
|
+
new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
840
|
+
new Field(Fields.SenderCompID, this.parser?.sender),
|
|
841
|
+
new Field(Fields.TargetCompID, this.parser?.target),
|
|
842
|
+
new Field(Fields.SendingTime, this.parser?.getTimestamp()),
|
|
843
|
+
new Field(Fields.ClOrdID, args.clOrdID),
|
|
844
|
+
new Field(Fields.Side, args.side),
|
|
845
|
+
new Field(Fields.Symbol, args.symbol),
|
|
846
|
+
new Field(Fields.OrderQty, Number.parseFloat(args.quantity)),
|
|
847
|
+
new Field(Fields.Price, Number.parseFloat(args.price)),
|
|
848
|
+
new Field(Fields.OrdType, args.ordType),
|
|
849
|
+
new Field(Fields.HandlInst, args.handlInst),
|
|
850
|
+
new Field(Fields.TimeInForce, args.timeInForce),
|
|
851
|
+
new Field(Fields.TransactTime, this.parser?.getTimestamp())
|
|
852
|
+
);
|
|
853
|
+
if (!this.parser?.connected) {
|
|
854
854
|
return {
|
|
855
|
-
|
|
855
|
+
content: [
|
|
856
856
|
{
|
|
857
857
|
type: "text",
|
|
858
|
-
text: "
|
|
859
|
-
uri: "stockPriceHistory"
|
|
858
|
+
text: "Error: Not connected. Ignoring message."
|
|
860
859
|
}
|
|
861
|
-
]
|
|
860
|
+
],
|
|
861
|
+
isError: true
|
|
862
862
|
};
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
863
|
+
}
|
|
864
|
+
this.parser?.send(order);
|
|
865
|
+
const fixData = await response;
|
|
866
|
+
this.verifiedOrders.delete(args.clOrdID);
|
|
867
|
+
return {
|
|
868
|
+
content: [
|
|
869
|
+
{
|
|
870
|
+
type: "text",
|
|
871
|
+
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())}`
|
|
872
|
+
}
|
|
873
|
+
]
|
|
874
|
+
};
|
|
875
|
+
} catch (error) {
|
|
876
|
+
return {
|
|
877
|
+
content: [
|
|
878
|
+
{
|
|
879
|
+
type: "text",
|
|
880
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`
|
|
877
881
|
}
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
882
|
+
],
|
|
883
|
+
isError: true
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
);
|
|
888
|
+
this.server.setRequestHandler(
|
|
889
|
+
z.object({
|
|
890
|
+
method: z.literal("marketDataRequest"),
|
|
891
|
+
params: marketDataRequestSchema
|
|
892
|
+
}),
|
|
893
|
+
async (request, extra) => {
|
|
894
|
+
try {
|
|
895
|
+
const args = request.params;
|
|
896
|
+
const response = new Promise((resolve) => {
|
|
897
|
+
this.pendingRequests.set(args.mdReqID, resolve);
|
|
898
|
+
});
|
|
899
|
+
const messageFields = [
|
|
900
|
+
new Field(Fields.MsgType, Messages.MarketDataRequest),
|
|
901
|
+
new Field(Fields.SenderCompID, this.parser?.sender),
|
|
902
|
+
new Field(Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
903
|
+
new Field(Fields.TargetCompID, this.parser?.target),
|
|
904
|
+
new Field(Fields.SendingTime, this.parser?.getTimestamp()),
|
|
905
|
+
new Field(Fields.MDReqID, args.mdReqID),
|
|
906
|
+
new Field(Fields.SubscriptionRequestType, args.subscriptionRequestType),
|
|
907
|
+
new Field(Fields.MarketDepth, 0),
|
|
908
|
+
new Field(Fields.MDUpdateType, args.mdUpdateType)
|
|
909
|
+
];
|
|
910
|
+
messageFields.push(new Field(Fields.NoRelatedSym, args.symbols.length));
|
|
911
|
+
args.symbols.forEach((symbol) => {
|
|
912
|
+
messageFields.push(new Field(Fields.Symbol, symbol));
|
|
913
|
+
});
|
|
914
|
+
messageFields.push(new Field(Fields.NoMDEntryTypes, args.mdEntryTypes.length));
|
|
915
|
+
args.mdEntryTypes.forEach((entryType) => {
|
|
916
|
+
messageFields.push(new Field(Fields.MDEntryType, entryType));
|
|
917
|
+
});
|
|
918
|
+
const mdr = this.parser?.createMessage(...messageFields);
|
|
919
|
+
if (!this.parser?.connected) {
|
|
920
|
+
return {
|
|
921
|
+
content: [
|
|
922
|
+
{
|
|
923
|
+
type: "text",
|
|
924
|
+
text: "Error: Not connected. Ignoring message."
|
|
925
|
+
}
|
|
926
|
+
],
|
|
927
|
+
isError: true
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
this.parser?.send(mdr);
|
|
931
|
+
const fixData = await response;
|
|
932
|
+
return {
|
|
933
|
+
content: [
|
|
934
|
+
{
|
|
935
|
+
type: "text",
|
|
936
|
+
text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`
|
|
937
|
+
}
|
|
938
|
+
]
|
|
939
|
+
};
|
|
940
|
+
} catch (error) {
|
|
941
|
+
return {
|
|
942
|
+
content: [
|
|
943
|
+
{
|
|
944
|
+
type: "text",
|
|
945
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`
|
|
946
|
+
}
|
|
947
|
+
],
|
|
948
|
+
isError: true
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
);
|
|
953
|
+
this.server.setRequestHandler(
|
|
954
|
+
z.object({
|
|
955
|
+
method: z.literal("stockGraph"),
|
|
956
|
+
params: symbolSchema
|
|
957
|
+
}),
|
|
958
|
+
async (request, extra) => {
|
|
959
|
+
this.parser?.logger.log({
|
|
960
|
+
level: "info",
|
|
961
|
+
message: "MCP Server Resource called: stockGraph"
|
|
962
|
+
});
|
|
963
|
+
const args = request.params;
|
|
964
|
+
const symbol = args.symbol;
|
|
965
|
+
const priceHistory = this.marketDataPrices.get(symbol) || [];
|
|
966
|
+
if (priceHistory.length === 0) {
|
|
967
|
+
return {
|
|
968
|
+
content: [
|
|
969
|
+
{
|
|
970
|
+
type: "text",
|
|
971
|
+
text: `No price data available for ${symbol}`
|
|
972
|
+
}
|
|
973
|
+
]
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
const width = 600;
|
|
977
|
+
const height = 300;
|
|
978
|
+
const padding = 40;
|
|
979
|
+
const xScale = (width - 2 * padding) / (priceHistory.length - 1);
|
|
980
|
+
const yMin = Math.min(...priceHistory.map((d) => d.price));
|
|
981
|
+
const yMax = Math.max(...priceHistory.map((d) => d.price));
|
|
982
|
+
const yScale = (height - 2 * padding) / (yMax - yMin);
|
|
983
|
+
const points = priceHistory.map((d, i) => {
|
|
984
|
+
const x = padding + i * xScale;
|
|
985
|
+
const y = height - padding - (d.price - yMin) * yScale;
|
|
986
|
+
return `${x},${y}`;
|
|
987
|
+
}).join(" L ");
|
|
988
|
+
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
|
891
989
|
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
|
892
990
|
<!-- Background -->
|
|
893
991
|
<rect width="100%" height="100%" fill="#f8f9fa"/>
|
|
@@ -895,9 +993,9 @@ To execute this order, call the executeOrder tool with these exact same paramete
|
|
|
895
993
|
<!-- Grid lines -->
|
|
896
994
|
<g stroke="#e9ecef" stroke-width="1">
|
|
897
995
|
${Array.from({ length: 5 }, (_, i) => {
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
996
|
+
const y = padding + (height - 2 * padding) * i / 4;
|
|
997
|
+
return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
|
|
998
|
+
}).join("\n")}
|
|
901
999
|
</g>
|
|
902
1000
|
|
|
903
1001
|
<!-- Price line -->
|
|
@@ -908,24 +1006,24 @@ To execute this order, call the executeOrder tool with these exact same paramete
|
|
|
908
1006
|
|
|
909
1007
|
<!-- Data points -->
|
|
910
1008
|
${priceHistory.map((d, i) => {
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
1009
|
+
const x = padding + i * xScale;
|
|
1010
|
+
const y = height - padding - (d.price - yMin) * yScale;
|
|
1011
|
+
return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
|
|
1012
|
+
}).join("\n")}
|
|
915
1013
|
|
|
916
1014
|
<!-- Labels -->
|
|
917
1015
|
<g font-family="Arial" font-size="12" fill="#495057">
|
|
918
1016
|
${Array.from({ length: 5 }, (_, i) => {
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
1017
|
+
const x = padding + (width - 2 * padding) * i / 4;
|
|
1018
|
+
const index = Math.floor((priceHistory.length - 1) * i / 4);
|
|
1019
|
+
const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
|
|
1020
|
+
return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
|
|
1021
|
+
}).join("\n")}
|
|
924
1022
|
${Array.from({ length: 5 }, (_, i) => {
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1023
|
+
const y = padding + (height - 2 * padding) * i / 4;
|
|
1024
|
+
const price = yMax - (yMax - yMin) * i / 4;
|
|
1025
|
+
return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
|
|
1026
|
+
}).join("\n")}
|
|
929
1027
|
</g>
|
|
930
1028
|
|
|
931
1029
|
<!-- Title -->
|
|
@@ -935,62 +1033,58 @@ To execute this order, call the executeOrder tool with these exact same paramete
|
|
|
935
1033
|
${symbol} - Price Chart (${priceHistory.length} points)
|
|
936
1034
|
</text>
|
|
937
1035
|
</svg>`;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
uri
|
|
944
|
-
}
|
|
945
|
-
]
|
|
946
|
-
};
|
|
1036
|
+
return {
|
|
1037
|
+
content: [
|
|
1038
|
+
{
|
|
1039
|
+
type: "text",
|
|
1040
|
+
text: svg
|
|
947
1041
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
1042
|
+
]
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
);
|
|
1046
|
+
this.server.setRequestHandler(
|
|
1047
|
+
z.object({
|
|
1048
|
+
method: z.literal("stockPriceHistory"),
|
|
1049
|
+
params: symbolSchema
|
|
1050
|
+
}),
|
|
1051
|
+
async (request, extra) => {
|
|
1052
|
+
this.parser?.logger.log({
|
|
1053
|
+
level: "info",
|
|
1054
|
+
message: "MCP Server Resource called: stockPriceHistory"
|
|
1055
|
+
});
|
|
1056
|
+
const args = request.params;
|
|
1057
|
+
const symbol = args.symbol;
|
|
1058
|
+
const priceHistory = this.marketDataPrices.get(symbol) || [];
|
|
1059
|
+
if (priceHistory.length === 0) {
|
|
1060
|
+
return {
|
|
1061
|
+
content: [
|
|
1062
|
+
{
|
|
1063
|
+
type: "text",
|
|
1064
|
+
text: `No price data available for ${symbol}`
|
|
961
1065
|
}
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
{
|
|
965
|
-
type: "text",
|
|
966
|
-
text: JSON.stringify(
|
|
967
|
-
{
|
|
968
|
-
symbol,
|
|
969
|
-
count: priceHistory.length,
|
|
970
|
-
prices: priceHistory.map((point) => ({
|
|
971
|
-
timestamp: new Date(point.timestamp).toISOString(),
|
|
972
|
-
price: point.price
|
|
973
|
-
}))
|
|
974
|
-
},
|
|
975
|
-
null,
|
|
976
|
-
2
|
|
977
|
-
),
|
|
978
|
-
uri
|
|
979
|
-
}
|
|
980
|
-
]
|
|
981
|
-
};
|
|
982
|
-
}
|
|
983
|
-
return {
|
|
984
|
-
contents: [
|
|
985
|
-
{
|
|
986
|
-
type: "text",
|
|
987
|
-
text: `Resource not found: ${uri}`,
|
|
988
|
-
uri
|
|
989
|
-
}
|
|
990
|
-
],
|
|
991
|
-
isError: true
|
|
992
|
-
};
|
|
1066
|
+
]
|
|
1067
|
+
};
|
|
993
1068
|
}
|
|
1069
|
+
return {
|
|
1070
|
+
content: [
|
|
1071
|
+
{
|
|
1072
|
+
type: "text",
|
|
1073
|
+
text: JSON.stringify(
|
|
1074
|
+
{
|
|
1075
|
+
symbol,
|
|
1076
|
+
count: priceHistory.length,
|
|
1077
|
+
prices: priceHistory.map((point) => ({
|
|
1078
|
+
timestamp: new Date(point.timestamp).toISOString(),
|
|
1079
|
+
price: point.price
|
|
1080
|
+
}))
|
|
1081
|
+
},
|
|
1082
|
+
null,
|
|
1083
|
+
2
|
|
1084
|
+
)
|
|
1085
|
+
}
|
|
1086
|
+
]
|
|
1087
|
+
};
|
|
994
1088
|
}
|
|
995
1089
|
);
|
|
996
1090
|
process.on("SIGINT", async () => {
|