This release includes 3 security fixes for security teams reviewing exposed deployments.
Topics
Affected surfaces
ReleasePort's take
Moderate signalNode 18 reaches end‑of‑life; the system now requires Node 20+. All HTTP endpoints (/mcp, /metrics) must present a Bearer token via `MCP_HTTP_SECRET`.
Why it matters: Node 18 EOL forces an upgrade to Node 20+ for runtime compatibility. Enforcing `MCP_HTTP_SECRET` bearer auth on critical HTTP endpoints prevents unauthorized access; operators must configure the secret before the next deployment cycle.
Summary
AI summaryUpdates Breaking changes, Internal, and pipe across a mixed release.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | High |
HTTP transport now requires `MCP_HTTP_SECRET` Bearer auth. HTTP transport now requires `MCP_HTTP_SECRET` Bearer auth. Source: llm_adapter@2026-05-29 Confidence: high |
— |
| Security | High |
Outbound URL scheme allow‑list now rejects non‑HTTPS schemes (defense‑in‑depth). Outbound URL scheme allow‑list now rejects non‑HTTPS schemes (defense‑in‑depth). Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Security | High |
Thread traversal caps cross‑origin replies to stubs by default, reducing attack surface from untrusted reply chains. Thread traversal caps cross‑origin replies to stubs by default, reducing attack surface from untrusted reply chains. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Security | High |
`verifyAccount` routes through SSRF guard; private IPs and localhost are blocked before request execution. `verifyAccount` routes through SSRF guard; private IPs and localhost are blocked before request execution. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Breaking | High |
Node 20+ required; Node 18 EOL. Node 20+ required; Node 18 EOL. Source: llm_adapter@2026-05-29 Confidence: high |
— |
| Breaking | High |
`ACTIVITYPUB_ACCOUNTS` delimiter changed from colon to pipe. `ACTIVITYPUB_ACCOUNTS` delimiter changed from colon to pipe. Source: llm_adapter@2026-05-29 Confidence: high |
— |
| Breaking | High |
`scheduledId` renamed to `scheduledPostId` in scheduling tools. `scheduledId` renamed to `scheduledPostId` in scheduling tools. Source: llm_adapter@2026-05-29 Confidence: high |
— |
| Breaking | Medium |
`MCP_HTTP_CORS_ORIGINS` no longer defaults to '*'; must be set explicitly. `MCP_HTTP_CORS_ORIGINS` no longer defaults to '*'; must be set explicitly. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Breaking | Medium |
'get-relationship' no longer accepts legacy `accountIds` array; requires single `acct` string. 'get-relationship' no longer accepts legacy `accountIds` array; requires single `acct` string. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
`post-status` now supports `mediaIds` and `scheduledAt` for end‑to‑end media upload flow. `post-status` now supports `mediaIds` and `scheduledAt` for end‑to‑end media upload flow. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
`search-instance` returns prose output matching other search tools (previously raw JSON). `search-instance` returns prose output matching other search tools (previously raw JSON). Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
`fetch-timeline` renders all posts, truncating each to 500 characters (was capped at 10). `fetch-timeline` renders all posts, truncating each to 500 characters (was capped at 10). Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
Dynamic `server-info` capabilities list tools/resources/prompts from a live registry. Dynamic `server-info` capabilities list tools/resources/prompts from a live registry. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
Thread traversal caps recursion depth (default 5) and total replies (default 50), configurable via env vars. Thread traversal caps recursion depth (default 5) and total replies (default 50), configurable via env vars. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
Audit logging wired into every write tool, firing on success or failure. Audit logging wired into every write tool, firing on success or failure. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
Added `npm run typecheck` script and corresponding CI step. Added `npm run typecheck` script and corresponding CI step. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
Daily integration test workflow runs against the live Fediverse on a schedule. Daily integration test workflow runs against the live Fediverse on a schedule. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Feature | Low |
`npm pack` contents check + published‑bin smoke test added to CI. `npm pack` contents check + published‑bin smoke test added to CI. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Performance | Medium |
Streaming responses abort if `MAX_RESPONSE_SIZE` (10 MB) exceeded, protecting against DoS without Content-Length headers. Streaming responses abort if `MAX_RESPONSE_SIZE` (10 MB) exceeded, protecting against DoS without Content-Length headers. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Deprecation | Medium |
`HEALTH_CHECK_ENABLED` env var removed; replaced by `HEALTH_CHECK_EXTERNAL_PROBE`. `HEALTH_CHECK_ENABLED` env var removed; replaced by `HEALTH_CHECK_EXTERNAL_PROBE`. Source: llm_adapter@2026-05-29 Confidence: high |
— |
| Deprecation | Low |
`post-thread` legacy `{postUrl}` URI form deprecated; new `activitypub://post-thread/{domain}/{statusId}` preferred (removed in 2.1.0). `post-thread` legacy `{postUrl}` URI form deprecated; new `activitypub://post-thread/{domain}/{statusId}` preferred (removed in 2.1.0). Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Bugfix | Low |
`extractNextCursor` no longer loops back to `collection.first` when `next` is absent. `extractNextCursor` no longer loops back to `collection.first` when `next` is absent. Source: granite4.1:30b@2026-05-29-audit Confidence: low |
— |
| Other | Low |
action_required action_required Source: llm_adapter@2026-05-29 Confidence: low |
— |
| Other | Low |
True True Source: llm_adapter@2026-05-29 Confidence: low |
— |
| Other | Low |
confidence_reason confidence_reason Source: llm_adapter@2026-05-29 Confidence: low |
— |
| Other | Low |
Explicit breaking change in changelog Explicit breaking change in changelog Source: llm_adapter@2026-05-29 Confidence: low |
— |
Full changelog
[2.0.0] - 2026-05-27
Major release. v2 is a security, correctness, and ergonomics overhaul of the v1 server. See
MIGRATION-v2.mdfor the full upgrade guide.
Breaking changes
- Node 20+ required. Node 18 reached EOL April 30, 2025. v2's minimum is
node >=20.0.0. - HTTP transport requires
MCP_HTTP_SECRET. The HTTP transport now refuses to start without aMCP_HTTP_SECRETenv var (32+ random chars recommended). All requests to/mcpand/metricsmust includeAuthorization: Bearer <secret>./healthremains unauthenticated. stdio transport is unaffected. - CORS default changed.
MCP_HTTP_CORS_ORIGINSno longer defaults to"*". Set it explicitly if cross-origin requests are needed. ACTIVITYPUB_ACCOUNTSdelimiter changed. Format is nowid|instance|token|username|label(pipe), not colon. v2 refuses to start if it sees the legacy:-delimited value.scheduledId→scheduledPostId. Thecancel-scheduled-postandupdate-scheduled-posttools renamed their identifier parameter for clarity and to match the README.HEALTH_CHECK_ENABLEDenv var removed. Replaced by the narrowerHEALTH_CHECK_EXTERNAL_PROBE(defaulttrue) which gates only the outboundmastodon.socialconnectivity probe.get-relationshipno longer accepts the legacyaccountIdsarray. The v1 README documentedaccountIds: string[]; the actual handler tookacct: string. v2 makesacct(single string) authoritative and throws a helpful error ifaccountIdsis passed. Callers scripting against the old README must update.- Outbound URL scheme allow-list.
validateExternalUrlnow rejects non-https schemes (file:,data:,http:,ftp:, …). Defence in depth — affects only callers that constructed non-https URLs (no v1 user code path did this), but flagged here for completeness.
Added
post-statusnow supportsmediaIdsandscheduledAt. Round-trip flow withupload-mediaworks end-to-end.search-instancereturns prose output matching the other search tools (was raw JSON in v1).fetch-timelinerenders all posts (was capped at 10) and truncates per-post content to 500 chars.- Dynamic
server-infocapabilities. Theactivitypub://server-inforesource now lists tools/resources/prompts from a live registry — no more hand-maintained arrays that drift. - Thread traversal caps.
get-post-threadcaps recursion depth at 5 and total replies at 50 (configurable viaMCP_THREAD_MAX_DEPTHandMCP_THREAD_MAX_REPLIES). - Cross-origin thread gate. Replies whose origin differs from the root post are returned as stubs by default (set
MCP_THREAD_CROSS_ORIGIN_FETCH=trueto opt in to v1 fetch-everything behavior). - Audit logging wired into every write tool.
auditLogger.logToolInvocationfires on success and failure across all 27 write-effect handlers. post-threadresource URI template. New formactivitypub://post-thread/{domain}/{statusId}(Mastodon-compatible). Legacy{postUrl}form still accepted with a deprecation warning; removed in 2.1.0.npm run typecheckscript and CI step.- Daily integration test workflow (
.github/workflows/integration.yml) runs against the live Fediverse on a schedule. npm packcontents check + published-bin smoke test in CI.
Changed
- Streaming response-size enforcement. Outgoing HTTP requests stream the body and abort if
MAX_RESPONSE_SIZE(10 MB default) is exceeded, even when the remote server omitsContent-Length. verifyAccountroutes through SSRF guard. No more rawfetchwith a bearer token; private IPs and localhost are blocked before the request.instance-discoveryraw fetches gated byDomainSchema+validateExternalUrl.DomainSchemarejects IP literals. Was permissive in v1.discover-instancesfilter composition fixed. Multiple filters now compose cumulatively (v1 silently dropped all but the last filter).- HTTP transport CORS warning at startup if wildcard origin is set.
fetchActorOutboxPaginatedpreserves cursor URL query params (v1 silently overwrotemax_id/min_idwith caller-supplied filters when both were passed).- ETag 304 handling. When the server returns 304 without a cache entry (TTL eviction), v2 re-fetches without
If-None-Matchinstead of throwing a spuriousHTTP 304: Not Modified. PerformanceMonitorinterval is.unref()'d and properly stopped on graceful shutdown. Removed the forcedprocess.exit(0)in the SIGTERM handler.InstanceBlocklist.importFromJsonvalidates input via Zod. Malformed entries throw instead of being silently skipped.LRUCache.has()removed. Useget(key) !== undefinedfor consistent promotion semantics.auditLogging: truecapability flag is now truthful. Was advertised but unwired in v1.
Fixed
extractNextCursorno longer loops back tocollection.first. v1 fell back tofirstwhennextwas absent, causing pagination to bounce back to page 1.
Removed
HEALTH_CHECK_ENABLEDenv var (dead code in v1 — replaced byHEALTH_CHECK_EXTERNAL_PROBE).LRUCache.has()method (see Changed).- Six unused placeholder directories under
src/(async/,security/,streaming/,errors/,translation/,media/). - Dead double-start guard in
src/mcp-server.ts. src/main.ts— informational entrypoint never exposed publicly.src/utils/index.ts— barrel export replaced by direct imports.src/resilience/adaptive-rate-limiter.ts— was never wired into any tool.
Security
- HTTP transport Bearer auth. Closes the gap where
/mcpwas reachable by any local client. - Thread traversal cross-origin gating. Reduces attack surface from following untrusted
inReplyTochains. - Streaming response size cap. DoS protection against servers that omit
Content-Length. InstanceBlocklist.importFromJsonruntime validation prevents silent corruption of the blocklist.DomainSchemarejects IP literals. Defense in depth against bypass attempts.
Internal
- Topic-dir refactor.
src/reorganized into 11 topic directories.src/utils.tssplit intosrc/validation/url.ts,src/utils/errors.ts,src/utils/html.ts.src/server/removed (re-exports inlined). SeeMIGRATION-v2.md§ "Internal refactor (FYI, not breaking)". - Stricter TypeScript and Biome flags enabled:
noUnusedLocals,noUnusedParameters, BiomenoUnusedVariables. fileswhitelist inpackage.jsoncontrols npm publish contents. Source maps and declaration maps no longer ship.- 624 unit tests (up from 533 at v2 start) covering every behavior change.
Breaking Changes
- Minimum Node.js version raised to >=20.0.0 (Node 18 EOL).
- HTTP transport refuses to start without env var MCP_HTTP_SECRET containing a 32+ character secret; all /mcp and /metrics requests must include Authorization: Bearer <secret>.
- `ACTIVITYPUB_ACCOUNTS` delimiter changed from colon to pipe (id|instance|token|username|label).
- Env var HEALTH_CHECK_ENABLED removed; replaced by narrower HEALTH_CHECK_EXTERNAL_PROBE (default true).
Security Fixes
- HTTP transport now requires Bearer token (MCP_HTTP_SECRET) – closes unauthenticated access to /mcp and /metrics.
- Thread traversal cross‑origin gating reduces attack surface from malicious inReplyTo chains.
- Streaming response size cap (MAX_RESPONSE_SIZE=10 MB) added for DoS protection.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About cameronrye/activitypub-mcp
A comprehensive MCP server that enables LLMs to explore and interact with the Fediverse through ActivityPub protocol. Features WebFinger discovery, timeline fetching, instance exploration, and cross-platform support for Mastodon, Pleroma, Misskey, and other ActivityPub servers.
Related context
Related tools
Beta — feedback welcome: [email protected]