end-of-life-mcp
Checks whether a Debian release is still supported, security-only, or end-of-life, with dates and upgrade targets.
Determines the support and LTS status of Django versions, including end-of-life dates and upgrade recommendations.
Determines the support status of Node.js versions, including LTS, EOL dates, and recommended upgrade paths.
Verifies the support status of Python versions, including security maintenance and upgrade suggestions.
Determines the support status of Ruby versions, including security fixes and upgrade paths.
Reports the end-of-life and support status of Ubuntu releases, with upgrade guidance.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@end-of-life-mcpCheck my Dockerfile and .nvmrc for end-of-life versions"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
End of Life
Is this version still supported? End of Life reads a project's Dockerfile, version files, manifests and CI workflows, finds every runtime, database and OS version it pins, and says for each: still supported, security fixes only, paid extended support only, or end of life, with the dates, the days left, the latest patch, the newest version to move to, and the smallest jump that still gets fixes.
Coding agents pick versions from memory: they write FROM node:18 or python-version: "3.8"
years after those stopped getting security fixes. This server answers from
endoflife.date (470+ products) and finds versions where they hide:
python:3.8-slim-bullseye is Python 3.8 on Debian 11, node:18-alpine3.17 carries Alpine 3.17,
.nvmrc may say lts/hydrogen (Node.js 18), runs-on: ubuntu-20.04 pins an OS.
Status | Meaning |
| Regular fixes |
| Active support ended; security fixes continue |
| End of life for most users; paid extended support continues |
| No fixes of any kind |
| Not released yet |
Status is computed from the dates on the day you ask, not from stored flags. For a range
(>=3.8, ^18.12), the lowest version it allows is checked: it is the oldest version the project
still promises to run on.
No account or key needed. Built and maintained by Arhan Canli.
Install
Needs Node.js 20 or newer. No account or key.
Claude Code
claude mcp add end-of-life -- npx -y end-of-life-mcpClaude Desktop: download end-of-life-mcp-<version>.mcpb from the latest release and open it. The bundle is signed; verify it with gh attestation verify <file> --repo arhancanli/end-of-life-mcp.
Any other client (Windsurf, Zed, Cline, Continue and others), in its MCP config file:
{
"mcpServers": {
"end-of-life": {
"command": "npx",
"args": [
"-y",
"end-of-life-mcp"
]
}
}
}Docker
docker build -t end-of-life-mcp https://github.com/arhancanli/end-of-life-mcp.git && docker run -i --rm end-of-life-mcpHosted (Streamable HTTP): node src/server.mjs --http serves stateless MCP at POST /mcp (port from PORT, default 3000).
Related MCP server: CanIUse MCP
Example
An agent calls check_project with:
{
"files": [
{
"path": "Dockerfile",
"content": "FROM node:18-alpine3.17 AS build\nFROM python:3.8-slim-bullseye\n"
},
{
"path": ".nvmrc",
"content": "lts/hydrogen\n"
},
{
"path": ".github/workflows/ci.yml",
"content": "jobs:\n test:\n runs-on: ubuntu-20.04\n strategy:\n matrix:\n node: [20, 22, 24]\n"
},
{
"path": "pyproject.toml",
"content": "[project]\nrequires-python = \">=3.10\"\n"
},
{
"path": "README.md",
"content": "hello"
}
]
}and gets back (recorded from the live server on 2026-09-26):
{
"counts": {
"end_of_life": 5,
"extended_only": 2,
"security_only": 2,
"supported": 1
},
"results": [
{
"product": "nodejs",
"version": "18",
"cycle": "18",
"status": "end_of_life",
"lts": true,
"eol": "2025-04-30",
"active_until": "2023-10-18",
"latest": "18.20.8",
"link": "https://endoflife.date/nodejs",
"upgrade_to": {
"cycle": "24",
"latest": "24.21.0",
"lts": true
},
"nearest_supported": {
"cycle": "22",
"latest": "22.23.3",
"eol": "2027-04-30"
},
"file": "Dockerfile",
"line": 1,
"found": "FROM node:18-alpine3.17 AS build"
},
{
"product": "alpine-linux",
"version": "3.17",
"cycle": "3.17",
"status": "end_of_life",
"eol": "2024-11-22",
"latest": "3.17.10",
"link": "https://endoflife.date/alpine-linux",
"upgrade_to": {
"cycle": "3.24",
"latest": "3.24.2"
},
"nearest_supported": {
"cycle": "3.21",
"latest": "3.21.8",
"eol": "2026-11-01"
},
"file": "Dockerfile",
"line": 1,
"found": "FROM node:18-alpine3.17 AS build"
},
{
"product": "python",
"version": "3.8",
"cycle": "3.8",
"status": "end_of_life",
"eol": "2024-10-07",
"active_until": "2021-05-03",
... (176 more lines)Tools
Tool | What it does |
| Finds the runtime, database and OS versions a project pins in its files (Dockerfile, .nvmrc, .node-version, .python-version, .ruby-version, runtime.txt, .tool-versions, package.json, pyproject.toml, go.mod, composer.json, Gemfile, global.json and GitHub Actions workflows) and checks each against endoflife.date, with file and line. Pass each file's path and text. |
| Checks up to 50 'product version' pairs (python 3.8, node@18, postgres:13, ubuntu 20.04, django 4.2) against endoflife.date: status (supported, security_only, extended_only, end_of_life, upcoming), end-of-life date, days left, latest patch and the version to upgrade to. Worst first. |
| Every maintained release cycle of a product (and the most recent ended ones) with status, release, end-of-active-support and end-of-life dates, LTS flag and latest patch, plus the recommended upgrade target. |
Files check_project reads: Dockerfile and Containerfile (FROM lines, including the OS in tags
such as -alpine3.19 and -bookworm), .nvmrc, .node-version, .python-version,
.ruby-version, runtime.txt, .tool-versions, package.json (engines, volta),
pyproject.toml (requires-python, Poetry), go.mod (go, toolchain), composer.json,
Gemfile, global.json and GitHub Actions workflows (setup-* versions, matrices, runs-on).
How it behaves
Read-only: no tool changes anything outside this process; files are read as text, never run.
Network: HTTPS only, to the hosts listed in
package.jsonunderfactory.allowHosts, with a deadline, a size cap and bounded retries; a stalled read is retried. Nothing else is contacted, and nothing is logged except unexpected failures (to stderr, without your inputs).Answers are cached for 12 hours.
Results are compact JSON with a matching output schema, worst status first.
Benchmark
Not yet measured.
Performance
Measured 2026-09-26 from Dubai, home connection against the live upstream, Node 24.19.0 (bench/perf.json, scripts/perf.mjs in the factory).
Call | First call | Repeat | Result size |
check_project: Dockerfile, .nvmrc, a CI workflow, pyproject | 1853 ms | 2.6 ms | 3,869 chars |
check_versions: 6 named versions | 2154 ms | 3.1 ms | 1,694 chars |
product_lifecycle: Node.js | 892 ms | 1.2 ms | 997 chars |
First call: a fresh server process, including the TLS connection and the upstream's own time. Repeat: the same call again, answered from the in-process cache, so it shows this server's own overhead.
Tool definitions the model reads on every turn (name, description, input schema): 1,706 characters, against 6,734 for endoflife-mcp, the only published end-of-life server. The full tool list, with the output schemas and annotations clients use to validate results, is 3,165 characters (8,958 for the alternative).
Data sources
endoflife.date, the community-maintained end-of-life database (MIT-licensed). Confirm dates that matter commercially with the vendor.
More MCP servers by Arhan Canli
Internet Standards: RFC sections, status, obsoleted-by chains, errata and IANA registries for coding agents.
Package Truth: Checks packages exist before install: version, deprecation, vulnerabilities, licence. 7 ecosystems.
Citation Check: Verifies citations: finds fabricated or mismatched references and retractions, returns clean BibTeX.
Drug Label: FDA drug label answers with section citations, RxNorm name resolution, recalls and shortages.
Recall Check: One recall check across CPSC, FDA and NHTSA: match by name, model number, UPC or VIN.
Satellite Imagery: Find the clearest Sentinel-2, Landsat, Sentinel-1 or NAIP scene for any place, with band links.
License
MIT, Copyright (c) 2026 Arhan Canli.
Available Tools
3 toolscheck_projectCheck a project's runtimesARead-onlyIdempotent
Finds the runtime, database and OS versions a project pins in its files (Dockerfile, .nvmrc, .node-version, .python-version, .ruby-version, runtime.txt, .tool-versions, package.json, pyproject.toml, go.mod, composer.json, Gemfile, global.json and GitHub Actions workflows) and checks each against endoflife.date, with file and line. Pass each file's path and text.
| Name | Required | Description | Default |
|---|---|---|---|
| files | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| counts | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, openWorldHint, idempotentHint, and destructiveHint=false, and the description adds concrete behavioral detail: it scans a specific set of file types and reports matches with file and line. It also clarifies that the caller must supply the file text rather than the tool reading paths, which is valuable beyond the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a long sentence but every part earns its place: the action, the supported file list, the output granularity, and the invocation requirement. It could be split for readability, but there is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with this complexity, the description covers the input format, supported file types, what is checked, and the output detail. The output schema exists, so return values do not need to be spelled out. The main missing piece is differentiation from sibling tools, but that is not required for a correct call.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description must compensate. It maps 'path and text' to the path/content structure and lists relevant file types, which adds meaning beyond the bare schema. However, it does not clarify constraints like 30-file limits or that content should be the full file text, so the compensation is only partial.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear action: it finds runtime, database, and OS versions pinned in project files and checks each against endoflife.date, returning file and line. It names the supported file types explicitly. It does not explicitly differentiate itself from siblings check_versions and product_lifecycle, so it misses the top score.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus check_versions or product_lifecycle, and no exclusions or prerequisites are stated. The only usage-related instruction is 'Pass each file's path and text,' which is about invocation, not selection context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
check_versionsCheck versions for end of lifeARead-onlyIdempotent
Checks up to 50 'product version' pairs (python 3.8, node@18, postgres:13, ubuntu 20.04, django 4.2) against endoflife.date: status (supported, security_only, extended_only, end_of_life, upcoming), end-of-life date, days left, latest patch and the version to upgrade to. Worst first.
| Name | Required | Description | Default |
|---|---|---|---|
| items | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
| counts | Yes | |
| results | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already carry readOnly=true, openWorldHint=true, idempotentHint=true, and destructiveHint=false. The description goes beyond those by disclosing the external data source (endoflife.date), the 'up to 50' cap, the returned status vocabulary, and the 'worst first' ordering behavior. That adds meaningful operational context without contradicting any annotation.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, information-dense sentence with no filler. It front-loads the key limit ('up to 50') and immediately gives representative inputs, followed by output fields and ordering. Every phrase earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given that an output schema exists (so return values are documented elsewhere) and annotations cover safety, the description supplies the remaining essentials: input limits, input format examples, external source, and sort order. The only gap is usage routing among siblings, but that affects selection more than invocation, so the tool itself is adequately described.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With schema description coverage at 0%, the description must explain the 'items' parameter, and it does by giving five concrete examples of product-version pairs (python 3.8, node@18, postgres:13, ubuntu 20.04, django 4.2). This conveys both the value type and the variety of accepted formats, though it stops short of an explicit grammar, so it is not a full 5.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description names a specific verb ('Checks') and a precise resource ('product version pairs against endoflife.date'), and lists the concrete outputs (status, EOL date, days left, patch, upgrade target). It is clear on its own, but it does not explicitly differentiate itself from siblings like product_lifecycle, so it earns a 4 rather than a 5.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no explicit guidance on when to use this tool versus the sibling tools check_project or product_lifecycle. There is no stated 'use this when...' or 'instead of...' condition; the only implicit signal is the batch-oriented wording 'up to 50', which is weaker than a clear routing instruction.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
product_lifecycleA product's release cyclesBRead-onlyIdempotent
Every maintained release cycle of a product (and the most recent ended ones) with status, release, end-of-active-support and end-of-life dates, LTS flag and latest patch, plus the recommended upgrade target.
| Name | Required | Description | Default |
|---|---|---|---|
| product | Yes | ||
| include_ended | No | How many ended cycles to include; default 3 |
Output Schema
| Name | Required | Description |
|---|---|---|
| cycles | Yes | |
| product | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The annotations already cover safety (readOnly, idempotent, non-destructive), and the description adds useful scoping details: only maintained cycles plus recent ended ones are included, and the recommended upgrade target is provided. Nothing contradicts the annotations, but the behavioral context is modest.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
One dense, front-loaded sentence covers the resource and all key output aspects without filler. Every clause adds information, and the most important constraint ('maintained release cycle') comes first.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema and safety annotations, the description covers the return content and basic scope sufficiently for an agent to call the tool. The main missing element is explicit usage or alternative-selection guidance, but that is not essential for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%: include_ended has a description, but product is documented only as a string. The description identifies product as the lifecycle subject and hints at include_ended via 'most recent ended ones', adding some meaning. However, it does not explain how to reference a product or the interaction between the two parameters.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description is specific and clear: it names the resource (a product's release cycles) and enumerates the returned fields (status, release, support/EOL dates, LTS flag, latest patch, upgrade target). It does not explicitly contrast itself with check_project or check_versions, so it misses the top score for sibling differentiation.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance about when to use this tool versus check_project or check_versions, nor any context about expected use cases or exclusions. The description only states what is returned, not how to choose it among siblings.
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.
3 tool updates
v0.1.0- First observed
check_project - First observed
check_versions - First observed
product_lifecycle
TDQS
Scored across 3 tools
check_project and check_versions both return EOL status for versions, but their inputs are clearly different (project files vs. explicit version pairs), so ambiguity is low. product_lifecycle is distinct, providing product-level release cycle information rather than checking specific versions. The slight overlap between checking a project's versions and checking explicit versions is resolvable from the descriptions.
Two tools follow a verb_noun pattern (check_project, check_versions) with the same verb 'check', but product_lifecycle is a noun phrase without a verb, breaking the pattern. This is a minor inconsistency, and the names are still readable and intuitive, but a more consistent convention (e.g., get_product_lifecycle) would improve coherence.
With 3 tools, the server is well-scoped for its purpose. Each tool covers a distinct need: scanning project files, checking explicit versions, and retrieving product lifecycle details. There is no redundancy or scope creep, and the count sits comfortably within the ideal 3-15 range.
The tool set covers the core end-of-life checking workflows: scanning project files, checking arbitrary version strings, and exploring a product's release lifecycle. This addresses the primary use cases without obvious dead ends. The only minor gap might be a dedicated single-version lookup, but check_versions handles that trivially by passing one version, so coverage feels complete.
Maintenance
Related MCP Connectors
Latest versions, LTS windows, and EOL dates for 300+ products. Fresh ground truth for stale models.
EOL dates, risk scores, CISA KEV exposure, SBOM audits and edge-device EOS for 500+ products.
Generate SBOMs, scan vulnerabilities, and analyze dependencies from local projects or Git repos.
Detects database migration table locks, terraform cost leaks, and OWASP API flaws.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to check software end-of-life dates and support status using the endoflife.date API, providing accurate information on software lifecycle, security status, and upgrade recommendations in real-time.58MIT
- AlicenseAqualityDmaintenanceProvides intelligent CSS/JS feature compatibility checking with configurable browser targets, polyfill support, and smart project scanning. Enables developers to automatically detect browser compatibility issues and get actionable remediation steps with build tool configurations.53 npm3MIT
- AlicenseAqualityDmaintenanceEnables inspection and analysis of project structures to detect languages, frameworks, entry points, and dependencies across multiple programming languages including Node.js, Python, PHP, Go, Java, and Rust.35 npm1ISC
- AlicenseNot gradedqualityAmaintenanceEnables users to look up package versions, scan for vulnerabilities, and analyze dependencies across multiple registries (npm, Maven, PyPI, etc.) using exact version recommendations for security.4MIT