All docs

Get your tools/list

The JSON Forecall scores, the shapes and limits it accepts, and how to get it from your MCP server with forecall dump or the official SDKs.

On this page

Forecall scores the list of tools your MCP server returns for a tools/list request: each tool's name, description, inputSchema, and when present outputSchema and annotations. This is what an AI agent reads before it chooses a tool, so it is what the score is about.

Accepted formats

The web linter and the CLI read the same three shapes, with the same limits:

An array of tools
[{ "name": … }, …]
An object with a tools array
{ "tools": [ … ] }
A JSON-RPC response
{ "result": { "tools": [ … ] } }

Up to 1 MB (1,048,576 bytes) and 200 tools.

Keys must use the names of the MCP wire format, in camelCase: inputSchema, outputSchema, annotations.readOnlyHint. Keys in snake_case, such as input_schema, are refused with a list of where they are and how to rename them; Forecall does not convert them, so that what it scores is exactly what your server sends.

Get it from your server

The quickest way is forecall dump, which starts or connects to your server and writes its tools/list in the shape above:

npx forecall dump -o tools.json -- npx -y @modelcontextprotocol/server-filesystem .
npx forecall dump -o tools.json https://example.com/mcp --header "Authorization: Bearer $TOKEN"

Score with the CLI lists its options. To use your own client instead, connect to your server with an MCP SDK and print the result of tools/list. The examples below start a server over stdio; replace the command and its arguments with your own server's. Both print {"tools": [...]}, which you can save to a file.

TypeScript

With the official SDK (@modelcontextprotocol/client), listTools() reads every page of the list:

import { Client } from "@modelcontextprotocol/client";
import { StdioClientTransport } from "@modelcontextprotocol/client/stdio";

const client = new Client({ name: "dump-tools", version: "1.0.0" });
await client.connect(
  new StdioClientTransport({
    command: "npx",
    args: ["-y", "@modelcontextprotocol/server-filesystem", "."],
  }),
);
const { tools } = await client.listTools();
console.log(JSON.stringify({ tools }, null, 2));
await client.close();

Python

With the official SDK (mcp 2.x), list_tools() reads one page at a time, so follow next_cursor. Write the tools with by_alias=True: without it, the SDK writes snake_case keys such as input_schema, which Forecall refuses.

import asyncio
import json

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from mcp.types import PaginatedRequestParams


async def main():
    server = StdioServerParameters(
        command="npx", args=["-y", "@modelcontextprotocol/server-filesystem", "."]
    )
    async with stdio_client(server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools, cursor = [], None
            while True:
                params = PaginatedRequestParams(cursor=cursor) if cursor else None
                result = await session.list_tools(params=params)
                # by_alias=True writes the MCP names (inputSchema, not input_schema).
                tools += [t.model_dump(mode="json", by_alias=True, exclude_none=True) for t in result.tools]
                cursor = result.next_cursor
                if cursor is None:
                    break
            print(json.dumps({"tools": tools}, indent=2, ensure_ascii=False))


asyncio.run(main())

Score it

Paste the JSON at the linter, or score the file on your own machine with the CLI:

npx forecall lint tools.json

Do not paste tool definitions that are not public yet: anyone with a result's URL can see it. forecall lint sends nothing anywhere.