fixparser-plugin-mcp 9.1.7-28bfe28b → 9.1.7-28edb130
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 +458 -571
- package/build/cjs/MCPLocal.js.map +2 -2
- package/build/esm/MCPLocal.mjs +459 -587
- package/build/esm/MCPLocal.mjs.map +2 -2
- package/build-examples/cjs/example_mcp_local.js +49 -3
- package/build-examples/cjs/example_mcp_local.js.map +4 -4
- package/build-examples/esm/example_mcp_local.mjs +49 -3
- package/build-examples/esm/example_mcp_local.mjs.map +4 -4
- package/package.json +10 -10
- package/types/MCPLocal.d.ts +3 -1
package/build/cjs/MCPLocal.js
CHANGED
|
@@ -25,179 +25,9 @@ __export(MCPLocal_exports, {
|
|
|
25
25
|
module.exports = __toCommonJS(MCPLocal_exports);
|
|
26
26
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
27
27
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
28
|
-
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
29
28
|
var import_fixparser = require("fixparser");
|
|
30
|
-
var
|
|
31
|
-
type: "object",
|
|
32
|
-
properties: {
|
|
33
|
-
fixString: {
|
|
34
|
-
type: "string",
|
|
35
|
-
description: "FIX message string to parse"
|
|
36
|
-
}
|
|
37
|
-
},
|
|
38
|
-
required: ["fixString"]
|
|
39
|
-
};
|
|
40
|
-
var parseToJSONInputSchema = {
|
|
41
|
-
type: "object",
|
|
42
|
-
properties: {
|
|
43
|
-
fixString: {
|
|
44
|
-
type: "string",
|
|
45
|
-
description: "FIX message string to parse"
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
required: ["fixString"]
|
|
49
|
-
};
|
|
50
|
-
var newOrderSingleInputSchema = {
|
|
51
|
-
type: "object",
|
|
52
|
-
properties: {
|
|
53
|
-
clOrdID: {
|
|
54
|
-
type: "string",
|
|
55
|
-
description: "Client Order ID"
|
|
56
|
-
},
|
|
57
|
-
handlInst: {
|
|
58
|
-
type: "string",
|
|
59
|
-
enum: ["1", "2", "3"],
|
|
60
|
-
default: import_fixparser.HandlInst.AutomatedExecutionNoIntervention,
|
|
61
|
-
description: "Handling instruction"
|
|
62
|
-
},
|
|
63
|
-
quantity: {
|
|
64
|
-
type: "number",
|
|
65
|
-
description: "Order quantity"
|
|
66
|
-
},
|
|
67
|
-
price: {
|
|
68
|
-
type: "number",
|
|
69
|
-
description: "Order price"
|
|
70
|
-
},
|
|
71
|
-
ordType: {
|
|
72
|
-
type: "string",
|
|
73
|
-
enum: [
|
|
74
|
-
"1",
|
|
75
|
-
"2",
|
|
76
|
-
"3",
|
|
77
|
-
"4",
|
|
78
|
-
"5",
|
|
79
|
-
"6",
|
|
80
|
-
"7",
|
|
81
|
-
"8",
|
|
82
|
-
"9",
|
|
83
|
-
"A",
|
|
84
|
-
"B",
|
|
85
|
-
"C",
|
|
86
|
-
"D",
|
|
87
|
-
"E",
|
|
88
|
-
"F",
|
|
89
|
-
"G",
|
|
90
|
-
"H",
|
|
91
|
-
"I",
|
|
92
|
-
"J",
|
|
93
|
-
"K",
|
|
94
|
-
"L",
|
|
95
|
-
"M",
|
|
96
|
-
"P",
|
|
97
|
-
"Q",
|
|
98
|
-
"R",
|
|
99
|
-
"S"
|
|
100
|
-
],
|
|
101
|
-
default: import_fixparser.OrdType.Market,
|
|
102
|
-
description: "Order type"
|
|
103
|
-
},
|
|
104
|
-
side: {
|
|
105
|
-
type: "string",
|
|
106
|
-
enum: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H"],
|
|
107
|
-
description: "Order side (1=Buy, 2=Sell)"
|
|
108
|
-
},
|
|
109
|
-
symbol: {
|
|
110
|
-
type: "string",
|
|
111
|
-
description: "Trading symbol"
|
|
112
|
-
},
|
|
113
|
-
timeInForce: {
|
|
114
|
-
type: "string",
|
|
115
|
-
enum: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"],
|
|
116
|
-
default: import_fixparser.TimeInForce.Day,
|
|
117
|
-
description: "Time in force"
|
|
118
|
-
}
|
|
119
|
-
},
|
|
120
|
-
required: ["clOrdID", "quantity", "price", "side", "symbol"]
|
|
121
|
-
};
|
|
122
|
-
var marketDataRequestInputSchema = {
|
|
123
|
-
type: "object",
|
|
124
|
-
properties: {
|
|
125
|
-
mdUpdateType: {
|
|
126
|
-
type: "string",
|
|
127
|
-
enum: ["0", "1"],
|
|
128
|
-
default: "0",
|
|
129
|
-
description: "Market data update type"
|
|
130
|
-
},
|
|
131
|
-
symbol: {
|
|
132
|
-
type: "string",
|
|
133
|
-
description: "Trading symbol"
|
|
134
|
-
},
|
|
135
|
-
mdReqID: {
|
|
136
|
-
type: "string",
|
|
137
|
-
description: "Market data request ID"
|
|
138
|
-
},
|
|
139
|
-
subscriptionRequestType: {
|
|
140
|
-
type: "string",
|
|
141
|
-
enum: ["0", "1", "2"],
|
|
142
|
-
default: import_fixparser.SubscriptionRequestType.SnapshotAndUpdates,
|
|
143
|
-
description: "Subscription request type"
|
|
144
|
-
},
|
|
145
|
-
mdEntryType: {
|
|
146
|
-
type: "string",
|
|
147
|
-
enum: [
|
|
148
|
-
"0",
|
|
149
|
-
"1",
|
|
150
|
-
"2",
|
|
151
|
-
"3",
|
|
152
|
-
"4",
|
|
153
|
-
"5",
|
|
154
|
-
"6",
|
|
155
|
-
"7",
|
|
156
|
-
"8",
|
|
157
|
-
"9",
|
|
158
|
-
"A",
|
|
159
|
-
"B",
|
|
160
|
-
"C",
|
|
161
|
-
"D",
|
|
162
|
-
"E",
|
|
163
|
-
"F",
|
|
164
|
-
"G",
|
|
165
|
-
"H",
|
|
166
|
-
"J",
|
|
167
|
-
"K",
|
|
168
|
-
"L",
|
|
169
|
-
"M",
|
|
170
|
-
"N",
|
|
171
|
-
"O",
|
|
172
|
-
"P",
|
|
173
|
-
"Q",
|
|
174
|
-
"S",
|
|
175
|
-
"R",
|
|
176
|
-
"T",
|
|
177
|
-
"U",
|
|
178
|
-
"V",
|
|
179
|
-
"W",
|
|
180
|
-
"X",
|
|
181
|
-
"Y",
|
|
182
|
-
"Z",
|
|
183
|
-
"a",
|
|
184
|
-
"b",
|
|
185
|
-
"c",
|
|
186
|
-
"d",
|
|
187
|
-
"e",
|
|
188
|
-
"g",
|
|
189
|
-
"h",
|
|
190
|
-
"i",
|
|
191
|
-
"t"
|
|
192
|
-
],
|
|
193
|
-
default: import_fixparser.MDEntryType.Bid,
|
|
194
|
-
description: "Market data entry type"
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
required: ["symbol", "mdReqID"]
|
|
198
|
-
};
|
|
29
|
+
var import_zod = require("zod");
|
|
199
30
|
var MCPLocal = class {
|
|
200
|
-
logger;
|
|
201
31
|
parser;
|
|
202
32
|
server = new import_server.Server(
|
|
203
33
|
{
|
|
@@ -207,7 +37,6 @@ var MCPLocal = class {
|
|
|
207
37
|
{
|
|
208
38
|
capabilities: {
|
|
209
39
|
tools: {},
|
|
210
|
-
prompts: {},
|
|
211
40
|
resources: {}
|
|
212
41
|
}
|
|
213
42
|
}
|
|
@@ -215,33 +44,69 @@ var MCPLocal = class {
|
|
|
215
44
|
transport = new import_stdio.StdioServerTransport();
|
|
216
45
|
onReady = void 0;
|
|
217
46
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
47
|
+
verifiedOrders = /* @__PURE__ */ new Map();
|
|
48
|
+
// Store market data prices with timestamps
|
|
49
|
+
marketDataPrices = /* @__PURE__ */ new Map();
|
|
50
|
+
MAX_PRICE_HISTORY = 1e5;
|
|
51
|
+
// Maximum number of price points to store per symbol
|
|
218
52
|
constructor({ logger, onReady }) {
|
|
219
|
-
if (logger) this.logger = logger;
|
|
220
53
|
if (onReady) this.onReady = onReady;
|
|
221
54
|
}
|
|
222
55
|
async register(parser) {
|
|
223
56
|
this.parser = parser;
|
|
224
57
|
this.parser.addOnMessageCallback((message) => {
|
|
225
|
-
this.logger
|
|
58
|
+
this.parser?.logger.log({
|
|
226
59
|
level: "info",
|
|
227
|
-
message: `
|
|
60
|
+
message: `MCP Server received message: ${message.messageType}: ${message.description}`
|
|
228
61
|
});
|
|
229
62
|
const msgType = message.messageType;
|
|
230
|
-
if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh || msgType === import_fixparser.Messages.ExecutionReport) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
63
|
+
if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh || msgType === import_fixparser.Messages.ExecutionReport || msgType === import_fixparser.Messages.Reject || msgType === import_fixparser.Messages.MarketDataIncrementalRefresh) {
|
|
64
|
+
this.parser?.logger.log({
|
|
65
|
+
level: "info",
|
|
66
|
+
message: `MCP Server handling message type: ${msgType}`
|
|
67
|
+
});
|
|
68
|
+
let id;
|
|
69
|
+
if (msgType === import_fixparser.Messages.MarketDataIncrementalRefresh || msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh) {
|
|
70
|
+
const symbol = message.getField(import_fixparser.Fields.Symbol);
|
|
71
|
+
const price = message.getField(import_fixparser.Fields.MDEntryPx);
|
|
72
|
+
const timestamp = message.getField(import_fixparser.Fields.MDEntryTime)?.value || Date.now();
|
|
73
|
+
if (symbol?.value && price?.value) {
|
|
74
|
+
const symbolStr = String(symbol.value);
|
|
75
|
+
const priceNum = Number(price.value);
|
|
76
|
+
const priceHistory = this.marketDataPrices.get(symbolStr) || [];
|
|
77
|
+
priceHistory.push({
|
|
78
|
+
timestamp: Number(timestamp),
|
|
79
|
+
price: priceNum
|
|
80
|
+
});
|
|
81
|
+
if (priceHistory.length > this.MAX_PRICE_HISTORY) {
|
|
82
|
+
priceHistory.shift();
|
|
239
83
|
}
|
|
84
|
+
this.marketDataPrices.set(symbolStr, priceHistory);
|
|
85
|
+
this.parser?.logger.log({
|
|
86
|
+
level: "info",
|
|
87
|
+
message: `MCP Server added ${symbol}: ${priceNum}`
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh) {
|
|
92
|
+
const mdReqID = message.getField(import_fixparser.Fields.MDReqID);
|
|
93
|
+
if (mdReqID) id = String(mdReqID.value);
|
|
94
|
+
} else if (msgType === import_fixparser.Messages.ExecutionReport) {
|
|
95
|
+
const clOrdID = message.getField(import_fixparser.Fields.ClOrdID);
|
|
96
|
+
if (clOrdID) id = String(clOrdID.value);
|
|
97
|
+
} else if (msgType === import_fixparser.Messages.Reject) {
|
|
98
|
+
const refSeqNum = message.getField(import_fixparser.Fields.RefSeqNum);
|
|
99
|
+
if (refSeqNum) id = String(refSeqNum.value);
|
|
100
|
+
}
|
|
101
|
+
if (id) {
|
|
102
|
+
const callback = this.pendingRequests.get(id);
|
|
103
|
+
if (callback) {
|
|
104
|
+
callback(message);
|
|
105
|
+
this.pendingRequests.delete(id);
|
|
240
106
|
}
|
|
241
107
|
}
|
|
242
108
|
}
|
|
243
109
|
});
|
|
244
|
-
this.logger = parser.logger;
|
|
245
110
|
this.addWorkflows();
|
|
246
111
|
await this.server.connect(this.transport);
|
|
247
112
|
if (this.onReady) {
|
|
@@ -250,450 +115,472 @@ var MCPLocal = class {
|
|
|
250
115
|
}
|
|
251
116
|
addWorkflows() {
|
|
252
117
|
if (!this.parser) {
|
|
253
|
-
this.logger?.log({
|
|
254
|
-
level: "error",
|
|
255
|
-
message: "FIXParser (MCP): -- FIXParser instance not initialized. Ignoring setup of workflows..."
|
|
256
|
-
});
|
|
257
118
|
return;
|
|
258
119
|
}
|
|
259
120
|
if (!this.server) {
|
|
260
|
-
this.logger?.log({
|
|
261
|
-
level: "error",
|
|
262
|
-
message: "FIXParser (MCP): -- MCP Server not initialized. Ignoring setup of workflows..."
|
|
263
|
-
});
|
|
264
121
|
return;
|
|
265
122
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
result[key] = value;
|
|
276
|
-
} else if (prop.default !== void 0) {
|
|
277
|
-
result[key] = prop.default;
|
|
123
|
+
this.server.setRequestHandler(import_zod.z.object({ method: import_zod.z.literal("parse") }), async (request, extra) => {
|
|
124
|
+
try {
|
|
125
|
+
const args = request.params;
|
|
126
|
+
const parsedMessage = this.parser?.parse(args.fixString);
|
|
127
|
+
if (!parsedMessage || parsedMessage.length === 0) {
|
|
128
|
+
return {
|
|
129
|
+
content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
|
|
130
|
+
isError: true
|
|
131
|
+
};
|
|
278
132
|
}
|
|
133
|
+
return {
|
|
134
|
+
content: [
|
|
135
|
+
{
|
|
136
|
+
type: "text",
|
|
137
|
+
text: `${parsedMessage[0].description}
|
|
138
|
+
${parsedMessage[0].messageTypeDescription}`
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
};
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return {
|
|
144
|
+
content: [
|
|
145
|
+
{
|
|
146
|
+
type: "text",
|
|
147
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
isError: true
|
|
151
|
+
};
|
|
279
152
|
}
|
|
280
|
-
return result;
|
|
281
|
-
};
|
|
282
|
-
this.server.setRequestHandler(import_types.ListResourcesRequestSchema, async () => {
|
|
283
|
-
return {
|
|
284
|
-
resources: []
|
|
285
|
-
};
|
|
286
153
|
});
|
|
287
|
-
this.server.setRequestHandler(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
name: "parseToJSON",
|
|
297
|
-
description: "Parses a FIX message into JSON",
|
|
298
|
-
inputSchema: parseToJSONInputSchema
|
|
299
|
-
},
|
|
300
|
-
{
|
|
301
|
-
name: "newOrderSingle",
|
|
302
|
-
description: "Creates and sends a New Order Single",
|
|
303
|
-
inputSchema: newOrderSingleInputSchema
|
|
304
|
-
},
|
|
305
|
-
{
|
|
306
|
-
name: "marketDataRequest",
|
|
307
|
-
description: "Sends a request for Market Data with the given symbol",
|
|
308
|
-
inputSchema: marketDataRequestInputSchema
|
|
309
|
-
}
|
|
310
|
-
]
|
|
311
|
-
};
|
|
312
|
-
});
|
|
313
|
-
this.server.setRequestHandler(import_types.CallToolRequestSchema, async (request) => {
|
|
314
|
-
const { name, arguments: args } = request.params;
|
|
315
|
-
switch (name) {
|
|
316
|
-
case "parse": {
|
|
317
|
-
try {
|
|
318
|
-
const { fixString } = validateArgs(args, parseInputSchema);
|
|
319
|
-
const parsedMessage = this.parser?.parse(fixString);
|
|
320
|
-
if (!parsedMessage || parsedMessage.length === 0) {
|
|
321
|
-
return {
|
|
322
|
-
isError: true,
|
|
323
|
-
content: [{ type: "text", text: "Error: Failed to parse FIX string" }]
|
|
324
|
-
};
|
|
325
|
-
}
|
|
154
|
+
this.server.setRequestHandler(
|
|
155
|
+
import_zod.z.object({ method: import_zod.z.literal("parseToJSON") }),
|
|
156
|
+
async (request, extra) => {
|
|
157
|
+
try {
|
|
158
|
+
const args = request.params;
|
|
159
|
+
const parsedMessage = this.parser?.parse(args.fixString);
|
|
160
|
+
if (!parsedMessage || parsedMessage.length === 0) {
|
|
326
161
|
return {
|
|
327
|
-
content: [
|
|
328
|
-
|
|
329
|
-
type: "text",
|
|
330
|
-
text: `Parsed FIX message: ${fixString} (placeholder implementation)`
|
|
331
|
-
}
|
|
332
|
-
]
|
|
333
|
-
};
|
|
334
|
-
} catch (error) {
|
|
335
|
-
return {
|
|
336
|
-
isError: true,
|
|
337
|
-
content: [
|
|
338
|
-
{
|
|
339
|
-
type: "text",
|
|
340
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
341
|
-
}
|
|
342
|
-
]
|
|
162
|
+
content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
|
|
163
|
+
isError: true
|
|
343
164
|
};
|
|
344
165
|
}
|
|
166
|
+
return {
|
|
167
|
+
content: [
|
|
168
|
+
{
|
|
169
|
+
type: "text",
|
|
170
|
+
text: `${parsedMessage[0].toFIXJSON()}`
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
return {
|
|
176
|
+
content: [
|
|
177
|
+
{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
180
|
+
}
|
|
181
|
+
],
|
|
182
|
+
isError: true
|
|
183
|
+
};
|
|
345
184
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
this.server.setRequestHandler(
|
|
188
|
+
import_zod.z.object({ method: import_zod.z.literal("verifyOrder") }),
|
|
189
|
+
async (request, extra) => {
|
|
190
|
+
try {
|
|
191
|
+
const args = request.params;
|
|
192
|
+
this.verifiedOrders.set(args.clOrdID, {
|
|
193
|
+
clOrdID: args.clOrdID,
|
|
194
|
+
handlInst: args.handlInst,
|
|
195
|
+
quantity: Number.parseFloat(args.quantity),
|
|
196
|
+
price: Number.parseFloat(args.price),
|
|
197
|
+
ordType: args.ordType,
|
|
198
|
+
side: args.side,
|
|
199
|
+
symbol: args.symbol,
|
|
200
|
+
timeInForce: args.timeInForce
|
|
201
|
+
});
|
|
202
|
+
const ordTypeNames = {
|
|
203
|
+
"1": "Market",
|
|
204
|
+
"2": "Limit",
|
|
205
|
+
"3": "Stop",
|
|
206
|
+
"4": "StopLimit",
|
|
207
|
+
"5": "MarketOnClose",
|
|
208
|
+
"6": "WithOrWithout",
|
|
209
|
+
"7": "LimitOrBetter",
|
|
210
|
+
"8": "LimitWithOrWithout",
|
|
211
|
+
"9": "OnBasis",
|
|
212
|
+
A: "OnClose",
|
|
213
|
+
B: "LimitOnClose",
|
|
214
|
+
C: "ForexMarket",
|
|
215
|
+
D: "PreviouslyQuoted",
|
|
216
|
+
E: "PreviouslyIndicated",
|
|
217
|
+
F: "ForexLimit",
|
|
218
|
+
G: "ForexSwap",
|
|
219
|
+
H: "ForexPreviouslyQuoted",
|
|
220
|
+
I: "Funari",
|
|
221
|
+
J: "MarketIfTouched",
|
|
222
|
+
K: "MarketWithLeftOverAsLimit",
|
|
223
|
+
L: "PreviousFundValuationPoint",
|
|
224
|
+
M: "NextFundValuationPoint",
|
|
225
|
+
P: "Pegged",
|
|
226
|
+
Q: "CounterOrderSelection",
|
|
227
|
+
R: "StopOnBidOrOffer",
|
|
228
|
+
S: "StopLimitOnBidOrOffer"
|
|
229
|
+
};
|
|
230
|
+
const sideNames = {
|
|
231
|
+
"1": "Buy",
|
|
232
|
+
"2": "Sell",
|
|
233
|
+
"3": "BuyMinus",
|
|
234
|
+
"4": "SellPlus",
|
|
235
|
+
"5": "SellShort",
|
|
236
|
+
"6": "SellShortExempt",
|
|
237
|
+
"7": "Undisclosed",
|
|
238
|
+
"8": "Cross",
|
|
239
|
+
"9": "CrossShort",
|
|
240
|
+
A: "CrossShortExempt",
|
|
241
|
+
B: "AsDefined",
|
|
242
|
+
C: "Opposite",
|
|
243
|
+
D: "Subscribe",
|
|
244
|
+
E: "Redeem",
|
|
245
|
+
F: "Lend",
|
|
246
|
+
G: "Borrow",
|
|
247
|
+
H: "SellUndisclosed"
|
|
248
|
+
};
|
|
249
|
+
const timeInForceNames = {
|
|
250
|
+
"0": "Day",
|
|
251
|
+
"1": "GoodTillCancel",
|
|
252
|
+
"2": "AtTheOpening",
|
|
253
|
+
"3": "ImmediateOrCancel",
|
|
254
|
+
"4": "FillOrKill",
|
|
255
|
+
"5": "GoodTillCrossing",
|
|
256
|
+
"6": "GoodTillDate",
|
|
257
|
+
"7": "AtTheClose",
|
|
258
|
+
"8": "GoodThroughCrossing",
|
|
259
|
+
"9": "AtCrossing",
|
|
260
|
+
A: "GoodForTime",
|
|
261
|
+
B: "GoodForAuction",
|
|
262
|
+
C: "GoodForMonth"
|
|
263
|
+
};
|
|
264
|
+
const handlInstNames = {
|
|
265
|
+
"1": "AutomatedExecutionNoIntervention",
|
|
266
|
+
"2": "AutomatedExecutionInterventionOK",
|
|
267
|
+
"3": "ManualOrder"
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
content: [
|
|
271
|
+
{
|
|
272
|
+
type: "text",
|
|
273
|
+
text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
|
|
274
|
+
|
|
275
|
+
Parameters verified:
|
|
276
|
+
- ClOrdID: ${args.clOrdID}
|
|
277
|
+
- HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
|
|
278
|
+
- Quantity: ${args.quantity}
|
|
279
|
+
- Price: ${args.price}
|
|
280
|
+
- OrdType: ${args.ordType} (${ordTypeNames[args.ordType]})
|
|
281
|
+
- Side: ${args.side} (${sideNames[args.side]})
|
|
282
|
+
- Symbol: ${args.symbol}
|
|
283
|
+
- TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
|
|
284
|
+
|
|
285
|
+
To execute this order, call the executeOrder tool with these exact same parameters.`
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
};
|
|
289
|
+
} catch (error) {
|
|
290
|
+
return {
|
|
291
|
+
content: [
|
|
292
|
+
{
|
|
293
|
+
type: "text",
|
|
294
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`
|
|
295
|
+
}
|
|
296
|
+
],
|
|
297
|
+
isError: true
|
|
298
|
+
};
|
|
375
299
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
386
|
-
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
387
|
-
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
388
|
-
new import_fixparser.Field(import_fixparser.Fields.ClOrdID, clOrdID),
|
|
389
|
-
new import_fixparser.Field(import_fixparser.Fields.Side, side),
|
|
390
|
-
new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol),
|
|
391
|
-
new import_fixparser.Field(import_fixparser.Fields.OrderQty, quantity),
|
|
392
|
-
new import_fixparser.Field(import_fixparser.Fields.Price, price),
|
|
393
|
-
new import_fixparser.Field(import_fixparser.Fields.OrdType, ordType),
|
|
394
|
-
new import_fixparser.Field(import_fixparser.Fields.HandlInst, handlInst),
|
|
395
|
-
new import_fixparser.Field(import_fixparser.Fields.TimeInForce, timeInForce),
|
|
396
|
-
new import_fixparser.Field(import_fixparser.Fields.TransactTime, this.parser?.getTimestamp())
|
|
397
|
-
);
|
|
398
|
-
if (!this.parser?.connected) {
|
|
399
|
-
this.logger?.log({
|
|
400
|
-
level: "error",
|
|
401
|
-
message: "FIXParser (MCP): -- Not connected. Ignoring message."
|
|
402
|
-
});
|
|
403
|
-
return {
|
|
404
|
-
isError: true,
|
|
405
|
-
content: [
|
|
406
|
-
{
|
|
407
|
-
type: "text",
|
|
408
|
-
text: "Error: Not connected. Ignoring message."
|
|
409
|
-
}
|
|
410
|
-
]
|
|
411
|
-
};
|
|
412
|
-
}
|
|
413
|
-
this.parser?.send(order);
|
|
414
|
-
this.logger?.log({
|
|
415
|
-
level: "info",
|
|
416
|
-
message: `FIXParser (MCP): (${this.parser?.protocol?.toUpperCase()}): >> sent ${order?.description}`
|
|
417
|
-
});
|
|
418
|
-
const fixData = await response;
|
|
419
|
-
return {
|
|
420
|
-
content: [
|
|
421
|
-
{
|
|
422
|
-
type: "text",
|
|
423
|
-
text: `Execution Report for order ${clOrdID}: ${JSON.stringify(fixData.toFIXJSON())}`
|
|
424
|
-
}
|
|
425
|
-
]
|
|
426
|
-
};
|
|
427
|
-
} catch (error) {
|
|
300
|
+
}
|
|
301
|
+
);
|
|
302
|
+
this.server.setRequestHandler(
|
|
303
|
+
import_zod.z.object({ method: import_zod.z.literal("executeOrder") }),
|
|
304
|
+
async (request, extra) => {
|
|
305
|
+
try {
|
|
306
|
+
const args = request.params;
|
|
307
|
+
const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
|
|
308
|
+
if (!verifiedOrder) {
|
|
428
309
|
return {
|
|
429
|
-
isError: true,
|
|
430
310
|
content: [
|
|
431
311
|
{
|
|
432
312
|
type: "text",
|
|
433
|
-
text: `Error: ${
|
|
313
|
+
text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`
|
|
434
314
|
}
|
|
435
|
-
]
|
|
315
|
+
],
|
|
316
|
+
isError: true
|
|
436
317
|
};
|
|
437
318
|
}
|
|
438
|
-
|
|
439
|
-
case "marketDataRequest": {
|
|
440
|
-
try {
|
|
441
|
-
const { mdUpdateType, symbol, mdReqID, subscriptionRequestType, mdEntryType } = validateArgs(
|
|
442
|
-
args,
|
|
443
|
-
marketDataRequestInputSchema
|
|
444
|
-
);
|
|
445
|
-
const response = new Promise((resolve) => {
|
|
446
|
-
this.pendingRequests.set(mdReqID, resolve);
|
|
447
|
-
});
|
|
448
|
-
const marketDataRequest = this.parser?.createMessage(
|
|
449
|
-
new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
|
|
450
|
-
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
451
|
-
new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
452
|
-
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
453
|
-
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
454
|
-
new import_fixparser.Field(import_fixparser.Fields.MarketDepth, 0),
|
|
455
|
-
new import_fixparser.Field(import_fixparser.Fields.MDUpdateType, mdUpdateType),
|
|
456
|
-
new import_fixparser.Field(import_fixparser.Fields.NoRelatedSym, 1),
|
|
457
|
-
new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol),
|
|
458
|
-
new import_fixparser.Field(import_fixparser.Fields.MDReqID, mdReqID),
|
|
459
|
-
new import_fixparser.Field(import_fixparser.Fields.SubscriptionRequestType, subscriptionRequestType),
|
|
460
|
-
new import_fixparser.Field(import_fixparser.Fields.NoMDEntryTypes, 1),
|
|
461
|
-
new import_fixparser.Field(import_fixparser.Fields.MDEntryType, mdEntryType)
|
|
462
|
-
);
|
|
463
|
-
if (!this.parser?.connected) {
|
|
464
|
-
this.logger?.log({
|
|
465
|
-
level: "error",
|
|
466
|
-
message: "FIXParser (MCP): -- Not connected. Ignoring message."
|
|
467
|
-
});
|
|
468
|
-
return {
|
|
469
|
-
isError: true,
|
|
470
|
-
content: [
|
|
471
|
-
{
|
|
472
|
-
type: "text",
|
|
473
|
-
text: "Error: Not connected. Ignoring message."
|
|
474
|
-
}
|
|
475
|
-
]
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
this.parser?.send(marketDataRequest);
|
|
479
|
-
this.logger?.log({
|
|
480
|
-
level: "info",
|
|
481
|
-
message: `FIXParser (MCP): (${this.parser?.protocol?.toUpperCase()}): >> sent ${marketDataRequest?.description}`
|
|
482
|
-
});
|
|
483
|
-
const fixData = await response;
|
|
319
|
+
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) {
|
|
484
320
|
return {
|
|
485
321
|
content: [
|
|
486
322
|
{
|
|
487
323
|
type: "text",
|
|
488
|
-
text:
|
|
324
|
+
text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified."
|
|
489
325
|
}
|
|
490
|
-
]
|
|
326
|
+
],
|
|
327
|
+
isError: true
|
|
491
328
|
};
|
|
492
|
-
}
|
|
329
|
+
}
|
|
330
|
+
const response = new Promise((resolve) => {
|
|
331
|
+
this.pendingRequests.set(args.clOrdID, resolve);
|
|
332
|
+
});
|
|
333
|
+
const order = this.parser?.createMessage(
|
|
334
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.NewOrderSingle),
|
|
335
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
336
|
+
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
337
|
+
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
338
|
+
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
339
|
+
new import_fixparser.Field(import_fixparser.Fields.ClOrdID, args.clOrdID),
|
|
340
|
+
new import_fixparser.Field(import_fixparser.Fields.Side, args.side),
|
|
341
|
+
new import_fixparser.Field(import_fixparser.Fields.Symbol, args.symbol),
|
|
342
|
+
new import_fixparser.Field(import_fixparser.Fields.OrderQty, Number.parseFloat(args.quantity)),
|
|
343
|
+
new import_fixparser.Field(import_fixparser.Fields.Price, Number.parseFloat(args.price)),
|
|
344
|
+
new import_fixparser.Field(import_fixparser.Fields.OrdType, args.ordType),
|
|
345
|
+
new import_fixparser.Field(import_fixparser.Fields.HandlInst, args.handlInst),
|
|
346
|
+
new import_fixparser.Field(import_fixparser.Fields.TimeInForce, args.timeInForce),
|
|
347
|
+
new import_fixparser.Field(import_fixparser.Fields.TransactTime, this.parser?.getTimestamp())
|
|
348
|
+
);
|
|
349
|
+
if (!this.parser?.connected) {
|
|
493
350
|
return {
|
|
494
|
-
isError: true,
|
|
495
351
|
content: [
|
|
496
352
|
{
|
|
497
353
|
type: "text",
|
|
498
|
-
text:
|
|
354
|
+
text: "Error: Not connected. Ignoring message."
|
|
499
355
|
}
|
|
500
|
-
]
|
|
356
|
+
],
|
|
357
|
+
isError: true
|
|
501
358
|
};
|
|
502
359
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
|
|
509
|
-
return {
|
|
510
|
-
prompts: [
|
|
511
|
-
{
|
|
512
|
-
name: "parse",
|
|
513
|
-
description: "Parses a FIX message and describes it in plain language",
|
|
514
|
-
arguments: [
|
|
515
|
-
{
|
|
516
|
-
name: "fixString",
|
|
517
|
-
description: "FIX message string to parse",
|
|
518
|
-
required: true
|
|
519
|
-
}
|
|
520
|
-
]
|
|
521
|
-
},
|
|
522
|
-
{
|
|
523
|
-
name: "parseToJSON",
|
|
524
|
-
description: "Parses a FIX message into JSON",
|
|
525
|
-
arguments: [
|
|
526
|
-
{
|
|
527
|
-
name: "fixString",
|
|
528
|
-
description: "FIX message string to parse",
|
|
529
|
-
required: true
|
|
530
|
-
}
|
|
531
|
-
]
|
|
532
|
-
},
|
|
533
|
-
{
|
|
534
|
-
name: "newOrderSingle",
|
|
535
|
-
description: "Creates and sends a New Order Single",
|
|
536
|
-
arguments: [
|
|
537
|
-
{
|
|
538
|
-
name: "clOrdID",
|
|
539
|
-
description: "Client Order ID",
|
|
540
|
-
required: true
|
|
541
|
-
},
|
|
542
|
-
{
|
|
543
|
-
name: "handlInst",
|
|
544
|
-
description: "Handling instruction",
|
|
545
|
-
required: false
|
|
546
|
-
},
|
|
547
|
-
{
|
|
548
|
-
name: "quantity",
|
|
549
|
-
description: "Order quantity",
|
|
550
|
-
required: true
|
|
551
|
-
},
|
|
552
|
-
{
|
|
553
|
-
name: "price",
|
|
554
|
-
description: "Order price",
|
|
555
|
-
required: true
|
|
556
|
-
},
|
|
557
|
-
{
|
|
558
|
-
name: "ordType",
|
|
559
|
-
description: "Order type",
|
|
560
|
-
required: false
|
|
561
|
-
},
|
|
562
|
-
{
|
|
563
|
-
name: "side",
|
|
564
|
-
description: "Order side (1=Buy, 2=Sell)",
|
|
565
|
-
required: true
|
|
566
|
-
},
|
|
567
|
-
{
|
|
568
|
-
name: "symbol",
|
|
569
|
-
description: "Trading symbol",
|
|
570
|
-
required: true
|
|
571
|
-
},
|
|
572
|
-
{
|
|
573
|
-
name: "timeInForce",
|
|
574
|
-
description: "Time in force",
|
|
575
|
-
required: false
|
|
576
|
-
}
|
|
577
|
-
]
|
|
578
|
-
},
|
|
579
|
-
{
|
|
580
|
-
name: "marketDataRequest",
|
|
581
|
-
description: "Sends a request for Market Data with the given symbol",
|
|
582
|
-
arguments: [
|
|
583
|
-
{
|
|
584
|
-
name: "mdUpdateType",
|
|
585
|
-
description: "Market data update type",
|
|
586
|
-
required: false
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
name: "symbol",
|
|
590
|
-
description: "Trading symbol",
|
|
591
|
-
required: true
|
|
592
|
-
},
|
|
593
|
-
{
|
|
594
|
-
name: "mdReqID",
|
|
595
|
-
description: "Market data request ID",
|
|
596
|
-
required: true
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
name: "subscriptionRequestType",
|
|
600
|
-
description: "Subscription request type",
|
|
601
|
-
required: false
|
|
602
|
-
},
|
|
360
|
+
this.parser?.send(order);
|
|
361
|
+
const fixData = await response;
|
|
362
|
+
this.verifiedOrders.delete(args.clOrdID);
|
|
363
|
+
return {
|
|
364
|
+
content: [
|
|
603
365
|
{
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
required: false
|
|
366
|
+
type: "text",
|
|
367
|
+
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())}`
|
|
607
368
|
}
|
|
608
369
|
]
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
};
|
|
612
|
-
});
|
|
613
|
-
this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
|
|
614
|
-
const { name, arguments: args } = request.params;
|
|
615
|
-
switch (name) {
|
|
616
|
-
case "parse": {
|
|
617
|
-
const fixString = args?.fixString || "";
|
|
370
|
+
};
|
|
371
|
+
} catch (error) {
|
|
618
372
|
return {
|
|
619
|
-
|
|
373
|
+
content: [
|
|
620
374
|
{
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
type: "text",
|
|
624
|
-
text: `Please parse and explain this FIX message: ${fixString}`
|
|
625
|
-
}
|
|
375
|
+
type: "text",
|
|
376
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`
|
|
626
377
|
}
|
|
627
|
-
]
|
|
378
|
+
],
|
|
379
|
+
isError: true
|
|
628
380
|
};
|
|
629
381
|
}
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
382
|
+
}
|
|
383
|
+
);
|
|
384
|
+
this.server.setRequestHandler(
|
|
385
|
+
import_zod.z.object({ method: import_zod.z.literal("marketDataRequest") }),
|
|
386
|
+
async (request, extra) => {
|
|
387
|
+
try {
|
|
388
|
+
const args = request.params;
|
|
389
|
+
const response = new Promise((resolve) => {
|
|
390
|
+
this.pendingRequests.set(args.mdReqID, resolve);
|
|
391
|
+
});
|
|
392
|
+
const messageFields = [
|
|
393
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
|
|
394
|
+
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
395
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
396
|
+
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
397
|
+
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
398
|
+
new import_fixparser.Field(import_fixparser.Fields.MDReqID, args.mdReqID),
|
|
399
|
+
new import_fixparser.Field(import_fixparser.Fields.SubscriptionRequestType, args.subscriptionRequestType),
|
|
400
|
+
new import_fixparser.Field(import_fixparser.Fields.MarketDepth, 0),
|
|
401
|
+
new import_fixparser.Field(import_fixparser.Fields.MDUpdateType, args.mdUpdateType)
|
|
402
|
+
];
|
|
403
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoRelatedSym, args.symbols.length));
|
|
404
|
+
args.symbols.forEach((symbol) => {
|
|
405
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol));
|
|
406
|
+
});
|
|
407
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoMDEntryTypes, args.mdEntryTypes.length));
|
|
408
|
+
args.mdEntryTypes.forEach((entryType) => {
|
|
409
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.MDEntryType, entryType));
|
|
410
|
+
});
|
|
411
|
+
const mdr = this.parser?.createMessage(...messageFields);
|
|
412
|
+
if (!this.parser?.connected) {
|
|
413
|
+
return {
|
|
414
|
+
content: [
|
|
415
|
+
{
|
|
637
416
|
type: "text",
|
|
638
|
-
text:
|
|
417
|
+
text: "Error: Not connected. Ignoring message."
|
|
639
418
|
}
|
|
419
|
+
],
|
|
420
|
+
isError: true
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
this.parser?.send(mdr);
|
|
424
|
+
const fixData = await response;
|
|
425
|
+
return {
|
|
426
|
+
content: [
|
|
427
|
+
{
|
|
428
|
+
type: "text",
|
|
429
|
+
text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`
|
|
640
430
|
}
|
|
641
431
|
]
|
|
642
432
|
};
|
|
643
|
-
}
|
|
644
|
-
case "newOrderSingle": {
|
|
645
|
-
const { clOrdID, handlInst, quantity, price, ordType, side, symbol, timeInForce } = args || {};
|
|
433
|
+
} catch (error) {
|
|
646
434
|
return {
|
|
647
|
-
|
|
435
|
+
content: [
|
|
648
436
|
{
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
type: "text",
|
|
652
|
-
text: [
|
|
653
|
-
"Create a New Order Single FIX message with the following parameters:",
|
|
654
|
-
`- ClOrdID: ${clOrdID}`,
|
|
655
|
-
`- HandlInst: ${handlInst ?? "default"}`,
|
|
656
|
-
`- Quantity: ${quantity}`,
|
|
657
|
-
`- Price: ${price}`,
|
|
658
|
-
`- OrdType: ${ordType ?? "default (Market)"}`,
|
|
659
|
-
`- Side: ${side}`,
|
|
660
|
-
`- Symbol: ${symbol}`,
|
|
661
|
-
`- TimeInForce: ${timeInForce ?? "default (Day)"}`,
|
|
662
|
-
"",
|
|
663
|
-
"Format the response as a JSON object with FIX tag numbers as keys and their corresponding values."
|
|
664
|
-
].join("\n")
|
|
665
|
-
}
|
|
437
|
+
type: "text",
|
|
438
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`
|
|
666
439
|
}
|
|
667
|
-
]
|
|
440
|
+
],
|
|
441
|
+
isError: true
|
|
668
442
|
};
|
|
669
443
|
}
|
|
670
|
-
|
|
671
|
-
|
|
444
|
+
}
|
|
445
|
+
);
|
|
446
|
+
this.server.setRequestHandler(
|
|
447
|
+
import_zod.z.object({ method: import_zod.z.literal("greeting-resource") }),
|
|
448
|
+
async (request, extra) => {
|
|
449
|
+
this.parser?.logger.log({
|
|
450
|
+
level: "info",
|
|
451
|
+
message: "MCP Server Resource called: greeting-resource"
|
|
452
|
+
});
|
|
453
|
+
return {
|
|
454
|
+
content: [
|
|
455
|
+
{
|
|
456
|
+
type: "text",
|
|
457
|
+
text: "Hello, world!"
|
|
458
|
+
}
|
|
459
|
+
]
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
);
|
|
463
|
+
this.server.setRequestHandler(
|
|
464
|
+
import_zod.z.object({ method: import_zod.z.literal("stockGraph") }),
|
|
465
|
+
async (request, extra) => {
|
|
466
|
+
this.parser?.logger.log({
|
|
467
|
+
level: "info",
|
|
468
|
+
message: "MCP Server Resource called: stockGraph"
|
|
469
|
+
});
|
|
470
|
+
const args = request.params;
|
|
471
|
+
const symbol = args.symbol;
|
|
472
|
+
const priceHistory = this.marketDataPrices.get(symbol) || [];
|
|
473
|
+
if (priceHistory.length === 0) {
|
|
672
474
|
return {
|
|
673
|
-
|
|
475
|
+
content: [
|
|
674
476
|
{
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
type: "text",
|
|
678
|
-
text: [
|
|
679
|
-
"Create a Market Data Request FIX message with the following parameters:",
|
|
680
|
-
`- MDUpdateType: ${mdUpdateType ?? "default (0 = FullRefresh)"}`,
|
|
681
|
-
`- Symbol: ${symbol}`,
|
|
682
|
-
`- MDReqID: ${mdReqID}`,
|
|
683
|
-
`- SubscriptionRequestType: ${subscriptionRequestType ?? "default (0 = Snapshot + Updates)"}`,
|
|
684
|
-
`- MDEntryType: ${mdEntryType ?? "default (0 = Bid)"}`,
|
|
685
|
-
"",
|
|
686
|
-
"Format the response as a JSON object with FIX tag numbers as keys and their corresponding values."
|
|
687
|
-
].join("\n")
|
|
688
|
-
}
|
|
477
|
+
type: "text",
|
|
478
|
+
text: `No price data available for ${symbol}`
|
|
689
479
|
}
|
|
690
480
|
]
|
|
691
481
|
};
|
|
692
482
|
}
|
|
693
|
-
|
|
694
|
-
|
|
483
|
+
const width = 600;
|
|
484
|
+
const height = 300;
|
|
485
|
+
const padding = 40;
|
|
486
|
+
const xScale = (width - 2 * padding) / (priceHistory.length - 1);
|
|
487
|
+
const yMin = Math.min(...priceHistory.map((d) => d.price));
|
|
488
|
+
const yMax = Math.max(...priceHistory.map((d) => d.price));
|
|
489
|
+
const yScale = (height - 2 * padding) / (yMax - yMin);
|
|
490
|
+
const points = priceHistory.map((d, i) => {
|
|
491
|
+
const x = padding + i * xScale;
|
|
492
|
+
const y = height - padding - (d.price - yMin) * yScale;
|
|
493
|
+
return `${x},${y}`;
|
|
494
|
+
}).join(" L ");
|
|
495
|
+
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
|
496
|
+
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
|
497
|
+
<!-- Background -->
|
|
498
|
+
<rect width="100%" height="100%" fill="#f8f9fa"/>
|
|
499
|
+
|
|
500
|
+
<!-- Grid lines -->
|
|
501
|
+
<g stroke="#e9ecef" stroke-width="1">
|
|
502
|
+
${Array.from({ length: 5 }, (_, i) => {
|
|
503
|
+
const y = padding + (height - 2 * padding) * i / 4;
|
|
504
|
+
return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
|
|
505
|
+
}).join("\n")}
|
|
506
|
+
</g>
|
|
507
|
+
|
|
508
|
+
<!-- Price line -->
|
|
509
|
+
<path d="M ${points}"
|
|
510
|
+
fill="none"
|
|
511
|
+
stroke="#007bff"
|
|
512
|
+
stroke-width="2"/>
|
|
513
|
+
|
|
514
|
+
<!-- Data points -->
|
|
515
|
+
${priceHistory.map((d, i) => {
|
|
516
|
+
const x = padding + i * xScale;
|
|
517
|
+
const y = height - padding - (d.price - yMin) * yScale;
|
|
518
|
+
return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
|
|
519
|
+
}).join("\n")}
|
|
520
|
+
|
|
521
|
+
<!-- Labels -->
|
|
522
|
+
<g font-family="Arial" font-size="12" fill="#495057">
|
|
523
|
+
${Array.from({ length: 5 }, (_, i) => {
|
|
524
|
+
const x = padding + (width - 2 * padding) * i / 4;
|
|
525
|
+
const index = Math.floor((priceHistory.length - 1) * i / 4);
|
|
526
|
+
const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
|
|
527
|
+
return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
|
|
528
|
+
}).join("\n")}
|
|
529
|
+
${Array.from({ length: 5 }, (_, i) => {
|
|
530
|
+
const y = padding + (height - 2 * padding) * i / 4;
|
|
531
|
+
const price = yMax - (yMax - yMin) * i / 4;
|
|
532
|
+
return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
|
|
533
|
+
}).join("\n")}
|
|
534
|
+
</g>
|
|
535
|
+
|
|
536
|
+
<!-- Title -->
|
|
537
|
+
<text x="${width / 2}" y="${padding / 2}"
|
|
538
|
+
font-family="Arial" font-size="16" font-weight="bold"
|
|
539
|
+
text-anchor="middle" fill="#212529">
|
|
540
|
+
${symbol} - Price Chart (${priceHistory.length} points)
|
|
541
|
+
</text>
|
|
542
|
+
</svg>`;
|
|
543
|
+
return {
|
|
544
|
+
content: [
|
|
545
|
+
{
|
|
546
|
+
type: "text",
|
|
547
|
+
text: svg
|
|
548
|
+
}
|
|
549
|
+
]
|
|
550
|
+
};
|
|
695
551
|
}
|
|
696
|
-
|
|
552
|
+
);
|
|
553
|
+
this.server.setRequestHandler(
|
|
554
|
+
import_zod.z.object({ method: import_zod.z.literal("stockPriceHistory") }),
|
|
555
|
+
async (request, extra) => {
|
|
556
|
+
this.parser?.logger.log({
|
|
557
|
+
level: "info",
|
|
558
|
+
message: "MCP Server Resource called: stockPriceHistory"
|
|
559
|
+
});
|
|
560
|
+
const args = request.params;
|
|
561
|
+
const symbol = args.symbol;
|
|
562
|
+
const priceHistory = this.marketDataPrices.get(symbol) || [];
|
|
563
|
+
return {
|
|
564
|
+
content: [
|
|
565
|
+
{
|
|
566
|
+
type: "text",
|
|
567
|
+
text: JSON.stringify(
|
|
568
|
+
{
|
|
569
|
+
symbol,
|
|
570
|
+
count: priceHistory.length,
|
|
571
|
+
prices: priceHistory.map((point) => ({
|
|
572
|
+
timestamp: new Date(point.timestamp).toISOString(),
|
|
573
|
+
price: point.price
|
|
574
|
+
}))
|
|
575
|
+
},
|
|
576
|
+
null,
|
|
577
|
+
2
|
|
578
|
+
)
|
|
579
|
+
}
|
|
580
|
+
]
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
);
|
|
697
584
|
process.on("SIGINT", async () => {
|
|
698
585
|
await this.server.close();
|
|
699
586
|
process.exit(0);
|