You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Serve Streamable HTTP from every reference server - #5536
Every reference server now serves Streamable HTTP as well as stdio. Until now only everything did.
TypeScript (filesystem, memory, sequentialthinking): a new http.ts in each serves the server's existing factory through the SDK's createMcpHandler on one /mcp endpoint. 2026-07-28 requests go to the modern handler, and 2025-era requests to the SDK's stateless fallback. The endpoint sits behind localhostOriginValidation and answers 404 on any other path. --transport streamable-http selects it, and stdio stays the default.
Python (fetch, git, time): a new transport.py in each serves the SDK's Streamable HTTP app under uvicorn when --transport streamable-http is given. It wraps the app in a localhost-Origin middleware that refuses foreign origins with 403 whatever HOST is, and on loopback the SDK's own Host and Origin checks also run.
Per-server behaviour over HTTP, where the stateless model matters:
filesystem: a stateless request has no channel back to the client, so the server cannot ask a client for its Roots. Over HTTP it requires command-line directories and refuses to start without one.
memory: the graph lives in its file, so a fresh server per request loses nothing. Graph changes are published on the handler's subscriptions/listen bus, so a 2026-07-28 listener hears about writes from any client.
sequentialthinking: no server outlives its request, so the process holds one shared SequentialThinkingServer. Over HTTP the history belongs to the process, not to a connection, and the README says so.
The boot smoke (scripts/smoke-servers.mjs) now boots every server over Streamable HTTP as well as stdio: 15 server/transport pairs, up from 9.
Server Details
Servers: filesystem, memory, sequentialthinking, fetch, git, time
Changes: a new --transport option (stdio | streamable-http), plus the HOST/PORT environment variables when HTTP is selected. No tool, resource or prompt changes.
Motivation and Context
#576: the reference servers should show how to serve a transport other than stdio, and Streamable HTTP is the standard one. It replaces SSE, as the issue's update notes.
How Has This Been Tested?
Tests. Each server has a new protocol-level HTTP suite that binds the real endpoint on a free loopback port and drives it with the SDK client in both eras (TS: __tests__/http.test.ts, Py: tests/test_http.py). The suites also cover the Origin 403 (Python: on a non-loopback Host too), a malformed request target, the 404 off-path, a refused request reaching onerror, EADDRINUSE, HOST/PORT parsing, --transport parsing, and the SIGINT/SIGTERM shutdown. Memory's suite checks that a 2026-07-28 listener receives another client's write. Every file clears the per-file coverage gate, and npm run local:gate passes.
Inspector CLI 2.12.0, each server started with --transport streamable-http and called at its URL, --protocol-era modern and --protocol-era legacy. All 12 runs exit 0 (trimmed):
Server
Call
2026-07-28
2025-11-25
filesystem
read_text_file on a file in the allowed dir
"hello over http", _meta.serverInfo = secure-filesystem-server
"hello over http"
memory
create_entities Ada
created Ada
skipped: ["Ada"]: the earlier request's write persisted
sequentialthinking
one thought
thoughtHistoryLength: 1
thoughtHistoryLength: 2: one history per process
time
convert_time 12:00 UTC → Asia/Tokyo
21:00+09:00
21:00+09:00
git
git_status of a fresh repo
On branch main / No commits yet
same
fetch
fetch https://example.com
# Example Domain …
same
LLM client: Claude Code 2.1.322 (claude -p --strict-mcp-config), with all six servers configured as {"type":"http","url":…} against the local builds. Prompt: "read note.txt with the filesystem server; create an entity Ada in memory; record one sequential thought; convert 12:00 UTC to Asia/Tokyo; get git status of the repo; fetch https://example.com and give its title." Result: all six calls succeeded. The file text came back, Ada was created, thought 1 was recorded, 21:00 Asia/Tokyo, main with no commits, and the title was "Example Domain". Claude Code has no era switch and does not print the version it negotiated, so this run is not labelled with an era. Both eras are covered by the Inspector rows above and by the tests.
Boot smoke: node scripts/smoke-servers.mjs reports smoke — OK (15 server/transport pairs booted), each server over stdio and Streamable HTTP from its built bin or console script.
Breaking Changes
None. Stdio stays the default, and no existing argument changes meaning. In filesystem, --transport is a new option, so a directory literally named --transport now needs -- before it, as the other options already do.
Types of changes
Bug fix (non-breaking change which fixes an issue)
New feature (non-breaking change which adds functionality)
Breaking change (fix or feature that would cause existing functionality to change)
Documentation update
Checklist
I have read the MCP Protocol Documentation (Transports: Streamable HTTP, and the security warning on Origin validation and binding to localhost)
My changes follow MCP security best practices: loopback by default, and Origin validated on every server whatever HOST is (TS: localhostOriginValidation, Py: a localhost-Origin middleware, plus the SDK's Host check on loopback). HOST widens the bind only deliberately. CORS for browser clients is Answer CORS on the new Streamable HTTP endpoints (filesystem, memory, sequential-thinking) #5538
I have updated the server's README accordingly: a Streamable HTTP section in all six, plus the root README's transports section
I have added a changeset (npm run changeset) if this changes what a TypeScript server publishes (.changeset/576-streamable-http.md, minor for all three)
I have tested this with an LLM client
My code follows the repository's style guidelines
New and existing tests pass locally
I have added appropriate error handling: bind failure, a bad PORT, an unknown transport, and a failed shutdown each exit non-zero with a message
I have documented all environment variables and configuration options (--transport, HOST, PORT in each README)
Additional context
The 2025-era HTTP serving is the SDK's stateless fallback, not everything's sessionful wiring. That keeps each server's HTTP layer about 100 lines. The cost is that nothing server-initiated reaches a 2025-era client between its requests (Roots, resources/subscribe updates). Each README states this. everything remains the showcase for sessionful 2025-era HTTP.
AGENTS.md's transport rule, docs/quality-gate.md, and the local-dev, testing and project-structure skills are updated to match. Only skill bodies changed, no descriptions, so no skills:eval run is needed.
Every server already served stdio, and only everything served Streamable
HTTP. Now filesystem, memory, sequential-thinking, fetch, git and time take
--transport streamable-http and serve one /mcp endpoint for 2026-07-28 and
2025-era clients alike, on HOST:PORT (127.0.0.1:3001 by default).
The TypeScript servers serve their existing factory through the SDK's
createMcpHandler (stateless for 2025-era clients) behind a localhost
Origin check; the Python servers run the SDK's Streamable HTTP app. Over
HTTP, filesystem requires command-line directories (Roots cannot reach
it), memory publishes graph changes on the handler's listen bus, and
sequential-thinking keeps one history per process.
The boot smoke now boots each server over Streamable HTTP as well.
Closes#576
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at a63392e235fa (Merge 97d89b9 into 09eb689)
Diffed because it was named (--server).
✅ No interface changes detected.
Tools
Prompts
Resources
Resource templates
Base
16
0
0
1
Head
16
0
0
1
Updated by this run for 97d89b92e3fb74a070cc38caf4e4b8062e1bb741.
Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at a63392e235fa (Merge 97d89b9 into 09eb689)
Diffed because it was named (--server).
✅ No interface changes detected.
Tools
Prompts
Resources
Resource templates
Base
3
0
0
0
Head
3
0
0
0
Updated by this run for 97d89b92e3fb74a070cc38caf4e4b8062e1bb741.
Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at a63392e235fa (Merge 97d89b9 into 09eb689)
Diffed because it was named (--server).
✅ No interface changes detected.
Tools
Prompts
Resources
Resource templates
Base
9
0
1
0
Head
9
0
1
0
Updated by this run for 97d89b92e3fb74a070cc38caf4e4b8062e1bb741.
Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at a63392e235fa (Merge 97d89b9 into 09eb689)
Diffed because it was named (--server).
✅ No interface changes detected.
Tools
Prompts
Resources
Resource templates
Base
1
1
0
0
Head
1
1
0
0
Updated by this run for 97d89b92e3fb74a070cc38caf4e4b8062e1bb741.
Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at a63392e235fa (Merge 97d89b9 into 09eb689)
Diffed because it was named (--server).
⚠️Interface changes detected in 1 part(s) of the interface. Review them below to confirm they are intended.
Tools
Prompts
Resources
Resource templates
Base
14
2
0
0
Head
14
2
0
0
tools
--- base/tools.json+++ target/tools.json- tools[git_remote].description: "Lists the repository's remotes with their fetch and push URLs, like `git remote -v`. Credentials emb..."+ tools[git_remote].description: "Lists the repository's remotes with their fetch and push URLs, like `git remote -v`. Credentials emb..."
Updated by this run for 97d89b92e3fb74a070cc38caf4e4b8062e1bb741.
Base: 9914133c0557 (Merge pull request #5532 from modelcontextprotocol/v2/docs/5524-everything-get-env-masking-wording)
Head: the build in this checkout, at a63392e235fa (Merge 97d89b9 into 09eb689)
Diffed because it was named (--server).
✅ No interface changes detected.
Tools
Prompts
Resources
Resource templates
Base
1
0
0
0
Head
1
0
0
0
Updated by this run for 97d89b92e3fb74a070cc38caf4e4b8062e1bb741.
Adds Streamable HTTP to the six reference servers that previously supported only stdio, addressing #576 while retaining stdio as the default and the loopback-first approach from #5371.
Changes:
Adds --transport streamable-http and HOST/PORT configuration, supporting both protocol eras.
Adapts filesystem directory requirements, memory notifications, and shared sequential-thinking history for HTTP.
Adds HTTP tests, expands boot smoke coverage to 15 server/transport pairs, and updates documentation and release metadata.
File
Description
src/time/tests/test_http.py
Adds HTTP and transport-option tests.
src/time/tests/test_entrypoints.py
Updates default-transport expectations.
src/time/src/mcp_server_time/transport.py
Adds transport dispatch and endpoint parsing.
src/time/src/mcp_server_time/server.py
Delegates serving to the selected transport.
src/time/src/mcp_server_time/__init__.py
Adds the transport CLI option.
src/time/README.md
Documents HTTP configuration.
src/sequentialthinking/README.md
Documents HTTP and shared history.
src/sequentialthinking/package.json
Adds the SDK Node adapter dependency.
src/sequentialthinking/index.ts
Adds transport selection and injectable history.
src/sequentialthinking/http.ts
Adds the HTTP endpoint with shared history.
src/sequentialthinking/__tests__/http.test.ts
Tests HTTP, shared history, and shutdown.
src/memory/README.md
Documents HTTP persistence and notifications.
src/memory/package.json
Adds the SDK Node adapter dependency.
src/memory/index.ts
Adds transport selection and notification publishing.
src/memory/http.ts
Adds HTTP serving and the subscription bus.
src/memory/__tests__/http.test.ts
Tests HTTP persistence, notifications, and shutdown.
src/git/tests/test_http.py
Adds HTTP and transport-option tests.
src/git/tests/test_entrypoints.py
Updates serving mocks for transport selection.
src/git/src/mcp_server_git/transport.py
Adds transport dispatch and endpoint parsing.
src/git/src/mcp_server_git/server.py
Delegates serving to the selected transport.
src/git/src/mcp_server_git/__init__.py
Adds the transport CLI option.
src/git/README.md
Documents HTTP and repository configuration.
src/filesystem/README.md
Documents HTTP directory requirements.
src/filesystem/package.json
Adds the SDK Node adapter dependency.
src/filesystem/index.ts
Selects HTTP and requires command-line directories.
A target such as `//[` made `new URL()` throw inside the Node request
listener, outside any handler, ending the process. The HTTP endpoints now
compare the path without parsing the target, so it is answered 404.
Over HTTP, memory now publishes every graph change on the handler's bus
whatever era wrote it, so a 2026-07-28 listener hears of a 2025-era
client's writes too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot round 1: both findings were defects in what this PR added, and both are fixed in 17d9f9e.
Malformed request target crashes the HTTP servers (high): the path is now compared without URL parsing, so a target like //[ gets a 404. A raw-socket regression test was added for filesystem, memory and sequentialthinking.
Legacy-era writes skip the shared notification bus (medium): memory now publishes every change on the bus over HTTP, whatever era wrote it. The test covers both writer eras.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Active issues leave Git, Fetch, and Time tools exposed to DNS-rebinding requests under HOST overrides, while missing CORS handling prevents localhost browser clients from connecting to Filesystem, Memory, and Sequential Thinking transports.
Memory and sequential-thinking transports lack CORS support
src/filesystem/http.ts:95
Localhost browser clients cannot connect because Origin validation does not add CORS headers: preflights are rejected and actual responses lack Access-Control-Allow-Origin. Handle allowed-origin OPTIONS requests and add CORS response headers before SDK dispatch, following the existing Everything transport. Apply the same fix in src/memory/http.ts:100 and src/sequentialthinking/http.ts:95.
The SDK's DNS-rebinding protection checks Origin only when it binds
loopback, so with HOST widened a page on any origin could reach the git,
fetch and time tools. Each transport.py now wraps the SDK's app in a
localhost-Origin middleware, as the TypeScript servers do with
localhostOriginValidation, and runs it under uvicorn itself.
The READMEs now say what each endpoint refuses, and that the TypeScript
ones send no CORS headers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
Copilot round 2: one finding fixed and one declined.
HOST override disables Origin validation on the Python servers (high, in its own thread): fixed in 97d89b9. A localhost-Origin middleware now wraps the SDK app whatever HOST is.
Memory and sequential-thinking transports lack CORS (medium, in the review's "previously missed" section, so it has no thread to reply in): declined as out of scope. Add Streamable HTTP support to all reference servers #576 asks for the Streamable HTTP transport, and serving browser-based clients directly (preflights, Access-Control-* headers) is an extra capability, which applies equally to filesystem and to the Python servers. Filed as Answer CORS on the new Streamable HTTP endpoints (filesystem, memory, sequential-thinking) #5538. The READMEs no longer imply that a localhost browser page can connect. They now say a foreign origin is refused with 403 and that the TypeScript endpoints send no CORS headers.
…y-write-caps
Resolve the createServer signature against #5536's Streamable HTTP
transport: limits becomes the fourth parameter, after
publishGraphUpdated. run() reads the limits for the HTTP path too, and
startHttpServer passes them to each request's server, with a test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: cliffhall <cliff@futurescale.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #576
Description
Every reference server now serves Streamable HTTP as well as stdio. Until now only
everythingdid.filesystem,memory,sequentialthinking): a newhttp.tsin each serves the server's existing factory through the SDK'screateMcpHandleron one/mcpendpoint. 2026-07-28 requests go to the modern handler, and 2025-era requests to the SDK's stateless fallback. The endpoint sits behindlocalhostOriginValidationand answers 404 on any other path.--transport streamable-httpselects it, and stdio stays the default.fetch,git,time): a newtransport.pyin each serves the SDK's Streamable HTTP app under uvicorn when--transport streamable-httpis given. It wraps the app in a localhost-Origin middleware that refuses foreign origins with 403 whateverHOSTis, and on loopback the SDK's own Host and Origin checks also run.HOST:PORT,127.0.0.1:3001by default. These are the same variables, defaults and loopback-first posture aseverything(everything: bind the HTTP transports to loopback by default, with HOST to widen #5371). A badPORTis a usage error at startup.Per-server behaviour over HTTP, where the stateless model matters:
subscriptions/listenbus, so a 2026-07-28 listener hears about writes from any client.SequentialThinkingServer. Over HTTP the history belongs to the process, not to a connection, and the README says so.The boot smoke (
scripts/smoke-servers.mjs) now boots every server over Streamable HTTP as well as stdio: 15 server/transport pairs, up from 9.Server Details
--transportoption (stdio|streamable-http), plus theHOST/PORTenvironment variables when HTTP is selected. No tool, resource or prompt changes.Motivation and Context
#576: the reference servers should show how to serve a transport other than stdio, and Streamable HTTP is the standard one. It replaces SSE, as the issue's update notes.
How Has This Been Tested?
Tests. Each server has a new protocol-level HTTP suite that binds the real endpoint on a free loopback port and drives it with the SDK client in both eras (TS:
__tests__/http.test.ts, Py:tests/test_http.py). The suites also cover the Origin 403 (Python: on a non-loopback Host too), a malformed request target, the 404 off-path, a refused request reachingonerror,EADDRINUSE,HOST/PORTparsing,--transportparsing, and the SIGINT/SIGTERM shutdown. Memory's suite checks that a 2026-07-28 listener receives another client's write. Every file clears the per-file coverage gate, andnpm run local:gatepasses.Inspector CLI 2.12.0, each server started with
--transport streamable-httpand called at its URL,--protocol-era modernand--protocol-era legacy. All 12 runs exit 0 (trimmed):read_text_fileon a file in the allowed dir"hello over http",_meta.serverInfo= secure-filesystem-server"hello over http"create_entitiesAdaskipped: ["Ada"]: the earlier request's write persistedthoughtHistoryLength: 1thoughtHistoryLength: 2: one history per processconvert_time12:00 UTC → Asia/Tokyo21:00+09:0021:00+09:00git_statusof a fresh repoOn branch main / No commits yetfetch https://example.com# Example Domain …LLM client: Claude Code 2.1.322 (
claude -p --strict-mcp-config), with all six servers configured as{"type":"http","url":…}against the local builds. Prompt: "read note.txt with the filesystem server; create an entity Ada in memory; record one sequential thought; convert 12:00 UTC to Asia/Tokyo; get git status of the repo; fetch https://example.com and give its title." Result: all six calls succeeded. The file text came back, Ada was created, thought 1 was recorded, 21:00 Asia/Tokyo,mainwith no commits, and the title was "Example Domain". Claude Code has no era switch and does not print the version it negotiated, so this run is not labelled with an era. Both eras are covered by the Inspector rows above and by the tests.Boot smoke:
node scripts/smoke-servers.mjsreportssmoke — OK (15 server/transport pairs booted), each server over stdio and Streamable HTTP from its built bin or console script.Breaking Changes
None. Stdio stays the default, and no existing argument changes meaning. In
filesystem,--transportis a new option, so a directory literally named--transportnow needs--before it, as the other options already do.Types of changes
Checklist
HOSTis (TS:localhostOriginValidation, Py: a localhost-Origin middleware, plus the SDK's Host check on loopback).HOSTwidens the bind only deliberately. CORS for browser clients is Answer CORS on the new Streamable HTTP endpoints (filesystem, memory, sequential-thinking) #5538npm run changeset) if this changes what a TypeScript server publishes (.changeset/576-streamable-http.md, minor for all three)PORT, an unknown transport, and a failed shutdown each exit non-zero with a message--transport,HOST,PORTin each README)Additional context
everything's sessionful wiring. That keeps each server's HTTP layer about 100 lines. The cost is that nothing server-initiated reaches a 2025-era client between its requests (Roots,resources/subscribeupdates). Each README states this.everythingremains the showcase for sessionful 2025-era HTTP.AGENTS.md's transport rule,docs/quality-gate.md, and thelocal-dev,testingandproject-structureskills are updated to match. Only skill bodies changed, no descriptions, so noskills:evalrun is needed.🤖 Generated with Claude Code