# upkeep-mcp (npm · upkeep-mcp)

Website maintenance checks: domains, SSL, uptime, technical SEO and accessibility

- Trust score: 89/100 (high trust)
- Change this week: +3
- Registry status: active
- Liveness: live
- Owner verified: no
- Last scored: 2026-09-21

## Components

- npm · `upkeep-mcp`: 89/100 (this document), [markdown](https://verifymcp.io/servers/tiagocalado86-upkeep-mcp/upkeep-mcp.md), [page](https://verifymcp.io/servers/tiagocalado86-upkeep-mcp/upkeep-mcp)

## Channel facts

- Registry: `npm`
- Package: `upkeep-mcp`
- Version: `0.6.1`
- Transport: `stdio`

## Trust breakdown

How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. Scores are 0–100 per category. Scoring method: https://verifymcp.io/docs/scoring (what has changed: https://verifymcp.io/docs/scoring/changelog)

Scored 2026-09-21.

- **Supply Chain Security**: 100/100
  - No malware found by supply-chain analysis.
  - No known CVEs affecting this package version or its production dependencies.
  - No install/post-install scripts declared.
  - 0 of 9 dependencies flagged as unhealthy.
- **Provenance & Transparency**: 100/100
  - Source repository is publicly reachable at the declared URL.
  - Cryptographically verified build provenance (signed, bound to tiagocalado86/upkeep-mcp).
  - Clear OSI-approved license (MIT).
  - Actively maintained (last published 14 days ago).
  - Publishes a security disclosure policy (SECURITY.md).
- **Schema Quality & AI Usability**: 76/100
  - 100% of prompts and resources have a non-trivial description (not blank, and not just the item's name).
  - AI-judged instruction clarity (excellent).
  - Context-footprint check failed: tool/resource definitions use about 3659 tokens (~406/item across 9 items; 8 tools + 1 resources), over budget; trim descriptions and params.
  - Usage-examples check failed: none of the tools include examples.
- **Stability & Change Management**: 53/100
  - Stability observed for 16 of 30 days with no destabilising changes; credit accrues until the full window elapses.
- **Tool Coverage**: 100/100
  - 100% of tools have a non-trivial description (not blank, and not just the tool's name).
  - 100% of tool parameters carry a description.
  - Structured output schemas are declared (100% of tools); any adoption earns full credit.
- **Tool Safety**: 100/100
  - No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.
  - We read all 8 captured tool definition(s), and no name or description among them implies an irreversible operation.
  - An AI judge read all 9 captured unit(s) of tool text and found none that tries to manipulate the model reading it.
- **Capabilities**: 100/100
  - Implements a current MCP spec version (2026-07-28).

## Install

### How do I install the upkeep-mcp server?

upkeep-mcp runs locally as an npm package, launched with npx -y upkeep-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.

### Claude

```bash
claude mcp add tiagocalado86-upkeep-mcp -- npx -y upkeep-mcp
```

### Cursor

```json
{
  "mcpServers": {
    "tiagocalado86-upkeep-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "upkeep-mcp"
      ]
    }
  }
}
```

### VS Code

```json
{
  "servers": {
    "tiagocalado86-upkeep-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "upkeep-mcp"
      ]
    }
  }
}
```

### Codex

```bash
codex mcp add tiagocalado86-upkeep-mcp -- npx -y upkeep-mcp
```

### opencode

```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "tiagocalado86-upkeep-mcp": {
      "type": "local",
      "command": [
        "npx",
        "-y",
        "upkeep-mcp"
      ],
      "enabled": true
    }
  }
}
```

### OpenClaw

```bash
openclaw mcp add tiagocalado86-upkeep-mcp --command npx --arg -y --arg upkeep-mcp
```

### Hermes

```yaml
mcp_servers:
  tiagocalado86-upkeep-mcp:
    command: "npx"
    args: ["-y", "upkeep-mcp"]
```

### Netclaw

```json
{
  "McpServers": {
    "tiagocalado86-upkeep-mcp": {
      "Transport": "stdio",
      "Command": "npx",
      "Arguments": [
        "-y",
        "upkeep-mcp"
      ]
    }
  }
}
```

### Vellum

```bash
assistant mcp add tiagocalado86-upkeep-mcp -t stdio -c npx -a -y upkeep-mcp
```

### Other

```json
{
  "mcpServers": {
    "tiagocalado86-upkeep-mcp": {
      "command": "npx",
      "args": [
        "-y",
        "upkeep-mcp"
      ]
    }
  }
}
```

## Changelog

Every change recorded for this component, newest first. Days that predate change tracking, or that we cannot explain, say so: "we were watching and nothing happened" and "we were not watching" are different claims.

### 2026-09-20 (score 89, +1)

No change was recorded against any check on this day. Stability & Change Management went from 47 to 50. That category is still filling its 30-day observation window: 14 days of observed history at the previous scan, 15 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-18 (score 88, +1)

No change was recorded against any check on this day. Stability & Change Management went from 40 to 43. That category is still filling its 30-day observation window: 12 days of observed history at the previous scan, 13 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-16 (score 87, +1)

No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-14 (score 86, +1)

No change was recorded against any check on this day. Stability & Change Management went from 27 to 30. That category is still filling its 30-day observation window: 8 days of observed history at the previous scan, 9 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-12 (score 85, +1)

No change was recorded against any check on this day. Stability & Change Management went from 20 to 23. That category is still filling its 30-day observation window: 6 days of observed history at the previous scan, 7 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-09 (score 84, +1)

No change was recorded against any check on this day. Stability & Change Management went from 10 to 13. That category is still filling its 30-day observation window: 3 days of observed history at the previous scan, 4 at this one. The score rises as the window fills, whether or not the server changes.

### 2026-09-08 (score 83, +26)

- [security improvement] Malware scan: unverified → pass
- [security improvement] Known CVEs: unverified → pass
- [functional improvement] Dependency health: unverified → 1.00

### 2026-09-07 (score 57, −10)

- [security regression] Known CVEs: pass → unverified
- [functional regression] Schema quality: 358 → 406
- [functional regression] Dependency health: 1.00 → unverified
- [functional] Package version: 0.5.0 → 0.6.1
- [functional] Package version: 0.5.0 → 0.6.0

## MCP tools (8)

### `health` (~127 tokens)

Server health

Confirms that the upkeep-mcp server is running and reports which version it is.

Use this to verify the connection after installing or reconfiguring the server, or
when another upkeep-mcp tool behaves unexpectedly and you need to establish whether
the server itself is reachable.

Do not use it to check whether a website is up — it says nothing about any domain or
URL, only about this server process. Use uptime_check for websites.

Returns the server name and version, the Node.js version it runs under, how long the
process has been alive, and the time of the check.

Output parameters:

- `checkedAt` (string): When the report was produced, ISO 8601 with timezone.
- `node` (string): Node.js version the server runs under, e.g. "v20.11.0".
- `server` (string): Server name, e.g. "upkeep-mcp".
- `status` (string): Always "ok". If the server can answer, it is healthy.
- `uptimeSeconds` (integer): Whole seconds since the server process started.
- `version` (string): Server version, e.g. "0.1.0".

### `domain_check` (~604 tokens)

Domain registration and DNS

Reports when a domain registration expires, who the registrar is, and how the domain is
configured in DNS — nameservers, address records, mail exchangers, TXT and CAA records,
whether the delegation is signed with DNSSEC, and what its SPF and DMARC records say
about who may send email as it.

Use it to answer "is this domain about to lapse?", "who do we renew this with?", "where
does this domain point?" or "why does the apex not work when www does?". It is the right
first call when a site has gone dark for no obvious reason.

Use it too for "why is this client's email going to spam?" or "can someone spoof this
domain?" — SPF and DMARC are read from the domain's own DNS.

It also asks each of the domain's own nameservers, directly, whether they agree about
the zone. That answers "why does this site work for some people and not others?" and
"is this old nameserver still in the delegation?" — a question no recursive resolver can
answer, because it replies with whatever one server told it and does not say which.
Pass checkNameservers: false to skip it.

Do not use it to check whether a website responds — that is uptime_check — or to inspect
an SSL certificate, which is ssl_check. It reads only what registries and DNS publish.
It reports no DKIM: finding a DKIM key needs its selector, and a selector cannot be
discovered without guessing at names, which this project will not do.

Registration data comes from RDAP. Some country registries (.de, .nl, .no, .au, .fi)
publish no expiry date at all; the result says so explicitly rather than reporting a gap
as if it were an unknown. An expiry inside 30 days is reported as a warning and inside
seven days as critical — a manual renewal needs that much lead time. Returns findings
ordered by how much attention they need, worst first.

Input parameters:

- `checkNameservers` (boolean): Whether to ask the domain's own nameservers whether they agree about it, which no recursive resolver can answer. Costs one DNS query over TCP to each nameserver the domain publishes, in parallel, and…
- `domain` (string, required): The domain to check, e.g. "example.com" — no scheme, no trailing slash. A full URL such as "https://example.com/pricing" is also accepted and reduced to its hostname. Internationalised names ("café.p…

Output parameters:

- `checkedAt` (string): When the check ran, ISO 8601 in UTC.
- `dns` (object): DNS records for the registrable domain.
- `dnsResolved` (boolean): Whether the DNS lookup answered at all. When false every field under "dns" is empty because nothing could be read, not because the domain has no records.
- `dnssec` (object): Whether the delegation is signed. This is not a validation of the DNSSEC chain.
- `domain` (string): The hostname that was checked, in ASCII (punycode) form.
- `email` (object): Email authentication, read from the domain's own TXT records. DKIM is not reported: finding a DKIM key needs its selector, which cannot be discovered without guessing.
- `findings` (array): What needs attention, worst first.
- `nameservers` (object): What the domain's own nameservers said when each was asked directly, over TCP port 53 with recursion off. This is the only way to see a nameserver left in the delegation after a migration, or a zone…
- `registrableDomain` (string): The domain registration was checked against, e.g. "example.co.uk".
- `registration` (object): What the registry publishes about this registration.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `unicodeDomain` (string|null): The Unicode form when the domain is internationalised, otherwise null.

### `ssl_check` (~458 tokens)

SSL certificate

Inspects the TLS certificate a host actually serves: when it expires, who issued it,
whether the chain verifies, which hostnames it covers, and which TLS version was
negotiated.

Use it to answer "when does this certificate need renewing?", "why does the browser warn
about this site?" or "does the certificate cover www as well as the bare domain?" — the
last being one of the most common real-world misconfigurations, along with a missing
intermediate certificate, which is reported as UNABLE_TO_VERIFY_LEAF_SIGNATURE.

Do not use it for domain registration expiry, which is a different date entirely — that is
domain_check. It connects to the host but does not request a page; use uptime_check for that.

Certificates that are expired, self-signed or untrusted are inspected and reported rather
than refused. Revocation is checked over OCSP, preferring the response a server staples to
the handshake and otherwise asking the issuing authority directly; the answer is only
believed once its signature verifies against that authority. Many healthy certificates
cannot be checked at all, because since 2025 the two largest issuers publish no OCSP
responder and distribute revocation by CRL instead — that is reported as an unavailable
reason rather than as a problem with the site, and produces no finding. Certificate
revocation lists are not downloaded.

A certificate is reported as a warning inside 14 days and as critical inside seven.
That window is deliberately shorter than the one domain_check uses for registrations:
ACME clients renew with 30 days left, so 28 days remaining is a healthy site in the
middle of a normal renewal, not a problem. Returns findings ordered by urgency, worst first.

Input parameters:

- `domain` (string, required): The host whose certificate should be inspected, e.g. "example.com" — no scheme, no trailing slash. A full URL is also accepted and reduced to its hostname. The certificate is read for exactly this ho…
- `port` (integer): TCP port to connect to. Defaults to 443. Use 8443, 993 and so on for other services.

Output parameters:

- `chain` (object): The certificate chain and whether it verified.
- `checkedAt` (string): When the check ran, ISO 8601 in UTC.
- `coverage` (object): Which hostnames this certificate is valid for.
- `daysUntilExpiry`: Whole days until the certificate expires, negative if already expired. Floored, so a certificate expiring in a few hours reads as 0.
- `expiresAt`: When the certificate expires, ISO 8601 UTC.
- `findings` (array): What needs attention, worst first.
- `fingerprintSha256` (string|null): SHA-256 fingerprint of the certificate.
- `host` (string): The host that was contacted, in ASCII (punycode) form.
- `issuedAt`: Start of the certificate validity, ISO 8601 UTC.
- `issuer` (string|null): Issuer common name, e.g. "R11" or "GTS CA 1P5".
- `port` (integer): The port that was contacted.
- `revocation` (object): Whether the certificate has been revoked, and how confidently that was established.
- `serialNumber` (string|null): Certificate serial number, hexadecimal.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `subject` (string|null): Subject common name.
- `tls` (object): What the handshake negotiated.

### `uptime_check` (~322 tokens)

Site reachability and headers

Requests a page and reports whether it answered, how long it took, the full redirect chain
it went through, whether plain HTTP is upgraded to HTTPS, and which security headers came
back.

Use it to answer "is this site up?", "why does this URL take four redirects to load?",
"does http:// still work and should it?" or "does this site send HSTS?". It is the check
to run when a client reports that a page is down or slow.

Do not use it to inspect a certificate — that is ssl_check — or for registration and DNS,
which is domain_check. It fetches one page, not a whole site: use seo_audit for crawling.

Redirects are followed one hop at a time so the whole chain is visible, up to ten hops.
Response time is wall clock to the first response headers and includes DNS, TCP and TLS
setup, so it is not a measure of server processing time.

Status codes are graded rather than lumped together: 5xx, 404 and 410 are critical, 401
and 403 are a warning because they are normal for a staging site, and 429 is reported as
unknown because a throttled check establishes nothing about real availability. Returns
findings ordered by urgency, worst first.

Input parameters:

- `url` (string, required): The page to request, e.g. "https://example.com". A bare domain such as "example.com" is accepted and tried over HTTPS. Include the path when a specific page matters; the homepage is checked otherwise.

Output parameters:

- `checkedAt` (string): When the check ran, ISO 8601 in UTC.
- `finalUrl` (string|null): Where the redirect chain ended.
- `findings` (array): What needs attention, worst first.
- `hsts` (object): The HSTS policy, read from the HTTPS response only.
- `https` (object): Whether visitors arriving over plain HTTP are moved to HTTPS.
- `reachable` (boolean): Whether the server answered at all.
- `redirects` (object): The redirect chain, followed manually one hop at a time.
- `responseTimeMs`: Wall-clock milliseconds to the first response headers. Includes DNS, TCP and TLS setup, so it is not server processing time.
- `securityHeaders` (object): Security-relevant response headers.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `status`: Status code at the end of the redirect chain.
- `url` (string): The URL that was requested first.

### `seo_audit` (~467 tokens)

Technical SEO of one page

Reads one page and reports the technical SEO facts a maintenance retainer is judged on:
title and meta description, heading structure, canonical, Open Graph, hreflang, language,
viewport, images with no alt text, the state of robots.txt and the sitemap, and which
internal links are broken. The sitemap is checked against the rules of the sitemaps
protocol, so a file that exists but that consumers drop is reported as broken rather
than as present.

Use it to answer "why is this page not being indexed?", "does this page have the metadata
it needs?" or "are there broken links on the homepage?". It is the check to run before a
quarterly report, and after a site rebuild.

Do not use it to check whether a site is up, which is uptime_check, or to inspect a
certificate, which is ssl_check. It audits one page: it does not crawl a site, and it
judges only what is in the HTML, never how a page ranks.

robots.txt is read first and obeyed. A page this crawler is not allowed to read is
reported as such and is not requested — and neither are internal links it forbids. An
unreadable robots.txt is treated as forbidding everything, per RFC 9309.

Link checking is one HTTP request per link, paced politely, so an audit with links takes
seconds rather than milliseconds; pass checkLinks: false when speed matters more.
Returns findings ordered by urgency, worst first.

Input parameters:

- `checkLinks` (boolean): Whether to request each internal link to find broken ones. Defaults to true. Set it to false for a fast metadata-only audit: link checking is one request per link and is what makes this tool take sec…
- `maxLinks` (integer): How many internal links to check at most. Defaults to 25. Links beyond the limit are counted and reported as unchecked, never silently ignored.
- `url` (string, required): The page to audit, e.g. "https://example.com/pricing". A bare domain such as "example.com" is accepted and read over HTTPS. One page is audited, not a whole site, so pass the page you care about; the…

Output parameters:

- `checkedAt` (string): When the audit ran, ISO 8601 in UTC.
- `contentType` (string|null): Content type, without parameters, e.g. "text/html".
- `fetched` (boolean): Whether the page was read at all. False when robots.txt forbids it.
- `finalUrl` (string|null): Where it ended up after redirects.
- `findings` (array): What needs attention, worst first.
- `links` (object): Internal link health. Only internal links are requested; external ones are counted.
- `page` (object): What the document itself says.
- `robots` (object): What the host publishes about crawling, and what it means for this audit.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `sitemap` (object): The sitemap declared in robots.txt, or /sitemap.xml when none is declared.
- `status`: HTTP status of the page.
- `truncated` (boolean): Whether the document was longer than the read limit and was cut off.
- `url` (string): The URL that was requested.

### `site_crawl` (~575 tokens)

Technical SEO across a site

Walks a site from a starting page and reports what only a crawl can see: pages sharing a
title or a meta description, internal links that are broken and which page links to
them, pages asking not to be indexed, and how much of the site was reachable at all.

Use it after a site rebuild or a migration, and before a quarterly report — the findings
here are the ones a client never notices and a search engine always does. Use it to
answer "why are these pages competing with each other?", "are there broken links",
anywhere on the site?" or "is anything still marked noindex from staging?".

Do not use it to audit one page in depth — that is seo_audit, which reports canonical,
Open Graph, hreflang, images without alt text and the sitemap for a single URL. Do not
use it to check whether a site is up (uptime_check) or to inspect a certificate
(ssl_check). It judges only what is in the HTML, never how a page ranks.

It stays on one origin: https://example.com and https://www.example.com are different
origins and only the one you start from is visited.

robots.txt is read first and obeyed for every URL before it is requested. An unreadable
robots.txt is treated as forbidding everything, per RFC 9309, and the crawl reports that
rather than proceeding.

It costs one request per page, paced at one every half second per host, so 25 pages is
about fifteen seconds. Three budgets bound it — pages, depth, and a two-minute deadline —
and whichever one stopped the crawl is reported along with how many URLs were left
unvisited, because a report that does not say it saw a quarter of the site is worse than
no report.

Input parameters:

- `maxDepth` (integer): How many links deep from the starting page to follow. Defaults to 3. 0 fetches only the starting page. Three reaches everything a small business site has; past that the page budget is the real bound…
- `maxPages` (integer): How many pages to fetch. Defaults to 25, at most 100. Every page is one request paced at one every half second, so this is the cost: 25 pages is about fifteen seconds, 100 about a minute. Pages found…
- `url` (string, required): The page to start from, e.g. "https://example.com/" — a full URL with scheme. The crawl stays on this URL's origin, so start at the address the site actually serves: https://example.com and https://w…

Output parameters:

- `broken` (array): Internal links that did not answer with a usable status, with the page each was found on — which is the half of a broken link report that makes it fixable.
- `checkedAt` (string): When the crawl ran, ISO 8601 in UTC.
- `crawl` (object): What the crawl covered, and what it did not.
- `duplicates` (object): What only a crawl can see. One page cannot tell you that its title is the same as forty others', and that is the commonest reason a site's pages compete with each other in search results.
- `findings` (array): What needs attention, worst first.
- `origin` (string): The origin it stayed on, e.g. "https://example.com".
- `pages` (array): Every page fetched, in the order they were visited.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `startUrl` (string): The URL the crawl was asked to start from.

### `accessibility_audit` (~403 tokens)

WCAG violations on one page

Opens one page in a real browser and runs axe-core over it, reporting the WCAG rules it
fails, how many elements fail each one, and where they are.

Use it to answer "does this page meet WCAG 2.2 AA?", "what would an accessibility audit
flag?" or "which of these fixes matters most?". It is the check to run before a site goes
live, and when a client asks about accessibility obligations.

Do not use it for metadata, headings or broken links, which seo_audit reports without a
browser and far faster. It audits one page, not a site, and it renders that page: it is
much slower than every other tool here.

It needs a browser. Nothing else in this server does, so if none is installed this tool
says so and names the one command that fixes it, while every other check keeps working.
A hosted instance runs it too: the published image ships a browser, and every request
that browser makes goes through the same public-address and web-port rules as the rest
of this server.

Automated rules find roughly a third of accessibility problems. A page with no violations
is a page that passed the machine-checkable part, which is not the same as being usable,
and the count of undecided rules is reported for exactly that reason. robots.txt is
obeyed. Returns findings ordered by urgency, worst first.

Input parameters:

- `standard` (string): Which rules to run. Defaults to "wcag2aa", the level most accessibility policies and procurement rules are written against. Each level includes the ones below it. "best-practice" runs axe's non-WCAG…
- `url` (string, required): The page to audit, e.g. "https://example.com/contact". A bare domain is accepted and read over HTTPS. One page is audited, not a site.

Output parameters:

- `affectedElements` (integer): How many elements failed a rule, counting repeats.
- `audited` (boolean): Whether the page was audited at all. False when robots.txt forbids it.
- `axeVersion` (string|null): Which axe-core did the judging, so a result can be reproduced.
- `checkedAt` (string): When the audit ran, ISO 8601 in UTC.
- `finalUrl` (string|null): Where the browser ended up.
- `findings` (array): What needs attention, worst first.
- `incompleteCount` (integer): Rules axe could not decide on its own. These are not passes: they are the ones needing a person, and a page with none is unusual rather than perfect.
- `pageTitle` (string|null): The title as rendered, which markup alone may not show.
- `passCount` (integer): How many rules passed.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `standard` (string): Which standard was asked for.
- `url` (string): The URL that was requested.
- `violationCount` (integer): How many rules failed.
- `violations` (array): Every rule that failed, worst impact first.

### `portfolio_report` (~636 tokens)

Whole portfolio, ranked by urgency

Runs the maintenance checks across every site in a portfolio and returns one report
ordered by what needs action first: what is down, what expires soonest, what regressed
since the last run.

Use it to answer "what needs attention this week?", "is anything down?" or "what do I put
in this quarter's report?" across a whole client list. It is the right call whenever the
question is about more than one site — running domain_check, ssl_check and uptime_check
once per site by hand is what this replaces.

The portfolio comes from the "sites" argument, or from a local JSON file ("sites.json" by
default) whose format is documented in sites.example.json. Pass "checks" to override what
each site asks for — ["uptime"] answers "is anything down right now?" in a fraction of the
time — and "tags" to report on part of the portfolio.

Do not use it to answer a question about one site: domain_check, ssl_check, uptime_check
and seo_audit answer those directly and in a fraction of the time. It runs checks and
reports; it never changes a site, and it never writes to the portfolio file.

A site that cannot be checked is reported as a finding, never as a failure of the whole
report. Only a portfolio that cannot be read at all is an error.

What changed since the previous run is compared against one recorded snapshot. By default
that snapshot lives in memory and is lost when this server restarts; a portfolio file may
add a "history" path, and then the snapshot is written there and comparisons survive a
restart for up to 90 days. Nothing is written unless the portfolio asks for it. Either way
the report says what it had to compare against, and why it had nothing when it had nothing,
rather than implying nothing changed. Only sites that both runs measured the same way are
compared, so a quick uptime-only pass never invents regressions in the run after it.

Input parameters:

- `checks` (array): Run exactly these checks for every site, ignoring what the file says. Use it for a quick pass — ["uptime"] answers "is anything down right now?" in a fraction of the time.
- `file` (string): Path to a portfolio JSON file, e.g. "/Users/you/sites.json". Give the full path: a relative one is resolved against the directory this server was started in, which for a desktop client is usually "/"…
- `sites` (array): The portfolio, passed inline. Use this for a one-off report over a handful of sites. When omitted, the portfolio is read from the local file instead.
- `tags` (array): Only report on sites carrying at least one of these tags, e.g. ["retainer"]. Matched case-insensitively. Omit to report on the whole portfolio.

Output parameters:

- `changes` (object): What changed since the previous run in this server process.
- `file` (string|null): The file it was read from, when it was read from one.
- `generatedAt` (string): When the report was produced, ISO 8601 in UTC.
- `needsAttention` (array): Everything worth acting on, worst first. This is the list the report exists to produce.
- `notes` (array): Anything the report could not do, said plainly rather than left out.
- `severity` (string): How much attention this needs. "critical" means act now; "warning" means act this month; "unknown" means the check could not establish the fact, which is not the same as it being fine.
- `siteCount` (integer): How many sites the report covers, after any tag filter.
- `sites` (array): Every site, most urgent first.
- `source` (string): Where the portfolio came from.
- `summary` (object): How many sites landed at each severity.

## Diagnostics

Captured diagnostic sections: Provenance, Dependencies. The full working is on the page: https://verifymcp.io/servers/tiagocalado86-upkeep-mcp/upkeep-mcp#diagnostics

## Score history

- 2026-09-21: 89
- 2026-09-20: 89
- 2026-09-19: 88
- 2026-09-18: 88
- 2026-09-17: 87
- 2026-09-16: 87
- 2026-09-15: 86
- 2026-09-14: 86
- 2026-09-13: 85
- 2026-09-12: 85
- 2026-09-11: 84
- 2026-09-10: 84
- 2026-09-09: 84
- 2026-09-08: 83
- 2026-09-07: 57
- 2026-09-06: 67
- 2026-09-05: 56

## Common questions

### What is the upkeep-mcp server?

upkeep-mcp is listed in the public MCP registry as io.github.tiagocalado86/upkeep-mcp. Website maintenance checks: domains, SSL, uptime, technical SEO and accessibility. This page covers its npm package (upkeep-mcp).

### Is the upkeep-mcp server safe to use?

upkeep-mcp scores 89 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 21 September 2026. It declares no install or post-install scripts. Its build provenance is signed and verified. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.

### What tools does the upkeep-mcp server expose?

upkeep-mcp exposes 8 tools: health, domain_check, ssl_check, uptime_check, seo_audit, and 3 more. Their descriptions and schemas cost roughly 3,592 tokens of context every time the server is loaded.

### Is the upkeep-mcp server still maintained?

upkeep-mcp is still listed as active in the MCP registry. We last reached this channel on 21 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.

### What licence is the upkeep-mcp server under?

upkeep-mcp declares the MIT licence, which is OSI-approved. That covers the source only, and says nothing about the cost of any service it calls.

## Links

- npm package: https://www.npmjs.com/package/upkeep-mcp
- Socket report: https://socket.dev/npm/package/upkeep-mcp
- Repository: https://github.com/tiagocalado86/upkeep-mcp
- Changelog RSS feed: https://verifymcp.io/servers/tiagocalado86-upkeep-mcp/upkeep-mcp.xml
- Changelog JSON feed: https://verifymcp.io/servers/tiagocalado86-upkeep-mcp/upkeep-mcp.json
- HTML version of this page: https://verifymcp.io/servers/tiagocalado86-upkeep-mcp/upkeep-mcp
