Skip to content
everloot
Get the addon

MCP tools over plain HTTP

Call any MCP tool with a GET request, no MCP client needed.

Prerequisites#

  • curl, Node 18 or newer, or Python 3 with only its standard library. The curl examples also use jq to pick fields; leave it off to see the whole answer.
  • No account. Every MCP tool answers GET /api/tools/{tool}, with the tool's arguments as query parameters.
  • Without a key, this endpoint allows 30 requests an hour for each IP address. Limits and keys covers the free key.

Steps#

  1. Find the tool's parameters

    Each tool has a schema file at /schema/mcp/<tool>.json. Its inputSchema lists the parameters, which are required, and the allowed values. Its outputSchema describes the answer. The tool names are listed on the MCP overview.

    Read a schema
    curl -s https://everloot.app/schema/mcp/get_by_name.json | jq '{title, description, required: .inputSchema.required, properties: .inputSchema.properties}'

    So get_by_name needs a name, and takes an optional kind from the list shown. Numbers and booleans go in the query string as plain text, such as itemId=19019 or true and false.

  2. Call it with curl

    Call get_by_name
    curl -s "https://everloot.app/api/tools/get_by_name?name=Thrall" | jq '{tool, arguments, text, schema}'

    The answer has five fields. tool and arguments repeat what you asked, with defaults applied. answer is the tool's structured result, in the shape its outputSchema declares. text is the same answer as words, as an MCP client sees it. schema is the address of the schema file. The jq filter leaves answer out to keep this short; it holds the hits.

  3. Call it from JavaScript or Python

    This script takes a tool name and name=value pairs, so it works for any tool. It prints the words, then the keys of the structured answer. Errors go to standard error and the script exits with 1.

    tool
    // Call one MCP tool over HTTP. Run: node tool.mjs talent_tree class=warrior
    const BASE = "https://everloot.app";
    const [tool, ...pairs] = process.argv.slice(2);
    const query = new URLSearchParams(pairs.map((pair) => pair.split(/=(.*)/s).slice(0, 2)));
    
    const res = await fetch(`${BASE}/api/tools/${tool}?${query}`);
    const body = await res.json();
    
    if (res.status === 429) {
      console.error(`Rate limited. Try again in ${res.headers.get("retry-after")} seconds.`);
      process.exit(1);
    }
    if (!res.ok) {
      // 400 says which argument is wrong; 404 lists the tools.
      console.error(`${res.status}: ${body.error}`);
      process.exit(1);
    }
    
    console.log(body.text);               // the answer as words
    console.log(Object.keys(body.answer)); // the structured answer, in body.answer
    Run
    node tool.mjs get_by_name name=Thrall
    Output
    [lore_page/lore] Thrall
    Thrall (birthname Go'el), son of Durotan and Draka, is the former Warchief of the restored shamanistic Horde, founder of the nation of Durotar in Kalimdor and o…
    Source: https://warcraft.wiki.gg/wiki/Thrall (CC-BY-SA-4.0, retrieved 2023-11-13)
    [ 'hits' ]
  4. Handle a 400

    A wrong or missing parameter is a 400, and error says which one and where its schema is.

    A missing and a wrong argument
    curl -s -w "\n%{http_code}\n" "https://everloot.app/api/tools/get_by_name?kind=npc"
  5. Handle a 404

    An unknown tool name is a 404, and the error lists every tool.

    curl https://everloot.app/api/tools/nope
    {"ok":false,"error":"No tool named nope. Tools: search_knowledge, get_entity, get_by_name, search, fetch, answer_sources, profession_path, recipe_thresholds, item_page, npc_page, object_page, quest_page, vendor_page, world_map_near, entity_relations, item_categories, talent_tree, talent_plan, collection_gap, completion_progress, guild_board, auction_history, market_value, gold_ledger, population, recent_changes, ecosystem_projects, everloot_plan."}
    404
  6. Handle a 429

    Over the limit, the answer is a 429. The Retry-After header says how many seconds to wait, and the body says where to get a free key. The scripts above print the wait and stop. Do not retry before it is over. A key sent as Authorization: Bearer <key> raises the limit.

    A 429
    HTTP/1.1 429 Too Many Requests
    Cache-Control: no-store
    Content-Type: application/json
    Retry-After: 2758
    
    {"ok":false,"error":"Without a key this endpoint answers 30 requests per hour per address. Try again in 2758 seconds, or get a free key at https://everloot.app/docs/api/keys and send it as \"Authorization: Bearer <key>\".","getAKey":"https://everloot.app/docs/api/keys"}

What each status means#

StatusMeaningWhat to do
200The tool answered.Read answer or text.
400An argument is missing or wrong. error says which.Fix it from the schema file.
404No tool by that name. error lists them.Pick a name from the list.
429Over the hourly limit.Wait Retry-After seconds, or use a free key.
503The data could not be read. The answer is unrecorded, not zero.Try again later.

Check your work#

  • The curl call returns a JSON object with tool, arguments, answer, text and schema, and arguments echoes what you sent.
  • The JavaScript and Python scripts print the same text as curl.
  • Leaving out name gives the 400 above. A made-up tool name gives the 404.
  • Answers built on player uploads carry the credit line, in the text and in the X-Everloot-Credit header. Show it if you publish them.

A tool that reads player uploads can answer with "unrecorded", in the text and in the structured answer. That is an answer, not an error. It means players have not reported enough yet.

Next steps#