java-spring MCP server
Commit Oct 21, 2026Provides AI agents with live JDK Javadoc, Spring Boot documentation, and Maven Central metadata as clean markdown.
Also known as KEEPEE/java-spring-mcp
- Stars
- 0
- Tools
- 11
- Downloads, 30 days
- —
- Latest version
- —
- Last commit
- Oct 21, 2026
- License
- MIT
What is java-spring MCP?
java-spring provides AI agents with live JDK Javadoc, Spring Boot documentation, and Maven Central metadata as clean markdown. It exposes 11 tools, such as java_docs, java_search, maven_package.
Summary generated from the project's README
About java-spring MCP server
Providing AI coding agents with live JDK Javadoc, Spring Boot documentation, and Maven Central metadata is what this MCP server enables. It replaces the outdated or hallucinated knowledge often found in large language models with real-time, scraped data from official sources. By returning clean markdown, it ensures that generated Java and Spring code targets current APIs rather than deprecated ones.
Developers can use this server locally via stdio with clients like Claude Desktop, Cursor, or Cline. It requires Python 3.10 or newer and uses uv for installation. The server features a local search index covering approximately 4,700 JDK classes and all eleven Spring Boot reference sections, which is rebuilt every seven days. To ensure efficiency, it utilizes a SQLite TTL cache for instant repeated lookups and includes a politeness layer to respect robots.txt and request budgets.
Specific tools include java_docs for resolving identifiers, spring_search_concepts for finding documentation via headings, and maven_package for retrieving artifact metadata. It also provides spring_dependency to generate paste-ready Maven or Gradle blocks for specific needs like JPA or OAuth2.
What you can do with it
- 1Resolve specific JDK class documentationAn AI agent can fetch the exact markdown documentation for a specific Java class to ensure correct method usage.
- 2Search Spring Boot conceptsDevelopers can query for Spring Boot topics like auto-configuration or security to get ranked hits from the official manual.
- 3Generate Spring Boot dependenciesYou can ask the agent to find the correct starter coordinates for a specific requirement like JPA.
- 4Retrieve Maven artifact metadataAn agent can look up the newest version and metadata for a specific Maven package.
Written from the project's README. Every use case is checked against a quote from it.
java-spring MCP tools 11
Detected from the READMEjava_docsjava_searchmaven_packagejava_statushealth_checkspring_search_conceptsspring_referencespring_guidesspring_initializrspring_dependencyspring_versions
| java_docs | — | |
| java_search | — | |
| maven_package | — | |
| java_status | — | |
| health_check | — | |
| spring_search_concepts | — | |
| spring_reference | — | |
| spring_guides | — | |
| spring_initializr | — | |
| spring_dependency | — | |
| spring_versions | — |
Configuration
JAVA_SPRING_MCP_CACHE_DIRJAVA_SPRING_MCP_POLITENESS_DISABLED
| Name | Description | |
|---|---|---|
| JAVA_SPRING_MCP_CACHE_DIR | — | |
| JAVA_SPRING_MCP_POLITENESS_DISABLED | — |
README
View on GitHubjava-spring-mcp
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 bareHttpClientresolves tojava.net.httpand 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 asdata jpaactually 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.
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/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").
java-spring MCP questions
What tools does java-spring MCP provide?
It exposes 11 tools, including java_docs, java_search, maven_package, java_status, health_check.
How does the server handle documentation updates?
Pages are scraped from official sites at request time and cached locally in a SQLite TTL cache to ensure speed and freshness.
Can I search for both JDK classes and Spring sections at once?
Yes, the java_search tool performs a ranked name search over a local index that includes all JDK classes and Spring Boot reference sections.