Skip to main content
Glama
JCF0

CG Alpha MCP

by JCF0

elfa_set_base

Configure the base URL for ELFA's sentiment data API to enable crypto market analysis with combined sentiment and technical indicators.

Instructions

Set ELFA base URL (e.g., https://api.elfa.ai).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
baseYes

Implementation Reference

  • The main handler function for the 'elfa_set_base' tool. It extracts the 'base' parameter from args, validates it as a URL, sets the global ELFA_BASE variable, and returns success or error response.
    "elfa_set_base": async (args) => {
      const base = args && args.base;
      try { const u = new URL(base); ELFA_BASE = u.toString().replace(/\/+$/,""); return { content: textContent({ ok:true, base: ELFA_BASE }) }; }
      catch { return { content: textContent({ ok:false, message:"Invalid URL for 'base'" }), isError:true }; }
    },
  • The schema definition for the 'elfa_set_base' tool, specifying input schema requiring a 'base' string, description, and annotations.
    { name:"elfa_set_base",
      description:"Set ELFA base URL (e.g., https://api.elfa.ai).",
      inputSchema:{ type:"object", properties:{ base:{type:"string"} }, required:["base"] },
      annotations:{ title:"ELFA: Set Base URL", readOnlyHint:false, openWorldHint:false }
    },
  • mcp-server.js:123-262 (registration)
    The toolHandlers object registers the 'elfa_set_base' handler function, which is used in tools/call method.
    const toolHandlers = {
      // Admin: set auth
      "elfa_set_auth": async (args) => {
        const key = args && args.key;
        const headerName = args && args.headerName;
        const scheme = args && args.scheme;
        if (!key || typeof key !== "string") return { content: textContent({ ok:false, message:"Missing 'key' (string)" }), isError:true };
        if (headerName && typeof headerName === "string") ELFA_AUTH.headerName = headerName;
        if (scheme !== undefined && typeof scheme === "string") ELFA_AUTH.scheme = scheme;
        ELFA_AUTH.key = key;
        return { content: textContent({ ok:true, headerName: ELFA_AUTH.headerName || "", scheme: ELFA_AUTH.scheme || "", key: maskKey(ELFA_AUTH.key) }) };
      },
    
      // Admin: set base URL
      "elfa_set_base": async (args) => {
        const base = args && args.base;
        try { const u = new URL(base); ELFA_BASE = u.toString().replace(/\/+$/,""); return { content: textContent({ ok:true, base: ELFA_BASE }) }; }
        catch { return { content: textContent({ ok:false, message:"Invalid URL for 'base'" }), isError:true }; }
      },
    
      // Admin: reload .env (no restart)
      "elfa_reload_env": async () => {
        loadEnvMulti();
        ELFA_AUTH = buildAuthFromEnv();
        ELFA_BASE = (process.env.ELFA_BASE || ELFA_BASE).replace(/\/+$/,"");
        return { content: textContent({
          ok: true,
          base: ELFA_BASE,
          loaded: ENV_INFO.loaded,
          from: ENV_INFO.from,
          vars: ENV_INFO.vars,
          auth: { headerName: ELFA_AUTH.headerName || "", scheme: ELFA_AUTH.scheme || "", key: maskKey(ELFA_AUTH.key) }
        }) };
      },
    
      // Admin: status
      "elfa_status": async () => {
        return { content: textContent({
          base: ELFA_BASE,
          loaded: ENV_INFO.loaded,
          from: ENV_INFO.from,
          vars: ENV_INFO.vars,
          auth: { headerName: ELFA_AUTH.headerName || "", scheme: ELFA_AUTH.scheme || "", key: maskKey(ELFA_AUTH.key) }
        }) };
      },
    
      // Generic proxy
      "elfa_query": async (args, meta) => {
        const path = args && args.path;
        if (!path || typeof path !== "string") return { content: textContent({ error:true, message:"Missing required 'path' (string)" }), isError:true };
        const method = (args.method || "GET").toUpperCase();
        const query  = args.query || undefined;
        const body   = args.body  || undefined;
        progressNotify(meta && meta.progressToken, 1, 3, "Calling ELFA");
        const { ok, status, data } = await elfaFetch(path, { method, query, body });
        progressNotify(meta && meta.progressToken, 2, 3, "Formatting result");
        if (!ok) return { content: textContent({ error:true, status, data }), isError:true, _meta:{ status } };
        progressNotify(meta && meta.progressToken, 3, 3, "Done");
        return { content: textContent({ ok:true, status, data }) };
      },
    
      // /v2/aggregations/trending-tokens  (this replaces the old /v2/data/trending)
      "elfa_trending": async (args, meta) => {
        return toolHandlers["elfa_trending_tokens"](args, meta);
      },
    
      "elfa_trending_tokens": async (args, meta) => {
        const query = {};
        if (args && args.timeframe !== undefined) query.timeframe = args.timeframe; // "24h","7d","30d"
        if (args && args.chain     !== undefined) query.chain     = args.chain;
        if (args && args.limit     !== undefined) query.limit     = args.limit;
        if (args && args.cursor    !== undefined) query.cursor    = args.cursor;
        return toolHandlers["elfa_query"]({ path: "/v2/aggregations/trending-tokens", method: "GET", query }, meta);
      },
    
      // /v2/data/token-news
      "elfa_token_news": async (args, meta) => {
        const query = {};
        if (args && args.symbols !== undefined) query.symbols = args.symbols; // "ETH,BTC"
        if (args && args.chain   !== undefined) query.chain   = args.chain;
        if (args && args.start   !== undefined) query.start   = args.start;
        if (args && args.end     !== undefined) query.end     = args.end;
        if (args && args.limit   !== undefined) query.limit   = args.limit;
        if (args && args.cursor  !== undefined) query.cursor  = args.cursor;
        if (args && args.sources !== undefined) query.sources = args.sources;
        return toolHandlers["elfa_query"]({ path: "/v2/data/token-news", method: "GET", query }, meta);
      },
    
      // /v2/data/keyword-mentions
      "elfa_keyword_mentions": async (args, meta) => {
        const query = {};
        if (args && args.keywords !== undefined) query.keywords = Array.isArray(args.keywords) ? args.keywords.join(",") : String(args.keywords);
        if (args && args.start   !== undefined) query.start   = args.start;
        if (args && args.end     !== undefined) query.end     = args.end;
        if (args && args.chain   !== undefined) query.chain   = args.chain;
        if (args && args.limit   !== undefined) query.limit   = args.limit;
        if (args && args.cursor  !== undefined) query.cursor  = args.cursor;
        if (args && args.sources !== undefined) query.sources = args.sources;
        return toolHandlers["elfa_query"]({ path: "/v2/data/keyword-mentions", method: "GET", query }, meta);
      },
    
      // ----- TA tools -----
    
      // Compute RSI on an array of closes (oldest → newest)
      "ta_rsi": async (args) => {
        const values = Array.isArray(args?.values) ? args.values : null;
        const period = Number.isFinite(Number(args?.period)) ? Number(args.period) : 14;
        if (!values || values.length === 0) {
          return { content: textContent({ error:true, message:"'values' must be a non-empty array of numbers (oldest → newest)" }), isError:true };
        }
        const out = taRSI(values, period);
        return { content: textContent({ ok:true, rsi: out, period }) };
      },
    
      // Compute Bollinger Bands on an array of closes (oldest → newest)
      "ta_bollinger": async (args) => {
        const values = Array.isArray(args?.values) ? args.values : null;
        const period = Number.isFinite(Number(args?.period)) ? Number(args.period) : 20;
        const mult   = Number.isFinite(Number(args?.mult))   ? Number(args.mult)   : 2;
        if (!values || values.length === 0) {
          return { content: textContent({ error:true, message:"'values' must be a non-empty array of numbers (oldest → newest)" }), isError:true };
        }
        const out = taBoll(values, period, mult);
        return { content: textContent({ ok:true, ...out, period, mult }) };
      },
    
      // Convenience: return both indicators in one call
      "ta_summary": async (args) => {
        const values = Array.isArray(args?.values) ? args.values : null;
        const rsiPeriod = Number.isFinite(Number(args?.rsiPeriod)) ? Number(args.rsiPeriod) : 14;
        const bbPeriod  = Number.isFinite(Number(args?.bbPeriod))  ? Number(args.bbPeriod)  : 20;
        const bbMult    = Number.isFinite(Number(args?.bbMult))    ? Number(args.bbMult)    : 2;
        if (!values || values.length === 0) {
          return { content: textContent({ error:true, message:"'values' must be a non-empty array of numbers (oldest → newest)" }), isError:true };
        }
        const rsiVal = taRSI(values, rsiPeriod);
        const bbVal  = taBoll(values, bbPeriod, bbMult);
        return { content: textContent({ ok:true, rsi: rsiVal, bollinger: bbVal, rsiPeriod, bbPeriod, bbMult }) };
      }
    };
Behavior3/5

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

Annotations indicate this is not read-only and not open-world, but the description adds minimal behavioral context beyond this. It specifies what gets configured (the base URL) but doesn't mention persistence, scope (session vs. global), validation of the URL format, or error behavior. The description doesn't contradict annotations but adds only basic operational context.

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

Conciseness5/5

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

The description is a single, efficient sentence that immediately conveys the tool's purpose with a helpful example. There's no wasted text, and it's appropriately sized for a simple configuration tool.

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

Completeness3/5

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

For a simple configuration tool with 1 parameter and no output schema, the description is minimally adequate. It explains what the tool does but lacks important context about when and why to use it, how it interacts with other ELFA tools, and what happens after configuration. The annotations provide basic safety hints but don't fill these gaps.

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

Parameters4/5

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

With only 1 parameter and 0% schema description coverage, the description compensates well by explaining that the 'base' parameter should be a URL like 'https://api.elfa.ai'. This provides clear semantic meaning beyond the schema's type information, though it could be more explicit about format requirements or constraints.

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

Purpose4/5

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

The description clearly states the action ('Set') and the resource ('ELFA base URL'), providing a specific example of what the base URL should look like. However, it doesn't explicitly differentiate this tool from its sibling 'elfa_set_auth', which likely handles authentication settings rather than base URL configuration.

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. It doesn't mention prerequisites (like whether authentication must be set first), when this configuration is needed, or how it relates to sibling tools like 'elfa_set_auth' or 'elfa_reload_env'.

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

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/JCF0/cg-alpha-mcp'

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