---
summary: "Major rewrite on @cyanheads/mcp-ts-core. New 4-tool surface (publish, manage, fetch, emoji search), topic snapshot resource, multi-server NTFY_SERVERS registry. Breaking changes from 1.x; NTFY_API_KEY accepted as a deprecated alias."
breaking: true
security: false
---

# 2.0.0 — 2026-05-09

Full migration from a hand-rolled MCP server to the [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) framework, plus a complete rebuild of the ntfy domain on top of it. The 1.x `send_ntfy` tool is replaced by a four-tool surface that covers publish, manage, fetch, and emoji-tag lookup; topics are also exposed as resources for snapshot reads. Configuration supports a multi-server registry (`NTFY_SERVERS`) so a single process can authenticate against multiple ntfy hosts at once.

## Added

- **`ntfy_publish_message`** tool — single tool covering all 18 publish parameters, including titles, priorities, tags, click/attach/icon URLs, action buttons (`view` / `broadcast` / `http` / `copy`), email and voice-call forwarding, scheduled delivery, markdown bodies, and message updates via `sequence_id`.
- **`ntfy_manage_message`** tool — clears or deletes a previously-published message by `sequence_id`; emits `message_clear` / `message_delete` events to subscribers.
- **`ntfy_fetch_messages`** tool — polls cached messages from one or more topics with filters for `since`, `scheduled`, `priority`, `tags`, `title`, and `message`. Default window is `10m`, capped at 100 messages.
- **`ntfy_search_emoji_tags`** tool — substring search over the bundled ntfy emoji-tag reference; returns the `tag` strings ready to plug into `ntfy_publish_message`'s `tags` field.
- **`ntfy://{topic}`** resource — snapshot of a topic's last 20 messages from the past hour, plus the topic's browser URL.
- **`NtfyService`** (`src/services/ntfy/ntfy-service.ts`) — HTTP client with `withRetry` + per-request timeout. Auth headers are scoped to specific registered base URLs; per-call `base_url` overrides that match a registered base forward that server's auth automatically, anything else goes out unauthenticated to avoid leaking credentials to arbitrary hosts.
- **`NTFY_SERVERS` multi-server registry** — JSON array of `{ baseUrl, authToken? | authUsername?+authPassword? }` entries. First entry is the default base. Lets a single process speak to multiple ntfy hosts with separate credentials. Falls back to the single-server `NTFY_BASE_URL` / `NTFY_AUTH_*` shorthand when unset.
- **`NTFY_API_KEY` deprecated alias** — accepted as a fallback for `NTFY_AUTH_TOKEN` in single-server mode so 1.x users with the var set keep authenticating after upgrade. Emits a one-line stderr deprecation notice on startup. Ignored when `NTFY_SERVERS` is set.
- **`EmojiTagService`** (`src/services/emoji-tags/emoji-tag-service.ts`) — in-memory tag → emoji lookup backed by `data.generated.ts`.
- **`scripts/build-emoji-tags.ts`** — regenerates `src/services/emoji-tags/data.generated.ts` from `docs/ntfy/emojis.md`.
- **Mirrored upstream docs** under `docs/ntfy/` (`publish.md`, `subscribe/api.md`, `emojis.md`, `examples.md`, `index.md`); pinned commit and refresh steps in `docs/ntfy/SOURCES.md`.
- **Test suite** — 137 unit tests across config parsing, `NtfyService` request shape and NDJSON parsing, the upstream-error classifier, tool error contracts (`forbidden_topic`, `rate_limited`, `payload_too_large`, `unverified_contact`, `invalid_since`, `not_found`, `upstream_unreachable`), and the topic resource.

## Changed

- **Framework** — runs on `@cyanheads/mcp-ts-core` ^0.8.19. Tool, resource, and prompt definitions use the framework builders; handlers throw and the framework catches/classifies/formats.
- **Transports** — stdio plus streamable-HTTP via the framework, configured by `MCP_HTTP_HOST`, `MCP_HTTP_PORT`, `MCP_HTTP_ENDPOINT_PATH`, `MCP_AUTH_MODE`.
- **Config** — env-driven, lazy-parsed in `src/config/server-config.ts`. Servers configured either via `NTFY_SERVERS` (JSON registry, first entry is the default base) or the single-server shorthand: `NTFY_BASE_URL` (default `https://ntfy.sh`), `NTFY_AUTH_TOKEN`, `NTFY_AUTH_USERNAME`, `NTFY_AUTH_PASSWORD`. Top-level: `NTFY_DEFAULT_TOPIC`, `NTFY_REQUEST_TIMEOUT_MS` (default `15000`), `NTFY_MAX_RETRIES` (default `3`). Token and username/password are mutually exclusive within a server entry; basic-auth pair must be set together.
- **Logging** — request-scoped `ctx.log` replaces the project's bespoke logger. `MCP_LOG_LEVEL` controls minimum level.
- **Errors** — typed contracts via `ctx.fail(reason, ...)` plus framework error factories (`notFound`, `forbidden`, `serviceUnavailable`, `validationError`, …); no project-level `errorHandler`. Each tool declares its domain failure modes inline.
- **Engines** — Bun ≥1.3.0, Node ≥24.0.0. 1.x ran on Node ≥16; upgrading from a Node 16/18/20 install requires a runtime bump first.

## Removed

- **`send_ntfy`** tool — replaced by `ntfy_publish_message`. Migration notes for existing callers:
  - **Param renames:** `id` → `sequence_id`; `baseUrl` → `base_url`; `attachment: { url, name }` → flat `attach` URL with separate `filename`.
  - **Type changes:** `cache` and `firebase` are booleans now (were a duration string and an FCM topic name respectively). Custom cache durations like `"10m"` are no longer accepted — pass `cache: false` to opt out of caching, omit it for the server default.
  - **Removed:** `expires`; per-action `id` field (1.x example payloads carried `"id": "view"` / `"restart"` per action — the new schema rejects unknown keys, so strip those on copy-paste).
  - **Behavioral:** `base_url` overrides go out unauthenticated unless the value matches a registered server. 1.x's `baseUrl` had no such scoping; if a 1.x flow relied on the configured auth applying to alternate hosts, register them in `NTFY_SERVERS` so each gets its own credentials.
  - **New:** `icon`, `filename`, `call` (voice forward), `copy` action type, ISO 8601 timestamps in responses.
- **1.x environment variables** — the framework reset replaced or removed several. Stale vars are silently ignored, so a 1.x config will appear to "work" until you hit the missing behavior. Audit and update:

  | 1.x var | Replacement / status |
  |:--|:--|
  | `NTFY_API_KEY` | accepted as a deprecated alias for `NTFY_AUTH_TOKEN` (single-server mode only); rename to silence the startup warning |
  | `LOG_LEVEL` | `MCP_LOG_LEVEL` |
  | `LOG_FILE_DIR` | `LOGS_DIR` |
  | `NTFY_MAX_MESSAGE_SIZE` | removed — 4096-byte limit hardcoded in the input schema |
  | `RATE_LIMIT_WINDOW_MS` / `RATE_LIMIT_MAX_REQUESTS` | removed — use the framework's rate-limiter config |
  | `NODE_ENV` | unused — the framework doesn't read it |

- **`ntfy://default`** URI shortcut — the 1.x default-topic readback resource is gone. Configure `NTFY_DEFAULT_TOPIC` and pass the resolved topic to `ntfy://<topic-name>`.
- **`smithery.yaml`** — Smithery deploy config dropped during the framework reset.
- **`src/utils/`** — bespoke logger, error handler, ID generator, request-context, rate limiter, sanitization, and security helpers all replaced by framework equivalents on `ctx`.
- **`src/types-global/`** — types collocated with their definitions under the framework.
