Skip to main content
Glama

java-spring-mcp

MCP License: MIT Source: docs.oracle.com

An MCP (Model Context Protocol) server that gives AI coding agents live JDK javadoc, Spring Boot reference documentation and Maven Central metadata instead of whatever the model happens to remember. Pages are scraped from the official sites at request time, cached locally, and returned as clean markdown — so generated Java and Spring code targets real, current APIs rather than deprecated or invented ones.

  • JDK classes — scraped live from docs.oracle.com (all 41 modules: java.base, java.sql, java.desktop, java.net.http, …), ~4,700 classes with their module recorded, so a bare HttpClient resolves to java.net.http and not to a guess

  • Spring Boot documentation — a first-class layer over the official Spring Boot reference (all 11 sections: using, features, actuator, security, testing, web, data, io, messaging, packaging, index), the Getting Started guides, Spring Initializr and the release notes. It searches section titles and the headings inside each page, which is what a topic query such as data jpa actually needs.

  • Maven Central — artifact metadata from the official search API plus a javadoc.io link

  • Local search index over every JDK class plus the Spring sections, rebuilt at most every 7 days, with a stale fallback when the network fails

  • SQLite 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 eleven tools

Java tools:

Tool

What it does

java_docs

Resolve one identifier (java.util.List, HttpClient, spring:using) to a single documentation page as markdown.

java_search

Ranked name search over the local index of all JDK classes and Spring Boot reference sections.

maven_package

Maven Central artifact metadata: newest matching version, the version list, the central.sonatype.com page and a javadoc.io link.

java_status

Real health check: index size/age, cache stats, live probes of docs.oracle.com, docs.spring.io and search.maven.org, and politeness counters.

health_check

Server liveness and version.

Spring tools (added for the Spring documentation layer — see Replacing @enokdev/springdocs-mcp):

Tool

What it does

spring_search_concepts

Ranked hits over the reference manual's nav titles and in-page headings, each hit with a quoted excerpt. Answers auto-configuration, profiles, actuator, web, data jpa, security.

spring_reference

One reference section or subsection as clean markdown, with topic-style heading filtering and a version selector.

spring_guides

List the spring.io guides catalogue, or fetch one guide as markdown.

spring_initializr

Spring Initializr metadata: the Boot and Java versions it offers, project types, and the dependency catalogue (filterable).

spring_dependency

A need ("jpa", "oauth2 client") → the starter coordinates Spring Initializr itself generates, with paste-ready Maven and Gradle blocks.

spring_versions

Release / migration notes for a Spring project, grouped into breaking changes, new features, deprecations and the rest.

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: linked-docs

Requirements

  • Python 3.10 or newer (developed and tested on 3.12)

  • uv for the one-command install below (uvx ships 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 of FastMCP

Install & run

One command — no checkout, no token

uvx --from git+https://github.com/KEEPEE/java-spring-mcp.git java-spring-docs

This 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/java-spring-mcp.git java-spring-docs

Local checkout — fastest startup, editable while developing

git clone https://github.com/KEEPEE/java-spring-mcp.git
cd java-spring-mcp
python3 -m venv .venv
.venv/bin/pip install -e .
.venv/bin/java-spring-docs        # stdio MCP server

MCP client configuration

Generic stdio client (Claude Desktop, Cursor, Cline, …)

{
  "mcpServers": {
    "java-spring": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/KEEPEE/java-spring-mcp.git",
        "java-spring-docs"
      ]
    }
  }
}

With an explicit cache directory (any env you set is passed straight through to the server):

{
  "mcpServers": {
    "java-spring": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/KEEPEE/java-spring-mcp.git",
        "java-spring-docs"
      ],
      "env": {
        "JAVA_SPRING_MCP_CACHE_DIR": "/tmp/java-spring-cache"
      }
    }
  }
}

DeepSeek Harness (cordis-style plugin list)

- id: mcp-java-spring
  name: '@deepseek-ai/dsh-mcp-client'
  config:
    serverName: java-spring
    transport: stdio
    command: uvx
    args:
      [
        '--from',
        'git+https://github.com/KEEPEE/java-spring-mcp.git',
        'java-spring-docs'
      ]

To skip the build at client start, point command at the console script of a checkout instead — command: /path/to/java-spring-mcp/.venv/bin/java-spring-docs with args: [].

Tools reference

java_docs(identifier, topic=None, max_tokens=8000)

Resolves an identifier to one documentation page. Identifier forms, tried in this order:

Form

Meaning

spring, spring:using

Spring Boot reference section (spring alone = the index section)

java.util.List, java.net.http.HttpClient

fully-qualified class; the module is read from the search index, falling back to java.base plus a fixed module list

HttpClient

exact-name lookup in the local index (a class beats an interface on ambiguity), then fetch

topic keeps only the section whose heading contains it (methods, method summary, fields, constructors, …) plus the page title and description; 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 and truncated is set.

Returns {"type", "identifier", "url", "title", "content", "truncated", "cached"}, plus an optional note. type is jdk_class or spring_section.

// java_docs({ "identifier": "java.net.http.HttpClient", "max_tokens": 250 })
// a bare name would also work: "HttpClient" resolves to this same module
{
  "type": "jdk_class",
  "identifier": "java.net.http.HttpClient",
  "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.net.http/java/net/http/HttpClient.html",
  "content": "# Class HttpClient\n\n```java\npublic abstract class HttpClient extends Object implements AutoCloseable\n```\n\nAn HTTP Client.\n\nAn `HttpClient` can be used to send requests and retrieve their responses … [truncated: showing ~243 of ~9883 estimated tokens]",
  "truncated": true,
  "cached": false,
  "title": "Class HttpClient"
}
// java_docs({ "identifier": "spring:using", "max_tokens": 250 })
{
  "type": "spring_section",
  "identifier": "spring:using",
  "url": "https://docs.spring.io/spring-boot/reference/using/index.html",
  "content": "# Developing with Spring Boot\n\nThis section goes into more detail about how you should use Spring Boot. … ## Contents\n\n- [Build Systems](…)\n- [Structuring Your Code](…) … [truncated: showing ~226 of ~429 estimated tokens]",
  "truncated": true,
  "cached": false,
  "title": "Developing with Spring Boot"
}

A topic that matches nothing tells you what the page does have:

// java_docs({ "identifier": "java.util.List", "topic": "nonexistent-section", "max_tokens": 120 })
{
  "type": "jdk_class",
  "identifier": "java.util.List",
  "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.base/java/util/List.html",
  "content": "# Interface List<E>\n\n```java\npublic interface List<E> extends SequencedCollection<E>\n``` … [truncated: showing ~113 of ~1040 estimated tokens]",
  "truncated": true,
  "cached": true,
  "title": "Interface List<E>",
  "note": "no section matching 'nonexistent-section'; available headings: Interface List<E>, Unmodifiable Lists, Method Summary, Methods declared in interface Collection, Methods declared in interface Iterable, …"
}

A name that exists nowhere is reported honestly — Oracle answers a wrong class with a redirect, not a 404, so the request budget is what stops the search (see budgets):

// java_docs({ "identifier": "com.example.NopeXYZ" })
{
  "error": "could not fetch javadoc for 'com.example.NopeXYZ': request budget exhausted for scope 'tool:java_docs'",
  "suggestion": "the per-call request budget (6) ran out while guessing modules; try java_search('com.example.NopeXYZ') to find the right package/module"
}

java_search(query, limit=8)

Ranks the local index by name similarity (exact > prefix > substring > fuzzy). Returns {"query", "count", "results": [{name, package, module, kind, url, score}], "index_stale"}. kind is class, interface, enum, record, annotation or spring_section.

// java_search({ "query": "http client", "limit": 3 })
{
  "query": "http client",
  "count": 3,
  "results": [
    { "name": "HttpClient",         "package": "java.net.http", "module": "java.net.http", "kind": "class",     "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.net.http/java/net/http/HttpClient.html",         "score": 14.0 },
    { "name": "HttpClient.Builder", "package": "java.net.http", "module": "java.net.http", "kind": "interface", "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.net.http/java/net/http/HttpClient.Builder.html", "score": 14.0 },
    { "name": "HttpClient.Redirect","package": "java.net.http", "module": "java.net.http", "kind": "enum",      "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.net.http/java/net/http/HttpClient.Redirect.html",  "score": 14.0 }
  ],
  "index_stale": false
}

Spring sections are ranked in the same list, so one search covers both halves of the index:

// java_search({ "query": "spring using", "limit": 3 })
{
  "query": "spring using",
  "count": 3,
  "results": [
    { "name": "Spring Boot: Using", "package": "using",      "module": "spring-boot", "kind": "spring_section", "url": "https://docs.spring.io/spring-boot/reference/using/index.html",                    "score": 14.0 },
    { "name": "Spring",             "package": "javax.swing","module": "java.desktop","kind": "class",          "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.desktop/javax/swing/Spring.html",     "score": 12.909 },
    { "name": "SpringLayout",       "package": "javax.swing","module": "java.desktop","kind": "class",          "url": "https://docs.oracle.com/en/java/javase/26/docs/api/java.desktop/javax/swing/SpringLayout.html", "score": 9.882 }
  ],
  "index_stale": false
}

maven_package(artifact_id, group_id=None, version=None, max_tokens=6000)

Maven Central metadata. artifact_id is the artifact to look up; group_id pins the group when several projects share an artifact name; version selects one release from the result list. Returns {"group_id", "artifact_id", "version", "versions", "url", "javadoc_url", "cached"} — versions is the newest 20 coordinates, url the central.sonatype.com page and javadoc_url the javadoc.io page. max_tokens is reserved for future javadoc content fetching and currently has no effect.

// maven_package({ "artifact_id": "guava", "group_id": "com.google.guava" })
{
  "group_id": "com.google.guava",
  "artifact_id": "guava",
  "version": "33.4.8-jre",
  "versions": ["33.4.8-jre", "33.4.8-android", "33.4.7-jre", "33.4.7-android", "33.4.6-jre", "… 20 entries in total"],
  "url": "https://central.sonatype.com/artifact/com.google.guava/guava",
  "javadoc_url": "https://www.javadoc.io/doc/com.google.guava/guava",
  "cached": false
}

Without group_id the search API ranks by relevance, so a bare spring-boot legitimately resolves to whichever project Maven Central scores highest — pass the group when you mean a specific one:

// maven_package({ "artifact_id": "spring-boot" })
{ "group_id": "org.apache.camel.springboot", "artifact_id": "spring-boot", "version": "4.20.0", "…": "…" }

An unknown coordinate is an error dict, not an empty result:

// maven_package({ "artifact_id": "nope-not-a-real-artifact-xyz" })
{
  "error": "maven lookup failed: artifact not found on Maven Central",
  "suggestion": "No results for 'nope-not-a-real-artifact-xyz'. Verify the coordinates, or retry without a group id: maven_package('nope-not-a-real-artifact-xyz')"
}

java_status()

Probes docs.oracle.com, docs.spring.io and search.maven.org 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.

// java_status()
{
  "server": "java-spring-mcp",
  "version": "0.2.0",
  "checks": {
    "search_index":    { "status": "ok", "entries": 4716, "built_at": "2026-10-07T06:52:16.422162+00:00", "stale": false },
    "cache":           { "status": "ok", "entries": 9, "expired": 0 },
    "docs_oracle_com": { "status": "ok", "http_status": 200 },
    "docs_spring_io":  { "status": "ok", "http_status": 200 },
    "search_maven_org":{ "status": "ok", "http_status": 200 }
  },
  "overall": "ok",
  "politeness": {
    "status": "ok", "disabled": false,
    "requests": 15, "robots_requests": 3, "robots_rows": 3, "robots_fetches": 3, "robots_cache_hits": 13,
    "blocked_by_robots": 0, "throttle_waits": 9, "throttle_sleep_s": 3.338,
    "host_delays": { "docs.oracle.com": 0.0, "docs.spring.io": 0.0, "search.maven.org": 0.0 },
    "budgets": { "tool:java_docs": [6, 6], "tool:maven_package": [1, 6] },
    "budget_denied": 1, "conditional": 0, "conditional_skipped": 0, "revalidated_304": 0,
    "redirect_hops": 3, "retries_429": 0, "retries_transport": 0, "stalls": 0, "errors": 0
  }
}

health_check()

{"status": "ok", "server": "java-spring-mcp", "version": "0.2.0"} — no network, no cache; safe as a liveness probe.

Spring tools reference

These six tools are the Spring documentation layer. They read four hosts — docs.spring.io (reference manual), spring.io (guides), start.spring.io (Initializr) and api.github.com (release notes) — and every one of them is budgeted, throttled and cached like the Java tools.

spring_search_concepts(query, version=None, limit=8, max_tokens=4000)

The question a Spring agent actually asks is by topic, not by page: "how do profiles work?", "what does the actuator give me?", "JPA with Spring Boot?". This tool ranks the reference manual's navigation titles (296 pages, read from the section index) and the headings inside the pages themselves, and quotes the text under each hit.

Ranking is the same ladder as java_search (exact word > prefix > substring, every query word must match), weighted by field (title > slug > section > parent) with a bonus for reference pages over how-to pages. When the titles cannot answer the query, the tool fetches up to HEADING_PAGE_LIMIT (4) candidate pages, harvests their h2–h4 headings plus a short excerpt for each, and adds those as hits. Harvested headings are cached, so the same query a second time costs zero requests.

Returns {"query", "hit_count", "hits", "index", "version", "docs_base", "pages_read", "headings_harvested", "truncated"}. Each hit is {title, url, section, version, score, matched_on, excerpt, excerpt_source, nav_depth, is_reference_page} (+ heading_level and page_title for a heading hit). matched_on is title, path or heading.

// spring_search_concepts({ "query": "auto-configuration", "max_tokens": 400 })   // live, cold: 3 requests
{
  "query": "auto-configuration",
  "hit_count": 5,
  "hits": [
    { "title": "Auto-configuration", "url": "https://docs.spring.io/spring-boot/reference/using/auto-configuration.html", "section": "using", "version": "4.1.1", "score": 6.9, "matched_on": "title", "excerpt": "Auto-configuration is non-invasive. At any point, you can start to define your own configuration to replace specific parts of the auto-configuration. For example, if you add your own DataSource bean, the default embedded database support…", "excerpt_source": "page", "nav_depth": 3, "is_reference_page": true },
    { "title": "Creating Your Own Auto-configuration", "url": "https://docs.spring.io/spring-boot/reference/features/developing-auto-configuration.html", "section": "features", "version": "4.1.1", "score": 6.9, "matched_on": "title", "excerpt": "Classes that implement auto-configuration are annotated with @AutoConfiguration . This annotation itself is meta-annotated with @Configuration …", "excerpt_source": "page", "nav_depth": 3, "is_reference_page": true },
    { "title": "Auto-configuration Packages", "url": "https://docs.spring.io/spring-boot/reference/using/auto-configuration.html#using.auto-configuration.packages", "section": "using", "version": "4.1.1", "score": 6.9, "matched_on": "heading", "excerpt": "Auto-configuration packages are the packages that various auto-configured features look in by default when scanning for things such as entities and Spring Data repositories. …", "excerpt_source": "page", "nav_depth": 3, "is_reference_page": true, "heading_level": "h2", "page_title": "Auto-configuration" }
  ],
  "index": { "cached": false },
  "version": "4.1.1",
  "docs_base": "https://docs.spring.io/spring-boot/reference",
  "pages_read": 2,
  "headings_harvested": 21,
  "truncated": false
}

Measured live for the six topics that the third-party Spring MCP server could not answer: auto-configuration 5 hits (2 pages read, 21 headings harvested), profiles 4, actuator 5, web 5, data jpa 5, security 5 — and the identical second call of each returns the same hits with pages_read: 0 and no requests at all (0.01 s).

data jpa is the case that proves the heading pass is needed: no page is called "data jpa", so the answer comes from a heading — JPA and Spring Data JPA at reference/data/sql.html#data.sql.jpa-and-spring-data.

spring_reference(section, subsection=None, version=None, max_tokens=8000)

One reference page as clean markdown, with the same topic filtering as java_docs. section is a reference section (web, data, using, actuator, …); subsection is a page inside it. Hyphens and spaces are the same thing, so ("web", "graceful shutdown") resolves to reference/web/graceful-shutdown.html.

version selects the docs tree: None → https://docs.spring.io/spring-boot/reference, "4.1" or "4.1.1" → https://docs.spring.io/spring-boot/4.1.1/reference. A value that is not major.minor[.patch] is rejected rather than guessed at.

Returns the same payload as java_docs (type: "spring-reference") plus version, headings (the page's own outline) and alternatives when several pages matched.

// spring_reference({ "section": "data", "subsection": "sql", "max_tokens": 200 })   // live
{
  "type": "spring-reference",
  "identifier": "data:sql",
  "url": "https://docs.spring.io/spring-boot/reference/data/sql.html",
  "title": "SQL Databases",
  "content": "# SQL Databases\n\nThe [Spring Framework](https://spring.io/projects/spring-framework) provides extensive support for work… [truncated: showing ~127 of ~11614 estimated tokens]",
  "truncated": true,
  "cached": false,
  "version": "4.1.1",
  "headings": ["h2 Configure a DataSource", "h3 Embedded Database Support", "h3 Connection to a Production Database", "h2 JPA and Spring Data JPA", "…"]
}

When the subsection names a heading rather than a page, the tool says so instead of pretending:

// spring_reference({ "section": "web", "subsection": "profiles" })   // live
{
  "…": "…",
  "url": "https://docs.spring.io/spring-boot/reference/features/profiles.html",
  "note": "'profiles' is not a page under 'web'; it lives at /spring-boot/reference/features/profiles.html (matched by page name)"
}

An unknown section lists what does exist instead of returning nothing:

// spring_reference({ "section": "nope" })   // live
{
  "error": "unknown Spring documentation section: no Spring Boot documentation section named 'nope'",
  "suggestion": "spring_search_concepts('<topic>') lists the sections that exist",
  "candidates": [ { "title": "Developing with Spring Boot", "url": "https://docs.spring.io/spring-boot/reference/using/index.html" }, "… 12 section landing pages …" ]
}

spring_guides(guide=None, topic=None, max_tokens=8000)

No arguments (or topic=) lists the guides catalogue; guide= fetches one guide as markdown. The catalogue is read from https://spring.io/page-data/guides/page-data.json, because https://spring.io/guides itself is a client-rendered page: fetched over HTTP it answers 308 → /guides/ and then contains zero links. If page-data ever stops working, the tool falls back to https://spring.io/sitemap-index.xml (allowed by spring.io's robots.txt) and says so in a note, because a sitemap gives URLs only and titles have to be derived from the slug. guide_count is the number of guides actually returned, so it drops with max_tokens (68 at 8000, 58 at 4000, 22 at 1500).

// spring_guides({})   // live: 68 guides, 1 request (cold); 0 requests warm
{
  "type": "spring-guide-catalogue",
  "source": "page-data",
  "url": "https://spring.io/page-data/guides/page-data.json",
  "guide_count": 68,
  "guides": [
    { "title": "Messaging with Redis", "url": "https://spring.io/guides/gs/messaging-redis/", "slug": "messaging-redis", "type": "getting-started", "category": ["Spring Data", "Redis"], "description": "Learn how to use Redis as a message broker." },
    "…"
  ],
  "truncated": true,
  "cached": false
}

guide= accepts a slug (spring-boot), a kind+slug (gs/spring-boot, topicals/spring-boot-3), a path (/guides/gs/spring-boot/) or a full URL — all resolve to the same canonical guide URL. The markdown keeps the guide body and drops the site chrome (nav, footer, social links, "report an issue").

spring_initializr(section="options", query=None, max_tokens=6000)

Spring Initializr metadata, read from https://start.spring.io/ with Accept: application/vnd.initializr.v2.1+json. This is the only place that says which Boot versions a project can actually be built with today.

// spring_initializr({ "section": "options" })   // live
{
  "type": "initializr-options",
  "source": "start.spring.io",
  "url": "https://start.spring.io/",
  "boot_version_default": "4.1.1.RELEASE",
  "boot_version_latest_released": "4.1.1.RELEASE",
  "boot_versions": {
    "default": "4.1.1.RELEASE",
    "latest_released": "4.1.1.RELEASE",
    "values": [
      { "id": "4.2.0.BUILD-SNAPSHOT", "name": "4.2.0 (SNAPSHOT)" },
      { "id": "4.2.0.M2", "name": "4.2.0 (M2)" },
      { "id": "4.1.2.BUILD-SNAPSHOT", "name": "4.1.2 (SNAPSHOT)" },
      { "id": "4.1.1.RELEASE", "name": "4.1.1" },
      "…"
    ]
  },
  "java_versions": { "default": "17", "values": [ { "id": "27", "name": "27" }, { "id": "25", "name": "25" }, { "id": "21", "name": "21" }, { "id": "17", "name": "17" } ] },
  "project_types": { "default": "gradle-project", "values": [ { "id": "gradle-project", "name": "Gradle - Groovy" }, "…" ] },
  "languages": { "default": "java", "values": [ { "id": "java", "name": "Java" }, { "id": "kotlin", "name": "Kotlin" }, "…" ] },
  "packagings": { "default": "jar", "values": [ { "id": "jar", "name": "Jar" }, { "id": "war", "name": "War" } ] },
  "defaults": { "groupId": "com.example", "artifactId": "demo", "version": "0.0.1-SNAPSHOT", "packageName": "com.example.demo" },
  "dependency_groups": [ { "name": "Developer Tools", "count": 7 }, { "name": "Web", "count": 18 }, { "name": "SQL", "count": 19 }, "…" ],
  "dependency_count": 204,
  "cached": false
}

section="dependencies" returns the catalogue (204 entries), optionally filtered: spring_initializr("dependencies", query="jpa") → data-jpa / Spring Data JPA / group SQL, with the reference_url pointing at the exact docs anchor for it.

spring_dependency(need, build="both", boot_version=None, max_tokens=4000)

Turns a need into coordinates. It never invents an artifact: the ids it recommends come from the Initializr catalogue, and the coordinates it prints are the ones Initializr's own build endpoints generate for those ids (https://start.spring.io/pom.xml?type=maven-build&dependencies=…&bootVersion=… and the /build.gradle twin).

That distinction matters. On Boot 4.1 the catalogue id web generates spring-boot-starter-webmvc — not the spring-boot-starter-web that a model would write from memory. Asking for spring-boot-starter-web because the need was "web" would be a plausible, wrong dependency.

// spring_dependency({ "need": "jpa" })   // live: 2 requests (pom + gradle)
{
  "type": "spring-dependency",
  "need": "jpa",
  "boot_version": "4.1.1",
  "matches": [ { "id": "data-jpa", "name": "Spring Data JPA", "group": "SQL", "score": 3.0 } ],
  "coordinates": [
    { "group_id": "org.springframework.boot", "artifact_id": "spring-boot-starter-data-jpa", "scope": "compile" },
    { "group_id": "org.springframework.boot", "artifact_id": "spring-boot-starter-data-jpa-test", "scope": "test" }
  ],
  "source": { "pom_url": "https://start.spring.io/pom.xml?type=maven-build&dependencies=data-jpa&bootVersion=4.1.1", "gradle_url": "…", "cached": false },
  "maven_snippet": "<dependencies>\n    <dependency>\n        <groupId>org.springframework.boot</groupId>\n        <artifactId>spring-boot-starter-data-jpa</artifactId>\n    </dependency>\n    …\n</dependencies>",
  "gradle_snippet": "dependencies {\n    implementation 'org.springframework.boot:spring-boot-starter-data-jpa'\n    testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa-test'\n    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'\n}",
  "truncated": false,
  "note": "coordinates are the ones Spring Initializr itself generates for these dependency ids, not names derived from the ids"
}

boot_version is normalized before it reaches Initializr: 4.1.1.RELEASE becomes 4.1.1, because bootVersion=4.1.1.RELEASE makes /build.gradle fail with Bom '…spring-boot-dependencies:4.1.1.RELEASE' could not be resolved (measured: HTTP 500). A need that matches nothing returns the catalogue's groups, not a guess:

// spring_dependency({ "need": "flux capacitor" })   // live
{
  "error": "no Spring Initializr dependency matches 'flux capacitor'",
  "suggestion": "spring_initializr('dependencies') lists every id in the catalogue; this tool only recommends artifacts that catalogue offers",
  "dependency_groups": [ { "name": "Web", "count": 12 }, "…" ]
}

spring_versions(project="spring-boot", version=None, focus="all", max_tokens=6000)

Release notes from the GitHub releases API (api.github.com), parsed into sections and classified: new-features, breaking-changes, deprecations, bug-fixes, dependency-upgrades, documentation, other. GitHub spells its section icons as ## :warning: Attention Required, so the classifier strips the emoji markers before deciding — without that step every section lands in other. focus= returns only the requested class (deprecations is a keyword view across all sections). version accepts 4.1, 4.1.1 or v4.1.1; the patch is optional and the nearest match in the release list is used.

// spring_versions({})   // live
{
  "type": "spring-release-notes",
  "project": "spring-boot",
  "repo": "spring-projects/spring-boot",
  "tag": "v4.1.1",
  "prerelease": false,
  "published_at": "2026-08-20T20:02:36Z",
  "latest_stable": "v4.1.1",          // newest non-prerelease in the list
  "latest_listed": "v4.2.0-M2",       // newest entry, prereleases included
  "focus": "all",
  "counts": { "new-features": 0, "breaking-changes": 1, "deprecations": 4, "bug-fixes": 33, "dependency-upgrades": 57, "documentation": 21, "other": 0 },
  "section_headings": [ ":warning: Attention Required", ":lady_beetle: Bug Fixes", ":notebook_with_decorative_cover: Documentation", ":hammer: Dependency Upgrades", ":heart: Contributors" ],
  "content": "# v4.1.1\n\n## Breaking Changes\n- Spring Boot's Gradle plugin no longer automatically configures gRPC when the Protobuf plugin is applied. … [#50822](…)\n\n## Bug Fixes\n- Kafka consumer-specific security protocol is not taken into account [#51369](…) …",
  "truncated": true,
  "cached": false,
  "url": "https://github.com/spring-projects/spring-boot/releases/tag/v4.1.1"
}

A version that does not exist lists the tags that do:

// spring_versions({ "version": "9.9" })   // live
{
  "error": "no spring-projects/spring-boot release matches version '9.9'",
  "suggestion": "spring_versions() with no version lists the newest one",
  "available_tags": [ "v4.2.0-M2", "v4.2.0-M1", "v4.1.1", "v4.0.8", "v3.5.16", "v4.1.0", "…" ]
}

project= covers spring-boot, spring-framework, spring-security, spring-data-jpa, spring-batch, spring-ai (live: spring_versions(project="spring-framework") → v7.0.9, published 2026-08-20).

Which Spring Boot version is current (and why two tools disagree)

The Spring layer had to answer "what is the latest Spring Boot?" from live data instead of from memory, and the two sources disagree:

Source

Says

Why

start.spring.io (Initializr metadata)

bootVersion.default = 4.1.1.RELEASE, latest released 4.1.1; 4.2.0 is offered only as M2 / BUILD-SNAPSHOT; no 3.x is offered at all

it is the version list the project generator will actually build with

GitHub releases (spring-projects/spring-boot)

newest stable v4.1.1 (published 2026-08-20), newest listed v4.2.0-M2 (prerelease)

it is the release ledger

search.maven.org/solrsearch?core=gav (what maven_package uses)

newest spring-boot = 3.5.3

the Maven Central search index is stale: repo1.maven.org/maven2/org/springframework/boot/spring-boot/maven-metadata.xml lists 294 versions with <latest>4.2.0-M2</latest> and newest stable 4.1.1

So "3.5.3" is not a Spring fact, it is a lagging search index: the same query re-fetched minutes apart returns byte-identical 3.5.x results while the repository itself already carries 4.1.1. maven_package is left as it is (it reports what its upstream API reports, and its versions list is honest about that), but no Spring tool derives a version from it — spring_dependency takes its Boot version from Initializr, spring_versions from GitHub, spring_reference from the docs tree it just read. A Spring agent that asks maven_package("spring-boot") for "the latest Boot" will get Camel's org.apache.camel.springboot:spring-boot:4.20.0 anyway, because relevance ranking beats the group you meant.

Replacing @enokdev/springdocs-mcp

The third-party @enokdev/springdocs-mcp was configured alongside this server to cover Spring. Its search_spring_concepts does not work — measured against the running server, auto-configuration, profiles and actuator all answered Concept not found in documentation. — and its other tools duplicate what this package now does against the same upstream pages. The mapping:

@enokdev/springdocs-mcp

This package

search_spring_concepts (broken)

spring_search_concepts — titles and in-page headings, with excerpts

get_spring_reference

spring_reference

get_spring_guide / get_all_spring_guides / search_spring_docs

spring_guides

get_spring_initializr / find_spring_dependency

spring_initializr / spring_dependency

get_release_notes / get_migration_guide / compare_spring_versions

spring_versions

java_status-equivalent health tools

java_status / health_check

Recommendation: drop @enokdev/springdocs-mcp from the MCP configuration. It is a second scraper of the same four hosts, with its own cache and no politeness layer, and the one capability it had that this package did not (topic search over the reference manual) is now the strongest tool here. Nothing in this package calls it or depends on it.

Caching & politeness

Cache

Everything lives in one directory: ~/.cache/java-spring-mcp by default, overridable with JAVA_SPRING_MCP_CACHE_DIR.

File

Contents

TTL

cache.db

fetched pages (parsed result + raw body + ETag / Last-Modified), the search index, the harvested Spring page headings, the Spring guides catalogue, Maven metadata, GitHub release lists

JDK docs 7 days, Spring reference pages 7 days, Spring concept index 7 days, Spring guides catalogue 7 days, Initializr metadata 1 day, GitHub releases 1 day, index 7 days, Maven 1 day

robots.db

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 java_status().checks.cache.

Politeness

Every outbound request — fetch_*, the index build and the java_status probes — goes through one small internal module, src/java_spring_mcp/politeness.py (stdlib + httpx, no extra dependency):

  • robots.txt is read and obeyed. Rules are matched per RFC 9309 (*, trailing $, %2A/%24 literals, merged User-agent groups, most-specific match wins, Allow beats Disallow on a tie). urllib.robotparser is deliberately not used: on Python < 3.14 it ignores wildcards and would report rules such as Disallow: /*/sp_common/*.htm as allowed. docs.oracle.com publishes over 5,700 rules in a 158 KB file, and among them the older JDK lines — /en/java/javase/12/ … /16/ and /18/ … /20/, plus /javase/6/, /7/, /9/, /10/, /1.5.0/ — are disallowed. This server fetches javase/26; if an older URL ever reached the layer the answer is {"ok": false, "error": "blocked by robots.txt for docs.oracle.com — Disallow: /en/java/javase/12/ matches …"} with no request made, not a silent fetch. The javadoc line is a single constant, fetchers.JDK_API_BASE, pinned to javase/26; the disallow list stops at /20/, so javase/17 and every line from /21/ up are open to crawlers — repointing that constant at the Java 21 LTS javadoc is the one change that stays robots-clean. 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 own Crawl-delay when it declares one — docs.spring.io declares crawl-delay: 1 and therefore gets at least one second between requests. A cold run touches three hosts and pays one robots fetch plus one page fetch each, which is exactly where that delay is felt — that is the point (measured: 3 robots fetches + 3 page fetches, 1.56 s spent waiting, crawl_delay_applied: 1).

  • 429 / 503 / 504 are handled. Retry-After is 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-Modified and the raw body are stored next to the cached result, so refreshing an expired entry costs a 304 instead of a full download. Oracle's javadoc pages send an ETag (no Last-Modified), so revalidation works there. search.maven.org sends neither ETag nor Last-Modified — verified against live response headers — so a conditional GET is not possible for Maven lookups: the fetcher stores no validators for that host and the layer counts the attempt under conditional_skipped rather than sending a request that could never yield a 304. The Spring hosts split the same way and were measured the same way: docs.spring.io pages send Last-Modified and no ETag, while spring.io's page-data, start.spring.io and api.github.com all send ETag — either validator is enough, so every Spring fetch stores one.

  • Request budgets. One tool call may make at most 6 requests in total, and one budget unit is one request on the wire: the robots fetch, every retry and every redirect hop all pay. That matters here because Oracle answers a wrong class name with a 302 to its landing page rather than a 404, so nothing in the status line stops the module-guessing loop — the budget does. Measured live: a cold legitimate java_docs costs 2–3 units, a cold typo spends all 6 and stops with the error dict above. A standalone index build gets its own index:docs.oracle.com budget of 6 and, if it runs out, returns a partial index instead of raising.

  • Host allowlist. Only the hosts this package documents can be contacted — docs.oracle.com, docs.spring.io, search.maven.org, spring.io, start.spring.io and api.github.com — so a malformed identifier or a surprising redirect cannot turn a docs lookup into a request to somebody else's site. What each of them actually publishes (re-measured live, 2026-10-07): docs.spring.io declares User-agent: * with crawl-delay: 1 and a second group that disallows /autorepo/; spring.io allows everything and publishes Sitemap: https://spring.io/sitemap-index.xml (the guides catalogue is read from page-data, and the sitemap is the fallback source); start.spring.io answers robots.txt with 404 — no rules, so everything under it is fair game and the negative result is cached for a week; api.github.com likewise 404s robots.txt and its rate-limit header reported {"limit": 60, "remaining": 57}, which is why spring_versions fetches one page of 100 releases per project and caches it for a day.

  • One transport, no curl fallback. Earlier versions shelled out to curl when httpx failed. That second transport is gone and stays gone: it bypassed robots.txt, the throttle, the budget and the stored validators, so a fallback request was an unpolite request; it doubled the worst case to roughly two minutes (--max-time 90 on top of a 20 s httpx timeout); and the premise it was written for — that some edges throttle Python's TLS fingerprint — did not reproduce under measurement. The layer's own transport retry, backoff and 10 s stall detection cover the real failure mode in about a fifth of the time, inside the same budget and robots accounting.

The layer never raises and never changes a tool's return shape. java_status reports its counters under the top-level politeness key.

Opt-out

export JAVA_SPRING_MCP_POLITENESS_DISABLED=1

This single switch turns off robots.txt, the throttle, retries, conditional GET and the host allowlist 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              # 257 offline tests; fixtures in tests/fixtures/
.venv/bin/python scripts/politeness_smoke.py /tmp/java-spring-smoke-cache
.venv/bin/python scripts/live_spring_smoke.py /tmp/java-spring-live-cache
.venv/bin/python scripts/e2e_mcp_test.py
  • pytest is fully offline: HTTP is simulated with httpx.MockTransport and time/jitter are injected, so the suite is deterministic and runs in seconds. tests/fixtures/ holds real captured pages (the JDK List javadoc, the Spring using section, Maven search JSON, the full ~2 MB all-classes index).

  • The Spring tools are tested the same way: tests/test_spring.py covers the parsers and rankers over fixtures trimmed from live responses (the reference nav, 14 reference pages, a guide page, Initializr's v2.1 metadata, GitHub's release list, Initializr's own generated pom.xml / build.gradle), and tests/test_spring_tools.py drives the six tools through a fixture-backed transport — including the six topic queries (auto-configuration, profiles, actuator, web, data jpa, security), a cache-hit assertion per tool, a request-budget assertion, and the assertion that spring_dependency returns spring-boot-starter-webmvc for the need web because that is what Initializr generates. Note that spring.py imports _http_get directly, so a test transport has to replace the binding in both fetchers and spring.

  • scripts/politeness_smoke.py <cache-dir> and scripts/live_spring_smoke.py <cache-dir> are live measurements, not tests: they drive the real tools against the real docs sites and print what the politeness layer did (requests, robots, throttle, conditional GET, budgets).

  • scripts/e2e_mcp_test.py spawns the installed java-spring-docs console 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. Set JAVA_SPRING_MCP_E2E_CMD to run a different server command.

Troubleshooting

The first java_docs call takes half a minute. That is the cold search-index build: the Oracle all-classes page plus a robots fetch, spaced by the per-host throttle, followed by the page you actually asked for. 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 'tool:java_docs'. One tool call hit its cap of 6 requests. Oracle redirects a wrong class name to its landing page instead of returning 404, so a name that is not in the index burns the budget on module guesses before the tool gives up. java_search first — it is offline and free — and pass the fully-qualified name. java_status().politeness.budgets shows how much each scope spent.

"partial": true, or fewer search results than expected. An index build ran out of its budget and stopped early, returning a partial index with a partial_reason. A partial index is not cached, so the next run rebuilds it. java_search still works — it just knows fewer classes — and java_status().checks.search_index.entries tells you how many it has (a complete JDK 26 index is ~4,700 entries).

"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 java_status().checks.cache.error — an unwritable JAVA_SPRING_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. For Oracle this normally means an older JDK javadoc line: /en/java/javase/12/–/16/ and /18/–/20/ are closed to crawlers. Fetch the page yourself, or accept the consequences of the opt-out switch above.

maven_package returns a group you did not expect. Without group_id the Maven search API ranks by relevance, and a common artifact name can belong to several projects. Pass group_id to pin the coordinates.

Support development

java-spring-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 last one matters concretely here, because docs.spring.io declares crawl-delay: 1). 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.

Same clean-room architecture as flutter-mcp: fetchers → pure parsers → TTL cache → search index → tools, with no circular delegation, a pinned 1.x MCP 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

11 tools
health_checkA

Check that the server is up and report its version.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.5/5.0
Behavior3/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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.

java_docsA

Fetch JDK class javadoc or a Spring Boot reference section as markdown.

identifier resolution order:

  1. "spring" or "spring:" (e.g. "spring:using") → Spring Boot reference section;

  2. dotted names like "java.util.List" → fully-qualified class; the module is resolved from the search index when possible, otherwise java.base is tried first and then a fixed list of fallback modules;

  3. plain names like "HttpClient" → exact-name lookup in the search index (class/interface preferred when the name is ambiguous, with a "note" listing alternates).

topic (e.g. "methods", "fields", "constructors", "nested") keeps only that heading section plus title/description; if no such section exists the full content is returned with a "note" listing the available headings. max_tokens truncates the final markdown to roughly that many tokens (estimated as len(text)//4), cut at a line boundary, and sets "truncated": true when applied.

On failure returns {"error", "suggestion"} — try java_search() to find the right name or package.

ParametersJSON Schema
NameRequiredDescriptionDefault
topicNo
identifierYes
max_tokensNo

TDQS

A4.6/5.0
Behavior5/5

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

With no annotations present, the description carries the full burden and does so richly: module resolution with a fixed fallback list, ambiguous-name handling that returns a 'note' with alternates, topic-section filtering and its full-content fallback, truncation clipped at line boundaries with a 'truncated' flag, and an error shape of {error, suggestion}. This is behavior well beyond what the schema exposes.

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

Conciseness4/5

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

Front-loaded one-line purpose followed by an ordered resolution list and two behavior paragraphs; every sentence conveys a rule an agent needs. It is dense rather than padded, though slightly longer than strictly necessary.

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

Completeness4/5

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

No output schema and no annotations exist, yet the description covers the return format (markdown), the 'truncated' and 'note' fields, and the error contract, so an agent knows what to expect. Minor gap: the success payload's overall shape is only loosely implied.

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

Parameters5/5

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 fully specifies the accepted 'identifier' forms (spring / spring:<section>, dotted FQN, plain name), the 'topic' heading filter with its no-section fallback, and exactly how 'max_tokens' is estimated (len//4) and applied. Every parameter is given meaning the schema lacks.

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

Purpose5/5

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

States a specific verb ('Fetch') and two concrete resources ('JDK class javadoc' or 'a Spring Boot reference section') with the output format ('as markdown'). An agent can distinguish this document-fetch tool from java_search 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.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The numbered identifier resolution order tells the agent exactly which input form maps to which source, and the failure clause explicitly routes to java_search(). It does not, however, distinguish this tool from the sibling spring_reference / spring_guides tools, which overlap with the 'spring:<section>' behavior.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

java_statusA

Real health check: search index, local cache and upstream endpoints.

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 docs.oracle.com, docs.spring.io and search.maven.org with a light GET (10s timeout each). Never raises; returns {"server", "version", "checks": {...}, "overall": "ok"|"degraded"|"error"} where overall is "error" when the search index is unavailable, "degraded" when any check fails, and "ok" otherwise.

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 / transport retries, stalls, request budgets and whether the layer is disabled (JAVA_SPRING_MCP_POLITENESS_DISABLED). It sits outside checks on purpose: it is diagnostics and must never change overall.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior5/5

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 richly: it discloses the 10s per-host GET timeout, that it never raises, the exact mapping of overall values (error when the search index is unavailable, degraded on any check failure, ok otherwise), the env var that disables the politeness layer, and the deliberate decision that a read-only cache stays 'ok'. This is exactly the kind of non-obvious behavior an agent cannot get from a zero-parameter schema.

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

Conciseness4/5

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

Front-loaded with the one-line purpose, then logically grouped paragraphs for the cache edge case, the probe/return contract, and the politeness block. It is longer than most zero-param definitions, but nearly every sentence carries operational detail that has nowhere else to live given the absent output schema.

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

Completeness5/5

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

With no output schema and no annotations, the description fully specifies the return shape (server, version, checks, overall) plus the top-level politeness counters and the environment flag. An agent has everything needed to call the tool and interpret the result.

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

Parameters4/5

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

The tool takes zero parameters, so there is nothing to document and the baseline of 4 applies. The description correctly spends no effort on parameter syntax and instead describes the return payload and semantics.

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

Purpose4/5

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

States a specific verb+resource: 'Real health check: search index, local cache and upstream endpoints.' The word 'Real' hints at contrast with the sibling health_check, and the enumerated components make the scope concrete. It stops short of explicitly routing the agent to or away from health_check, so it falls short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit when-to-use, when-not-to-use, or alternative guidance is given, despite a sibling named health_check that an agent must distinguish this from. The 'Real health check' phrasing and the 'never raises' note imply it is a safe diagnostic call, but the agent is left to infer the selection rule.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

maven_packageA

Look up an artifact on Maven Central (metadata only).

Returns the newest matching version, the top 20 versions and project / javadoc links. When version is given and not found, the error carries a suggestion listing available versions. Metadata is cached for 1 day keyed by the search-API URL; failures are never cached.

Note: max_tokens is reserved for future javadoc content fetching and currently has no effect — only metadata is returned.

ParametersJSON Schema
NameRequiredDescriptionDefault
versionNo
group_idNo
max_tokensNo
artifact_idYes

TDQS

A3.9/5.0
Behavior4/5

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

With no annotations, the description carries the full burden and largely meets it: it discloses the return shape (newest matching version, top 20 versions, project/javadoc links), a 1-day cache keyed by search-API URL, that failures are never cached, and that a missing version yields an error suggesting available versions. It does not state permission/auth requirements or rate limits, and never explicitly says the operation is non-mutating, so it falls 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.

Conciseness4/5

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

The core purpose is front-loaded in the first sentence, followed by return shape, error behavior and caching. The max_tokens disclaimer is a slight detour but earns its place by preventing an agent from supplying a parameter that does nothing; overall it is tight and waste-free.

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

Completeness4/5

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

For a read-only lookup tool with no annotations and no output schema, the description covers the essentials: what is returned, what happens on a miss, and caching behavior. It is only short on group_id resolution rules, auth/prerequisite requirements and pagination or rate-limit limits, which are secondary for this tool.

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

Parameters3/5

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

Schema coverage is 0%, so the description must compensate, and it only covers two of four parameters: version lookup behavior (not-found error with suggestions) and the max_tokens no-op. The semantics of artifact_id (required) and the optional group_id - especially how the search resolves when group_id is omitted - receive no explanation, leaving a meaningful gap.

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

Purpose5/5

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

States a specific verb and resource ('Look up an artifact on Maven Central') plus an explicit scope qualifier ('metadata only'). This clearly separates it from the java_docs, java_search and spring_* siblings, which fetch source/docs or Spring-specific content rather than Maven artifact metadata. An agent can route correctly without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by 'metadata only' and the caching/error notes, but there is no explicit when-to-use statement and no named alternative among the ten sibling tools. It never says, for example, to prefer java_docs when full javadoc content is wanted (despite the max_tokens note hinting at that future capability). Context is inferable but not stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spring_dependencyA

Turn a need ("jpa", "web", "oauth2 client") into real starter coordinates.

No artifact name is invented. The need is matched against Spring Initializr's own dependency catalogue, then Initializr is asked to generate the Maven pom and the Gradle build file for those ids, and those are returned — parsed coordinates plus ready-to-paste snippets. That matters: the catalogue id "web" generates spring-boot-starter-webmvc on Spring Boot 4.1, not the spring-boot-starter-web that string-building spring-boot-starter-<id> would produce.

build is "both" (default), "maven" or "gradle". boot_version pins the line (plain form, e.g. "4.1.1"); omitted uses Initializr's newest released default. max_tokens truncates the snippets.

On failure returns {"error", "suggestion"} — an unmatched need returns an error rather than a guess.

ParametersJSON Schema
NameRequiredDescriptionDefault
needYes
buildNoboth
max_tokensNo
boot_versionNo

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries the burden well: it discloses that no artifact name is invented, that the catalogue is the source of truth, that the returned coordinates come from actually generated pom/build files rather than string concatenation, and that an unmatched need produces an error with a suggestion rather than a guess. Auth, rate limits, and network-dependence are unstated.

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

Conciseness4/5

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

Front-loaded with the core purpose and then layered with justification and parameter semantics; every paragraph adds real information. Slightly longer than necessary, with the bolded emphasis and parenthetical example costing some tightness but earning their place.

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

Completeness4/5

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

For a 4-parameter tool with no annotations and no output schema, the description covers purpose, parameter semantics, failure shape ({"error","suggestion"}), and the nature of the return payload (parsed coordinates plus ready-to-paste snippets). The internal structure of the parsed coordinates is left unspecified, a minor gap.

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

Parameters5/5

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

Schema description coverage is 0% and titles are bare, so the description does the full job: it supplies the allowed values for `build` ("both"/"maven"/"gradle"), the exact format and default semantics for `boot_version` (plain form like "4.1.1", newest released default when omitted), what `max_tokens` truncates, and example values for `need`.

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

Purpose5/5

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

States a specific transformation (a need string into real starter coordinates) and explains the mechanism: matching against Spring Initializr's catalogue and generating the Maven/Gradle file. The concrete example ("web" yielding spring-boot-starter-webmvc on Boot 4.1) makes it clearly distinguishable from spring_initializr and maven_package.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is clear: call it when you have a human-style need ("jpa", "web", "oauth2 client") and want verified coordinates. It does not explicitly name sibling alternatives (e.g. spring_initializr for whole projects) or state when not to use it, so it stops short of full routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spring_guidesA

List the spring.io guides, or read one guide as markdown.

No guide → the catalogue (title, url, type, category, description) from spring.io's own guide data: the HTML index is rendered client-side, so scraping it yields no guides at all, and the sitemap fallback yields URLs without titles. topic filters the catalogue by title, slug, category or description.

guide → that guide as markdown. Guide pages are server-rendered even though the index is not, so "gs/spring-boot", "accessing-data-jpa" or a full https://spring.io/guides/… URL all work. topic then keeps only the matching heading section, as java_docs(topic=...) does. max_tokens truncates (estimated as len(text)//4).

On failure returns {"error", "suggestion"}.

ParametersJSON Schema
NameRequiredDescriptionDefault
guideNo
topicNo
max_tokensNo

TDQS

A4.7/5.0
Behavior5/5

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 explains why the HTML index cannot be scraped (client-side rendering), that the sitemap fallback lacks titles, that guide pages are server-rendered and therefore retrievable, how `max_tokens` truncation is estimated (len(text)//4), and the exact failure payload shape {"error", "suggestion"}. This is unusually rich disclosure of internal behavior and failure modes.

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

Conciseness4/5

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

The two-mode structure is front-loaded and the description is scannable, with each paragraph mapping to one branch of the tool. The aside about client-side rendering versus server-rendered guide pages is longer than strictly necessary, though it does justify result quality, so it is not pure waste.

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

Completeness5/5

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

For a three-parameter, no-output-schema tool with zero annotation coverage, the description supplies both modes' return shapes (catalogue fields title/url/type/category/description; markdown for a single guide) plus the error contract. An agent has everything needed to call it correctly and interpret the result.

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

Parameters5/5

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 for all three parameters: `guide` accepts slugs, short forms like "gs/spring-boot", or full spring.io URLs; `topic` filters the catalogue by title/slug/category/description and, in guide mode, narrows to a matching heading section; `max_tokens` truncates with a stated estimation formula.

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

Purpose5/5

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

The opening sentence states a concrete verb and resource ('List the spring.io guides, or read one guide as markdown') and immediately splits the tool into two clearly delineated modes keyed on the `guide` parameter. It also anchors itself against a sibling by noting the `topic` behavior matches `java_docs(topic=...)`, so an agent can place it in the toolset without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit routing rules: omit `guide` for the catalogue, supply `guide` for markdown, and describes what `topic` does in each mode. What's missing is guidance on when to prefer this over other documentation siblings such as `spring_reference` or `spring_search_concepts`, so the alternative-selection story is incomplete.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spring_initializrA

Read Spring Initializr's own metadata: project options or dependency catalogue.

section="options" → the choices start.spring.io offers (Spring Boot versions with the newest released one called out, Java versions, project types, packaging, and the defaults for groupId/artifactId/version). No version is hardcoded anywhere: it is whatever Initializr is serving today.

section="dependencies" → the dependency catalogue (id, name, description, group, reference doc link), filtered by query over those fields. Without query the whole catalogue is returned, truncated to max_tokens.

On failure returns {"error", "suggestion"}.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryNo
sectionNooptions
max_tokensNo

TDQS

A3.8/5.0
Behavior4/5

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

With no annotations provided, the description carries the full burden and does reasonably well: it discloses that no version is hardcoded and content is whatever Initializr serves today, that output is truncated to `max_tokens`, and that failures return {"error", "suggestion"}. It omits auth/rate-limit considerations, but the disclosed truncation and error-shape behavior are genuinely useful.

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

Conciseness4/5

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

Front-loads the core purpose in one line, then uses parallel `section=` blocks that are easy to scan. Slightly more verbiage than strictly necessary, but each section earns its place by describing a distinct mode.

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

Completeness4/5

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

For a read-only metadata tool with no output schema, no annotations, and zero schema coverage, the description covers both return shapes (options payload and truncated catalogue) and the error contract, which is enough for an agent to call it correctly. Only external concerns like auth or pagination beyond `max_tokens` are absent.

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

Parameters4/5

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

Schema description coverage is 0%, so the description must compensate and largely does: it defines both enumerable `section` values with their contents, specifies that `query` matches over id/name/description/group/reference-doc-link, and explains `max_tokens` as the truncation limit. This adds real meaning beyond the bare schema.

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

Purpose4/5

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

States a specific verb+resource (read Spring Initializr's own metadata) and clearly enumerates the two operational modes via the `section` parameter, mapping each to distinct content (options vs dependency catalogue). It does not, however, differentiate itself from siblings like spring_versions or spring_dependency, which an agent could easily confuse with this tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains what each `section` value returns and that `query` filters the catalogue, which implies when each mode is appropriate. But it gives no explicit when-to-use guidance against the many related siblings (spring_dependency, spring_search_concepts, spring_versions), leaving the routing decision to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spring_referenceA

Read a Spring Boot reference section or subsection as clean markdown.

section / subsection are resolved against the documentation's own navigation tree, so "web", "data"+"sql", "using"+"auto-configuration", "how-to"+"actuator" and "installing" all work, and a name that does not exist comes back with the sections that do. Resolution is offline when the index is cached, so no request is wasted guessing a URL and getting a 404.

When subsection is not a page of its own, the section page is returned with only the matching heading section kept (plus title and description) — the same behaviour as java_docs(topic=...) — and a "note" lists the available headings when nothing matches.

version (e.g. "4.1") reads that docs line; omitted reads the latest. max_tokens truncates the markdown to roughly that many tokens (estimated as len(text)//4) at a line boundary and sets "truncated".

On failure returns {"error", "suggestion", "candidates"}.

ParametersJSON Schema
NameRequiredDescriptionDefault
sectionYes
versionNo
max_tokensNo
subsectionNo

TDQS

A4.7/5.0
Behavior5/5

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: it discloses offline resolution when the index is cached, that max_tokens truncates at a line boundary and sets a 'truncated' flag, that narrowing keeps only the matching heading section plus title/description, and that failure returns {"error", "suggestion", "candidates"}. These are behavioral traits well beyond a bare read.

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

Conciseness4/5

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

Front-loaded with the core purpose and organized by parameter, with each paragraph earning its place. It is somewhat long and mildly discursive ('no request is wasted guessing a URL and getting a 404'), but the length is justified by the amount of non-obvious behavior being documented.

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

Completeness5/5

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

No output schema and no annotations, so the description must cover everything, and it does: purpose, parameter semantics, resolution fallback, truncation, and failure payload. An agent has enough to call the tool correctly and interpret both success and error results.

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

Parameters5/5

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 for every parameter: section/subsection resolution semantics, version ('e.g. "4.1"', omitted reads latest), and max_tokens including the estimation formula (len(text)//4) and the resulting 'truncated' flag. This adds far more meaning than the bare schema types.

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

Purpose5/5

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

States a specific verb and resource ('Read a Spring Boot reference section or subsection as clean markdown') and explains exactly what 'section'/'subsection' resolve against. It also positions itself relative to java_docs by saying the narrowing behavior is 'the same behaviour as java_docs(topic=...)', letting an agent distinguish it from siblings without opening both schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete usage context: valid resolution examples ('web', 'data'+'sql', 'how-to'+'actuator'), behavior when subsection is not its own page, and the note fallback when nothing matches. However, it never states when to reach for this tool over spring_guides, spring_search_concepts, or spring_search_concepts for discovering section names, so explicit alternatives/exclusions are absent.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spring_search_conceptsA

Find where a Spring Boot topic is documented: section titles and headings.

Matches the documentation's own titles ("Auto-configuration", "Profiles", "Web", "Security") and the h2/h3/h4 headings inside pages, so "data jpa" finds reference/data/sql.html#jpa-and-spring-data even though no page is titled "JPA". Each hit carries title, url, section, version, score, matched_on ("title", "path" or "heading") and a short excerpt.

version (e.g. "4.1") searches that docs line; omitted searches the latest one and the result reports which version that was. limit caps hits (1-25). max_tokens bounds the excerpt text across the hits (estimated as len(text)//4): whole hits past the budget are dropped and "truncated" is set.

The index is a single page — the reference navigation tree (296 entries on the measured 4.1.1 docs) — cached for a week. Headings are harvested only for queries the titles cannot answer, at most 4 pages per call, and are persisted under their own cache key, so a repeat search costs zero requests.

On failure returns {"error", "suggestion"}.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
versionNo
max_tokensNo

TDQS

A4.3/5.0
Behavior5/5

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

With no annotations, the description carries the full burden and discharges it well: it discloses the week-long single-page index cache, that headings are only harvested for queries titles cannot answer and at most 4 pages per call, that heading results are cached under their own key so repeats cost zero requests, and the exact max_tokens truncation contract (len(text)//4 budget, whole hits dropped past it, 'truncated' flag set). It also documents the failure shape {"error", "suggestion"}.

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

Conciseness4/5

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

The purpose is front-loaded in the first line and each subsequent sentence is about a distinct concern (matching surface, result fields, parameter behavior, caching, error shape). It runs long and the caching paragraph, while useful, is the least essential, but there is little outright waste.

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

Completeness5/5

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

There is no output schema, yet the description enumerates the returned fields (title, url, section, version, score, matched_on, excerpt) and the error payload, so an agent knows both what it gets back and how failures surface. For a 4-parameter, annotation-free tool this is complete.

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

Parameters5/5

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 for all four parameters: version (docs line, omitted = latest with the resolved version reported back), limit (caps hits, 1-25), and max_tokens (budget semantics and estimation formula), with query self-evident. This adds substantive meaning beyond the bare schema titles.

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

Purpose4/5

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

The description gives a specific verb and resource ('Find where a Spring Boot topic is documented: section titles and headings') and explains the matching surface (page titles plus h2/h3/h4 headings), which is far more than a restatement of the name. It stops short of distinguishing itself from close siblings such as spring_reference or spring_guides, which an agent could plausibly confuse with a documentation-lookup tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Use is implied clearly by the opening line and the worked 'data jpa' example, and the version-omitted default is spelled out. However, there is no explicit when-to-use-this-vs-alternative guidance and no exclusions, so selection against siblings like java_search or spring_reference is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

spring_versionsA

Release and migration notes for a Spring project, from its GitHub releases.

project is "spring-boot" (default), "spring-framework", "spring-security", "spring-data-jpa", "spring-batch" or "spring-ai". version picks a release ("4.1.1", or "4.1" for its newest patch); omitted picks the newest released version — never a milestone or snapshot, and never a hardcoded number: it is whatever GitHub lists today.

focus is "all", "breaking-changes" (the release's Attention Required / Noteworthy Changes sections), "new-features" (New Features) or "deprecations". Deprecations are found by keyword across every section because no spring-boot release body has a deprecation heading (measured over 100 releases). Migration-guide and release-notes wiki links found in the body are returned under wiki_links.

max_tokens truncates the rendered notes (estimated as len(text)//4). Releases are cached for a day: unauthenticated GitHub allows 60 requests per hour per IP, and one call spends at most one of them.

On failure returns {"error", "suggestion"}.

ParametersJSON Schema
NameRequiredDescriptionDefault
focusNoall
projectNospring-boot
versionNo
max_tokensNo

TDQS

A4.3/5.0
Behavior4/5

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 the 60 requests/hour unauthenticated GitHub limit, one-call-at-most-one-request accounting, a one-day cache, max_tokens truncation estimated as len(text)//4, and the {"error", "suggestion"} failure shape. It stops short of describing the returned notes payload structure beyond wiki_links, and the read-only nature is only implied.

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

Conciseness4/5

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

Front-loaded with the purpose, then parameter semantics, then operational caveats; nearly every sentence earns its place. The measured-over-100-releases parenthetical and the request-budget detail are slightly more than strictly needed, pushing length past the tightest possible form.

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

Completeness5/5

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

Given zero schema descriptions, zero annotations, and no output schema, the description supplies the equivalent of all three: full enum semantics, mutation-free read behavior and rate limits, plus concrete return details (rendered notes and wiki_links) and an error contract. Nothing essential for a correct call is missing.

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

Parameters5/5

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 enumerates every valid project value, explains that version accepts either a full release or a major.minor that resolves to the newest patch, defines all four focus modes including the substring matches for breaking-changes and new-features, and gives the truncation formula for max_tokens.

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

Purpose5/5

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

States a specific verb and resource: release and migration notes for a Spring project sourced from GitHub releases. The project list (spring-boot, spring-framework, spring-security, etc.) and the 'wiki_links' mention make it clearly distinct from siblings like spring_reference, spring_guides, and spring_search_concepts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives rich in-tool guidance (which focus values map to which release sections, why deprecations are keyword-searched, and that version pinning resolves to the newest patch), but never says when to reach for this tool rather than spring_reference or spring_guides for migration information. Usage is implied rather than routed against alternatives.

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.

  1. 11 tool updatesv0.2.0
    • First observedhealth_check
    • First observedjava_docs
    • First observedjava_search
    • First observedjava_status
    • First observedmaven_package
    • First observedspring_dependency
    • First observedspring_guides
    • First observedspring_initializr
    • First observedspring_reference
    • First observedspring_search_concepts
    • First observedspring_versions

TDQS

A3.9/5.0

Scored across 11 tools

Disambiguation3/5

Several tools have genuinely overlapping purposes: java_docs can fetch Spring Boot reference sections (via 'spring' / 'spring:section'), which duplicates spring_reference, and java_search covers both JDK classes and Spring Boot reference sections, overlapping with spring_search_concepts. health_check vs java_status are also both health checks, though their descriptions clearly differentiate depth. Descriptions help, but boundaries between the doc/search tools require careful reading.

Naming Consistency4/5

Names are uniformly snake_case with a domain prefix (spring_, java_, maven_, health_), which is a readable, predictable convention. The prefixes vary by domain rather than being a single verb_noun pattern, and health_check is a slight outlier, but nothing is chaotic or camelCase-mixed.

Tool Count5/5

11 tools is well within a healthy range and each tool maps to a distinct capability (health, dependency resolution, docs, search, Maven lookup, guides, Initializr metadata, versions). No redundant or filler tools inflate the count.

Completeness4/5

The surface covers discovery, search, documentation reading, dependency coordinates, Initializr metadata and release/migration notes, which is broad lifecycle coverage for a Java/Spring dev assistant. Minor gaps remain (e.g. no actual project scaffolding/generation, and maven_package is metadata-only), but agents can work around them.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Provides semantic search over markdown documentation using RAG, allowing natural language queries and integration with MCP clients.
    1
    MIT
  • F
    license
    Not graded
    quality
    D
    maintenance
    Enables AI assistants to intelligently search and reference documentation using hybrid semantic + keyword search via MCP protocol.
    -
  • F
    license
    A
    quality
    D
    maintenance
    Enables searching documentation from GitHub repositories and web pages via MCP tools, with in-memory indexing and caching for fast retrieval.
    3
    -