Skip to main content
Glama
sammcj

Bybit MCP Server

by sammcj

get_ml_rsi

Calculate ML-enhanced RSI values using the K-Nearest Neighbors algorithm, providing adaptive overbought/oversold levels and improved accuracy through pattern recognition on Bybit MCP Server.

Instructions

Get ML-enhanced RSI using K-Nearest Neighbors algorithm for pattern recognition. Provides adaptive overbought/oversold levels and enhanced RSI values based on historical pattern similarity.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
categoryYesCategory of the instrument
featureCountNoNumber of features to use 1-5 (default: 3)
intervalYesKline interval
knnLookbackNoHistorical period for pattern matching (default: 100)
knnNeighborsNoNumber of neighbors for KNN algorithm (default: 5)
limitNoNumber of data points to return (default: 200)
mlWeightNoML influence weight 0-1 (default: 0.4)
rsiLengthNoRSI calculation period (default: 14)
smoothingMethodNoSmoothing method to apply (default: none)
symbolYesTrading pair symbol (e.g., 'BTCUSDT')

Implementation Reference

  • Main handler function that orchestrates the entire get_ml_rsi tool execution: input validation, data fetching, RSI calculation, feature extraction, KNN processing, smoothing, and response formatting.
    async toolCall(request: z.infer<typeof CallToolRequestSchema>): Promise<CallToolResult> {
      const startTime = Date.now()
    
      try {
        this.logInfo("Starting get_ml_rsi tool call")
    
        // Parse and validate input
        const validationResult = inputSchema.safeParse(request.params.arguments)
        if (!validationResult.success) {
          const errorDetails = validationResult.error.errors.map(err => ({
            field: err.path.join('.'),
            message: err.message,
            code: err.code
          }))
          throw new Error(`Invalid input: ${JSON.stringify(errorDetails)}`)
        }
    
        const args = validationResult.data
    
        // Fetch kline data
        const klineData = await this.fetchKlineData(args)
    
        if (klineData.length < args.rsiLength + args.knnLookback) {
          throw new Error(`Insufficient data. Need at least ${args.rsiLength + args.knnLookback} data points, got ${klineData.length}`)
        }
    
        // Calculate standard RSI
        const closePrices = klineData.map(k => k.close)
        const rsiValues = calculateRSI(closePrices, args.rsiLength)
    
        if (rsiValues.length === 0) {
          throw new Error("Failed to calculate RSI values")
        }
    
        // Extract features for all data points
        const allFeatures: FeatureVector[] = []
        for (let i = 0; i < klineData.length; i++) {
          const features = extractFeatures(klineData, i, rsiValues, args.featureCount, args.knnLookback)
          allFeatures.push(features || { rsi: rsiValues[i] || 0 })
        }
    
        // Configure KNN
        const knnConfig: KNNConfig = {
          neighbors: args.knnNeighbors,
          lookbackPeriod: args.knnLookback,
          mlWeight: args.mlWeight,
          featureCount: args.featureCount
        }
    
        // Apply KNN enhancement
        const knnResults = batchProcessKNN(rsiValues, allFeatures, klineData, knnConfig)
    
        // Apply smoothing if requested
        const smoothedResults = this.applySmoothingToResults(knnResults, rsiValues, args.smoothingMethod)
    
        // Format response data
        const responseData = this.formatMLRSIData(klineData, rsiValues, smoothedResults, args.limit)
    
        const calculationTime = Date.now() - startTime
    
        const response: MLRSIResponse = {
          symbol: args.symbol,
          interval: args.interval,
          data: responseData,
          metadata: {
            mlEnabled: true,
            featuresUsed: this.getFeatureNames(args.featureCount),
            smoothingApplied: args.smoothingMethod,
            calculationTime,
            rsiLength: args.rsiLength,
            knnConfig
          }
        }
    
        this.logInfo(`ML-RSI calculation completed in ${calculationTime}ms`)
        return this.formatResponse(response)
    
      } catch (error) {
        this.logInfo(`ML-RSI calculation failed: ${error instanceof Error ? error.message : String(error)}`)
        return this.handleError(error)
      }
    }
  • Zod schema defining and validating the input parameters for the get_ml_rsi tool.
    const inputSchema = z.object({
      symbol: z.string()
        .min(1, "Symbol is required")
        .regex(/^[A-Z0-9]+$/, "Symbol must contain only uppercase letters and numbers"),
      category: z.enum(["spot", "linear", "inverse"]),
      interval: z.enum(["1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M"]),
      rsiLength: z.number().min(2).max(50).optional().default(14),
      knnNeighbors: z.number().min(1).max(50).optional().default(5),
      knnLookback: z.number().min(20).max(500).optional().default(100),
      mlWeight: z.number().min(0).max(1).optional().default(0.4),
      featureCount: z.number().min(1).max(5).optional().default(3),
      smoothingMethod: z.enum(["none", "kalman", "alma", "double_ema"]).optional().default("none"),
      limit: z.number().min(50).max(1000).optional().default(200)
    })
  • MCP-standard tool definition object containing name, description, and JSON input schema for get_ml_rsi.
    toolDefinition: Tool = {
      name: this.name,
      description: "Get ML-enhanced RSI using K-Nearest Neighbors algorithm for pattern recognition. Provides adaptive overbought/oversold levels and enhanced RSI values based on historical pattern similarity.",
      inputSchema: {
        type: "object",
        properties: {
          symbol: {
            type: "string",
            description: "Trading pair symbol (e.g., 'BTCUSDT')",
            pattern: "^[A-Z0-9]+$"
          },
          category: {
            type: "string",
            description: "Category of the instrument",
            enum: ["spot", "linear", "inverse"]
          },
          interval: {
            type: "string",
            description: "Kline interval",
            enum: ["1", "3", "5", "15", "30", "60", "120", "240", "360", "720", "D", "W", "M"]
          },
          rsiLength: {
            type: "number",
            description: "RSI calculation period (default: 14)",
            minimum: 2,
            maximum: 50
          },
          knnNeighbors: {
            type: "number",
            description: "Number of neighbors for KNN algorithm (default: 5)",
            minimum: 1,
            maximum: 50
          },
          knnLookback: {
            type: "number",
            description: "Historical period for pattern matching (default: 100)",
            minimum: 20,
            maximum: 500
          },
          mlWeight: {
            type: "number",
            description: "ML influence weight 0-1 (default: 0.4)",
            minimum: 0,
            maximum: 1
          },
          featureCount: {
            type: "number",
            description: "Number of features to use 1-5 (default: 3)",
            minimum: 1,
            maximum: 5
          },
          smoothingMethod: {
            type: "string",
            description: "Smoothing method to apply (default: none)",
            enum: ["none", "kalman", "alma", "double_ema"]
          },
          limit: {
            type: "number",
            description: "Number of data points to return (default: 200)",
            minimum: 50,
            maximum: 1000
          }
        },
        required: ["symbol", "category", "interval"]
      }
    }
  • Dynamic loader that discovers, imports, instantiates, and validates all tool classes in src/tools (including GetMLRSI), adding valid ones to the tools array.
    export async function loadTools(): Promise<BaseToolImplementation[]> {
      try {
        const toolsPath = await findToolsPath()
        const files = await fs.readdir(toolsPath)
        const tools: BaseToolImplementation[] = []
    
        for (const file of files) {
          if (!isToolFile(file)) {
            continue
          }
    
          try {
            const modulePath = `file://${join(toolsPath, file)}`
            const { default: ToolClass } = await import(modulePath)
    
            if (!ToolClass || typeof ToolClass !== 'function') {
              console.warn(JSON.stringify({
                type: "warning",
                message: `Invalid tool class in ${file}`
              }))
              continue
            }
    
            const tool = new ToolClass()
    
            if (
              tool instanceof BaseToolImplementation &&
              tool.name &&
              tool.toolDefinition &&
              typeof tool.toolCall === "function"
            ) {
              tools.push(tool)
              console.info(JSON.stringify({
                type: "info",
                message: `Loaded tool: ${tool.name}`
              }))
            } else {
              console.warn(JSON.stringify({
                type: "warning",
                message: `Invalid tool implementation in ${file}`
              }))
            }
          } catch (error) {
            console.error(JSON.stringify({
              type: "error",
              message: `Error loading tool from ${file}: ${error instanceof Error ? error.message : String(error)}`
            }))
          }
        }
    
        return tools
      } catch (error) {
        console.error(JSON.stringify({
          type: "error",
          message: `Failed to load tools: ${error instanceof Error ? error.message : String(error)}`
        }))
        return []
      }
    }
  • src/index.ts:129-152 (registration)
    Initializes the MCP server, loads all tools via toolLoader (including get_ml_rsi), creates toolsMap for dispatch, sets up ListTools and CallTool request handlers that use the loaded tools.
    async function main() {
      try {
        // Validate environment configuration
        validateEnv()
    
        const tools = await loadTools()
        toolsMap = createToolsMap(tools)
    
        if (tools.length === 0) {
          console.log(JSON.stringify(formatJsonRpcMessage(
            "warning",
            "No tools were loaded. Server will start but may have limited functionality."
          )))
        } else {
          console.log(JSON.stringify(formatJsonRpcMessage(
            "info",
            `Loaded ${tools.length} tools: ${tools.map(t => t.name).join(", ")}`
          )))
        }
    
        const transport = new StdioServerTransport()
        await server.connect(transport)
    
        console.log(JSON.stringify(formatJsonRpcMessage(
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It mentions the algorithm and outputs but fails to describe critical behaviors like computational intensity, rate limits, error handling, or response format. For a complex ML tool with 10 parameters, this omission leaves significant gaps in understanding how the tool operates beyond its basic function.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is appropriately sized with two sentences that efficiently convey the core purpose and outputs. It is front-loaded with the main action and avoids unnecessary details, though it could be slightly more structured by explicitly separating algorithm details from output descriptions. Overall, it earns its place without waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (10 parameters, ML algorithm) and lack of annotations and output schema, the description is incomplete. It does not explain the return values, error conditions, performance characteristics, or how outputs differ from standard RSI tools. For a sophisticated financial analysis tool, this leaves too much unspecified for reliable agent use.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds minimal value beyond the schema by hinting at 'historical pattern similarity' (related to knnLookback) and 'ML influence' (related to mlWeight), but it does not provide additional syntax, format details, or deeper insights into parameter interactions. Baseline 3 is appropriate as the schema handles most of the parameter documentation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose with specific verbs ('Get ML-enhanced RSI') and resources ('using K-Nearest Neighbors algorithm'), distinguishing it from sibling tools that focus on market data, orders, or wallet information. It explicitly mentions what it provides ('adaptive overbought/oversold levels and enhanced RSI values') and the method ('historical pattern similarity'), making it distinct and well-defined.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives, such as which sibling tools might be more appropriate for different scenarios (e.g., basic RSI vs. ML-enhanced). It lacks context on prerequisites, use cases, or exclusions, leaving the agent to infer usage based solely on the purpose statement without explicit direction.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sammcj/bybit-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server