-
Notifications
You must be signed in to change notification settings - Fork 7
HTTP Transport
HTTP transport configuration and reference for Memory Journal MCP Server.
Memory Journal supports two MCP transport protocols simultaneously when running in HTTP mode:
| Protocol | Spec Version | Endpoints | Session Management |
|---|---|---|---|
| Streamable HTTP | 2025-03-26 | POST/GET/DELETE /mcp |
mcp-session-id header |
| Legacy SSE | 2024-11-05 |
GET /sse, POST /messages
|
sessionId query param |
Why both? Streamable HTTP is the current MCP standard. Legacy SSE ensures backward compatibility with older MCP clients (e.g., MCP Inspector, older SDK versions).
Note
Legacy SSE is only available in stateful mode. Stateless mode only supports Streamable HTTP.
npm:
# Stateful (default) — supports both protocols
memory-journal-mcp --transport http --port 3000
# Bind to all interfaces (required for containers)
memory-journal-mcp --transport http --port 3000 --server-host 0.0.0.0
# Stateless (serverless) — Streamable HTTP only
memory-journal-mcp --transport http --port 3000 --statelessDocker:
# Stateful
docker run --rm -p 3000:3000 \
-v ./data:/app/data \
writenotenow/memory-journal-mcp:latest \
--transport http --port 3000 --server-host 0.0.0.0
# Stateless
docker run --rm -p 3000:3000 \
-v ./data:/app/data \
writenotenow/memory-journal-mcp:latest \
--transport http --port 3000 --server-host 0.0.0.0 --statelessImportant
Docker containers must use --server-host 0.0.0.0 to accept connections from outside the container.
| Endpoint | Description | Mode |
|---|---|---|
GET / |
Server info (name, version, protocols, endpoints) | Both |
POST /mcp |
JSON-RPC requests (initialize, tools/call, etc.) | Both |
GET /mcp |
SSE stream for server-to-client notifications | Stateful |
DELETE /mcp |
Session termination | Stateful |
GET /sse |
Legacy SSE connection (MCP 2024-11-05) | Stateful |
POST /messages |
Legacy SSE message endpoint | Stateful |
GET /health |
Health check ({ status: "ok", timestamp }) |
Both |
GET /.well-known/oauth-protected-resource |
RFC 9728 Protected Resource Metadata | Both |
Returns server metadata and available endpoints:
{
"name": "memory-journal-mcp",
"version": "4.x.x",
"status": "running",
"protocols": [
"Streamable HTTP (MCP 2025-03-26)",
"Legacy SSE (MCP 2024-11-05)"
],
"endpoints": {
"POST /mcp": "Streamable HTTP endpoint",
"GET /mcp": "SSE stream (stateful)",
"DELETE /mcp": "Session termination",
"GET /sse": "Legacy SSE connection (MCP 2024-11-05)",
"POST /messages": "Legacy SSE message endpoint",
"GET /health": "Health check"
}
}Always public (no authentication, no rate limiting):
{
"status": "healthy",
"timestamp": "2026-03-05T07:00:00.000Z"
}Every HTTP response includes the following security headers:
| Header | Value |
|---|---|
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Content-Security-Policy |
default-src 'none'; frame-ancestors 'none' |
Cache-Control |
no-store, no-cache, must-revalidate |
Referrer-Policy |
no-referrer |
Permissions-Policy |
camera=(), microphone=(), geolocation=() |
Optionally, when --enable-hsts is set:
| Header | Value |
|---|---|
Strict-Transport-Security |
max-age=31536000; includeSubDomains |
- 100 requests per minute per IP (sliding window)
- Returns
429 Too Many Requestswhen exceeded - Health check (
GET /health) is exempt
Configurable via CLI or environment variable. Supports comma-separated multiple origins (exact-match only):
# Single origin
memory-journal-mcp --transport http --cors-origin "http://localhost:3000"
# Multiple origins (comma-separated)
memory-journal-mcp --transport http --cors-origin "http://localhost:3000,https://app.example.com"
# Environment variable
MCP_CORS_ORIGIN=http://localhost:3000 memory-journal-mcp --transport httpDefault: blank (strict opt-in). Use specific origins to enable CORS access.
Request bodies are limited to 1 MB to prevent memory exhaustion. Requests exceeding this limit receive 413 Payload Too Large.
Unknown paths return a structured JSON response:
{
"error": "Not found"
}Session IDs are protocol-scoped:
- SSE session IDs are rejected on
/mcpendpoints - Streamable HTTP session IDs are rejected on
/messagesendpoint
This prevents session confusion when both protocols are active simultaneously.
- UUID-based session IDs via
crypto.randomUUID() - 30-minute idle timeout with automatic cleanup
- 24-hour absolute TTL limit for streamable HTTP sessions
- 5-minute sweep interval for expired sessions
Streamable HTTP sessions:
# 1. Initialize — server returns mcp-session-id header
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# 2. Subsequent requests — include mcp-session-id
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: YOUR_SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# 3. Terminate session
curl -X DELETE http://localhost:3000/mcp \
-H "mcp-session-id: YOUR_SESSION_ID"Legacy SSE sessions:
# 1. Open SSE connection — server sends endpoint event with sessionId
curl -N http://localhost:3000/sse
# 2. Send messages to the returned endpoint
curl -X POST "http://localhost:3000/messages?sessionId=YOUR_SSE_SESSION_ID" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'No session tracking. Each request is independent. Best for serverless deployments.
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"test_simple","arguments":{"message":"Hello"}}}'| Feature | Stateful (default) | Stateless (--stateless) |
|---|---|---|
| Progress Notifications | ✅ Yes | ❌ No |
| SSE Streaming | ✅ Yes | ❌ No |
| Legacy SSE Protocol | ✅ Yes | ❌ No |
| Session Management | ✅ Yes | ❌ No |
| Serverless Compatible | ✅ Native | |
| Horizontal Scaling | ✅ Any instance |
Choose Stateful when:
- Running on persistent infrastructure (VMs, containers, dedicated servers)
- Need progress notifications for long-running tools
- Need Legacy SSE for backward compatibility
Choose Stateless when:
- Deploying to serverless (AWS Lambda, Cloudflare Workers, Vercel)
- Horizontal scaling without sticky sessions
- Simple request-response patterns
The HTTP transport is implemented as a modular directory:
src/transports/http/
types.ts — Configuration, constants, rate limiting types
security.ts — CORS, security headers, rate limiting, client IP
handlers.ts — Health check, root info, auth middleware
server/ — HTTP server implementations
index.ts — HttpTransport class
stateful.ts — Streamable HTTP (session management)
stateless.ts — Stateless HTTP (serverless)
legacy-sse.ts — SSE transport (MCP 2024-11-05)
index.ts — Barrel re-export
Key design decisions:
-
Extracted from mcp-server.ts — HTTP logic lives in its own module, keeping
mcp-server.tsfocused on tool/resource/prompt registration -
Dual-protocol — Both
StreamableHTTPServerTransportandSSEServerTransportshare the same Express app - Built-in rate limiting — Zero-dependency sliding window with automatic cleanup
- Server timeouts — Request (120s), keep-alive (65s), and headers (66s) for DoS mitigation
- Session isolation — Each protocol maintains its own session map; cross-protocol access is blocked
| CLI Flag | Env Variable | Default | Description |
|---|---|---|---|
--transport http |
— | stdio |
Enable HTTP transport |
--port <number> |
PORT |
3000 |
HTTP port |
--server-host <host> |
MCP_HOST |
localhost |
Bind address (0.0.0.0 for containers) |
--stateless |
— | false |
Disable session management |
--cors-origin <origins> |
MCP_CORS_ORIGIN |
(blank) | CORS allowed origins (comma-separated) |
--oauth-enabled |
OAUTH_ENABLED |
false |
Enable OAuth 2.1 authentication |
--oauth-issuer <url> |
OAUTH_ISSUER |
— | OAuth issuer URL |
--oauth-audience <aud> |
OAUTH_AUDIENCE |
— | Expected JWT audience claim |
--oauth-jwks-uri <url> |
OAUTH_JWKS_URI |
— | JWKS endpoint for token verification |
--oauth-clock-tolerance <seconds> |
— | 60 |
Clock skew tolerance for JWT validation |
--backup-interval <minutes> |
— | 0 |
Automated backup interval (0 = off) |
--keep-backups <count> |
— | 5 |
Max backups retained during cleanup |
--vacuum-interval <minutes> |
— | 0 |
Database optimize interval (0 = off) |
--rebuild-index-interval <minutes> |
— | 0 |
Vector index rebuild interval (0 = off) |
--digest-interval <minutes> |
— | 0 |
Proactive analytics computation interval (0 = off) |
--briefing-entries <n> |
BRIEFING_ENTRY_COUNT |
3 |
Journal entries in briefing |
--briefing-summaries <n> |
BRIEFING_SUMMARY_COUNT |
1 |
Session summaries in briefing |
--briefing-include-team |
BRIEFING_INCLUDE_TEAM |
false |
Include team DB entries in briefing |
--briefing-issues <n> |
BRIEFING_ISSUE_COUNT |
0 |
Issues to list in briefing (0 = count only) |
--briefing-prs <n> |
BRIEFING_PR_COUNT |
0 |
PRs to list in briefing (0 = count only) |
--briefing-pr-status |
BRIEFING_PR_STATUS |
false |
Show PR status breakdown (open/merged/closed) |
--rules-file <path> |
RULES_FILE_PATH |
— | Path to user rules file for agent awareness |
--skills-dir <path> |
SKILLS_DIR_PATH |
— | Path to skills directory for agent awareness |
--briefing-workflows <n> |
BRIEFING_WORKFLOW_COUNT |
0 |
Workflow runs to list in briefing (0 = status only) |
--briefing-workflow-status |
BRIEFING_WORKFLOW_STATUS |
false |
Show workflow status breakdown in briefing |
--briefing-copilot |
BRIEFING_COPILOT_REVIEWS |
false |
Aggregate Copilot review state in briefing |
Next: Learn about Configuration or check Security.