@toromarket/mcp-server 0.2.2 → 0.2.3
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/dist/index.cjs +29 -299
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +27 -297
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
28
28
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
29
29
|
var import_types = require("@modelcontextprotocol/sdk/types.js");
|
|
30
|
-
var
|
|
30
|
+
var import_sdk6 = require("@toromarket/sdk");
|
|
31
31
|
|
|
32
32
|
// src/tools/definitions.ts
|
|
33
33
|
var import_zod = require("zod");
|
|
@@ -2999,17 +2999,11 @@ async function executeTool(client, baseUrl2, name, rawArgs, options) {
|
|
|
2999
2999
|
}
|
|
3000
3000
|
case "get_compliance_status": {
|
|
3001
3001
|
const me = await client.auth.me();
|
|
3002
|
-
const
|
|
3003
|
-
enabled: false,
|
|
3004
|
-
flags: { velocityWarning: false, regionBlocked: false, largeTransaction: false },
|
|
3005
|
-
velocity: { tradesLastMinute: 0, sessionVolume: 0 },
|
|
3006
|
-
region: null,
|
|
3007
|
-
blockedRegions: []
|
|
3008
|
-
};
|
|
3002
|
+
const compliance = await client.auth.complianceStatus();
|
|
3009
3003
|
return {
|
|
3010
3004
|
trustTier: me.trustTier,
|
|
3011
3005
|
trustScore: me.trustScore,
|
|
3012
|
-
compliance
|
|
3006
|
+
compliance
|
|
3013
3007
|
};
|
|
3014
3008
|
}
|
|
3015
3009
|
// Registry tools
|
|
@@ -3378,6 +3372,18 @@ function classifyError(error) {
|
|
|
3378
3372
|
if (error.code === "NETWORK_ERROR") {
|
|
3379
3373
|
return { error: error.message, statusCode: 0, retryable: true, recovery: "Toromarket API is unreachable. Check that the server is running." };
|
|
3380
3374
|
}
|
|
3375
|
+
if (error.code === "SPENDING_LIMIT") {
|
|
3376
|
+
return { error: error.message, statusCode: 403, retryable: false, recovery: "Daily spending limit reached. Read-only tools still work. Call get_upgrade_url to increase your limit." };
|
|
3377
|
+
}
|
|
3378
|
+
if (error.code === "SPOOFING_DETECTED") {
|
|
3379
|
+
return { error: error.message, statusCode: 403, retryable: false, recovery: "Suspicious trading pattern detected (high cancel ratio). Slow down and wait 30 seconds." };
|
|
3380
|
+
}
|
|
3381
|
+
if (error.code === "REGISTRATION_THROTTLE") {
|
|
3382
|
+
return { error: error.message, statusCode: 403, retryable: false, recovery: "Registration limit reached for this IP. Use authenticate to log in to an existing account." };
|
|
3383
|
+
}
|
|
3384
|
+
if (error.code === "RATE_LIMITED") {
|
|
3385
|
+
return { error: error.message, statusCode: 429, retryable: true, recovery: "Rate limit exceeded. Wait for the indicated cooldown period, or call get_upgrade_url to increase your tier." };
|
|
3386
|
+
}
|
|
3381
3387
|
if (status >= 500) {
|
|
3382
3388
|
return { error: error.message, statusCode: status, retryable: true, recovery: "Server error. Try again in a few seconds." };
|
|
3383
3389
|
}
|
|
@@ -3711,28 +3717,8 @@ function createLogger(level = "info") {
|
|
|
3711
3717
|
};
|
|
3712
3718
|
}
|
|
3713
3719
|
|
|
3714
|
-
// src/middleware/registration.ts
|
|
3715
|
-
var import_sdk3 = require("@toromarket/sdk");
|
|
3716
|
-
var RegistrationThrottle = class {
|
|
3717
|
-
hasRegistered = false;
|
|
3718
|
-
async beforeExecute(toolName, _args) {
|
|
3719
|
-
if (toolName === "register_agent" && this.hasRegistered) {
|
|
3720
|
-
throw new import_sdk3.ToromarketError(
|
|
3721
|
-
"Only one account can be registered per session. Use authenticate to log in to an existing account.",
|
|
3722
|
-
403,
|
|
3723
|
-
"REGISTRATION_THROTTLE"
|
|
3724
|
-
);
|
|
3725
|
-
}
|
|
3726
|
-
}
|
|
3727
|
-
async afterExecute(toolName, _args, _result, error) {
|
|
3728
|
-
if (toolName === "register_agent" && !error) {
|
|
3729
|
-
this.hasRegistered = true;
|
|
3730
|
-
}
|
|
3731
|
-
}
|
|
3732
|
-
};
|
|
3733
|
-
|
|
3734
3720
|
// src/middleware/rate-limiter.ts
|
|
3735
|
-
var
|
|
3721
|
+
var import_sdk3 = require("@toromarket/sdk");
|
|
3736
3722
|
var ORDER_TOOLS = /* @__PURE__ */ new Set(["place_order", "cancel_order", "trade_crypto", "place_fund_order", "trade_fund_crypto", "cancel_fund_order"]);
|
|
3737
3723
|
var CHAT_TOOLS = /* @__PURE__ */ new Set(["post_market_comment", "post_fund_message", "react_to_chat"]);
|
|
3738
3724
|
var HEAVY_TOOLS = /* @__PURE__ */ new Set(["get_trading_context"]);
|
|
@@ -3779,7 +3765,7 @@ var RateLimiter = class {
|
|
|
3779
3765
|
const waitSec = Math.ceil((oldestInWindow + config.windowMs - now) / 1e3);
|
|
3780
3766
|
const proLimits = { orders: "100 orders/min", chat: "30 messages/min", reads: "300 reads/min" };
|
|
3781
3767
|
const upgradeHint = this.currentTier === "FREE" ? ` To increase your ${category} limit to ${proLimits[category] ?? "higher"}, call get_upgrade_url with tier PRO ($29/mo) or ENTERPRISE ($99/mo, unlimited). It will generate a payment link for your operator.` : this.currentTier === "PRO" ? ` To remove all ${category} limits, call get_upgrade_url with tier ENTERPRISE ($99/mo, unlimited). It will generate a payment link for your operator.` : "";
|
|
3782
|
-
throw new
|
|
3768
|
+
throw new import_sdk3.ToromarketError(
|
|
3783
3769
|
`Rate limit exceeded for ${category}. Limit: ${config.limit} per minute. Wait ${waitSec}s.${upgradeHint}`,
|
|
3784
3770
|
429,
|
|
3785
3771
|
"RATE_LIMITED"
|
|
@@ -3801,7 +3787,7 @@ function buildCategories(config) {
|
|
|
3801
3787
|
}
|
|
3802
3788
|
|
|
3803
3789
|
// src/middleware/error-budget.ts
|
|
3804
|
-
var
|
|
3790
|
+
var import_sdk4 = require("@toromarket/sdk");
|
|
3805
3791
|
var ErrorBudget = class {
|
|
3806
3792
|
consecutiveErrors = 0;
|
|
3807
3793
|
cooldownUntil = 0;
|
|
@@ -3815,7 +3801,7 @@ var ErrorBudget = class {
|
|
|
3815
3801
|
const now = Date.now();
|
|
3816
3802
|
if (now < this.cooldownUntil) {
|
|
3817
3803
|
const waitSec = Math.ceil((this.cooldownUntil - now) / 1e3);
|
|
3818
|
-
throw new
|
|
3804
|
+
throw new import_sdk4.ToromarketError(
|
|
3819
3805
|
`Too many consecutive errors. Automatic backoff for ${waitSec}s.`,
|
|
3820
3806
|
429,
|
|
3821
3807
|
"ERROR_BUDGET_EXCEEDED"
|
|
@@ -3823,20 +3809,20 @@ var ErrorBudget = class {
|
|
|
3823
3809
|
}
|
|
3824
3810
|
}
|
|
3825
3811
|
async afterExecute(_toolName, _args, _result, error) {
|
|
3826
|
-
if (error instanceof
|
|
3827
|
-
const
|
|
3812
|
+
if (error instanceof import_sdk4.ToromarketError) {
|
|
3813
|
+
const EXCLUDED_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
3828
3814
|
"ERROR_BUDGET_EXCEEDED",
|
|
3829
3815
|
"RATE_LIMITED",
|
|
3830
3816
|
"REGISTRATION_THROTTLE",
|
|
3831
3817
|
"SPOOFING_DETECTED",
|
|
3832
3818
|
"SPENDING_LIMIT"
|
|
3833
3819
|
]);
|
|
3834
|
-
if (error.code &&
|
|
3820
|
+
if (error.code && EXCLUDED_ERROR_CODES.has(error.code)) {
|
|
3835
3821
|
return;
|
|
3836
3822
|
}
|
|
3837
3823
|
}
|
|
3838
3824
|
if (error) {
|
|
3839
|
-
const statusCode = error instanceof
|
|
3825
|
+
const statusCode = error instanceof import_sdk4.ToromarketError ? error.statusCode : void 0;
|
|
3840
3826
|
if (statusCode !== void 0 && statusCode < 500) {
|
|
3841
3827
|
return;
|
|
3842
3828
|
}
|
|
@@ -3855,241 +3841,6 @@ var ErrorBudget = class {
|
|
|
3855
3841
|
}
|
|
3856
3842
|
};
|
|
3857
3843
|
|
|
3858
|
-
// src/middleware/spoofing.ts
|
|
3859
|
-
var import_sdk6 = require("@toromarket/sdk");
|
|
3860
|
-
var SpoofingDetector = class {
|
|
3861
|
-
events = [];
|
|
3862
|
-
windowMs;
|
|
3863
|
-
maxCancelRatio;
|
|
3864
|
-
minActions;
|
|
3865
|
-
constructor(config = {}) {
|
|
3866
|
-
this.windowMs = config.windowMs ?? 3e4;
|
|
3867
|
-
this.maxCancelRatio = config.maxCancelRatio ?? 0.8;
|
|
3868
|
-
this.minActions = config.minActions ?? 5;
|
|
3869
|
-
}
|
|
3870
|
-
pruneExpired() {
|
|
3871
|
-
const cutoff = Date.now() - this.windowMs;
|
|
3872
|
-
while (this.events.length > 0 && this.events[0].time < cutoff) {
|
|
3873
|
-
this.events.shift();
|
|
3874
|
-
}
|
|
3875
|
-
}
|
|
3876
|
-
async beforeExecute(toolName, _args) {
|
|
3877
|
-
if (toolName !== "place_order" && toolName !== "place_fund_order") return;
|
|
3878
|
-
this.pruneExpired();
|
|
3879
|
-
if (this.events.length < this.minActions) return;
|
|
3880
|
-
const cancels = this.events.filter((e) => e.type === "cancel").length;
|
|
3881
|
-
const ratio = cancels / this.events.length;
|
|
3882
|
-
if (ratio > this.maxCancelRatio) {
|
|
3883
|
-
throw new import_sdk6.ToromarketError(
|
|
3884
|
-
`Suspicious trading pattern detected: ${Math.round(ratio * 100)}% of recent orders were cancelled. Slow down.`,
|
|
3885
|
-
403,
|
|
3886
|
-
"SPOOFING_DETECTED"
|
|
3887
|
-
);
|
|
3888
|
-
}
|
|
3889
|
-
}
|
|
3890
|
-
async afterExecute(toolName, _args, _result, error) {
|
|
3891
|
-
if (error) return;
|
|
3892
|
-
this.pruneExpired();
|
|
3893
|
-
if (toolName === "place_order" || toolName === "place_fund_order") {
|
|
3894
|
-
this.events.push({ type: "place", time: Date.now() });
|
|
3895
|
-
} else if (toolName === "cancel_order" || toolName === "cancel_fund_order") {
|
|
3896
|
-
this.events.push({ type: "cancel", time: Date.now() });
|
|
3897
|
-
}
|
|
3898
|
-
}
|
|
3899
|
-
};
|
|
3900
|
-
|
|
3901
|
-
// src/middleware/spending.ts
|
|
3902
|
-
var import_sdk7 = require("@toromarket/sdk");
|
|
3903
|
-
var TRADE_TOOLS = /* @__PURE__ */ new Set(["place_order", "trade_crypto", "place_fund_order", "trade_fund_crypto"]);
|
|
3904
|
-
var SpendingGuardrails = class {
|
|
3905
|
-
startingBalance = null;
|
|
3906
|
-
blocked = false;
|
|
3907
|
-
maxLoss;
|
|
3908
|
-
constructor(maxSessionLoss2) {
|
|
3909
|
-
this.maxLoss = maxSessionLoss2 ?? 5e3;
|
|
3910
|
-
}
|
|
3911
|
-
async beforeExecute(toolName, _args) {
|
|
3912
|
-
if (!TRADE_TOOLS.has(toolName)) return;
|
|
3913
|
-
if (this.blocked) {
|
|
3914
|
-
throw new import_sdk7.ToromarketError(
|
|
3915
|
-
`Session spending limit reached (${this.maxLoss} TC). No further trades allowed. Read-only tools still work.`,
|
|
3916
|
-
403,
|
|
3917
|
-
"SPENDING_LIMIT"
|
|
3918
|
-
);
|
|
3919
|
-
}
|
|
3920
|
-
if (this.startingBalance === null) {
|
|
3921
|
-
throw new import_sdk7.ToromarketError(
|
|
3922
|
-
"Call get_balance or get_portfolio before your first trade so spending limits can be tracked.",
|
|
3923
|
-
400,
|
|
3924
|
-
"BALANCE_REQUIRED"
|
|
3925
|
-
);
|
|
3926
|
-
}
|
|
3927
|
-
}
|
|
3928
|
-
async afterExecute(toolName, _args, result, error) {
|
|
3929
|
-
if (error) return;
|
|
3930
|
-
if (this.maxLoss === null) return;
|
|
3931
|
-
if (this.startingBalance === null) {
|
|
3932
|
-
const bal = extractBalance(result);
|
|
3933
|
-
if (bal !== null) {
|
|
3934
|
-
this.startingBalance = bal;
|
|
3935
|
-
return;
|
|
3936
|
-
}
|
|
3937
|
-
}
|
|
3938
|
-
if (TRADE_TOOLS.has(toolName) && result) {
|
|
3939
|
-
const remaining = extractRemainingBalance(result);
|
|
3940
|
-
if (remaining !== null && this.startingBalance !== null) {
|
|
3941
|
-
const loss = this.startingBalance - remaining;
|
|
3942
|
-
if (loss >= this.maxLoss) {
|
|
3943
|
-
this.blocked = true;
|
|
3944
|
-
}
|
|
3945
|
-
}
|
|
3946
|
-
}
|
|
3947
|
-
}
|
|
3948
|
-
};
|
|
3949
|
-
function extractBalance(result) {
|
|
3950
|
-
if (typeof result !== "object" || result === null) return null;
|
|
3951
|
-
const r = result;
|
|
3952
|
-
if (typeof r.balance === "number") return r.balance;
|
|
3953
|
-
if (typeof r.totalValue === "number") return r.totalValue;
|
|
3954
|
-
return null;
|
|
3955
|
-
}
|
|
3956
|
-
function extractRemainingBalance(result) {
|
|
3957
|
-
if (typeof result !== "object" || result === null) return null;
|
|
3958
|
-
const r = result;
|
|
3959
|
-
if (typeof r.remainingBalance === "number") return r.remainingBalance;
|
|
3960
|
-
return null;
|
|
3961
|
-
}
|
|
3962
|
-
|
|
3963
|
-
// src/middleware/compliance-gate.ts
|
|
3964
|
-
var FINANCIAL_TOOLS = /* @__PURE__ */ new Set([
|
|
3965
|
-
"place_order",
|
|
3966
|
-
"cancel_order",
|
|
3967
|
-
"trade_crypto",
|
|
3968
|
-
"place_fund_order",
|
|
3969
|
-
"trade_fund_crypto",
|
|
3970
|
-
"cancel_fund_order",
|
|
3971
|
-
"create_escrow",
|
|
3972
|
-
"settle_escrow",
|
|
3973
|
-
"create_delegation",
|
|
3974
|
-
"topup_stake",
|
|
3975
|
-
"claim_challenge",
|
|
3976
|
-
"claim_quest",
|
|
3977
|
-
"claim_mystery_box"
|
|
3978
|
-
]);
|
|
3979
|
-
var ComplianceGate = class {
|
|
3980
|
-
constructor(config, logger) {
|
|
3981
|
-
this.config = config;
|
|
3982
|
-
this.logger = logger;
|
|
3983
|
-
}
|
|
3984
|
-
tradeTimestamps = [];
|
|
3985
|
-
sessionTcVolume = 0;
|
|
3986
|
-
region = null;
|
|
3987
|
-
flags = {
|
|
3988
|
-
velocityWarning: false,
|
|
3989
|
-
regionBlocked: false,
|
|
3990
|
-
largeTransaction: false
|
|
3991
|
-
};
|
|
3992
|
-
/**
|
|
3993
|
-
* Set the session's region for geographic restriction checks.
|
|
3994
|
-
* Call this when region info is available (e.g., from HTTP transport
|
|
3995
|
-
* X-Forwarded-For / CF-IPCountry headers, or operator-provided region).
|
|
3996
|
-
*/
|
|
3997
|
-
setRegion(region) {
|
|
3998
|
-
this.region = region.toUpperCase();
|
|
3999
|
-
if (this.config.blockedRegions.length > 0 && this.config.blockedRegions.includes(this.region)) {
|
|
4000
|
-
this.flags.regionBlocked = true;
|
|
4001
|
-
this.logger.warn({
|
|
4002
|
-
event: "compliance:region_blocked",
|
|
4003
|
-
region: this.region,
|
|
4004
|
-
blockedRegions: this.config.blockedRegions
|
|
4005
|
-
});
|
|
4006
|
-
}
|
|
4007
|
-
}
|
|
4008
|
-
async beforeExecute(toolName, args) {
|
|
4009
|
-
if (!this.config.enabled) return;
|
|
4010
|
-
if (!FINANCIAL_TOOLS.has(toolName)) return;
|
|
4011
|
-
const now = Date.now();
|
|
4012
|
-
this.tradeTimestamps.push(now);
|
|
4013
|
-
const oneMinAgo = now - 6e4;
|
|
4014
|
-
this.tradeTimestamps = this.tradeTimestamps.filter((t) => t > oneMinAgo);
|
|
4015
|
-
const amount = this.parseAmount(args);
|
|
4016
|
-
if (amount > 0) {
|
|
4017
|
-
this.sessionTcVolume += amount;
|
|
4018
|
-
}
|
|
4019
|
-
if (this.tradeTimestamps.length > this.config.velocityTradesPerMin) {
|
|
4020
|
-
this.flags.velocityWarning = true;
|
|
4021
|
-
this.logger.warn({
|
|
4022
|
-
event: "compliance:velocity_warning",
|
|
4023
|
-
tradesInLastMinute: this.tradeTimestamps.length,
|
|
4024
|
-
threshold: this.config.velocityTradesPerMin,
|
|
4025
|
-
tool: toolName
|
|
4026
|
-
});
|
|
4027
|
-
}
|
|
4028
|
-
if (this.sessionTcVolume > this.config.velocityTcPerHour) {
|
|
4029
|
-
this.flags.velocityWarning = true;
|
|
4030
|
-
this.logger.warn({
|
|
4031
|
-
event: "compliance:volume_warning",
|
|
4032
|
-
sessionVolume: this.sessionTcVolume,
|
|
4033
|
-
threshold: this.config.velocityTcPerHour,
|
|
4034
|
-
tool: toolName
|
|
4035
|
-
});
|
|
4036
|
-
}
|
|
4037
|
-
if (amount > 1e4) {
|
|
4038
|
-
this.flags.largeTransaction = true;
|
|
4039
|
-
this.logger.warn({
|
|
4040
|
-
event: "compliance:large_transaction",
|
|
4041
|
-
amount,
|
|
4042
|
-
tool: toolName
|
|
4043
|
-
});
|
|
4044
|
-
}
|
|
4045
|
-
if (this.flags.regionBlocked) {
|
|
4046
|
-
this.logger.warn({
|
|
4047
|
-
event: "compliance:region_blocked_trade_attempt",
|
|
4048
|
-
region: this.region,
|
|
4049
|
-
tool: toolName
|
|
4050
|
-
});
|
|
4051
|
-
}
|
|
4052
|
-
this.logger.info({
|
|
4053
|
-
event: "compliance:financial_tool",
|
|
4054
|
-
tool: toolName,
|
|
4055
|
-
amount: amount > 0 ? amount : void 0,
|
|
4056
|
-
sessionVolume: this.sessionTcVolume,
|
|
4057
|
-
tradesInLastMinute: this.tradeTimestamps.length,
|
|
4058
|
-
region: this.region,
|
|
4059
|
-
regionBlocked: this.flags.regionBlocked
|
|
4060
|
-
});
|
|
4061
|
-
}
|
|
4062
|
-
async afterExecute(_toolName, _args, _result, _error) {
|
|
4063
|
-
}
|
|
4064
|
-
getStatus() {
|
|
4065
|
-
const now = Date.now();
|
|
4066
|
-
const oneMinAgo = now - 6e4;
|
|
4067
|
-
const recentTrades = this.tradeTimestamps.filter((t) => t > oneMinAgo);
|
|
4068
|
-
return {
|
|
4069
|
-
enabled: this.config.enabled,
|
|
4070
|
-
flags: { ...this.flags },
|
|
4071
|
-
velocity: {
|
|
4072
|
-
tradesLastMinute: recentTrades.length,
|
|
4073
|
-
sessionVolume: this.sessionTcVolume
|
|
4074
|
-
},
|
|
4075
|
-
region: this.region,
|
|
4076
|
-
blockedRegions: this.config.blockedRegions
|
|
4077
|
-
};
|
|
4078
|
-
}
|
|
4079
|
-
parseAmount(args) {
|
|
4080
|
-
if (typeof args !== "object" || args === null) return 0;
|
|
4081
|
-
const a = args;
|
|
4082
|
-
if (typeof a.quantity === "number" && typeof a.price === "number") {
|
|
4083
|
-
return a.quantity * a.price;
|
|
4084
|
-
}
|
|
4085
|
-
if (typeof a.quantity === "number") return a.quantity;
|
|
4086
|
-
if (typeof a.amount === "number") return a.amount;
|
|
4087
|
-
if (typeof a.stake === "number") return a.stake;
|
|
4088
|
-
if (typeof a.initialStake === "number") return a.initialStake;
|
|
4089
|
-
return 0;
|
|
4090
|
-
}
|
|
4091
|
-
};
|
|
4092
|
-
|
|
4093
3844
|
// src/audit.ts
|
|
4094
3845
|
var AUDITED_TOOLS = /* @__PURE__ */ new Set([
|
|
4095
3846
|
"place_order",
|
|
@@ -4140,7 +3891,7 @@ function summarizeResult(result) {
|
|
|
4140
3891
|
}
|
|
4141
3892
|
|
|
4142
3893
|
// src/middleware/trace-collector.ts
|
|
4143
|
-
var
|
|
3894
|
+
var import_sdk5 = require("@toromarket/sdk");
|
|
4144
3895
|
var STATE_CHANGING_TOOLS = /* @__PURE__ */ new Set([
|
|
4145
3896
|
"place_order",
|
|
4146
3897
|
"cancel_order",
|
|
@@ -4189,7 +3940,7 @@ var TraceCollector = class {
|
|
|
4189
3940
|
}
|
|
4190
3941
|
}
|
|
4191
3942
|
if (!this.pendingReasoning) {
|
|
4192
|
-
throw new
|
|
3943
|
+
throw new import_sdk5.ToromarketError(
|
|
4193
3944
|
"Call log_reasoning before trading. Every trade requires a reasoning explanation.",
|
|
4194
3945
|
400,
|
|
4195
3946
|
"REASONING_REQUIRED"
|
|
@@ -4538,7 +4289,7 @@ function normalizeOrder(raw) {
|
|
|
4538
4289
|
}
|
|
4539
4290
|
|
|
4540
4291
|
// src/metrics.ts
|
|
4541
|
-
var
|
|
4292
|
+
var TRADE_TOOLS = /* @__PURE__ */ new Set(["place_order", "cancel_order", "trade_crypto"]);
|
|
4542
4293
|
var MetricsCollector = class {
|
|
4543
4294
|
metrics;
|
|
4544
4295
|
logger;
|
|
@@ -4572,7 +4323,7 @@ var MetricsCollector = class {
|
|
|
4572
4323
|
metric.errors++;
|
|
4573
4324
|
this.metrics.totalErrors++;
|
|
4574
4325
|
}
|
|
4575
|
-
if (
|
|
4326
|
+
if (TRADE_TOOLS.has(toolName) && !error) {
|
|
4576
4327
|
this.metrics.totalTrades++;
|
|
4577
4328
|
}
|
|
4578
4329
|
}
|
|
@@ -4599,7 +4350,7 @@ var MetricsCollector = class {
|
|
|
4599
4350
|
|
|
4600
4351
|
// src/server.ts
|
|
4601
4352
|
function createToromarketClient(options) {
|
|
4602
|
-
return new
|
|
4353
|
+
return new import_sdk6.ToromarketClient({
|
|
4603
4354
|
baseUrl: options.baseUrl,
|
|
4604
4355
|
clientId: "mcp-server/0.2.0",
|
|
4605
4356
|
...options.token ? { token: options.token } : {},
|
|
@@ -4737,20 +4488,8 @@ function createServerFactory(options) {
|
|
|
4737
4488
|
const metrics = new MetricsCollector(logger);
|
|
4738
4489
|
const rateLimiter = new RateLimiter();
|
|
4739
4490
|
const traceCollector = new TraceCollector(client, logger, `session-${Date.now()}`);
|
|
4740
|
-
const complianceEnabled = !!process.env.TOROMARKET_COMPLIANCE_MODE;
|
|
4741
|
-
const blockedRegions = (process.env.TOROMARKET_BLOCKED_REGIONS ?? "").split(",").map((r) => r.trim().toUpperCase()).filter(Boolean);
|
|
4742
|
-
const velocityTradesPerMin = Number(process.env.TOROMARKET_VELOCITY_TRADES_PER_MIN) || 30;
|
|
4743
|
-
const velocityTcPerHour = Number(process.env.TOROMARKET_VELOCITY_TC_PER_HOUR) || 5e4;
|
|
4744
|
-
const complianceGate = new ComplianceGate(
|
|
4745
|
-
{ enabled: complianceEnabled, blockedRegions, velocityTradesPerMin, velocityTcPerHour },
|
|
4746
|
-
logger
|
|
4747
|
-
);
|
|
4748
4491
|
const middlewares = [
|
|
4749
|
-
new RegistrationThrottle(),
|
|
4750
4492
|
rateLimiter,
|
|
4751
|
-
new SpoofingDetector(),
|
|
4752
|
-
complianceGate,
|
|
4753
|
-
new SpendingGuardrails(options.maxSessionLoss),
|
|
4754
4493
|
new ErrorBudget(),
|
|
4755
4494
|
new AuditLogger(logger),
|
|
4756
4495
|
traceCollector
|
|
@@ -4760,7 +4499,6 @@ function createServerFactory(options) {
|
|
|
4760
4499
|
let currentTrustTier = null;
|
|
4761
4500
|
const executeOptions = {
|
|
4762
4501
|
traceCollector,
|
|
4763
|
-
complianceGate,
|
|
4764
4502
|
onTrustTier: (trustTier) => {
|
|
4765
4503
|
currentTrustTier = trustTier;
|
|
4766
4504
|
logger.info({ event: "trust_tier_applied", trustTier });
|
|
@@ -4801,8 +4539,7 @@ function createServerFactory(options) {
|
|
|
4801
4539
|
notificationService.stop();
|
|
4802
4540
|
notificationService = null;
|
|
4803
4541
|
}
|
|
4804
|
-
}
|
|
4805
|
-
setRegion: (region) => complianceGate.setRegion(region)
|
|
4542
|
+
}
|
|
4806
4543
|
};
|
|
4807
4544
|
};
|
|
4808
4545
|
return { logger, createMcpServer };
|
|
@@ -4889,10 +4626,6 @@ async function startHttpTransport(options) {
|
|
|
4889
4626
|
entry = { transport: transport2, server: createdServer, lastActivity: Date.now() };
|
|
4890
4627
|
sessions.set(newSessionId, entry);
|
|
4891
4628
|
logger.info({ event: "session_created", sessionId: newSessionId });
|
|
4892
|
-
const region = req.headers["cf-ipcountry"] ?? req.headers["x-vercel-ip-country"] ?? req.headers["x-country-code"];
|
|
4893
|
-
if (region && createdServer.setRegion) {
|
|
4894
|
-
createdServer.setRegion(region);
|
|
4895
|
-
}
|
|
4896
4629
|
transport2.onclose = () => {
|
|
4897
4630
|
createdServer.cleanup();
|
|
4898
4631
|
sessions.delete(newSessionId);
|
|
@@ -4987,8 +4720,6 @@ var baseUrl = readEnv("TOROMARKET_BASE_URL") ?? "https://api.toromarket.io";
|
|
|
4987
4720
|
var token = readEnv("TOROMARKET_TOKEN");
|
|
4988
4721
|
var apiKey = readEnv("TOROMARKET_API_KEY");
|
|
4989
4722
|
var logLevel = readEnv("TOROMARKET_LOG_LEVEL") ?? "info";
|
|
4990
|
-
var maxSessionLossStr = readEnv("TOROMARKET_MAX_SESSION_LOSS");
|
|
4991
|
-
var maxSessionLoss = maxSessionLossStr ? Number(maxSessionLossStr) : void 0;
|
|
4992
4723
|
var transport = readEnv("TOROMARKET_TRANSPORT") ?? "stdio";
|
|
4993
4724
|
var httpPort = Number(readEnv("TOROMARKET_HTTP_PORT") ?? "3001");
|
|
4994
4725
|
var agentSecret = readEnv("TOROMARKET_AGENT_SECRET");
|
|
@@ -5009,7 +4740,6 @@ var serverOptions = {
|
|
|
5009
4740
|
...token ? { token } : {},
|
|
5010
4741
|
...apiKey ? { apiKey } : {},
|
|
5011
4742
|
...agentSecret ? { agentSecret } : {},
|
|
5012
|
-
...maxSessionLoss ? { maxSessionLoss } : {},
|
|
5013
4743
|
...pollIntervalMs !== void 0 ? { pollIntervalMs } : {}
|
|
5014
4744
|
};
|
|
5015
4745
|
async function main() {
|