fixparser-plugin-mcp 9.1.7-b2bba224 → 9.1.7-b2f9f891
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 +828 -566
- package/build/cjs/MCPLocal.js.map +2 -2
- package/build/esm/MCPLocal.mjs +829 -582
- package/build/esm/MCPLocal.mjs.map +2 -2
- package/build-examples/cjs/example_mcp_local.js +49 -4
- package/build-examples/cjs/example_mcp_local.js.map +4 -4
- package/build-examples/esm/example_mcp_local.mjs +49 -4
- package/build-examples/esm/example_mcp_local.mjs.map +4 -4
- package/package.json +4 -5
- package/types/MCPLocal.d.ts +3 -0
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "1" for Manual, "2" for Automated, "3" for AutomatedNoIntervention)'
|
|
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "1" for Market, "2" for Limit, "3" for Stop)'
|
|
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "1" for Buy, "2" for Sell, "3" for BuyMinus, "4" for SellPlus, "5" for SellShort, "6" for SellShortExempt, "7" for Undisclosed, "8" for Cross, "9" for CrossShort, "A" for CrossShortExempt, "B" for AsDefined, "C" for Opposite, "D" for Subscribe, "E" for Redeem, "F" for Lend, "G" for Borrow, "H" for SellUndisclosed)'
|
|
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "0" for Day, "1" for Good Till Cancel, "2" for At Opening, "3" for Immediate or Cancel, "4" for Fill or Kill, "5" for Good Till Crossing, "6" for Good Till Date)'
|
|
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "0" for FullRefresh, "1" for IncrementalRefresh)'
|
|
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "0" for Snapshot + Updates, "1" for Snapshot, "2" for Unsubscribe)'
|
|
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 (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use "0" for Bid, "1" for Offer, "2" for Trade, "3" for Index Value, "4" for Opening Price)'
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
required: ["symbol", "mdReqID"]
|
|
198
|
-
};
|
|
29
|
+
var import_zod = require("zod");
|
|
199
30
|
var MCPLocal = class {
|
|
200
|
-
// private logger: Logger | undefined;
|
|
201
31
|
parser;
|
|
202
32
|
server = new import_server.Server(
|
|
203
33
|
{
|
|
@@ -206,43 +36,241 @@ var MCPLocal = class {
|
|
|
206
36
|
},
|
|
207
37
|
{
|
|
208
38
|
capabilities: {
|
|
209
|
-
tools: {
|
|
210
|
-
|
|
211
|
-
|
|
39
|
+
tools: {
|
|
40
|
+
parse: {
|
|
41
|
+
description: "Parses a FIX message and describes it in plain language",
|
|
42
|
+
parameters: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
fixString: { type: "string" }
|
|
46
|
+
},
|
|
47
|
+
required: ["fixString"]
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
parseToJSON: {
|
|
51
|
+
description: "Parses a FIX message into JSON",
|
|
52
|
+
parameters: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
fixString: { type: "string" }
|
|
56
|
+
},
|
|
57
|
+
required: ["fixString"]
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
verifyOrder: {
|
|
61
|
+
description: "Verifies order parameters before execution",
|
|
62
|
+
parameters: {
|
|
63
|
+
type: "object",
|
|
64
|
+
properties: {
|
|
65
|
+
clOrdID: { type: "string" },
|
|
66
|
+
handlInst: { type: "string", enum: ["1", "2", "3"] },
|
|
67
|
+
quantity: { type: "string" },
|
|
68
|
+
price: { type: "string" },
|
|
69
|
+
ordType: {
|
|
70
|
+
type: "string",
|
|
71
|
+
enum: [
|
|
72
|
+
"1",
|
|
73
|
+
"2",
|
|
74
|
+
"3",
|
|
75
|
+
"4",
|
|
76
|
+
"5",
|
|
77
|
+
"6",
|
|
78
|
+
"7",
|
|
79
|
+
"8",
|
|
80
|
+
"9",
|
|
81
|
+
"A",
|
|
82
|
+
"B",
|
|
83
|
+
"C",
|
|
84
|
+
"D",
|
|
85
|
+
"E",
|
|
86
|
+
"F",
|
|
87
|
+
"G",
|
|
88
|
+
"H",
|
|
89
|
+
"I",
|
|
90
|
+
"J",
|
|
91
|
+
"K",
|
|
92
|
+
"L",
|
|
93
|
+
"M",
|
|
94
|
+
"P",
|
|
95
|
+
"Q",
|
|
96
|
+
"R",
|
|
97
|
+
"S"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
side: {
|
|
101
|
+
type: "string",
|
|
102
|
+
enum: [
|
|
103
|
+
"1",
|
|
104
|
+
"2",
|
|
105
|
+
"3",
|
|
106
|
+
"4",
|
|
107
|
+
"5",
|
|
108
|
+
"6",
|
|
109
|
+
"7",
|
|
110
|
+
"8",
|
|
111
|
+
"9",
|
|
112
|
+
"A",
|
|
113
|
+
"B",
|
|
114
|
+
"C",
|
|
115
|
+
"D",
|
|
116
|
+
"E",
|
|
117
|
+
"F",
|
|
118
|
+
"G",
|
|
119
|
+
"H"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
symbol: { type: "string" },
|
|
123
|
+
timeInForce: {
|
|
124
|
+
type: "string",
|
|
125
|
+
enum: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"]
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
required: [
|
|
129
|
+
"clOrdID",
|
|
130
|
+
"handlInst",
|
|
131
|
+
"quantity",
|
|
132
|
+
"price",
|
|
133
|
+
"ordType",
|
|
134
|
+
"side",
|
|
135
|
+
"symbol",
|
|
136
|
+
"timeInForce"
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
executeOrder: {
|
|
141
|
+
description: "Executes a verified order",
|
|
142
|
+
parameters: {
|
|
143
|
+
type: "object",
|
|
144
|
+
properties: {
|
|
145
|
+
clOrdID: { type: "string" },
|
|
146
|
+
handlInst: { type: "string", enum: ["1", "2", "3"] },
|
|
147
|
+
quantity: { type: "string" },
|
|
148
|
+
price: { type: "string" },
|
|
149
|
+
ordType: { type: "string" },
|
|
150
|
+
side: { type: "string" },
|
|
151
|
+
symbol: { type: "string" },
|
|
152
|
+
timeInForce: { type: "string" }
|
|
153
|
+
},
|
|
154
|
+
required: [
|
|
155
|
+
"clOrdID",
|
|
156
|
+
"handlInst",
|
|
157
|
+
"quantity",
|
|
158
|
+
"price",
|
|
159
|
+
"ordType",
|
|
160
|
+
"side",
|
|
161
|
+
"symbol",
|
|
162
|
+
"timeInForce"
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
marketDataRequest: {
|
|
167
|
+
description: "Requests market data for specified symbols",
|
|
168
|
+
parameters: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
mdUpdateType: { type: "string", enum: ["0", "1"] },
|
|
172
|
+
symbols: { type: "array", items: { type: "string" } },
|
|
173
|
+
mdReqID: { type: "string" },
|
|
174
|
+
subscriptionRequestType: { type: "string", enum: ["0", "1", "2"] },
|
|
175
|
+
mdEntryTypes: { type: "array", items: { type: "string" } }
|
|
176
|
+
},
|
|
177
|
+
required: ["mdUpdateType", "symbols", "mdReqID", "subscriptionRequestType", "mdEntryTypes"]
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
resources: {
|
|
182
|
+
greeting: {
|
|
183
|
+
description: "A simple greeting resource",
|
|
184
|
+
uri: "greeting-resource"
|
|
185
|
+
},
|
|
186
|
+
stockGraph: {
|
|
187
|
+
description: "Generates a price chart for a given symbol",
|
|
188
|
+
uri: "stockGraph",
|
|
189
|
+
parameters: {
|
|
190
|
+
type: "object",
|
|
191
|
+
properties: {
|
|
192
|
+
symbol: { type: "string" }
|
|
193
|
+
},
|
|
194
|
+
required: ["symbol"]
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
stockPriceHistory: {
|
|
198
|
+
description: "Returns price history for a given symbol",
|
|
199
|
+
uri: "stockPriceHistory",
|
|
200
|
+
parameters: {
|
|
201
|
+
type: "object",
|
|
202
|
+
properties: {
|
|
203
|
+
symbol: { type: "string" }
|
|
204
|
+
},
|
|
205
|
+
required: ["symbol"]
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
212
209
|
}
|
|
213
210
|
}
|
|
214
211
|
);
|
|
215
212
|
transport = new import_stdio.StdioServerTransport();
|
|
216
213
|
onReady = void 0;
|
|
217
214
|
pendingRequests = /* @__PURE__ */ new Map();
|
|
215
|
+
verifiedOrders = /* @__PURE__ */ new Map();
|
|
216
|
+
// Store market data prices with timestamps
|
|
217
|
+
marketDataPrices = /* @__PURE__ */ new Map();
|
|
218
|
+
MAX_PRICE_HISTORY = 1e5;
|
|
219
|
+
// Maximum number of price points to store per symbol
|
|
218
220
|
constructor({ logger, onReady }) {
|
|
219
221
|
if (onReady) this.onReady = onReady;
|
|
220
222
|
}
|
|
221
223
|
async register(parser) {
|
|
222
224
|
this.parser = parser;
|
|
223
225
|
this.parser.addOnMessageCallback((message) => {
|
|
226
|
+
this.parser?.logger.log({
|
|
227
|
+
level: "info",
|
|
228
|
+
message: `MCP Server received message: ${message.messageType}: ${message.description}`
|
|
229
|
+
});
|
|
224
230
|
const msgType = message.messageType;
|
|
225
|
-
if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh || msgType === import_fixparser.Messages.ExecutionReport || msgType === import_fixparser.Messages.Reject) {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
231
|
+
if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh || msgType === import_fixparser.Messages.ExecutionReport || msgType === import_fixparser.Messages.Reject || msgType === import_fixparser.Messages.MarketDataIncrementalRefresh) {
|
|
232
|
+
this.parser?.logger.log({
|
|
233
|
+
level: "info",
|
|
234
|
+
message: `MCP Server handling message type: ${msgType}`
|
|
235
|
+
});
|
|
236
|
+
let id;
|
|
237
|
+
if (msgType === import_fixparser.Messages.MarketDataIncrementalRefresh || msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh) {
|
|
238
|
+
const symbol = message.getField(import_fixparser.Fields.Symbol);
|
|
239
|
+
const price = message.getField(import_fixparser.Fields.MDEntryPx);
|
|
240
|
+
const timestamp = message.getField(import_fixparser.Fields.MDEntryTime)?.value || Date.now();
|
|
241
|
+
if (symbol?.value && price?.value) {
|
|
242
|
+
const symbolStr = String(symbol.value);
|
|
243
|
+
const priceNum = Number(price.value);
|
|
244
|
+
const priceHistory = this.marketDataPrices.get(symbolStr) || [];
|
|
245
|
+
priceHistory.push({
|
|
246
|
+
timestamp: Number(timestamp),
|
|
247
|
+
price: priceNum
|
|
248
|
+
});
|
|
249
|
+
if (priceHistory.length > this.MAX_PRICE_HISTORY) {
|
|
250
|
+
priceHistory.shift();
|
|
245
251
|
}
|
|
252
|
+
this.marketDataPrices.set(symbolStr, priceHistory);
|
|
253
|
+
this.parser?.logger.log({
|
|
254
|
+
level: "info",
|
|
255
|
+
message: `MCP Server added ${symbol}: ${priceNum}`
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
if (msgType === import_fixparser.Messages.MarketDataSnapshotFullRefresh) {
|
|
260
|
+
const mdReqID = message.getField(import_fixparser.Fields.MDReqID);
|
|
261
|
+
if (mdReqID) id = String(mdReqID.value);
|
|
262
|
+
} else if (msgType === import_fixparser.Messages.ExecutionReport) {
|
|
263
|
+
const clOrdID = message.getField(import_fixparser.Fields.ClOrdID);
|
|
264
|
+
if (clOrdID) id = String(clOrdID.value);
|
|
265
|
+
} else if (msgType === import_fixparser.Messages.Reject) {
|
|
266
|
+
const refSeqNum = message.getField(import_fixparser.Fields.RefSeqNum);
|
|
267
|
+
if (refSeqNum) id = String(refSeqNum.value);
|
|
268
|
+
}
|
|
269
|
+
if (id) {
|
|
270
|
+
const callback = this.pendingRequests.get(id);
|
|
271
|
+
if (callback) {
|
|
272
|
+
callback(message);
|
|
273
|
+
this.pendingRequests.delete(id);
|
|
246
274
|
}
|
|
247
275
|
}
|
|
248
276
|
}
|
|
@@ -260,431 +288,665 @@ var MCPLocal = class {
|
|
|
260
288
|
if (!this.server) {
|
|
261
289
|
return;
|
|
262
290
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
291
|
+
this.server.setRequestHandler(
|
|
292
|
+
import_zod.z.object({ method: import_zod.z.literal("resources/list") }),
|
|
293
|
+
async (request, extra) => {
|
|
294
|
+
return {
|
|
295
|
+
resources: [
|
|
296
|
+
{
|
|
297
|
+
name: "greeting",
|
|
298
|
+
description: "A simple greeting resource",
|
|
299
|
+
uri: "greeting-resource"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
name: "stockGraph",
|
|
303
|
+
description: "Generates a price chart for a given symbol",
|
|
304
|
+
uri: "stockGraph",
|
|
305
|
+
parameters: {
|
|
306
|
+
type: "object",
|
|
307
|
+
properties: {
|
|
308
|
+
symbol: { type: "string" }
|
|
309
|
+
},
|
|
310
|
+
required: ["symbol"]
|
|
311
|
+
}
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
name: "stockPriceHistory",
|
|
315
|
+
description: "Returns price history for a given symbol",
|
|
316
|
+
uri: "stockPriceHistory",
|
|
317
|
+
parameters: {
|
|
318
|
+
type: "object",
|
|
319
|
+
properties: {
|
|
320
|
+
symbol: { type: "string" }
|
|
321
|
+
},
|
|
322
|
+
required: ["symbol"]
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
};
|
|
276
327
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
328
|
+
);
|
|
329
|
+
this.server.setRequestHandler(
|
|
330
|
+
import_zod.z.object({ method: import_zod.z.literal("tools/list") }),
|
|
331
|
+
async (request, extra) => {
|
|
332
|
+
return {
|
|
333
|
+
tools: [
|
|
334
|
+
{
|
|
335
|
+
name: "parse",
|
|
336
|
+
description: "Parses a FIX message and describes it in plain language",
|
|
337
|
+
parameters: {
|
|
338
|
+
type: "object",
|
|
339
|
+
properties: {
|
|
340
|
+
fixString: { type: "string" }
|
|
341
|
+
},
|
|
342
|
+
required: ["fixString"]
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
name: "parseToJSON",
|
|
347
|
+
description: "Parses a FIX message into JSON",
|
|
348
|
+
parameters: {
|
|
349
|
+
type: "object",
|
|
350
|
+
properties: {
|
|
351
|
+
fixString: { type: "string" }
|
|
352
|
+
},
|
|
353
|
+
required: ["fixString"]
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: "verifyOrder",
|
|
358
|
+
description: "Verifies order parameters before execution",
|
|
359
|
+
parameters: {
|
|
360
|
+
type: "object",
|
|
361
|
+
properties: {
|
|
362
|
+
clOrdID: { type: "string" },
|
|
363
|
+
handlInst: { type: "string", enum: ["1", "2", "3"] },
|
|
364
|
+
quantity: { type: "string" },
|
|
365
|
+
price: { type: "string" },
|
|
366
|
+
ordType: {
|
|
367
|
+
type: "string",
|
|
368
|
+
enum: [
|
|
369
|
+
"1",
|
|
370
|
+
"2",
|
|
371
|
+
"3",
|
|
372
|
+
"4",
|
|
373
|
+
"5",
|
|
374
|
+
"6",
|
|
375
|
+
"7",
|
|
376
|
+
"8",
|
|
377
|
+
"9",
|
|
378
|
+
"A",
|
|
379
|
+
"B",
|
|
380
|
+
"C",
|
|
381
|
+
"D",
|
|
382
|
+
"E",
|
|
383
|
+
"F",
|
|
384
|
+
"G",
|
|
385
|
+
"H",
|
|
386
|
+
"I",
|
|
387
|
+
"J",
|
|
388
|
+
"K",
|
|
389
|
+
"L",
|
|
390
|
+
"M",
|
|
391
|
+
"P",
|
|
392
|
+
"Q",
|
|
393
|
+
"R",
|
|
394
|
+
"S"
|
|
395
|
+
]
|
|
396
|
+
},
|
|
397
|
+
side: {
|
|
398
|
+
type: "string",
|
|
399
|
+
enum: [
|
|
400
|
+
"1",
|
|
401
|
+
"2",
|
|
402
|
+
"3",
|
|
403
|
+
"4",
|
|
404
|
+
"5",
|
|
405
|
+
"6",
|
|
406
|
+
"7",
|
|
407
|
+
"8",
|
|
408
|
+
"9",
|
|
409
|
+
"A",
|
|
410
|
+
"B",
|
|
411
|
+
"C",
|
|
412
|
+
"D",
|
|
413
|
+
"E",
|
|
414
|
+
"F",
|
|
415
|
+
"G",
|
|
416
|
+
"H"
|
|
417
|
+
]
|
|
418
|
+
},
|
|
419
|
+
symbol: { type: "string" },
|
|
420
|
+
timeInForce: {
|
|
421
|
+
type: "string",
|
|
422
|
+
enum: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C"]
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
required: [
|
|
426
|
+
"clOrdID",
|
|
427
|
+
"handlInst",
|
|
428
|
+
"quantity",
|
|
429
|
+
"price",
|
|
430
|
+
"ordType",
|
|
431
|
+
"side",
|
|
432
|
+
"symbol",
|
|
433
|
+
"timeInForce"
|
|
434
|
+
]
|
|
435
|
+
}
|
|
436
|
+
},
|
|
437
|
+
{
|
|
438
|
+
name: "executeOrder",
|
|
439
|
+
description: "Executes a verified order",
|
|
440
|
+
parameters: {
|
|
441
|
+
type: "object",
|
|
442
|
+
properties: {
|
|
443
|
+
clOrdID: { type: "string" },
|
|
444
|
+
handlInst: { type: "string", enum: ["1", "2", "3"] },
|
|
445
|
+
quantity: { type: "string" },
|
|
446
|
+
price: { type: "string" },
|
|
447
|
+
ordType: { type: "string" },
|
|
448
|
+
side: { type: "string" },
|
|
449
|
+
symbol: { type: "string" },
|
|
450
|
+
timeInForce: { type: "string" }
|
|
451
|
+
},
|
|
452
|
+
required: [
|
|
453
|
+
"clOrdID",
|
|
454
|
+
"handlInst",
|
|
455
|
+
"quantity",
|
|
456
|
+
"price",
|
|
457
|
+
"ordType",
|
|
458
|
+
"side",
|
|
459
|
+
"symbol",
|
|
460
|
+
"timeInForce"
|
|
461
|
+
]
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
name: "marketDataRequest",
|
|
466
|
+
description: "Requests market data for specified symbols",
|
|
467
|
+
parameters: {
|
|
468
|
+
type: "object",
|
|
469
|
+
properties: {
|
|
470
|
+
mdUpdateType: { type: "string", enum: ["0", "1"] },
|
|
471
|
+
symbols: { type: "array", items: { type: "string" } },
|
|
472
|
+
mdReqID: { type: "string" },
|
|
473
|
+
subscriptionRequestType: { type: "string", enum: ["0", "1", "2"] },
|
|
474
|
+
mdEntryTypes: { type: "array", items: { type: "string" } }
|
|
475
|
+
},
|
|
476
|
+
required: [
|
|
477
|
+
"mdUpdateType",
|
|
478
|
+
"symbols",
|
|
479
|
+
"mdReqID",
|
|
480
|
+
"subscriptionRequestType",
|
|
481
|
+
"mdEntryTypes"
|
|
482
|
+
]
|
|
483
|
+
}
|
|
322
484
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
{
|
|
337
|
-
type: "text",
|
|
338
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
339
|
-
}
|
|
340
|
-
]
|
|
341
|
-
};
|
|
342
|
-
}
|
|
485
|
+
]
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
);
|
|
489
|
+
this.server.setRequestHandler(import_zod.z.object({ method: import_zod.z.literal("parse") }), async (request, extra) => {
|
|
490
|
+
try {
|
|
491
|
+
const args = request.params;
|
|
492
|
+
const parsedMessage = this.parser?.parse(args.fixString);
|
|
493
|
+
if (!parsedMessage || parsedMessage.length === 0) {
|
|
494
|
+
return {
|
|
495
|
+
content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
|
|
496
|
+
isError: true
|
|
497
|
+
};
|
|
343
498
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
isError: true,
|
|
351
|
-
content: [{ type: "text", text: "Error: Failed to parse FIX string" }]
|
|
352
|
-
};
|
|
499
|
+
return {
|
|
500
|
+
content: [
|
|
501
|
+
{
|
|
502
|
+
type: "text",
|
|
503
|
+
text: `${parsedMessage[0].description}
|
|
504
|
+
${parsedMessage[0].messageTypeDescription}`
|
|
353
505
|
}
|
|
506
|
+
]
|
|
507
|
+
};
|
|
508
|
+
} catch (error) {
|
|
509
|
+
return {
|
|
510
|
+
content: [
|
|
511
|
+
{
|
|
512
|
+
type: "text",
|
|
513
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
514
|
+
}
|
|
515
|
+
],
|
|
516
|
+
isError: true
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
this.server.setRequestHandler(
|
|
521
|
+
import_zod.z.object({ method: import_zod.z.literal("parseToJSON") }),
|
|
522
|
+
async (request, extra) => {
|
|
523
|
+
try {
|
|
524
|
+
const args = request.params;
|
|
525
|
+
const parsedMessage = this.parser?.parse(args.fixString);
|
|
526
|
+
if (!parsedMessage || parsedMessage.length === 0) {
|
|
354
527
|
return {
|
|
355
|
-
content: [
|
|
356
|
-
|
|
357
|
-
type: "text",
|
|
358
|
-
text: `${parsedMessage[0].toFIXJSON()}`
|
|
359
|
-
}
|
|
360
|
-
]
|
|
361
|
-
};
|
|
362
|
-
} catch (error) {
|
|
363
|
-
return {
|
|
364
|
-
isError: true,
|
|
365
|
-
content: [
|
|
366
|
-
{
|
|
367
|
-
type: "text",
|
|
368
|
-
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
369
|
-
}
|
|
370
|
-
]
|
|
528
|
+
content: [{ type: "text", text: "Error: Failed to parse FIX string" }],
|
|
529
|
+
isError: true
|
|
371
530
|
};
|
|
372
531
|
}
|
|
532
|
+
return {
|
|
533
|
+
content: [
|
|
534
|
+
{
|
|
535
|
+
type: "text",
|
|
536
|
+
text: `${parsedMessage[0].toFIXJSON()}`
|
|
537
|
+
}
|
|
538
|
+
]
|
|
539
|
+
};
|
|
540
|
+
} catch (error) {
|
|
541
|
+
return {
|
|
542
|
+
content: [
|
|
543
|
+
{
|
|
544
|
+
type: "text",
|
|
545
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to parse FIX string"}`
|
|
546
|
+
}
|
|
547
|
+
],
|
|
548
|
+
isError: true
|
|
549
|
+
};
|
|
373
550
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
551
|
+
}
|
|
552
|
+
);
|
|
553
|
+
this.server.setRequestHandler(
|
|
554
|
+
import_zod.z.object({ method: import_zod.z.literal("verifyOrder") }),
|
|
555
|
+
async (request, extra) => {
|
|
556
|
+
try {
|
|
557
|
+
const args = request.params;
|
|
558
|
+
this.verifiedOrders.set(args.clOrdID, {
|
|
559
|
+
clOrdID: args.clOrdID,
|
|
560
|
+
handlInst: args.handlInst,
|
|
561
|
+
quantity: Number.parseFloat(args.quantity),
|
|
562
|
+
price: Number.parseFloat(args.price),
|
|
563
|
+
ordType: args.ordType,
|
|
564
|
+
side: args.side,
|
|
565
|
+
symbol: args.symbol,
|
|
566
|
+
timeInForce: args.timeInForce
|
|
567
|
+
});
|
|
568
|
+
const ordTypeNames = {
|
|
569
|
+
"1": "Market",
|
|
570
|
+
"2": "Limit",
|
|
571
|
+
"3": "Stop",
|
|
572
|
+
"4": "StopLimit",
|
|
573
|
+
"5": "MarketOnClose",
|
|
574
|
+
"6": "WithOrWithout",
|
|
575
|
+
"7": "LimitOrBetter",
|
|
576
|
+
"8": "LimitWithOrWithout",
|
|
577
|
+
"9": "OnBasis",
|
|
578
|
+
A: "OnClose",
|
|
579
|
+
B: "LimitOnClose",
|
|
580
|
+
C: "ForexMarket",
|
|
581
|
+
D: "PreviouslyQuoted",
|
|
582
|
+
E: "PreviouslyIndicated",
|
|
583
|
+
F: "ForexLimit",
|
|
584
|
+
G: "ForexSwap",
|
|
585
|
+
H: "ForexPreviouslyQuoted",
|
|
586
|
+
I: "Funari",
|
|
587
|
+
J: "MarketIfTouched",
|
|
588
|
+
K: "MarketWithLeftOverAsLimit",
|
|
589
|
+
L: "PreviousFundValuationPoint",
|
|
590
|
+
M: "NextFundValuationPoint",
|
|
591
|
+
P: "Pegged",
|
|
592
|
+
Q: "CounterOrderSelection",
|
|
593
|
+
R: "StopOnBidOrOffer",
|
|
594
|
+
S: "StopLimitOnBidOrOffer"
|
|
595
|
+
};
|
|
596
|
+
const sideNames = {
|
|
597
|
+
"1": "Buy",
|
|
598
|
+
"2": "Sell",
|
|
599
|
+
"3": "BuyMinus",
|
|
600
|
+
"4": "SellPlus",
|
|
601
|
+
"5": "SellShort",
|
|
602
|
+
"6": "SellShortExempt",
|
|
603
|
+
"7": "Undisclosed",
|
|
604
|
+
"8": "Cross",
|
|
605
|
+
"9": "CrossShort",
|
|
606
|
+
A: "CrossShortExempt",
|
|
607
|
+
B: "AsDefined",
|
|
608
|
+
C: "Opposite",
|
|
609
|
+
D: "Subscribe",
|
|
610
|
+
E: "Redeem",
|
|
611
|
+
F: "Lend",
|
|
612
|
+
G: "Borrow",
|
|
613
|
+
H: "SellUndisclosed"
|
|
614
|
+
};
|
|
615
|
+
const timeInForceNames = {
|
|
616
|
+
"0": "Day",
|
|
617
|
+
"1": "GoodTillCancel",
|
|
618
|
+
"2": "AtTheOpening",
|
|
619
|
+
"3": "ImmediateOrCancel",
|
|
620
|
+
"4": "FillOrKill",
|
|
621
|
+
"5": "GoodTillCrossing",
|
|
622
|
+
"6": "GoodTillDate",
|
|
623
|
+
"7": "AtTheClose",
|
|
624
|
+
"8": "GoodThroughCrossing",
|
|
625
|
+
"9": "AtCrossing",
|
|
626
|
+
A: "GoodForTime",
|
|
627
|
+
B: "GoodForAuction",
|
|
628
|
+
C: "GoodForMonth"
|
|
629
|
+
};
|
|
630
|
+
const handlInstNames = {
|
|
631
|
+
"1": "AutomatedExecutionNoIntervention",
|
|
632
|
+
"2": "AutomatedExecutionInterventionOK",
|
|
633
|
+
"3": "ManualOrder"
|
|
634
|
+
};
|
|
635
|
+
return {
|
|
636
|
+
content: [
|
|
637
|
+
{
|
|
638
|
+
type: "text",
|
|
639
|
+
text: `VERIFICATION: All parameters valid. Ready to proceed with order execution.
|
|
640
|
+
|
|
641
|
+
Parameters verified:
|
|
642
|
+
- ClOrdID: ${args.clOrdID}
|
|
643
|
+
- HandlInst: ${args.handlInst} (${handlInstNames[args.handlInst]})
|
|
644
|
+
- Quantity: ${args.quantity}
|
|
645
|
+
- Price: ${args.price}
|
|
646
|
+
- OrdType: ${args.ordType} (${ordTypeNames[args.ordType]})
|
|
647
|
+
- Side: ${args.side} (${sideNames[args.side]})
|
|
648
|
+
- Symbol: ${args.symbol}
|
|
649
|
+
- TimeInForce: ${args.timeInForce} (${timeInForceNames[args.timeInForce]})
|
|
650
|
+
|
|
651
|
+
To execute this order, call the executeOrder tool with these exact same parameters.`
|
|
652
|
+
}
|
|
653
|
+
]
|
|
654
|
+
};
|
|
655
|
+
} catch (error) {
|
|
656
|
+
return {
|
|
657
|
+
content: [
|
|
658
|
+
{
|
|
659
|
+
type: "text",
|
|
660
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to verify order parameters"}`
|
|
661
|
+
}
|
|
662
|
+
],
|
|
663
|
+
isError: true
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
);
|
|
668
|
+
this.server.setRequestHandler(
|
|
669
|
+
import_zod.z.object({ method: import_zod.z.literal("executeOrder") }),
|
|
670
|
+
async (request, extra) => {
|
|
671
|
+
try {
|
|
672
|
+
const args = request.params;
|
|
673
|
+
const verifiedOrder = this.verifiedOrders.get(args.clOrdID);
|
|
674
|
+
if (!verifiedOrder) {
|
|
418
675
|
return {
|
|
419
|
-
isError: true,
|
|
420
676
|
content: [
|
|
421
677
|
{
|
|
422
678
|
type: "text",
|
|
423
|
-
text: `Error: ${
|
|
679
|
+
text: `Error: Order ${args.clOrdID} has not been verified. Please call verifyOrder first.`
|
|
424
680
|
}
|
|
425
|
-
]
|
|
681
|
+
],
|
|
682
|
+
isError: true
|
|
426
683
|
};
|
|
427
684
|
}
|
|
428
|
-
|
|
429
|
-
case "marketDataRequest": {
|
|
430
|
-
try {
|
|
431
|
-
const { mdUpdateType, symbol, mdReqID, subscriptionRequestType, mdEntryType } = validateArgs(
|
|
432
|
-
args,
|
|
433
|
-
marketDataRequestInputSchema
|
|
434
|
-
);
|
|
435
|
-
const response = new Promise((resolve) => {
|
|
436
|
-
this.pendingRequests.set(mdReqID, resolve);
|
|
437
|
-
});
|
|
438
|
-
const marketDataRequest = this.parser?.createMessage(
|
|
439
|
-
new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
|
|
440
|
-
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
441
|
-
new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
442
|
-
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
443
|
-
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
444
|
-
new import_fixparser.Field(import_fixparser.Fields.MarketDepth, 0),
|
|
445
|
-
new import_fixparser.Field(import_fixparser.Fields.MDUpdateType, mdUpdateType),
|
|
446
|
-
new import_fixparser.Field(import_fixparser.Fields.NoRelatedSym, 1),
|
|
447
|
-
new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol),
|
|
448
|
-
new import_fixparser.Field(import_fixparser.Fields.MDReqID, mdReqID),
|
|
449
|
-
new import_fixparser.Field(import_fixparser.Fields.SubscriptionRequestType, subscriptionRequestType),
|
|
450
|
-
new import_fixparser.Field(import_fixparser.Fields.NoMDEntryTypes, 1),
|
|
451
|
-
new import_fixparser.Field(import_fixparser.Fields.MDEntryType, mdEntryType)
|
|
452
|
-
);
|
|
453
|
-
if (!this.parser?.connected) {
|
|
454
|
-
return {
|
|
455
|
-
isError: true,
|
|
456
|
-
content: [
|
|
457
|
-
{
|
|
458
|
-
type: "text",
|
|
459
|
-
text: "Error: Not connected. Ignoring message."
|
|
460
|
-
}
|
|
461
|
-
]
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
this.parser?.send(marketDataRequest);
|
|
465
|
-
const fixData = await response;
|
|
685
|
+
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) {
|
|
466
686
|
return {
|
|
467
687
|
content: [
|
|
468
688
|
{
|
|
469
689
|
type: "text",
|
|
470
|
-
text:
|
|
690
|
+
text: "Error: Order parameters do not match the verified order. Please use the exact same parameters that were verified."
|
|
471
691
|
}
|
|
472
|
-
]
|
|
692
|
+
],
|
|
693
|
+
isError: true
|
|
473
694
|
};
|
|
474
|
-
}
|
|
695
|
+
}
|
|
696
|
+
const response = new Promise((resolve) => {
|
|
697
|
+
this.pendingRequests.set(args.clOrdID, resolve);
|
|
698
|
+
});
|
|
699
|
+
const order = this.parser?.createMessage(
|
|
700
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.NewOrderSingle),
|
|
701
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
702
|
+
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
703
|
+
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
704
|
+
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
705
|
+
new import_fixparser.Field(import_fixparser.Fields.ClOrdID, args.clOrdID),
|
|
706
|
+
new import_fixparser.Field(import_fixparser.Fields.Side, args.side),
|
|
707
|
+
new import_fixparser.Field(import_fixparser.Fields.Symbol, args.symbol),
|
|
708
|
+
new import_fixparser.Field(import_fixparser.Fields.OrderQty, Number.parseFloat(args.quantity)),
|
|
709
|
+
new import_fixparser.Field(import_fixparser.Fields.Price, Number.parseFloat(args.price)),
|
|
710
|
+
new import_fixparser.Field(import_fixparser.Fields.OrdType, args.ordType),
|
|
711
|
+
new import_fixparser.Field(import_fixparser.Fields.HandlInst, args.handlInst),
|
|
712
|
+
new import_fixparser.Field(import_fixparser.Fields.TimeInForce, args.timeInForce),
|
|
713
|
+
new import_fixparser.Field(import_fixparser.Fields.TransactTime, this.parser?.getTimestamp())
|
|
714
|
+
);
|
|
715
|
+
if (!this.parser?.connected) {
|
|
475
716
|
return {
|
|
476
|
-
isError: true,
|
|
477
717
|
content: [
|
|
478
718
|
{
|
|
479
719
|
type: "text",
|
|
480
|
-
text:
|
|
720
|
+
text: "Error: Not connected. Ignoring message."
|
|
481
721
|
}
|
|
482
|
-
]
|
|
722
|
+
],
|
|
723
|
+
isError: true
|
|
483
724
|
};
|
|
484
725
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
this.server.setRequestHandler(import_types.ListPromptsRequestSchema, async () => {
|
|
491
|
-
return {
|
|
492
|
-
prompts: [
|
|
493
|
-
{
|
|
494
|
-
name: "parse",
|
|
495
|
-
description: "Parses a FIX message and describes it in plain language",
|
|
496
|
-
arguments: [
|
|
497
|
-
{
|
|
498
|
-
name: "fixString",
|
|
499
|
-
description: "FIX message string to parse",
|
|
500
|
-
required: true
|
|
501
|
-
}
|
|
502
|
-
]
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
name: "parseToJSON",
|
|
506
|
-
description: "Parses a FIX message into JSON",
|
|
507
|
-
arguments: [
|
|
508
|
-
{
|
|
509
|
-
name: "fixString",
|
|
510
|
-
description: "FIX message string to parse",
|
|
511
|
-
required: true
|
|
512
|
-
}
|
|
513
|
-
]
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
name: "newOrderSingle",
|
|
517
|
-
description: "Creates and sends a New Order Single",
|
|
518
|
-
arguments: [
|
|
519
|
-
{
|
|
520
|
-
name: "clOrdID",
|
|
521
|
-
description: "Client Order ID",
|
|
522
|
-
required: true
|
|
523
|
-
},
|
|
524
|
-
{
|
|
525
|
-
name: "handlInst",
|
|
526
|
-
description: "Handling instruction",
|
|
527
|
-
required: true
|
|
528
|
-
},
|
|
529
|
-
{
|
|
530
|
-
name: "quantity",
|
|
531
|
-
description: "Order quantity",
|
|
532
|
-
required: true
|
|
533
|
-
},
|
|
534
|
-
{
|
|
535
|
-
name: "price",
|
|
536
|
-
description: "Order price",
|
|
537
|
-
required: true
|
|
538
|
-
},
|
|
539
|
-
{
|
|
540
|
-
name: "ordType",
|
|
541
|
-
description: "Order type",
|
|
542
|
-
required: true
|
|
543
|
-
},
|
|
544
|
-
{
|
|
545
|
-
name: "side",
|
|
546
|
-
description: "Order side (1=Buy, 2=Sell, 3=BuyMinus, 4=SellPlus, 5=SellShort, 6=SellShortExempt, 7=Undisclosed, 8=Cross, 9=CrossShort, A=CrossShortExempt, B=AsDefined, C=Opposite, D=Subscribe, E=Redeem, F=Lend, G=Borrow, H=SellUndisclosed)",
|
|
547
|
-
required: true
|
|
548
|
-
},
|
|
549
|
-
{
|
|
550
|
-
name: "symbol",
|
|
551
|
-
description: "Trading symbol",
|
|
552
|
-
required: true
|
|
553
|
-
},
|
|
554
|
-
{
|
|
555
|
-
name: "timeInForce",
|
|
556
|
-
description: "Time in force",
|
|
557
|
-
required: true
|
|
558
|
-
}
|
|
559
|
-
]
|
|
560
|
-
},
|
|
561
|
-
{
|
|
562
|
-
name: "marketDataRequest",
|
|
563
|
-
description: "Sends a request for Market Data with the given symbol",
|
|
564
|
-
arguments: [
|
|
565
|
-
{
|
|
566
|
-
name: "mdUpdateType",
|
|
567
|
-
description: "Market data update type",
|
|
568
|
-
required: true
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
name: "symbol",
|
|
572
|
-
description: "Trading symbol",
|
|
573
|
-
required: true
|
|
574
|
-
},
|
|
575
|
-
{
|
|
576
|
-
name: "mdReqID",
|
|
577
|
-
description: "Market data request ID",
|
|
578
|
-
required: true
|
|
579
|
-
},
|
|
580
|
-
{
|
|
581
|
-
name: "subscriptionRequestType",
|
|
582
|
-
description: "Subscription request type",
|
|
583
|
-
required: true
|
|
584
|
-
},
|
|
726
|
+
this.parser?.send(order);
|
|
727
|
+
const fixData = await response;
|
|
728
|
+
this.verifiedOrders.delete(args.clOrdID);
|
|
729
|
+
return {
|
|
730
|
+
content: [
|
|
585
731
|
{
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
required: true
|
|
732
|
+
type: "text",
|
|
733
|
+
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())}`
|
|
589
734
|
}
|
|
590
735
|
]
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
};
|
|
594
|
-
});
|
|
595
|
-
this.server.setRequestHandler(import_types.GetPromptRequestSchema, async (request) => {
|
|
596
|
-
const { name, arguments: args } = request.params;
|
|
597
|
-
switch (name) {
|
|
598
|
-
case "parse": {
|
|
599
|
-
const fixString = args?.fixString || "";
|
|
736
|
+
};
|
|
737
|
+
} catch (error) {
|
|
600
738
|
return {
|
|
601
|
-
|
|
739
|
+
content: [
|
|
602
740
|
{
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
type: "text",
|
|
606
|
-
text: `Please parse and explain this FIX message: ${fixString}`
|
|
607
|
-
}
|
|
741
|
+
type: "text",
|
|
742
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to execute order"}`
|
|
608
743
|
}
|
|
609
|
-
]
|
|
744
|
+
],
|
|
745
|
+
isError: true
|
|
610
746
|
};
|
|
611
747
|
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
this.server.setRequestHandler(
|
|
751
|
+
import_zod.z.object({ method: import_zod.z.literal("marketDataRequest") }),
|
|
752
|
+
async (request, extra) => {
|
|
753
|
+
try {
|
|
754
|
+
const args = request.params;
|
|
755
|
+
const response = new Promise((resolve) => {
|
|
756
|
+
this.pendingRequests.set(args.mdReqID, resolve);
|
|
757
|
+
});
|
|
758
|
+
const messageFields = [
|
|
759
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgType, import_fixparser.Messages.MarketDataRequest),
|
|
760
|
+
new import_fixparser.Field(import_fixparser.Fields.SenderCompID, this.parser?.sender),
|
|
761
|
+
new import_fixparser.Field(import_fixparser.Fields.MsgSeqNum, this.parser?.getNextTargetMsgSeqNum()),
|
|
762
|
+
new import_fixparser.Field(import_fixparser.Fields.TargetCompID, this.parser?.target),
|
|
763
|
+
new import_fixparser.Field(import_fixparser.Fields.SendingTime, this.parser?.getTimestamp()),
|
|
764
|
+
new import_fixparser.Field(import_fixparser.Fields.MDReqID, args.mdReqID),
|
|
765
|
+
new import_fixparser.Field(import_fixparser.Fields.SubscriptionRequestType, args.subscriptionRequestType),
|
|
766
|
+
new import_fixparser.Field(import_fixparser.Fields.MarketDepth, 0),
|
|
767
|
+
new import_fixparser.Field(import_fixparser.Fields.MDUpdateType, args.mdUpdateType)
|
|
768
|
+
];
|
|
769
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoRelatedSym, args.symbols.length));
|
|
770
|
+
args.symbols.forEach((symbol) => {
|
|
771
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.Symbol, symbol));
|
|
772
|
+
});
|
|
773
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.NoMDEntryTypes, args.mdEntryTypes.length));
|
|
774
|
+
args.mdEntryTypes.forEach((entryType) => {
|
|
775
|
+
messageFields.push(new import_fixparser.Field(import_fixparser.Fields.MDEntryType, entryType));
|
|
776
|
+
});
|
|
777
|
+
const mdr = this.parser?.createMessage(...messageFields);
|
|
778
|
+
if (!this.parser?.connected) {
|
|
779
|
+
return {
|
|
780
|
+
content: [
|
|
781
|
+
{
|
|
619
782
|
type: "text",
|
|
620
|
-
text:
|
|
783
|
+
text: "Error: Not connected. Ignoring message."
|
|
621
784
|
}
|
|
785
|
+
],
|
|
786
|
+
isError: true
|
|
787
|
+
};
|
|
788
|
+
}
|
|
789
|
+
this.parser?.send(mdr);
|
|
790
|
+
const fixData = await response;
|
|
791
|
+
return {
|
|
792
|
+
content: [
|
|
793
|
+
{
|
|
794
|
+
type: "text",
|
|
795
|
+
text: `Market data for ${args.symbols.join(", ")}: ${JSON.stringify(fixData.toFIXJSON())}`
|
|
622
796
|
}
|
|
623
797
|
]
|
|
624
798
|
};
|
|
625
|
-
}
|
|
626
|
-
case "newOrderSingle": {
|
|
627
|
-
const { clOrdID, handlInst, quantity, price, ordType, side, symbol, timeInForce } = args || {};
|
|
799
|
+
} catch (error) {
|
|
628
800
|
return {
|
|
629
|
-
|
|
801
|
+
content: [
|
|
630
802
|
{
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
type: "text",
|
|
634
|
-
text: [
|
|
635
|
-
"Create a New Order Single FIX message with the following parameters:",
|
|
636
|
-
`- ClOrdID: ${clOrdID}`,
|
|
637
|
-
`- HandlInst: ${handlInst ?? "3"} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '1' for Manual, '2' for Automated, '3' for AutomatedNoIntervention)`,
|
|
638
|
-
`- Quantity: ${quantity}`,
|
|
639
|
-
`- Price: ${price}`,
|
|
640
|
-
`- OrdType: ${ordType ?? "1"} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '1' for Market, '2' for Limit, '3' for Stop)`,
|
|
641
|
-
`- Side: ${side} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '1' for Buy, '2' for Sell)`,
|
|
642
|
-
`- Symbol: ${symbol}`,
|
|
643
|
-
`- TimeInForce: ${timeInForce ?? "0"} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '0' for Day, '1' for Good Till Cancel, '2' for At Opening, '3' for Immediate or Cancel, '4' for Fill or Kill, '5' for Good Till Crossing, '6' for Good Till Date)`,
|
|
644
|
-
"",
|
|
645
|
-
'Note: For the OrdType parameter, always use the numeric/alphabetic value (e.g., "1" for Market, "2" for Limit, "3" for Stop) as defined in the FIX protocol, not the descriptive name.',
|
|
646
|
-
'Note: For the TimeInForce parameter, always use the numeric/alphabetic value (e.g., "0" for Day, "1" for Good Till Cancel, "2" for At Opening) as defined in the FIX protocol, not the descriptive name.',
|
|
647
|
-
"",
|
|
648
|
-
"IMPORTANT: The response will be either:",
|
|
649
|
-
"1. An Execution Report (MsgType=8) if the order was successfully placed",
|
|
650
|
-
"2. A Reject message (MsgType=3) if the order failed to execute (e.g., due to missing or invalid parameters)"
|
|
651
|
-
].join("\n")
|
|
652
|
-
}
|
|
803
|
+
type: "text",
|
|
804
|
+
text: `Error: ${error instanceof Error ? error.message : "Failed to request market data"}`
|
|
653
805
|
}
|
|
654
|
-
]
|
|
806
|
+
],
|
|
807
|
+
isError: true
|
|
655
808
|
};
|
|
656
809
|
}
|
|
657
|
-
|
|
658
|
-
|
|
810
|
+
}
|
|
811
|
+
);
|
|
812
|
+
this.server.setRequestHandler(
|
|
813
|
+
import_zod.z.object({ method: import_zod.z.literal("greeting-resource") }),
|
|
814
|
+
async (request, extra) => {
|
|
815
|
+
this.parser?.logger.log({
|
|
816
|
+
level: "info",
|
|
817
|
+
message: "MCP Server Resource called: greeting-resource"
|
|
818
|
+
});
|
|
819
|
+
return {
|
|
820
|
+
content: [
|
|
821
|
+
{
|
|
822
|
+
type: "text",
|
|
823
|
+
text: "Hello, world!"
|
|
824
|
+
}
|
|
825
|
+
]
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
);
|
|
829
|
+
this.server.setRequestHandler(
|
|
830
|
+
import_zod.z.object({ method: import_zod.z.literal("stockGraph") }),
|
|
831
|
+
async (request, extra) => {
|
|
832
|
+
this.parser?.logger.log({
|
|
833
|
+
level: "info",
|
|
834
|
+
message: "MCP Server Resource called: stockGraph"
|
|
835
|
+
});
|
|
836
|
+
const args = request.params;
|
|
837
|
+
const symbol = args.symbol;
|
|
838
|
+
const priceHistory = this.marketDataPrices.get(symbol) || [];
|
|
839
|
+
if (priceHistory.length === 0) {
|
|
659
840
|
return {
|
|
660
|
-
|
|
841
|
+
content: [
|
|
661
842
|
{
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
type: "text",
|
|
665
|
-
text: [
|
|
666
|
-
"Create a Market Data Request FIX message with the following parameters:",
|
|
667
|
-
`- MDUpdateType: ${mdUpdateType ?? "0"} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '0' for FullRefresh, '1' for IncrementalRefresh)`,
|
|
668
|
-
`- Symbol: ${symbol}`,
|
|
669
|
-
`- MDReqID: ${mdReqID}`,
|
|
670
|
-
`- SubscriptionRequestType: ${subscriptionRequestType ?? "0"} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '0' for Snapshot + Updates, '1' for Snapshot, '2' for Unsubscribe)`,
|
|
671
|
-
`- MDEntryType: ${mdEntryType ?? "0"} (IMPORTANT: Use the numeric/alphabetic value, not the descriptive name. For example, use '0' for Bid, '1' for Offer, '2' for Trade, '3' for Index Value, '4' for Opening Price)`,
|
|
672
|
-
"",
|
|
673
|
-
"Format the response as a JSON object with FIX tag numbers as keys and their corresponding values.",
|
|
674
|
-
"",
|
|
675
|
-
'Note: For the MDUpdateType parameter, always use the numeric/alphabetic value (e.g., "0" for FullRefresh, "1" for IncrementalRefresh) as defined in the FIX protocol, not the descriptive name.',
|
|
676
|
-
'Note: For the SubscriptionRequestType parameter, always use the numeric/alphabetic value (e.g., "0" for Snapshot + Updates, "1" for Snapshot, "2" for Unsubscribe) as defined in the FIX protocol, not the descriptive name.',
|
|
677
|
-
'Note: For the MDEntryType parameter, always use the numeric/alphabetic value (e.g., "0" for Bid, "1" for Offer, "2" for Trade, "3" for Index Value, "4" for Opening Price) as defined in the FIX protocol, not the descriptive name.'
|
|
678
|
-
].join("\n")
|
|
679
|
-
}
|
|
843
|
+
type: "text",
|
|
844
|
+
text: `No price data available for ${symbol}`
|
|
680
845
|
}
|
|
681
846
|
]
|
|
682
847
|
};
|
|
683
848
|
}
|
|
684
|
-
|
|
685
|
-
|
|
849
|
+
const width = 600;
|
|
850
|
+
const height = 300;
|
|
851
|
+
const padding = 40;
|
|
852
|
+
const xScale = (width - 2 * padding) / (priceHistory.length - 1);
|
|
853
|
+
const yMin = Math.min(...priceHistory.map((d) => d.price));
|
|
854
|
+
const yMax = Math.max(...priceHistory.map((d) => d.price));
|
|
855
|
+
const yScale = (height - 2 * padding) / (yMax - yMin);
|
|
856
|
+
const points = priceHistory.map((d, i) => {
|
|
857
|
+
const x = padding + i * xScale;
|
|
858
|
+
const y = height - padding - (d.price - yMin) * yScale;
|
|
859
|
+
return `${x},${y}`;
|
|
860
|
+
}).join(" L ");
|
|
861
|
+
const svg = `<?xml version="1.0" encoding="UTF-8"?>
|
|
862
|
+
<svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
|
|
863
|
+
<!-- Background -->
|
|
864
|
+
<rect width="100%" height="100%" fill="#f8f9fa"/>
|
|
865
|
+
|
|
866
|
+
<!-- Grid lines -->
|
|
867
|
+
<g stroke="#e9ecef" stroke-width="1">
|
|
868
|
+
${Array.from({ length: 5 }, (_, i) => {
|
|
869
|
+
const y = padding + (height - 2 * padding) * i / 4;
|
|
870
|
+
return `<line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}"/>`;
|
|
871
|
+
}).join("\n")}
|
|
872
|
+
</g>
|
|
873
|
+
|
|
874
|
+
<!-- Price line -->
|
|
875
|
+
<path d="M ${points}"
|
|
876
|
+
fill="none"
|
|
877
|
+
stroke="#007bff"
|
|
878
|
+
stroke-width="2"/>
|
|
879
|
+
|
|
880
|
+
<!-- Data points -->
|
|
881
|
+
${priceHistory.map((d, i) => {
|
|
882
|
+
const x = padding + i * xScale;
|
|
883
|
+
const y = height - padding - (d.price - yMin) * yScale;
|
|
884
|
+
return `<circle cx="${x}" cy="${y}" r="3" fill="#007bff"/>`;
|
|
885
|
+
}).join("\n")}
|
|
886
|
+
|
|
887
|
+
<!-- Labels -->
|
|
888
|
+
<g font-family="Arial" font-size="12" fill="#495057">
|
|
889
|
+
${Array.from({ length: 5 }, (_, i) => {
|
|
890
|
+
const x = padding + (width - 2 * padding) * i / 4;
|
|
891
|
+
const index = Math.floor((priceHistory.length - 1) * i / 4);
|
|
892
|
+
const timestamp = new Date(priceHistory[index].timestamp).toLocaleTimeString();
|
|
893
|
+
return `<text x="${x + padding}" y="${height - padding + 20}" text-anchor="middle">${timestamp}</text>`;
|
|
894
|
+
}).join("\n")}
|
|
895
|
+
${Array.from({ length: 5 }, (_, i) => {
|
|
896
|
+
const y = padding + (height - 2 * padding) * i / 4;
|
|
897
|
+
const price = yMax - (yMax - yMin) * i / 4;
|
|
898
|
+
return `<text x="${padding - 5}" y="${y + 4}" text-anchor="end">$${price.toFixed(2)}</text>`;
|
|
899
|
+
}).join("\n")}
|
|
900
|
+
</g>
|
|
901
|
+
|
|
902
|
+
<!-- Title -->
|
|
903
|
+
<text x="${width / 2}" y="${padding / 2}"
|
|
904
|
+
font-family="Arial" font-size="16" font-weight="bold"
|
|
905
|
+
text-anchor="middle" fill="#212529">
|
|
906
|
+
${symbol} - Price Chart (${priceHistory.length} points)
|
|
907
|
+
</text>
|
|
908
|
+
</svg>`;
|
|
909
|
+
return {
|
|
910
|
+
content: [
|
|
911
|
+
{
|
|
912
|
+
type: "text",
|
|
913
|
+
text: svg
|
|
914
|
+
}
|
|
915
|
+
]
|
|
916
|
+
};
|
|
686
917
|
}
|
|
687
|
-
|
|
918
|
+
);
|
|
919
|
+
this.server.setRequestHandler(
|
|
920
|
+
import_zod.z.object({ method: import_zod.z.literal("stockPriceHistory") }),
|
|
921
|
+
async (request, extra) => {
|
|
922
|
+
this.parser?.logger.log({
|
|
923
|
+
level: "info",
|
|
924
|
+
message: "MCP Server Resource called: stockPriceHistory"
|
|
925
|
+
});
|
|
926
|
+
const args = request.params;
|
|
927
|
+
const symbol = args.symbol;
|
|
928
|
+
const priceHistory = this.marketDataPrices.get(symbol) || [];
|
|
929
|
+
return {
|
|
930
|
+
content: [
|
|
931
|
+
{
|
|
932
|
+
type: "text",
|
|
933
|
+
text: JSON.stringify(
|
|
934
|
+
{
|
|
935
|
+
symbol,
|
|
936
|
+
count: priceHistory.length,
|
|
937
|
+
prices: priceHistory.map((point) => ({
|
|
938
|
+
timestamp: new Date(point.timestamp).toISOString(),
|
|
939
|
+
price: point.price
|
|
940
|
+
}))
|
|
941
|
+
},
|
|
942
|
+
null,
|
|
943
|
+
2
|
|
944
|
+
)
|
|
945
|
+
}
|
|
946
|
+
]
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
);
|
|
688
950
|
process.on("SIGINT", async () => {
|
|
689
951
|
await this.server.close();
|
|
690
952
|
process.exit(0);
|