flutter-docs-mcp
Resolves Dart SDK identifiers (e.g. dart:async.Future) to live-scraped documentation pages from api.dart.dev covering libraries such as dart:core, dart:async, dart:collection and dart:io, returned as markdown with topic filtering and truncation support. Dart SDK classes are also included in the local search index alongside Flutter classes.
Resolves Flutter framework identifiers (e.g. ListView, material.AppBar) to live-scraped documentation pages from api.flutter.dev, returned as clean markdown with optional topic filtering (methods, properties, examples, constructors) and a token budget. Also provides ranked name search over a locally cached index of Flutter classes, enums, mixins and typedefs, so generated code targets current, real APIs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@flutter-docs-mcplook up widgets.ListView.builder and show its constructors and properties"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
flutter-mcp
Live Flutter, Dart SDK and pub.dev documentation for AI coding agents. The MCP server inside is published as flutter-docs-mcp; the repository is flutter-mcp.
An MCP (Model Context Protocol) server that gives AI coding agents live Flutter, Dart SDK and pub.dev documentation instead of whatever the model happens to remember. Pages are scraped from the official docs at request time, cached locally, and returned as clean markdown — so generated Flutter code targets real, current APIs rather than deprecated or invented ones.
Flutter classes — scraped live from api.flutter.dev (widgets, material, cupertino, foundation, services, rendering, …)
Dart SDK classes — scraped live from api.dart.dev (dart:core, dart:async, dart:collection, dart:io, …)
pub.dev packages — metadata + README from the pub.dev API
Local search index over ~4,500 Flutter / Dart SDK classes (46 libraries,
dart:convert/dart:isolate/dart:ffi/dart:typed_data/dart:uiincluded), rebuilt at most every 7 days, with a stale fallback when the network failsSQLite TTL cache so repeated lookups are instant
Politeness layer — robots.txt (RFC 9309), per-host throttle incl.
Crawl-delay,Retry-After, conditional GET and request budgets (details)
The six tools
Tool | What it does |
| Resolve one identifier ( |
| Ranked name search over the local index of all Flutter + Dart SDK classes, enums, mixins and typedefs. |
| Parse every |
| pub.dev package metadata (version, publisher, likes, pub points) plus its README as markdown. |
| Real health check: index size/age, cache stats, live probes of api.flutter.dev and pub.dev, and politeness counters. |
| Server liveness and version. |
Every tool returns a plain dict. Failures come back as {"error": …, "suggestion": …} — a bad lookup never raises into the MCP layer, and no tool delegates to another tool.
Related MCP server: RTFD (Read The F*****g Docs)
Requirements
Python 3.10 or newer (developed and tested on 3.12)
uvfor the one-command install below (uvxships with it)The MCP Python SDK 1.x is pinned (
mcp>=1.2.0,<2.0); this package does not support the mcp 2.x rename ofFastMCP
Install & run
One command — no checkout, no token
uvx --from git+https://github.com/KEEPEE/flutter-mcp.git flutter-docsThis builds the package in an isolated environment and starts the stdio MCP server. It prints nothing on purpose: stdout is the protocol channel. Stop it with Ctrl-C.
After the repository is updated, force uv to re-resolve the commit:
uvx --refresh --from git+https://github.com/KEEPEE/flutter-mcp.git flutter-docsLocal checkout — fastest startup, editable while developing
git clone https://github.com/KEEPEE/flutter-mcp.git
cd flutter-mcp
python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/flutter-docs # stdio MCP serverMCP client configuration
Generic stdio client (Claude Desktop, Cursor, Cline, …)
{
"mcpServers": {
"flutter-docs": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/KEEPEE/flutter-mcp.git",
"flutter-docs"
]
}
}
}With an explicit cache directory (any env you set is passed straight through to the server):
{
"mcpServers": {
"flutter-docs": {
"command": "uvx",
"args": [
"--from",
"git+https://github.com/KEEPEE/flutter-mcp.git",
"flutter-docs"
],
"env": {
"FLUTTER_DOCS_MCP_CACHE_DIR": "/tmp/flutter-docs-cache"
}
}
}
}DeepSeek Harness (cordis-style plugin list)
- id: mcp-flutter-docs
name: '@deepseek-ai/dsh-mcp-client'
config:
serverName: flutter-docs
transport: stdio
command: uvx
args:
[
'--from',
'git+https://github.com/KEEPEE/flutter-mcp.git',
'flutter-docs'
]To skip the build at client start, point command at the console script of a checkout instead — command: /path/to/flutter-mcp/.venv/bin/flutter-docs with args: [].
Tools reference
flutter_docs(identifier, topic=None, max_tokens=8000)
Resolves an identifier to one documentation page. Identifier forms, tried in this order:
Form | Meaning |
| pub.dev package: description + README |
| Dart SDK class on api.dart.dev (the library keeps its colon) |
| Flutter class on api.flutter.dev, library given |
| exact match in the local index → |
topic keeps only the section whose heading contains it (methods, properties, examples, constructors, …) plus the class title; if nothing matches, the full page is returned with a note listing the available headings. max_tokens is a rough budget (1 token ≈ 4 characters): the markdown is cut at a line boundary.
truncated is true whenever the returned content is not the whole page — a token-budget cut, a topic filter, or both — and truncation then says which, names the sections that were dropped, and gives tokens (returned), source_tokens (the whole page) and tokens_payload (the payload including its own [truncated: …] marker).
Returns {"type", "identifier", "url", "title", "content", "truncated", "tokens", "source_tokens", "max_tokens", "cached"}, plus truncation when something was cut and an optional note.
// flutter_docs({ "identifier": "material.AppBar", "topic": "properties", "max_tokens": 300 })
{
"type": "flutter_class",
"identifier": "material.AppBar",
"url": "https://api.flutter.dev/flutter/material/AppBar-class.html",
"title": "AppBar class",
"content": "# AppBar class\n\nA Material Design app bar.\n\nAn app bar consists of a toolbar and potentially other widgets, such as a\n[TabBar](https://api.flutter.dev/flutter/material/TabBar-class.html) and a [FlexibleSpaceBar](https://… [truncated: showing ~273 of ~7367 estimated tokens]",
"truncated": true,
"tokens": 273,
"source_tokens": 7367,
"max_tokens": 300,
"cached": true,
"truncation": {
"reasons": ["topic_filter", "token_budget"],
"sections_dropped": ["Troubleshooting", "Constructors", "Methods", "Operators", "Static Methods"],
"tokens_returned": 273,
"tokens_source": 7367,
"tokens_payload": 286,
"max_tokens": 300,
"explanation": "topic 'properties' dropped 6 section(s): …; token budget 300: body cut at a line boundary, ~273 of ~7367 estimated tokens returned"
}
}// flutter_docs({ "identifier": "dart:async.Future", "topic": "methods", "max_tokens": 250 })
{
"type": "dart_class",
"identifier": "dart:async.Future",
"url": "https://api.dart.dev/dart-async/Future-class.html",
"content": "# Future<T> class abstract interface\n\nThe result of an asynchronous computation. … ## Methods\n\nasStream() → Stream<T> …",
"truncated": true,
"cached": true,
"title": "Future< T > class abstract interface"
}A name that resolves nowhere reports every place it looked:
// flutter_docs({ "identifier": "NopeXYZ" })
{
"error": "could not resolve 'NopeXYZ': flutter widgets: not found (HTTP 404): https://api.flutter.dev/flutter/widgets/NopeXYZ-class.html; flutter material: not found (HTTP 404): …; pub.dev: not found (HTTP 404): …",
"suggestion": "try flutter_search('NopeXYZ') to find the right name"
}flutter_search(query, limit=8)
Ranks the local index by name similarity (exact > prefix > substring > fuzzy). Returns {"query", "count", "results": [{name, library, kind, url, score}], "index_stale"}.
// flutter_search({ "query": "text field", "limit": 3 })
{
"query": "text field",
"count": 3,
"results": [
{ "name": "TextField", "library": "material", "kind": "class", "url": "https://api.flutter.dev/flutter/material/TextField-class.html", "score": 0.9474 },
{ "name": "TextFormField", "library": "material", "kind": "class", "url": "https://api.flutter.dev/flutter/material/TextFormField-class.html", "score": 0.7826 },
{ "name": "BitField", "library": "foundation", "kind": "class", "url": "https://api.flutter.dev/flutter/foundation/BitField-class.html", "score": 0.6667 }
],
"index_stale": false
}flutter_mentions(text, max_tokens=4000)
Parses every @flutter_mcp … mention in a text and resolves each one, returning exactly one entry per mention, in document order — a failing mention never removes, duplicates or leaks state into another one.
Mention | Resolved as |
| pub.dev package, latest release |
| caret constraint → newest published release satisfying it |
| exact published release |
| range → newest published release satisfying it |
| explicit latest |
| explicit package form |
| Flutter class, library given |
| Dart SDK class |
| plain name (index → widgets/material → pub.dev) |
Version constraints are answered from pub.dev's own list of published releases. An exact pin that was never published is reported as missing with the nearest published releases — never silently replaced by a different version — and a resolved range always states both the constraint that was requested and the version it resolved to. Pre-releases are excluded unless the constraint mentions one.
Returns {"mentions": n, "max_tokens": …, "results": […]}; {"mentions": 0, …, "note": …} when the text has no mention.
// flutter_mentions({ "text": "Use @flutter_mcp provider:^6.0.0 for state and @flutter_mcp material.AppBar for the bar.", "max_tokens": 120 })
{
"mentions": 2,
"max_tokens": 120,
"results": [
{
"mention": "@flutter_mcp provider:^6.0.0",
"identifier": "provider",
"requested_constraint": "^6.0.0",
"type": "pub_package",
"version": "6.1.5+1",
"url": "https://pub.dev/api/packages/provider/versions/6.1.5+1",
"title": "provider 6.1.5+1",
"content": "A wrapper around InheritedWidget to make them easier to use and more reusable.\n\n\n[truncated: showing ~19 of ~6240 estimated tokens]",
"truncated": true, "tokens": 19, "source_tokens": 6240, "max_tokens": 120,
"cached": true,
"note": "constraint '^6.0.0' resolved to 6.1.5+1: the newest published release satisfying it"
},
{
"mention": "@flutter_mcp material.AppBar",
"identifier": "material.AppBar",
"requested_constraint": null,
"type": "flutter_class",
"version": null,
"url": "https://api.flutter.dev/flutter/material/AppBar-class.html",
"title": "AppBar class",
"content": "# AppBar class\n\nA Material Design app bar. … [truncated: showing ~80 of ~7367 estimated tokens]",
"truncated": true, "tokens": 80, "source_tokens": 7367, "max_tokens": 120,
"cached": true
}
]
}A mention that resolves nowhere is a per-mention failure, not a missing entry:
// results entry for "@flutter_mcp NotARealThing"
{
"mention": "@flutter_mcp NotARealThing",
"identifier": "NotARealThing",
"type": "not_found",
"version": null,
"error": "could not resolve 'NotARealThing': flutter widgets: not found (HTTP 404): …",
"suggestion": "try flutter_search('NotARealThing') to find the right name"
}pub_package(package_name, version=None, max_tokens=6000)
pub.dev metadata plus the README. package_name is exact and case-sensitive; version pins a release. Returns {"name", "version", "description", "publisher", "likes", "pub_points", "url", "readme", "truncated", "tokens", "source_tokens", "max_tokens", "cached"} (plus truncation when the README was cut); publisher / likes / pub_points may be null when pub.dev does not report them.
// pub_package({ "package_name": "dio", "max_tokens": 150 })
{
"name": "dio",
"version": "5.11.1",
"description": "A powerful HTTP networking package,\nsupports Interceptors,\nAborting and canceling a request,\nCustom adapters, Transformers, etc.\n",
"publisher": "flutter.cn",
"likes": "8.36k",
"pub_points": 160,
"url": "https://pub.dev/api/packages/dio",
"readme": "# dio\n\n… [truncated: showing ~137 of ~7837 estimated tokens]",
"truncated": true,
"cached": true
}flutter_status()
Probes api.flutter.dev and pub.dev with a light GET (10 s timeout) and reports the index and cache state. overall is ok, degraded or error. The politeness block is diagnostics only and never changes overall.
// flutter_status()
{
"server": "flutter-docs-mcp",
"version": "0.3.0",
"checks": {
"search_index": { "status": "ok", "entries": 3503, "built_at": "2026-10-06T08:46:45.893682+00:00", "stale": false },
"cache": { "status": "ok", "entries": 5, "expired": 0 },
"api_flutter_dev": { "status": "ok", "http_status": 200 },
"pub_dev": { "status": "ok", "http_status": 200 }
},
"overall": "ok",
"politeness": {
"status": "ok", "disabled": false,
"requests": 3, "robots_requests": 2, "robots_rows": 2, "robots_fetches": 2, "robots_cache_hits": 1,
"blocked_by_robots": 0, "throttle_waits": 3, "throttle_sleep_s": 1.203,
"host_delays": { "api.flutter.dev": 0.0, "pub.dev": 0.0 },
"budgets": { "fetch:api.flutter.dev": [2, 6] },
"budget_denied": 0, "conditional": 0, "revalidated_304": 0,
"retries_429": 0, "retries_transport": 0, "stalls": 0, "errors": 0
}
}health_check()
{"status": "ok", "server": "flutter-docs-mcp", "version": "0.3.0"} — no network, no cache; safe as a liveness probe.
Replacing the legacy flutter-mcp-server
This server exists so the third-party flutter-mcp-server (0.1.1, upstream
flutter-mcp/flutter-mcp) can be
removed from our MCP client configuration. We do not maintain or patch that
package; the three defects below are what we observed in its code while
checking whether it could cover our use case, and they are the reason we wrote
this one instead:
flutter_docscrashes on the pub.dev path.server.py:1562callscreate_truncator(tokens), but that package'struncation.py:250definescreate_truncator()with no parameters, so any call that passestokensfails withError executing tool flutter_docs: create_truncator() takes 0 positional arguments but 1 was given. The same block then calls atruncator.truncate(content)method that does not exist.process_flutter_mentionsinvents results. Its version-constrained pub branch does notcontinueafter resolving the package, so execution falls through into the shared "process the result fromflutter_docs" block with the previous iteration'sdoc_resultstill bound. The output contains a phantom second entry (type: flutter_widget, empty content) for a mention that was never in the text.Its Flutter-class path needs a writable Flutter SDK. It shells out to a local
/opt/fluttercheckout, whosebin/cacheis read-only on our hosts, so Flutter class lookups fail there. This server scrapes api.flutter.dev and api.dart.dev instead and needs no Flutter installation.
flutter_mentions is the replacement for process_flutter_mentions: same
mention grammar, one result per mention, and version constraints resolved
against pub.dev's published release list. The truncation contract is also
stricter here — truncated, tokens and source_tokens describe the payload
that was actually returned (see flutter_docs).
Caching & politeness
Cache
Everything lives in one directory: ~/.cache/flutter-docs-mcp by default, overridable with FLUTTER_DOCS_MCP_CACHE_DIR.
File | Contents | TTL |
| fetched pages (parsed result + raw body + | docs 7 days, pub.dev 1 day, index 7 days |
| the politeness layer's robots.txt cache | 7 days per host |
Delete the files to force a full refresh. A cache problem is never a tool failure: if the directory is unwritable the server simply runs without a cache and says so in flutter_status().checks.cache.
Politeness
Every outbound request — fetch_*, the index build and the flutter_status probes — goes through one small internal module, src/flutter_docs_mcp/politeness.py (stdlib + httpx, no extra dependency):
robots.txt is read and obeyed. Rules are matched per RFC 9309 (
*, trailing$,%2A/%24literals, mergedUser-agentgroups, most-specific match wins,AllowbeatsDisallowon a tie).urllib.robotparseris deliberately not used: on Python < 3.14 it ignores wildcards and would report rules such asDisallow: /pypi/*/jsonas allowed. A disallowed URL returns{"error": "… blocked by robots.txt …"}— no request is made and no exception escapes. Files are cached 7 days per host, including negative results (404 / 403 / 5xx), so a host costs one robots request per week.Per-host throttle, including
Crawl-delay. Requests to one host are spaced 0.35–0.9 s apart, or by the site's ownCrawl-delaywhen it declares one. A cold index build is ~31 requests, which is exactly where that delay is felt — that is the point.429/503/504are handled.Retry-Afteris honoured to the second; without it the per-host delay is escalated and the request is retried once. A response that stalls past the read timeout escalates the delay instead of being retried blindly.Conditional GET.
ETag/Last-Modifiedand the raw body are stored next to the cached result, so refreshing an expired entry costs a304instead of a full download.Request budgets. One
fetch_*call may make at most 6 requests per host — the robots fetch, every retry and every redirect hop all pay. A coldpub_packagelegitimately costs 3 (robots + API JSON + package page). An index build gets 40 layer calls per host; it charges one unit per page and the layer does robots and any hop inside it (measured cold build: 24 units on api.flutter.dev, 7 on api.dart.dev).Host allowlist. Only
api.flutter.dev,api.dart.devandpub.devcan be contacted, so a malformed identifier or a surprising redirect cannot turn a docs lookup into a request to somebody else's site.
The layer never raises and never changes a tool's return shape. flutter_status reports its counters under the top-level politeness key.
Opt-out
export FLUTTER_DOCS_MCP_POLITENESS_DISABLED=1This single switch turns off robots.txt, the throttle, retries and conditional GET at once. Use it at your own risk: you take over responsibility for respecting each site's crawling rules, and you are far more likely to be rate-limited or blocked. There is no partial opt-out.
Development
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest -q # 255 offline tests; fixtures in tests/fixtures/
.venv/bin/python scripts/politeness_smoke.py /tmp/flutter-docs-smoke-cache
.venv/bin/python scripts/e2e_mcp_test.pypytestis fully offline: HTTP is simulated withhttpx.MockTransportand time/jitter are injected, so the suite is deterministic and runs in seconds.scripts/politeness_smoke.py <cache-dir>is a live measurement, not a test: it drives the real tools against the real docs sites and prints what the politeness layer did (requests, robots, throttle, conditional GET, budgets).scripts/e2e_mcp_test.pyspawns the installedflutter-docsconsole script and speaks newline-delimited JSON-RPC to it (initialize→tools/list→ every tool → a negative case →health_check). Exit code 0 means every check passed. SetFLUTTER_DOCS_MCP_E2E_CMDto run a different server command.
Troubleshooting
The first flutter_docs call takes half a minute. That is the cold search-index build: ~47 page fetches across api.flutter.dev (1 index page + 25 libraries) and api.dart.dev (1 index page + 20 libraries), spaced by the per-host throttle (a measured cold run finished in ~26-30 s). It happens once every 7 days; later calls hit the cached index. Delete cache.db and you pay for it again.
request budget exhausted for scope 'fetch:pub.dev'. One fetch_* call hit its cap of 6 requests to that host. Redirect chains, 429 retries and transport retries all consume the budget, so this usually means the site redirected more than expected or the connection kept failing. The tool returns an error dict instead of hammering the host; flutter_status().politeness.budgets shows how much each scope spent.
"partial": true, or fewer search results than expected. An index build ran out of its 40-calls-per-host budget and stopped early, returning a partial index with a partial_reason such as request budget of 40 per host exhausted for index:api.flutter.dev; the index is incomplete. A partial index is not cached, so the next run rebuilds it. flutter_search still works — it just knows fewer classes — and flutter_status().checks.search_index.entries tells you how many it has.
"stale": true / index_stale: true. The rebuild failed (offline, DNS failure, 5xx) and the server fell back to the older cached index instead of failing the lookup.
Nothing is cached and overall is degraded. Read flutter_status().checks.cache.error — an unwritable FLUTTER_DOCS_MCP_CACHE_DIR (read-only mount, missing permission) is the usual cause. Tools keep working without a cache; they are just slower and noisier on the network.
A lookup returns blocked by robots.txt. The site's rules disallow that path for this user agent, and the request was not sent. Fetch the page yourself, or accept the consequences of the opt-out switch above.
Support development
flutter-mcp is built and maintained by Michal in his spare time. If it saves you time or makes your team's docs easier to work with, a coffee (or more) would mean a lot — every contribution helps keep the project moving. 🙏
Pay via Revolut: revolut.me/michal4zvc
License & attribution
MIT — see LICENSE. Copyright (c) 2026 Michal Gaspierik.
The design of the politeness layer was inspired by Crawl4AI (© Unclecode, Apache-2.0): a TTL-cached robots store, the wildcard rule translation, and a per-domain rate limiter with escalating backoff. The implementation is a clean-room rewrite in this project's own synchronous stdlib-plus-httpx style — no line was transcribed, translated or mechanically adapted from Crawl4AI, and Crawl4AI is not a dependency of this package. Three defects of the original design are fixed (robots fetched_at refresh, negative-result caching, Crawl-delay support). The GPL-3.0 part of Crawl4AI — its vendored html2text fork — is deliberately excluded: no code, data or dependency from that tree is used or shipped here. The full statement is in NOTICE.
This project is also a clean-room replacement for adamsmaka/flutter-mcp, whose main branch pins mcp to a 2.x development commit while importing the 1.x-only FastMCP, and which recurses between its "unified" tool and the deprecated tool it delegates to. The useful part of that project — fetching real docs — is kept here as fetchers → parsers → cache → search index → tools, with no circular delegation, a pinned 1.x SDK, error-dict failures and an offline test suite.
Part of the keepee self-hosted family. Also see Wiki4AI — a self-hosted collaborative wiki for teams and AI agents, with hybrid semantic search and an MCP server of its own — and the full project index.
Available Tools
6 toolsflutter_docsA
Fetch one Flutter / Dart SDK / pub.dev documentation page as clean markdown.
Identifier forms (resolved in this order):
"pub:dio" or "pub:dio:5.4.0" → pub.dev package (description + README)
"dart:async.Future" → Dart SDK class (api.dart.dev)
"material.AppBar" / "widgets.ListView" → Flutter class with explicit library
"ListView" → exact match in the local search index, then a widgets/material fallback, then pub.dev as a last resort
Args: identifier: one of the forms above. topic: optional section filter (e.g. "methods", "properties", "examples", "constructors") — keeps only the matching section plus the class title/description; if no section matches, the full content is returned with a "note" listing the available section headings. max_tokens: rough token budget for the returned markdown (1 token ≈ 4 chars); longer output is cut at a line boundary with a trailing note.
Returns {"type", "identifier", "url", "title", "content", "truncated", "tokens", "source_tokens", "max_tokens", "cached"} — plus "truncation" (what was cut and why) whenever "truncated" is true, and an optional "note" — or {"error", "suggestion"} on failure.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | No | ||
| identifier | Yes | ||
| max_tokens | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does well: it discloses caching (the 'cached' field), truncation policy (cut at a line boundary with a trailing note, plus a 'truncation' field explaining what was cut and why), and the topic-filter fallback that returns full content plus a note listing available sections. It does not mention network, auth, or rate-limit behavior, which keeps it short of a 5.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded in one sentence, followed by scannable indented blocks for identifier forms, args, and returns. Length is justified because the description is the only place the parameters and return contract are documented.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Absent annotations and absent output schema, the description still supplies the full return shape (type, identifier, url, title, content, truncated, tokens, source_tokens, max_tokens, cached), the error shape with a 'suggestion', and the conditional fields. Nothing an agent needs to call or interpret this tool correctly is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: identifier forms are spelled out with resolution order, topic is explained with its fallback semantics, and max_tokens is given units (1 token ≈ 4 chars) plus the truncation consequence. This is meaning well beyond the bare string/integer schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (Fetch), a precisely bounded resource (one Flutter / Dart SDK / pub.dev documentation page) and the output form (clean markdown). The word 'one' plus the three named source families make it distinguishable from the search-oriented siblings without opening any schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives real invocation context by enumerating the identifier forms and the exact resolution order used to disambiguate them (pub:, dart:, library-qualified, bare name with index/fallback chain), which is the main usage decision for this tool. It stops short of explicitly routing between itself and overlapping siblings such as pub_package or flutter_search.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
flutter_mentionsA
Parse every @flutter_mcp mention in a text and resolve each one to its docs.
Mention grammar (identifier forms are the same flutter_docs accepts):
"@flutter_mcp provider" pub.dev package, latest release
"@flutter_mcp provider:^6.0.0" caret constraint
"@flutter_mcp provider:6.1.5" exact published release
"@flutter_mcp dio:>=5.0.0 <6.0.0" range, space separated
"@flutter_mcp provider:latest" explicit latest
"@flutter_mcp pub:dio" / "pub:dio:5.4.0"
"@flutter_mcp material.AppBar" Flutter class, library given
"@flutter_mcp dart:async.Future" Dart SDK class
"@flutter_mcp Container" plain name
A version constraint is answered from pub.dev's own list of published releases: an exact pin is returned when published, a range resolves to the newest release that satisfies it (pre-releases are excluded unless the constraint asks for one), and the constraint that was requested is always reported next to the version that was resolved. A version that does not exist is reported as missing with the nearest published releases — never replaced by a different version.
Args: text: any text containing zero or more @flutter_mcp mentions. max_tokens: rough token budget for each mention's documentation payload (1 token ≈ 4 chars). Non-positive disables truncation.
Returns {"mentions": n, "max_tokens": ..., "results": [...]} with exactly one entry per mention, in document order. Each entry carries "mention" (as written), "type" (flutter_class | dart_class | pub_package | not_found | error), the resolved "identifier" and "version", and the docs payload ("content", "truncated", "tokens", "source_tokens", "max_tokens", plus "truncation" when something was cut). Failures are per-mention {"type": "not_found" | "error", "error", "suggestion"} — one bad mention never removes or duplicates another mention's entry.
| Name | Required | Description | Default |
|---|---|---|---|
| text | Yes | ||
| max_tokens | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full behavioral burden and does so thoroughly: it states per-mention failure isolation, that one bad mention never removes or duplicates another entry, the version-resolution policy (exact pins honored, ranges resolve to newest satisfying release, pre-releases excluded, nonexistent versions reported as missing with nearest releases rather than substituted), and truncation behavior when max_tokens is non-positive.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The response is long but front-loads the purpose, then uses clearly labeled sections (grammar, resolution semantics, args, returns) so nothing is buried. The grammar catalog is verbose yet functional, since each example documents a distinct accepted form; it stops short of padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
In the absence of an output schema, the description fully specifies the return object shape, the enum-like set of entry types, per-mention result fields, and the error/suggestion structure. An agent has everything needed to call it and interpret the result without additional fields.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and largely does: text is defined as any text with zero or more mentions, and max_tokens is explained as a rough token budget with a concrete conversion (1 token ≈ 4 chars) and the non-positive-disables-truncation rule, which the schema does not convey.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence gives a specific verb and resource: parse every @flutter_mcp mention in text and resolve each to its docs. It is a distinct batch-resolution operation, but it never explicitly distinguishes itself from siblings like flutter_docs or pub_package; it only notes that identifier forms match flutter_docs, which mildly blurs the boundary rather than sharpening it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the extensive mention-grammar catalog, which suggests the tool is for free text containing @-mentions. However, there is no explicit when-to-use versus flutter_docs/flutter_search/pub_package guidance and no stated exclusions, leaving the agent to infer the routing decision.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
flutter_searchA
Search Flutter and Dart SDK class names (plus enums/mixins/typedefs).
Ranks the local search index by name similarity (exact > prefix > substring
fuzzy). Use this to find the right identifier before calling flutter_docs.
Args: query: text to match against class names, e.g. "text field" or "ListView". limit: maximum number of results (default 8).
Returns {"query", "count", "results": [{name, library, kind, url, score}], "index_stale"} or {"error", "suggestion"} when the index is unavailable.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| query | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden and does well: it discloses the ranking algorithm (exact > prefix > substring > fuzzy), the 'index_stale' flag, and the alternate error/suggestion return shape. It doesn't state read-only safety explicitly or mention rate limits, but search semantics make the safety profile unambiguous.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loads purpose and ranking, then organizes Args and Returns into clearly labeled blocks with no filler. The multi-line Returns blob is slightly dense but every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema, but the description compensates by enumerating the return fields (name, library, kind, url, score) and the failure shape. Combined with parameter docs, an agent has enough to invoke and interpret results correctly; only error-handling guidance is thin.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate, and it does: it defines query as text matched against class names with concrete examples ('text field', 'ListView') and limit as the maximum result count with its default. This is meaningful added value beyond the bare schema, though it is terse.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource ('Search Flutter and Dart SDK class names') and clarifies scope with 'plus enums/mixins/typedefs'. It also names the sibling relation to flutter_docs, letting an agent distinguish it from the other lookup tools without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly instructs the agent to 'find the right identifier before calling flutter_docs', which is clear routing context. It stops short of stating when NOT to use it or naming non-flutter_docs alternatives, so it does not reach a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
flutter_statusA
Health check: search index state, cache stats, and live API probes.
The cache check gains read_only: true plus read_only_reason
when the database cannot be written (P5). It stays an "ok" check on
purpose: a cache that only reads is a degraded optimisation, not a sick
server, so overall does not change.
Probes api.flutter.dev and pub.dev with a light GET (10s timeout) and reports per-check status plus an overall verdict ("ok" / "degraded" / "error"). Never raises — individual failures only mark that check as error.
Also returns a top-level "politeness" block with the politeness layer's counters: robots cache rows/fetches/hits, requests blocked by robots.txt, throttle waits and per-host delays, conditional GETs / 304 revalidations, 429 and transport retries, request budgets and whether the layer is disabled (FLUTTER_DOCS_MCP_POLITENESS_DISABLED). It is diagnostics only and never affects "overall".
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does so well: it discloses the 10s probe timeout, that it never raises (per-check errors only), the degraded-vs-sick distinction for the cache check, the exact overall verdict vocabulary, and the environment variable that disables the politeness layer. These are non-obvious operational traits an agent could not infer from the schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The core purpose is front-loaded in the first sentence, and the remaining paragraphs each cover a distinct concern (degraded cache semantics, probe behaviour, politeness counters). It is prose-heavy and the doubled backticks read awkwardly, but very little of it is wasted.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
There is no output schema and no annotations, so the description must convey the return shape — and it does, describing per-check status, the overall verdict, and the top-level politeness block. Only the usage/alternative question against 'health_check' is left unanswered.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters, so there is nothing for the description to disambiguate; the baseline for a no-argument tool is 4. No parameter-level detail is missing.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening line states a specific verb+resource and enumerates what the check inspects (search index state, cache stats, live API probes), which is far more than a tautology. However, it never distinguishes itself from the sibling tool literally named 'health_check', leaving the agent to guess which diagnostic to call.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use or when-not-to-use guidance, and no alternative is named even though 'health_check' is a sibling with an overlapping purpose. Usage is only implied by the word 'Health check'; for a diagnostics tool with a near-duplicate sibling, that is a real gap.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
health_checkA
Check that the server is up and report its version.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It implicitly conveys a safe, idempotent read and discloses the return payload (version), which is useful. It does not state whether auth is required, whether it can fail/timeout, or what 'up' means operationally, but for a trivial probe the disclosure is reasonable.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single tight sentence with zero waste, front-loading the primary action (check server is up) before the secondary outcome (report version).
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description steps in to say it reports the version, which is the key return detail. For a zero-param probe with no annotations this is nearly complete; a note on failure mode or the distinction from flutter_status would close the remaining gap.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool takes zero parameters (schema coverage 100%), so there is nothing to document and the baseline of 4 applies. The description correctly signals a no-argument call.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: checks server availability and reports its version. The action is unambiguous. However, it offers no differentiation from the similarly-named sibling flutter_status, which an agent could plausibly confuse with a server health probe.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no explicit when-to-use guidance, no prerequisites, and no mention of when this should be preferred over flutter_status. Usage is only vaguely implied by the verb 'check'.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pub_packageA
Fetch pub.dev package metadata plus its README as markdown.
Args: package_name: exact pub.dev package name (case-sensitive), e.g. "dio". version: optional specific version, e.g. "5.4.0" (default: latest). max_tokens: rough token budget for the returned README (1 token ≈ 4 chars); longer output is cut at a line boundary with a trailing note.
Returns {"name", "version", "description", "publisher", "likes", "pub_points",
"url", "readme", "truncated", "tokens", "source_tokens", "max_tokens",
"cached"} or {"error", "suggestion"} on failure. truncated is true when
the README was cut, in which case a truncation block explains the cut
and tokens / source_tokens give the returned / full-page estimates.
publisher / likes / pub_points may be null when pub.dev does not report them.
| Name | Required | Description | Default |
|---|---|---|---|
| version | No | ||
| max_tokens | No | ||
| package_name | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does most of it: it discloses the exact return shape, truncation semantics (cut at a line boundary with a trailing note), the error object, nullability of publisher/likes/pub_points, and a cached flag. It stops short of noting rate limits or caching/TTL behavior beyond the flag.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Front-loaded one-line purpose followed by tight Args and Returns blocks; the field enumeration of the return object is long but justified by the absence of an output schema. Minor redundancy in re-explaining truncation and token counts, but nothing egregious.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 3-param tool with no annotations and no output schema, the description covers purpose, every parameter, the full return contract, truncation, and the error case. Nothing an agent needs to invoke or interpret the call correctly appears missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate and it does: package_name is called out as exact and case-sensitive with an example, version is optional with a 'latest' default and example, and max_tokens is defined as a rough budget with a 1 token ≈ 4 chars conversion and cut behavior.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource: 'Fetch pub.dev package metadata plus its README as markdown.' It is clearly distinct from siblings like flutter_docs (documentation), flutter_search (search), and flutter_mentions, so an agent can route to it without ambiguity.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied through the args section (need an exact package name, optional version) rather than stated. There is no explicit 'use this when... / not when...' and no named alternative, though none of the listed siblings competes for package-metadata retrieval.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
6 tool updates
v0.3.0- First observed
flutter_docs - First observed
flutter_mentions - First observed
flutter_search - First observed
flutter_status - First observed
health_check - First observed
pub_package
TDQS
Scored across 6 tools
Two real overlaps exist: health_check and flutter_status are both described as health checks (up/version vs. index/cache/API probes), and flutter_docs accepts a 'pub:' form that fetches package description+README, duplicating pub_package's core output. Descriptions do clarify the differences (metadata vs. README, version vs. diagnostics), so it's workable but an agent could reasonably pick the wrong one.
All names are consistently snake_case with no verbs, but the prefixes vary by family (flutter_*, pub_*, plain health_check), which is defensible but not a single predictable pattern. Still readable and mostly uniform.
Six tools is well within a healthy scope for a docs server, and each maps to a distinct capability: discovery (flutter_search), fetch (flutter_docs, pub_package), batch resolution (flutter_mentions), and diagnostics (health_check, flutter_status).
The search-then-fetch workflow, package metadata, and batch mention resolution cover the core docs-retrieval lifecycle well. Minor gaps: no tool to rebuild/refresh the search index or clear the cache despite exposing index_stale and cache state.
Maintenance
Related MCP Connectors
Versioned documentation registry and semantic search for AI tools and coding assistants.
Provide your AI coding tools with token-efficient access to up-to-date technical documentation for…
@latest documentation and code examples to 9000+ libraries for LLMs and AI code editors in a singl…
Web search and any page as LLM-ready Markdown for AI agents. Free keyless tier, no signup.
Related MCP Servers
- AlicenseAqualityFmaintenanceA real-time server that provides Flutter/Dart documentation and pub.dev package information to AI assistants, ensuring they generate accurate and up-to-date Flutter code.874MIT
- AlicenseAqualityCmaintenanceProvides LLMs with real-time access to up-to-date documentation from PyPI, npm, crates.io, GoDocs, DockerHub, GitHub, and GCP, preventing outdated code generation and API hallucinations.2714MIT
- FlicenseAqualityDmaintenanceProvides AI models with direct access to documentation for over 600 technologies from DevDocs.io, including popular languages, frameworks, and tools. It enables comprehensive searching, content retrieval, and offline access via an intelligent local caching system.122-
- FlicenseBqualityBmaintenanceProvides Flutter and Dart reference data, code generation, live API lookups, and a verified sample corpus for AI coding assistants, enabling efficient Flutter development with accurate guidance and boilerplate generation.14-