# MCP Tools Token Profiler and Schema Linter (`leadharbor/mcp-tools-token-profiler`) Actor

Paste an MCP tools/list or give public server URLs. Get estimated tokens per tool, the 5 heaviest tools, ranked savings with exact JSON paths, and lint cited to the MCP spec. No login.

- **URL**: https://apify.com/leadharbor/mcp-tools-token-profiler.md
- **Developed by:** [Mohammed Samaan](https://apify.com/leadharbor) (community)
- **Categories:** AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 server or tools list profileds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## MCP Tools Token Profiler and Schema Linter

Every tool an MCP server lists is sent to the model in each request, so a heavy `tools/list` costs context on every call. This actor reads a `tools/list` result and tells you where the tokens go, what could be shortened, and which definitions break rules in the MCP specification.

Give it a pasted `tools/list` JSON or the URL of a public MCP server (no login). For each input you get one row with:

- estimated tokens per tool, split into name, description, input schema, output schema and the rest, plus server totals
- the 5 heaviest tools and their share of the total
- ranked savings ideas, each with the exact JSON path and the tokens that would be saved if you shortened it (computed by counting the edited text again)
- lint findings with the exact JSON path, a minimal fix, and the specification sentence the rule comes from

**All token numbers are estimates** from an OpenAI-style vocabulary (`o200k_base` or `cl100k_base`). They are not counts for Claude, Gemini or any other model, and they leave out any framing a provider adds around tool definitions. Use them to compare tools and servers with each other and to find the heavy spots.

### Sample input

```json
{
  "toolsListJson": "{\"tools\":[{\"name\":\"search docs\",\"description\":\"Search documents.\\n\\nParameters:\\n- query: the text to find\\n- tags: labels to match\",\"inputSchema\":{\"type\":\"object\",\"properties\":{\"query\":{\"type\":\"string\",\"description\":\"the text to find\"},\"tags\":{\"type\":\"array\",\"description\":\"labels to match\"}},\"required\":[\"query\"]}}]}",
  "serverUrls": ["https://docs.mcp.cloudflare.com/mcp"],
  "tokenizer": "o200k_base"
}
```

### Sample output

Real output of a local run for the pasted list above (shortened: the `tools` array, `source` and timestamps are left out here):

```json
{
  "status": "ok",
  "tokenizer": "o200k_base",
  "tokenCountsAreEstimates": true,
  "toolCount": 1,
  "totals": { "toolCount": 1, "nameTokens": 2, "descriptionTokens": 19, "inputSchemaTokens": 39, "outputSchemaTokens": 0, "otherTokens": 0, "totalTokens": 60 },
  "heaviestTools": [ { "rank": 1, "index": 0, "name": "search docs", "totalTokens": 60, "share": 1 } ],
  "suggestions": [
    {
      "rank": 1,
      "kind": "description-repeats-parameters",
      "path": "/tools/0/description",
      "currentTokens": 19,
      "savedTokensEstimate": 16,
      "message": "The description restates 2 parameter(s) (query, tags) that inputSchema already describes. Remove those lines from the description."
    }
  ],
  "lint": {
    "counts": { "error": 0, "warning": 1, "info": 1 },
    "issues": [
      {
        "ruleId": "tool-name-chars",
        "severity": "warning",
        "path": "/tools/0/name",
        "message": "Name \"search docs\" contains characters outside A-Z, a-z, 0-9, \"_\", \"-\" and \".\".",
        "fix": "Rename using only A-Z, a-z, 0-9, underscore, hyphen and dot (for example replace spaces with \"_\")."
      },
      {
        "ruleId": "schema-array-items-missing",
        "severity": "info",
        "path": "/tools/0/inputSchema/properties/tags",
        "message": "Array without \"items\": the element type is not described.",
        "fix": "Add \"items\", for example \"items\": { \"type\": \"string\" }."
      }
    ]
  },
  "warnings": [],
  "error": null
}
```

A row for a server that could not be read has `"status": "error"`, a specific `error` text and an `errorKind` (`auth`, `not-mcp`, `session`, `protocol`, `unreachable`, `timeout`, `too-large`, `unsafe-url`, `invalid-input`, `internal`, `unexpected`). It is never charged, and a failure on one input never stops the other inputs.

### Input

| Field | Type | What it does |
|---|---|---|
| `toolsListJson` | text | A pasted `tools/list` result. Accepted: a bare array of tools, an object with a `tools` array, or the full JSON-RPC response. At most 10,000 tools and 8 million characters. |
| `serverUrls` | list of URLs | Public Streamable HTTP MCP endpoints that need no login, at most 50. |
| `tokenizer` | `o200k_base` or `cl100k_base` | Vocabulary used for the estimates. Default `o200k_base`. |

Give at least one of `toolsListJson` and `serverUrls`. Both may be given; each is its own row.

### How a server is read

The actor never sends credentials and only sends `tools/list` and the handshake messages. It first tries the stateless form (specification revision 2026-07-28: `tools/list` with the per-request `_meta` fields and the `MCP-Protocol-Version` header). If the server answers with an error that is not a modern version error, it falls back to the handshake of revisions 2025-03-26 to 2025-11-25: `initialize`, `notifications/initialized`, then `tools/list` with the `MCP-Session-Id` the server returned, following `nextCursor` pages (up to 100 pages). JSON and Server-Sent Events responses are both handled, and the session is closed with `DELETE` when the server opened one.

Requests identify themselves with the User-Agent `ApifyActor-mcp-tools-token-profiler/0.1 (+https://apify.com/leadharbor/mcp-tools-token-profiler)`. Responses larger than 8 MB (also after decompression) are refused.

Addresses are checked before every request and every redirect: private, loopback, link-local and other reserved addresses are refused, only ports 80 and 443 are allowed, and the connection is pinned to the checked address.

Tested live on 21 Oct 2026 against `https://mcp.deepwiki.com/mcp` (handshake form) and `https://docs.mcp.cloudflare.com/mcp` (stateless form). Servers that require a login (HTTP 401 or 403) are reported as such and are not profiled.

### Lint rules and their sources

Only rules with a quotable source are implemented. Revisions read from the `modelcontextprotocol/modelcontextprotocol` repository on 21 Oct 2026: 2025-11-25 and 2026-07-28. JSON Schema rules come from the 2020-12 specification (MCP schemas default to that dialect).

| Rule id | Severity | Rule and source |
|---|---|---|
| `tool-not-object` | error | Every entry of `tools` is a `Tool` object. [schema ListToolsResult](https://modelcontextprotocol.io/specification/2025-11-25/schema#listtoolsresult) |
| `tool-name-missing` | error | `name: string` is required (`BaseMetadata`). [schema.ts](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/schema/2025-11-25/schema.ts) |
| `tool-name-length` | warning | "Tool names SHOULD be between 1 and 128 characters in length (inclusive)." [Tool names](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names) |
| `tool-name-chars` | warning | Only A-Z, a-z, 0-9, `_`, `-`, `.` are allowed; no spaces, commas or other special characters. [Tool names](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names) |
| `tool-name-duplicate` | warning | "Tool names SHOULD be unique within a server." Names are compared case-sensitively, as the same page says they SHOULD be considered case-sensitive. [Tool names](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names) |
| `description-missing` | info | `description` is optional; it is "a hint to the model". A quality hint, not a violation. [schema Tool](https://modelcontextprotocol.io/specification/2025-11-25/schema#tool) |
| `input-schema-missing` | error | `inputSchema` "MUST be a valid JSON Schema object (not null)". [Tool](https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool) |
| `input-schema-root-type` | error | `inputSchema` is declared with `type: "object"` at the root. [schema Tool](https://modelcontextprotocol.io/specification/2025-11-25/schema#tool) |
| `output-schema-root-type` | warning | Revision 2025-11-25 restricts `outputSchema` to `type: "object"` at the root; 2026-07-28 allows any schema, so this only matters for clients on the older revision. [schema Tool](https://modelcontextprotocol.io/specification/2025-11-25/schema#tool) |
| `schema-dialect` | info | Without `$schema` the default is 2020-12; implementations MUST support 2020-12 and only SHOULD document other dialects. [JSON Schema usage](https://modelcontextprotocol.io/specification/2026-07-28/basic#json-schema-usage) |
| `schema-type-invalid` | error | `type` is one of the seven JSON Schema names, or an array of unique ones. [Validation 6.1.1](https://json-schema.org/draft/2020-12/json-schema-validation#name-type) |
| `schema-enum-empty` | warning | `enum` MUST be an array and SHOULD have at least one element. [Validation 6.1.2](https://json-schema.org/draft/2020-12/json-schema-validation#name-enum) |
| `schema-enum-duplicate` | warning | Elements of `enum` SHOULD be unique. [Validation 6.1.2](https://json-schema.org/draft/2020-12/json-schema-validation#name-enum) |
| `schema-required-invalid` | error | `required` MUST be an array of unique strings. [Validation 6.5.3](https://json-schema.org/draft/2020-12/json-schema-validation#name-required) |
| `schema-array-items-missing` | info | Heuristic from JSON Schema 2020-12 (not a quotation): without `items` (or `prefixItems`) array elements are unconstrained. Valid, but gives the model no element type. [Core 10.3.1.2](https://json-schema.org/draft/2020-12/json-schema-core#name-items) |
| `annotations-invalid` | warning | `annotations` is an object; the four hints are booleans and `title` is a string. [schema ToolAnnotations](https://modelcontextprotocol.io/specification/2025-11-25/schema#toolannotations) |
| `x-mcp-header-invalid` | error | `x-mcp-header` must be a non-empty HTTP header token and sit on a string, integer or boolean parameter (not number). Revision 2026-07-28. [x-mcp-header](https://modelcontextprotocol.io/specification/2026-07-28/server/tools#x-mcp-header) |
| `x-mcp-header-union-type` | info | `x-mcp-header` "MUST only be applied to parameters with primitive types (integer, string, boolean)". The text does not say whether a union such as `["string","null"]` counts, so a union is a note, not an error. Revision 2026-07-28. [x-mcp-header](https://modelcontextprotocol.io/specification/2026-07-28/server/tools#x-mcp-header) |
| `schema-too-deep` | warning | Not a specification rule: an analysis limit. A tool whose JSON nests deeper than 200 levels is reported and left out of the counts. [JSON Schema usage](https://modelcontextprotocol.io/specification/2026-07-28/basic#json-schema-usage) |
| `x-mcp-header-duplicate` | error | `x-mcp-header` values must be case-insensitively unique within an `inputSchema`. Revision 2026-07-28. [x-mcp-header](https://modelcontextprotocol.io/specification/2026-07-28/server/tools#x-mcp-header) |

Not implemented, on purpose: rules about what a specific model provider accepts (for example strict-mode restrictions or dropped `format` keywords). I could not read those from a source I can cite here, so the actor does not guess.

### Savings ideas (heuristics, not specification rules)

The thresholds are our own choices. Each saving is computed by counting the edited text again with the same tokenizer, and is labelled an estimate. Ideas can overlap, so do not add the savings of different ideas together.

| Kind | Fires when | Saving computed as |
|---|---|---|
| `description-repeats-parameters` | the description has lines that restate parameters which the schema already describes | description tokens minus tokens after removing those lines |
| `large-enum` | an `enum` costs 150 tokens or more | schema tokens minus tokens with the `enum` removed (validation is lost, so check first) |
| `long-description` | a tool description is 300+ tokens or a property description 100+ | tokens minus tokens of its first paragraph (or sentence) |
| `redundant-schema-keyword` | `$schema` names 2020-12, the default | sum over the listed paths |
| `duplicate-description` | several tools share the same description of 30+ tokens | copies after the first |
| `deep-nesting` | schemas nest more than 4 levels | none: only the subtree size is shown |

### Pricing

Pay per event: **$0.005 per input successfully profiled** (event `schema-profiled`): one pasted list or one server URL. Inputs that fail (unreachable, login required, not MCP, invalid JSON, empty list, too many tools) produce an error row and are not charged. The run stops cleanly when your maximum charge limit is reached and says so in the status message and the run summary.

Worked example: 1 pasted list plus 10 server URLs where 7 answer and 3 need a login: 8 profiled inputs, 3 error rows, **$0.04**.

### Use with AI agents / MCP

Send this input and read these fields from each dataset row:

```json
{ "serverUrls": ["https://example.com/mcp"], "tokenizer": "o200k_base" }
```

| Read this | To learn |
|---|---|
| `status`, `error`, `errorKind` | whether the server could be read and why not |
| `totalTokens`, `toolCount` | estimated size of the whole tool list |
| `heaviestTools[]` | which 5 tools to look at first |
| `suggestions[]` | `path`, `savedTokensEstimate`, `message` for each ranked idea |
| `lint.counts`, `lint.issues[]` | `ruleId`, `severity`, `path`, `fix`, `source` for each finding |

The dataset item can be fetched through the Apify API once the run finishes. `RUN_SUMMARY` in the key-value store has run-level counts.

### Limits

- Tokens are estimates for the chosen OpenAI-style vocabulary only; no Claude or Gemini counts.
- `totalTokens` is the sum of separately counted parts (name, description, input schema, output schema, other fields), so rows and totals add up exactly. It is not the count of the whole definition tokenized as one string.
- Strings over 100,000 characters are counted by sampling 24 slices; the row says so in `warnings`. Sampling error was measured at 0.05 to 2.05 percent (our run on English and JSON text of 120k to 600k characters) and 0.1 to 1.2 percent (independent review); CJK-heavy text over that size has not been independently verified.
- Tools whose JSON nests deeper than 200 levels are not analysed (lint rule `schema-too-deep`).
- Memory: the default is 1024 MB. Profiling three 7.3 MB server responses in one run peaked at about 340 MB resident memory in a local measurement (a run with batches of two peaked at 356 to 377 MB, so limiting parallel fetches did not help; the report building dominates).
- A row lists at most 1,000 lint issues and 100 savings ideas for very large lists; `lint.counts`, `lint.byRule` and `suggestionCount` always hold the full numbers.
- Only Streamable HTTP servers that need no login can be read by URL. Servers on stdio, the old HTTP+SSE transport, or behind OAuth or API keys cannot; paste their `tools/list` instead.
- The stateless read (revision 2026-07-28) was seen working on two public servers. Other servers may differ; a failure is reported with the server's own error.
- Lint reports what the specification text says. A server can be perfectly usable and still get warnings or info findings.

### FAQ

**Where do I get a `tools/list` to paste?** From your MCP client's debug output, from the MCP Inspector, or by sending `{"jsonrpc":"2.0","id":1,"method":"tools/list"}` to a server after `initialize`.

**Why do rows and totals sometimes differ from my provider's token count?** Different vocabularies and different framing around tools. Compare relatively, not absolutely.

**Is my pasted JSON stored?** It is processed in the run and only the profile is written to your dataset. Run storage follows your Apify account settings.

**Can it fix my schemas?** No. It points at the path and describes the minimal edit.

### Open-source credits

| Package | Version | Licence | Checked from |
|---|---|---|---|
| js-tiktoken | 1.0.21 | MIT | `license` field in its `package.json`; the repository `dqbd/tiktoken` root `LICENSE` is MIT (the npm tarball ships no LICENSE file) |
| base64-js (dependency of js-tiktoken) | 1.5.1 | MIT | `node_modules/base64-js/LICENSE` |
| apify (Apify SDK for JavaScript) | 3.7.2 | Apache-2.0 | `node_modules/apify/LICENSE.md` |
| caniuse-lite (transitive, via the SDK; browser data) | 1.0.30001817 | CC-BY-4.0 | `node_modules/caniuse-lite/LICENSE` (Creative Commons Attribution 4.0); attribution: caniuse.com data |
| map-stream (transitive) | 0.1.0 | MIT text, no `license` field in its package.json | `node_modules/map-stream/LICENCE` (Copyright 2011 Dominic Tarr, MIT permission text) |
| pause-stream (transitive) | 0.0.11 | MIT and Apache-2.0 (dual) | `node_modules/pause-stream/LICENSE` ("Dual Licensed MIT and Apache 2") |
| sax (transitive) | 1.6.1 | BlueOak-1.0.0 | `node_modules/sax/LICENSE.md` (Blue Oak Model License 1.0.0) |
| tslib (transitive) | 2.8.1 | 0BSD | `node_modules/tslib/LICENSE.txt` |
| type-fest (transitive) | 2.19.0 and 4.41.0 | MIT or CC0-1.0 | `license-mit` and `license-cc0` files in each package |

The remaining transitive packages in the image are MIT, Apache-2.0, ISC, BSD-2-Clause or BSD-3-Clause according to the `license` field of the lockfile; the packages above are the ones outside that list or without a plain `license` field.

Tests use tool lists captured on 21 Oct 2026 from the published `@modelcontextprotocol/server-filesystem`, `server-memory`, `server-everything` and `server-sequential-thinking` packages (version 2.0.0) of the `modelcontextprotocol/servers` repository. That repository's LICENSE states a transition from MIT to Apache-2.0 (new contributions Apache-2.0, earlier ones MIT); both permit this use with attribution.

# Actor input Schema

## `toolsListJson` (type: `string`):

The result of an MCP tools/list call. Accepted: a bare array of tool definitions, an object with a "tools" array, or the full JSON-RPC response. Up to 10,000 tools and 8 million characters. Leave empty if you only use server URLs.

## `serverUrls` (type: `array`):

Streamable HTTP endpoints of public MCP servers that need no login, for example https://example.com/mcp. The actor sends initialize and tools/list only; it never sends credentials and refuses private or local addresses. At most 50.

## `tokenizer` (type: `string`):

Vocabulary used for the token estimates. Counts are estimates for this vocabulary, not counts for Claude, Gemini or any other model.

## Actor input object example

```json
{
  "toolsListJson": "{\"tools\":[{\"name\":\"sequentialthinking\",\"title\":\"Sequential Thinking\",\"description\":\"A detailed tool for dynamic and reflective problem-solving through thoughts.\\nThis tool helps analyze problems through a flexible thinking process that can adapt and evolve.\\nEach thought can build on, question, or revise previous insights as understanding deepens.\\n\\nWhen to use this tool:\\n- Breaking down complex problems into steps\\n- Planning and design with room for revision\\n- Analysis that might need course correction\\n- Problems where the full scope might not be clear initially\\n- Problems that require a multi-step solution\\n- Tasks that need to maintain context over multiple steps\\n- Situations where irrelevant information needs to be filtered out\\n\\nKey features:\\n- You can adjust total_thoughts up or down as you progress\\n- You can question or revise previous thoughts\\n- You can add more thoughts even after reaching what seemed like the end\\n- You can express uncertainty and explore alternative approaches\\n- Not every thought needs to build linearly - you can branch or backtrack\\n- Generates a solution hypothesis\\n- Verifies the hypothesis based on the Chain of Thought steps\\n- Repeats the process until satisfied\\n- Provides a correct answer\\n\\nParameters explained:\\n- thought: Your current thinking step, which can include:\\n  * Regular analytical steps\\n  * Revisions of previous thoughts\\n  * Questions about previous decisions\\n  * Realizations about needing more analysis\\n  * Changes in approach\\n  * Hypothesis generation\\n  * Hypothesis verification\\n- nextThoughtNeeded: True if you need more thinking, even if at what seemed like the end\\n- thoughtNumber: Current number in sequence (can go beyond initial total if needed)\\n- totalThoughts: Current estimate of thoughts needed (can be adjusted up/down)\\n- isRevision: A boolean indicating if this thought revises previous thinking\\n- revisesThought: If is_revision is true, which thought number is being reconsidered\\n- branchFromThought: If branching, which thought number is the branching point\\n- branchId: Identifier for the current branch (if any)\\n- needsMoreThoughts: If reaching end but realizing more thoughts needed\\n\\nYou should:\\n1. Start with an initial estimate of needed thoughts, but be ready to adjust\\n2. Feel free to question or revise previous thoughts\\n3. Don't hesitate to add more thoughts if needed, even at the \\\"end\\\"\\n4. Express uncertainty when present\\n5. Mark thoughts that revise previous thinking or branch into new paths\\n6. Ignore information that is irrelevant to the current step\\n7. Generate a solution hypothesis when appropriate\\n8. Verify the hypothesis based on the Chain of Thought steps\\n9. Repeat the process until satisfied with the solution\\n10. Provide a single, ideally correct answer as the final output\\n11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached\",\"inputSchema\":{\"type\":\"object\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"thought\":{\"type\":\"string\",\"description\":\"Your current thinking step\"},\"nextThoughtNeeded\":{\"description\":\"Whether another thought step is needed\",\"type\":[\"boolean\",\"string\"]},\"thoughtNumber\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991,\"description\":\"Current thought number (numeric value, e.g., 1, 2, 3)\"},\"totalThoughts\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991,\"description\":\"Estimated total thoughts needed (numeric value, e.g., 5, 10)\"},\"isRevision\":{\"description\":\"Whether this revises previous thinking\",\"type\":[\"boolean\",\"string\"]},\"revisesThought\":{\"description\":\"Which thought is being reconsidered\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"branchFromThought\":{\"description\":\"Branching point thought number\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"branchId\":{\"description\":\"Branch identifier\",\"type\":\"string\"},\"needsMoreThoughts\":{\"description\":\"If more thoughts are needed\",\"type\":[\"boolean\",\"string\"]}},\"required\":[\"thought\",\"nextThoughtNeeded\",\"thoughtNumber\",\"totalThoughts\"]},\"annotations\":{\"readOnlyHint\":false,\"destructiveHint\":false,\"idempotentHint\":false,\"openWorldHint\":false},\"outputSchema\":{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"type\":\"object\",\"properties\":{\"thoughtNumber\":{\"type\":\"number\"},\"totalThoughts\":{\"type\":\"number\"},\"nextThoughtNeeded\":{\"type\":\"boolean\"},\"branches\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"thoughtHistoryLength\":{\"type\":\"number\"}},\"required\":[\"thoughtNumber\",\"totalThoughts\",\"nextThoughtNeeded\",\"branches\",\"thoughtHistoryLength\"],\"additionalProperties\":false}}]}",
  "tokenizer": "o200k_base"
}
```

# Actor output Schema

## `profiles` (type: `string`):

Table of profiles: estimated tokens, heaviest tool, lint counts, status.

## `summary` (type: `string`):

Counts of profiled, failed and skipped inputs for this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "toolsListJson": "{\"tools\":[{\"name\":\"sequentialthinking\",\"title\":\"Sequential Thinking\",\"description\":\"A detailed tool for dynamic and reflective problem-solving through thoughts.\\nThis tool helps analyze problems through a flexible thinking process that can adapt and evolve.\\nEach thought can build on, question, or revise previous insights as understanding deepens.\\n\\nWhen to use this tool:\\n- Breaking down complex problems into steps\\n- Planning and design with room for revision\\n- Analysis that might need course correction\\n- Problems where the full scope might not be clear initially\\n- Problems that require a multi-step solution\\n- Tasks that need to maintain context over multiple steps\\n- Situations where irrelevant information needs to be filtered out\\n\\nKey features:\\n- You can adjust total_thoughts up or down as you progress\\n- You can question or revise previous thoughts\\n- You can add more thoughts even after reaching what seemed like the end\\n- You can express uncertainty and explore alternative approaches\\n- Not every thought needs to build linearly - you can branch or backtrack\\n- Generates a solution hypothesis\\n- Verifies the hypothesis based on the Chain of Thought steps\\n- Repeats the process until satisfied\\n- Provides a correct answer\\n\\nParameters explained:\\n- thought: Your current thinking step, which can include:\\n  * Regular analytical steps\\n  * Revisions of previous thoughts\\n  * Questions about previous decisions\\n  * Realizations about needing more analysis\\n  * Changes in approach\\n  * Hypothesis generation\\n  * Hypothesis verification\\n- nextThoughtNeeded: True if you need more thinking, even if at what seemed like the end\\n- thoughtNumber: Current number in sequence (can go beyond initial total if needed)\\n- totalThoughts: Current estimate of thoughts needed (can be adjusted up/down)\\n- isRevision: A boolean indicating if this thought revises previous thinking\\n- revisesThought: If is_revision is true, which thought number is being reconsidered\\n- branchFromThought: If branching, which thought number is the branching point\\n- branchId: Identifier for the current branch (if any)\\n- needsMoreThoughts: If reaching end but realizing more thoughts needed\\n\\nYou should:\\n1. Start with an initial estimate of needed thoughts, but be ready to adjust\\n2. Feel free to question or revise previous thoughts\\n3. Don't hesitate to add more thoughts if needed, even at the \\\"end\\\"\\n4. Express uncertainty when present\\n5. Mark thoughts that revise previous thinking or branch into new paths\\n6. Ignore information that is irrelevant to the current step\\n7. Generate a solution hypothesis when appropriate\\n8. Verify the hypothesis based on the Chain of Thought steps\\n9. Repeat the process until satisfied with the solution\\n10. Provide a single, ideally correct answer as the final output\\n11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached\",\"inputSchema\":{\"type\":\"object\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"thought\":{\"type\":\"string\",\"description\":\"Your current thinking step\"},\"nextThoughtNeeded\":{\"description\":\"Whether another thought step is needed\",\"type\":[\"boolean\",\"string\"]},\"thoughtNumber\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991,\"description\":\"Current thought number (numeric value, e.g., 1, 2, 3)\"},\"totalThoughts\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991,\"description\":\"Estimated total thoughts needed (numeric value, e.g., 5, 10)\"},\"isRevision\":{\"description\":\"Whether this revises previous thinking\",\"type\":[\"boolean\",\"string\"]},\"revisesThought\":{\"description\":\"Which thought is being reconsidered\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"branchFromThought\":{\"description\":\"Branching point thought number\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"branchId\":{\"description\":\"Branch identifier\",\"type\":\"string\"},\"needsMoreThoughts\":{\"description\":\"If more thoughts are needed\",\"type\":[\"boolean\",\"string\"]}},\"required\":[\"thought\",\"nextThoughtNeeded\",\"thoughtNumber\",\"totalThoughts\"]},\"annotations\":{\"readOnlyHint\":false,\"destructiveHint\":false,\"idempotentHint\":false,\"openWorldHint\":false},\"outputSchema\":{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"type\":\"object\",\"properties\":{\"thoughtNumber\":{\"type\":\"number\"},\"totalThoughts\":{\"type\":\"number\"},\"nextThoughtNeeded\":{\"type\":\"boolean\"},\"branches\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"thoughtHistoryLength\":{\"type\":\"number\"}},\"required\":[\"thoughtNumber\",\"totalThoughts\",\"nextThoughtNeeded\",\"branches\",\"thoughtHistoryLength\"],\"additionalProperties\":false}}]}"
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadharbor/mcp-tools-token-profiler").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "toolsListJson": "{\"tools\":[{\"name\":\"sequentialthinking\",\"title\":\"Sequential Thinking\",\"description\":\"A detailed tool for dynamic and reflective problem-solving through thoughts.\\nThis tool helps analyze problems through a flexible thinking process that can adapt and evolve.\\nEach thought can build on, question, or revise previous insights as understanding deepens.\\n\\nWhen to use this tool:\\n- Breaking down complex problems into steps\\n- Planning and design with room for revision\\n- Analysis that might need course correction\\n- Problems where the full scope might not be clear initially\\n- Problems that require a multi-step solution\\n- Tasks that need to maintain context over multiple steps\\n- Situations where irrelevant information needs to be filtered out\\n\\nKey features:\\n- You can adjust total_thoughts up or down as you progress\\n- You can question or revise previous thoughts\\n- You can add more thoughts even after reaching what seemed like the end\\n- You can express uncertainty and explore alternative approaches\\n- Not every thought needs to build linearly - you can branch or backtrack\\n- Generates a solution hypothesis\\n- Verifies the hypothesis based on the Chain of Thought steps\\n- Repeats the process until satisfied\\n- Provides a correct answer\\n\\nParameters explained:\\n- thought: Your current thinking step, which can include:\\n  * Regular analytical steps\\n  * Revisions of previous thoughts\\n  * Questions about previous decisions\\n  * Realizations about needing more analysis\\n  * Changes in approach\\n  * Hypothesis generation\\n  * Hypothesis verification\\n- nextThoughtNeeded: True if you need more thinking, even if at what seemed like the end\\n- thoughtNumber: Current number in sequence (can go beyond initial total if needed)\\n- totalThoughts: Current estimate of thoughts needed (can be adjusted up/down)\\n- isRevision: A boolean indicating if this thought revises previous thinking\\n- revisesThought: If is_revision is true, which thought number is being reconsidered\\n- branchFromThought: If branching, which thought number is the branching point\\n- branchId: Identifier for the current branch (if any)\\n- needsMoreThoughts: If reaching end but realizing more thoughts needed\\n\\nYou should:\\n1. Start with an initial estimate of needed thoughts, but be ready to adjust\\n2. Feel free to question or revise previous thoughts\\n3. Don't hesitate to add more thoughts if needed, even at the \\\"end\\\"\\n4. Express uncertainty when present\\n5. Mark thoughts that revise previous thinking or branch into new paths\\n6. Ignore information that is irrelevant to the current step\\n7. Generate a solution hypothesis when appropriate\\n8. Verify the hypothesis based on the Chain of Thought steps\\n9. Repeat the process until satisfied with the solution\\n10. Provide a single, ideally correct answer as the final output\\n11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached\",\"inputSchema\":{\"type\":\"object\",\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"thought\":{\"type\":\"string\",\"description\":\"Your current thinking step\"},\"nextThoughtNeeded\":{\"description\":\"Whether another thought step is needed\",\"type\":[\"boolean\",\"string\"]},\"thoughtNumber\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991,\"description\":\"Current thought number (numeric value, e.g., 1, 2, 3)\"},\"totalThoughts\":{\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991,\"description\":\"Estimated total thoughts needed (numeric value, e.g., 5, 10)\"},\"isRevision\":{\"description\":\"Whether this revises previous thinking\",\"type\":[\"boolean\",\"string\"]},\"revisesThought\":{\"description\":\"Which thought is being reconsidered\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"branchFromThought\":{\"description\":\"Branching point thought number\",\"type\":\"integer\",\"minimum\":1,\"maximum\":9007199254740991},\"branchId\":{\"description\":\"Branch identifier\",\"type\":\"string\"},\"needsMoreThoughts\":{\"description\":\"If more thoughts are needed\",\"type\":[\"boolean\",\"string\"]}},\"required\":[\"thought\",\"nextThoughtNeeded\",\"thoughtNumber\",\"totalThoughts\"]},\"annotations\":{\"readOnlyHint\":false,\"destructiveHint\":false,\"idempotentHint\":false,\"openWorldHint\":false},\"outputSchema\":{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\",\"type\":\"object\",\"properties\":{\"thoughtNumber\":{\"type\":\"number\"},\"totalThoughts\":{\"type\":\"number\"},\"nextThoughtNeeded\":{\"type\":\"boolean\"},\"branches\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"thoughtHistoryLength\":{\"type\":\"number\"}},\"required\":[\"thoughtNumber\",\"totalThoughts\",\"nextThoughtNeeded\",\"branches\",\"thoughtHistoryLength\"],\"additionalProperties\":false}}]}" }

# Run the Actor and wait for it to finish
run = client.actor("leadharbor/mcp-tools-token-profiler").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "toolsListJson": "{\\"tools\\":[{\\"name\\":\\"sequentialthinking\\",\\"title\\":\\"Sequential Thinking\\",\\"description\\":\\"A detailed tool for dynamic and reflective problem-solving through thoughts.\\\\nThis tool helps analyze problems through a flexible thinking process that can adapt and evolve.\\\\nEach thought can build on, question, or revise previous insights as understanding deepens.\\\\n\\\\nWhen to use this tool:\\\\n- Breaking down complex problems into steps\\\\n- Planning and design with room for revision\\\\n- Analysis that might need course correction\\\\n- Problems where the full scope might not be clear initially\\\\n- Problems that require a multi-step solution\\\\n- Tasks that need to maintain context over multiple steps\\\\n- Situations where irrelevant information needs to be filtered out\\\\n\\\\nKey features:\\\\n- You can adjust total_thoughts up or down as you progress\\\\n- You can question or revise previous thoughts\\\\n- You can add more thoughts even after reaching what seemed like the end\\\\n- You can express uncertainty and explore alternative approaches\\\\n- Not every thought needs to build linearly - you can branch or backtrack\\\\n- Generates a solution hypothesis\\\\n- Verifies the hypothesis based on the Chain of Thought steps\\\\n- Repeats the process until satisfied\\\\n- Provides a correct answer\\\\n\\\\nParameters explained:\\\\n- thought: Your current thinking step, which can include:\\\\n  * Regular analytical steps\\\\n  * Revisions of previous thoughts\\\\n  * Questions about previous decisions\\\\n  * Realizations about needing more analysis\\\\n  * Changes in approach\\\\n  * Hypothesis generation\\\\n  * Hypothesis verification\\\\n- nextThoughtNeeded: True if you need more thinking, even if at what seemed like the end\\\\n- thoughtNumber: Current number in sequence (can go beyond initial total if needed)\\\\n- totalThoughts: Current estimate of thoughts needed (can be adjusted up/down)\\\\n- isRevision: A boolean indicating if this thought revises previous thinking\\\\n- revisesThought: If is_revision is true, which thought number is being reconsidered\\\\n- branchFromThought: If branching, which thought number is the branching point\\\\n- branchId: Identifier for the current branch (if any)\\\\n- needsMoreThoughts: If reaching end but realizing more thoughts needed\\\\n\\\\nYou should:\\\\n1. Start with an initial estimate of needed thoughts, but be ready to adjust\\\\n2. Feel free to question or revise previous thoughts\\\\n3. Don'\''t hesitate to add more thoughts if needed, even at the \\\\\\"end\\\\\\"\\\\n4. Express uncertainty when present\\\\n5. Mark thoughts that revise previous thinking or branch into new paths\\\\n6. Ignore information that is irrelevant to the current step\\\\n7. Generate a solution hypothesis when appropriate\\\\n8. Verify the hypothesis based on the Chain of Thought steps\\\\n9. Repeat the process until satisfied with the solution\\\\n10. Provide a single, ideally correct answer as the final output\\\\n11. Only set nextThoughtNeeded to false when truly done and a satisfactory answer is reached\\",\\"inputSchema\\":{\\"type\\":\\"object\\",\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"properties\\":{\\"thought\\":{\\"type\\":\\"string\\",\\"description\\":\\"Your current thinking step\\"},\\"nextThoughtNeeded\\":{\\"description\\":\\"Whether another thought step is needed\\",\\"type\\":[\\"boolean\\",\\"string\\"]},\\"thoughtNumber\\":{\\"type\\":\\"integer\\",\\"minimum\\":1,\\"maximum\\":9007199254740991,\\"description\\":\\"Current thought number (numeric value, e.g., 1, 2, 3)\\"},\\"totalThoughts\\":{\\"type\\":\\"integer\\",\\"minimum\\":1,\\"maximum\\":9007199254740991,\\"description\\":\\"Estimated total thoughts needed (numeric value, e.g., 5, 10)\\"},\\"isRevision\\":{\\"description\\":\\"Whether this revises previous thinking\\",\\"type\\":[\\"boolean\\",\\"string\\"]},\\"revisesThought\\":{\\"description\\":\\"Which thought is being reconsidered\\",\\"type\\":\\"integer\\",\\"minimum\\":1,\\"maximum\\":9007199254740991},\\"branchFromThought\\":{\\"description\\":\\"Branching point thought number\\",\\"type\\":\\"integer\\",\\"minimum\\":1,\\"maximum\\":9007199254740991},\\"branchId\\":{\\"description\\":\\"Branch identifier\\",\\"type\\":\\"string\\"},\\"needsMoreThoughts\\":{\\"description\\":\\"If more thoughts are needed\\",\\"type\\":[\\"boolean\\",\\"string\\"]}},\\"required\\":[\\"thought\\",\\"nextThoughtNeeded\\",\\"thoughtNumber\\",\\"totalThoughts\\"]},\\"annotations\\":{\\"readOnlyHint\\":false,\\"destructiveHint\\":false,\\"idempotentHint\\":false,\\"openWorldHint\\":false},\\"outputSchema\\":{\\"$schema\\":\\"https://json-schema.org/draft/2020-12/schema\\",\\"type\\":\\"object\\",\\"properties\\":{\\"thoughtNumber\\":{\\"type\\":\\"number\\"},\\"totalThoughts\\":{\\"type\\":\\"number\\"},\\"nextThoughtNeeded\\":{\\"type\\":\\"boolean\\"},\\"branches\\":{\\"type\\":\\"array\\",\\"items\\":{\\"type\\":\\"string\\"}},\\"thoughtHistoryLength\\":{\\"type\\":\\"number\\"}},\\"required\\":[\\"thoughtNumber\\",\\"totalThoughts\\",\\"nextThoughtNeeded\\",\\"branches\\",\\"thoughtHistoryLength\\"],\\"additionalProperties\\":false}}]}"
}' |
apify call leadharbor/mcp-tools-token-profiler --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leadharbor/mcp-tools-token-profiler"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/p0wMNReqaa34Ij3cO/builds/Dxm3sVEhEkU8eu7On/openapi.json
