resources/templates.ts:
- `parseResourceId` had a guard that compared the URI against both
`textUriBase` and `blobUriBase` with `&&`. Those prefixes are mutually
exclusive, so the condition is always false and the branch is dead.
Drop it; the SDK's template-based routing already guarantees the URI
prefix is one of the two before the handler runs. The remaining
positive-integer check on `resourceId` is preserved.
resources/subscriptions.ts:
- `sendSimulatedResourceUpdates` had an `else` branch that called
`subscribers.delete(sessionId)` whenever the session wasn't in a URI's
subscriber set, with a comment claiming the session had disconnected.
That conclusion doesn't follow — a session not subscribed to URI A
can still be subscribed to URI B — and the delete is a no-op when
the element is absent anyway. Remove the branch.
No behavioral change. Existing 95 / 95 tests in `__tests__` still pass.
Co-authored-by: Jia Xuan <1060996408+jiaxuan@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Return '0 B' for negative byte values instead of 'NaN B'.
File sizes cannot be negative, so this is a safe default.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Users hitting the 2.0.0 startup failure land on the README first. Also makes
the three packages qualify for the next release: the release workflow selects
packages by .py/.ts/.md changes, so a dependency-only fix would merge without
publishing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
mcp 2.0.0 renamed McpError to MCPError and replaced the low-level Server
decorators with constructor handlers. The three servers had no upper bound,
so a fresh uvx install has resolved 2.0.0 since 2026-07-28 and failed to
start: fetch and time raise ImportError on the McpError import, git raises
AttributeError on server.list_tools().
The floor moves to 1.29.0, the last 1.x release. The previous floors (1.0.0
for git, 1.1.3 for fetch, 1.23.0 for time) resolved versions affected by
GHSA-vj7q-gjh5-988w, GHSA-jpw9-pfvf-9f58, and GHSA-hvrp-rf83-w775.
Porting to the v2 SDK is tracked separately.
Closes#4560Closes#4570Closes#4580
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every filesystem tool operates only on the local filesystem within its allowed
directories — none reaches an open or external world — but none declared the
MCP `openWorldHint` annotation, leaving open-world-hint coverage at 0/14. Set
`openWorldHint: false` on all 14 tools and document it in the README's tool
annotations section.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(filesystem): return valid MCP content type from read_media_file (#4029)
read_media_file returned a content block with type: "blob" for non-image/
non-audio files. "blob" is not a valid MCP content type (the union is
text | image | audio | resource_link | resource), so a strict client rejects
the result on schema validation.
Map the MIME type to a valid block: image/* -> image, audio/* -> audio, and
any other file type -> an embedded resource (the spec-correct carrier for
arbitrary bytes) with a pathToFileURL-encoded uri. Widen the tool's
outputSchema to the matching union and drop the now-unnecessary
`as unknown as CallToolResult` cast so tsc enforces the returned shape.
Tests cover the image, audio, and resource content types, verify the base64
blob round-trips to the original bytes, and assert the resource uri is
percent-encoded (space + non-ASCII) rather than a raw file:// concatenation.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(filesystem): assert structuredContent mirrors content in read_media_file tests
The SDK validates content and structuredContent independently against the
outputSchema union, so a refactor that lets the two drift apart (e.g. a
stale image block in structuredContent while content carries the resource)
stays schema-valid and every test stays green. Mirror-equality assertions
on all four callTool cases close that gap (mutation-verified: a schema-valid
drifted structuredContent fails exactly these four assertions).
Also drops a vacuous .not.toBe('blob') assertion that could never add
signal, noting where the real "never blob" guard actually lives (the SDK
client's CallToolResultSchema rejection, upstream of any local assertion).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-ref cancellation is for superseded branch amend-pushes; on main,
every merge keeps its own CI run so failures stay attributable to a
specific commit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Previously push-triggered CI ran only on main, so same-repo branches
got no CI until a PR was opened (fork contributions were and remain
covered by pull_request, which fires on open and on every subsequent
push). Run on all pushes, and add a per-ref concurrency group so rapid
amend-pushes cancel superseded runs. Same-repo branches with an open
PR will run under both events; acceptable at this repo's scale.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Releases are now triggered deliberately via workflow_dispatch. The
schedule produced a year of unattended runs stuck awaiting environment
approval, every real release was manually dispatched anyway, and two
runs on the same UTC day collide on the date-based tag. Rename the
workflow to "Release" accordingly and update RELEASING.md.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Review findings: RELEASING.md claimed publishing happens exclusively
from release.yml with no registry tokens, but typescript.yml and
python.yml still carried release:[published] publish jobs — never
fired (releases are created with GITHUB_TOKEN, which suppresses
workflow triggers) and unable to succeed under OIDC (typescript.yml's
still referenced the deleted NPM_TOKEN). Remove both jobs and their
release triggers; the workflows are now pure CI.
Also:
- add skip-existing to the PyPI publish action so re-runs tolerate
already-uploaded files, and describe the per-registry guard
accurately in RELEASING.md (npm aborts; PyPI skips)
- correct the fresh-dispatch note: same-day dispatches collide on the
date-granular tag rather than minting a new version
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
RELEASING.md documents the pipeline this PR ships (scheduled/dispatch
CalVer releases, OIDC publishing, retry runbook), with a footer noting
the planned Phase 2 changes tracked in #4463. The doc gets updated
when that work merges, not before.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Rewrite RELEASING.md as the reference for the target process rather
than a snapshot of the interim state: semver via changesets for
TypeScript / CalVer for Python, versions changed only by PRs on main,
publishing triggered by creating a GitHub Release, per-package
registry-diff guard, OIDC trusted publishing with no registry tokens,
and the failed-publish retry runbook. Implementation tracked in #4463.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Describes how packages publish (release.yml + release environment,
trusted publishing on npm and PyPI, no registry tokens, provenance),
what a release run does, and how to recover from a failed publish leg
(rerun --failed within the window, or ride the next release). Linked
from the root README alongside the other maintainer docs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Review findings from the PR:
- release.py's has_changes() only counted .py/.ts files, so the README
touches meant to enroll the four TS packages in the npm matrix would
have been ignored. Include .md — READMEs ship inside the published
package (npm tarball / PyPI long_description), so doc changes are
release-relevant.
- uv run --frozen pytest for consistency with the job's other uv calls.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Gate both publish jobs on the package's test suite: npm test
--if-present before the npm build/publish, and pytest (when a tests/
or test/ directory exists, matching python.yml's guard) before the
PyPI build/publish. Previously nothing between tagging and publishing
ran a single test.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
The NPM_TOKEN used by publish-npm is dead (npm classic-token sunset) —
both June release attempts failed publishing with a disguised auth error
(E404 on the publish PUT) after PyPI legs succeeded. Each
@modelcontextprotocol/* package's npm trusted publisher is being bound to
this workflow (release.yml) + the release environment, so publish-npm now
authenticates via OIDC instead of a token:
- add id-token: write permission to publish-npm (required to mint the
OIDC token; contents: read added explicitly since setting permissions
drops the defaults)
- upgrade the npm CLI in the publish job (trusted publishing requires
npm >= 11.5.1; Node 22 bundles an older npm)
- drop NODE_AUTH_TOKEN from the publish step
Also touch each TypeScript server's README (add everything's missing
License section; link each package's npm page) so scripts/release.py
includes all four packages in the next npm publish matrix — they've been
stranded on npm at 2026.1.26 while PyPI advanced to 2026.6.16.
See #4463 for the rollout plan.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
Resolves a fresh batch of Dependabot alerts (disclosed 2026-06-15/16,
after PR #4376 merged) across the fetch, git, and time server lockfiles:
- python-multipart 0.0.27 -> 0.0.32 (CVE-2026-53539 HIGH querystring DoS,
plus CVE-2026-53537/53538/53540 parameter smuggling / buffering)
- cryptography 48.0.0 -> 49.0.0 (GHSA-537c-gmf6-5ccf HIGH, vulnerable
OpenSSL statically linked in wheels < 48.0.1)
- pyjwt 2.12.1 -> 2.13.0 (HIGH + medium JWT advisories)
All three are transitive deps; lockfiles only. Diff scoped to exactly
these 9 version bumps with no other resolution churn. git server tests
unchanged vs base (43 passed; 37 pre-existing Windows tempdir errors).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(memory): expose knowledge graph as MCP Resource
Register the knowledge graph as a readable MCP Resource at
memory://knowledge-graph, enabling clients to discover and read
the graph via the Resources protocol without invoking a tool.
Mutation tools (create_entities, create_relations, add_observations,
delete_entities, delete_observations, delete_relations) now emit
notifications/resources/updated so subscribed clients see live
graph changes.
Resource registration is extracted into the exported
registerKnowledgeGraphResource() function so it can be unit-tested
in isolation, mirroring the everything server's registration
pattern. New tests assert registerResource is called with the
correct kebab-case name, URI, mime type, and that the handler
returns the graph as JSON in the contents array.
Addresses PR #3323 review feedback:
- kebab-case resource name per CLAUDE.md style guide
- single-line comment per CLAUDE.md style guide
- title metadata for client discoverability
- resource update notifications after mutations
- test coverage matching everything/registrations.test.ts pattern
- README documents the new Resource
* feat(memory): support resources/subscribe for the knowledge-graph resource
PR #3323 emitted notifications/resources/updated after every mutation but
the server only advertised resources.listChanged, so resources/subscribe
returned -32601 and a spec-compliant client could never actually subscribe.
Declare resources.subscribe, add subscribe/unsubscribe request handlers that
track subscribed URIs, and only emit notifications/resources/updated when the
client has subscribed to that URI.
Co-authored-by: Lazreg Raed <lazregraed222@gmail.com>
---------
Co-authored-by: olaservo <olahungerford@gmail.com>
Real fixes (deps):
- python (git/fetch/time): idna 3.10->3.18 (GHSA-65pc), starlette
0.49.1->1.3.1 (GHSA-86qp)
- npm: vitest ^2.1.8->^4.1.8 (critical GHSA-5xrq UI server RCE). vitest 4
pulls vite 8 (rolldown, no esbuild), clearing the esbuild/vite advisories
natively. Remaining root overrides: qs>=6.15.2 (GHSA-q8mj, via express),
hono>=4.12.21 (via @modelcontextprotocol/sdk).
- npm audit: 0 vulnerabilities; all TS (313) + Python (git/fetch/time) suites
and TS builds pass.
Hardening (git_add):
- The repo already mitigates CVE-2026-27735 by using repo.git.add (the git
CLI rejects out-of-tree paths, PR #3164's fix), not repo.index.add (which
did not). Add an explicit path-boundary check in git_add as defense in
depth plus regression tests asserting the security invariant (out-of-tree
paths are never staged), guarding against a regression to repo.index.add.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>