Skip to content

feat: consolidate iii-engine + agentmemory into single Docker image - #940

Closed
Noodle05 wants to merge 28 commits into
rohitg00:mainfrom
Noodle05:002-single-docker-image
Closed

feat: consolidate iii-engine + agentmemory into single Docker image#940
Noodle05 wants to merge 28 commits into
rohitg00:mainfrom
Noodle05:002-single-docker-image

Conversation

@Noodle05

@Noodle05 Noodle05 commented Jun 15, 2026

Copy link
Copy Markdown

Summary

Consolidate the multi-container Docker setup (separate iii-engine + agentmemory + iii-init containers) into a single Docker image that runs both processes in one container.

Motivation

The iii-engine and agentmemory are tightly coupled (1:1, version-pinned). Splitting across containers adds orchestration complexity without benefit. The platform deploys already use this pattern.

Changes

File Action
Dockerfile 3-stage build (iii binary + source-build + runtime)
entrypoint.sh Dual-process management with graceful shutdown (based on deploy/coolify)
docker-compose.yml Single agentmemory service
iii-config.docker.yaml Deleted (entrypoint generates dynamically)
.env.example Added AGENTMEMORY_SECRET_FILE and AGENTMEMORY_REST_HOST

Verification

  • docker build succeeds, container starts with both processes
  • livez health check passes, viewer responds with Bearer auth
  • HMAC persists across restarts
  • Zero TypeScript changes — no test regressions
  • Minimal diff vs deploy/coolify/ upstream

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added MCP Streamable HTTP transport protocol for JSON-RPC-based client connections
    • Added Docker containerization support with docker-compose for streamlined deployment
  • Documentation

    • Added Docker deployment guide and quick-start instructions
    • Added feature specification documentation for new transport and deployment methods
  • Chores

    • Updated environment variable examples with new Docker and HTTP transport configuration options

Noodle05 and others added 28 commits June 14, 2026 23:52
- Install @modelcontextprotocol/node SDK for MCP Streamable HTTP transport
- Add @modelcontextprotocol/node to tsdown external array (ESM, not bundled)
- Add mcpPort field to AgentMemoryConfig type (defaults to restPort + 3)
- Add AGENTMEMORY_MCP_PORT env var support in loadConfig()

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Move handleToolsList() and handleToolCall() plus all their internal
helpers (validate, handleProxy, handleLocal, handleProxyGeneric,
normalizeList, parseLimit, textResponse, announceMode) from
src/mcp/standalone.ts into a new src/mcp/handler.ts shared module.

The extracted functions accept explicit sdk, kv, and config parameters
so they can be reused by both the existing stdio transport and the
upcoming Stream HTTP transport without code duplication.

standalone.ts retains:
- InMemoryKV instantiation
- createStdioTransport and the stdio message dispatch loop
- SIGINT/SIGTERM handlers
- Backward-compatible wrapper exports (old signatures) for tests

All 1410 non-integration tests pass; no behavioral changes.

Co-Authored-By: Claude <noreply@anthropic.com>
Implement JSON-RPC-over-HTTP MCP transport that coexists with existing stdio
transport. Uses Node.js http.createServer() with manual JSON-RPC handling,
Bearer auth via timingSafeCompare(), and session management via a Map.

- src/mcp/stream-http.ts: HTTP server with MCP JSON-RPC handling (initialize,
  tools/list, tools/call, notifications)
- test/mcp-stream-http.test.ts: 22 tests covering initialize, tools/list,
  tools/call, Bearer auth, JSON-RPC errors, session management
- test/mcp-transport-coexistence.test.ts: 5 tests verifying HTTP and stdio
  transports expose identical tool sets and handle notifications
- src/index.ts: Wire up MCP server on config.mcpPort (3114), add to boot log
  and graceful shutdown
- package.json: Add @cfworker/json-schema peer dependency

Port scheme: restPort + 3 (3114), configurable via AGENTMEMORY_MCP_PORT

Co-Authored-By: Claude <noreply@anthropic.com>
- Add session TTL with lastUsedAt tracking and periodic eviction (every 10 min)
- Fix session-not-found error code from -32001 to -32000 per contract
- Add explanation comment for manual JSON-RPC vs SDK transport
- Add 5MB request body size limit with 413 response
- Add HTTP method validation (POST/DELETE only, 405 for others)
- Add Content-Type validation (application/json required, 415 otherwise)

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix JsonRpcMessage.id type to allow null (JSON-RPC notification compliant)
- Remove as cast in errorResponse() now that id type includes null
- Remove dead req.headers["Mcp-Session-Id"] branch (Node.js lowercases headers)
- DELETE handler now removes session from sessions Map
- Remove unused @cfworker/json-schema dependency
- Tighten parse error test to assert exactly -32700
- Add SDK bug reference URL in header comment
- Add safety guard comment for res.headersSent in body-too-large handler

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
…cker

- T039: Add "stream-http" connection category to ConnectAdapter types
- T040: Add AGENTMEMORY_MCP_HTTP_BLOCK constant and HTTP transport comments
  in CLI connect adapters for Stream HTTP URL-based connections
- T041: Add AGENTMEMORY_MCP_PORT entry to .env.example ports section
- T042: Add Docker quickstart section to README.md
- T045: Add mcpPort to startup banner alongside REST API and MCP surface lines

Co-Authored-By: Claude <noreply@anthropic.com>
- Remove @modelcontextprotocol/node from package.json dependencies
- Remove @modelcontextprotocol/node from tsdown.config.ts external array
- Update stream-http.ts comment to remove SDK revisit suggestion
- Update spec.md assumptions: Stream HTTP uses hand-rolled JSON-RPC
  server on port 3114 (separate from REST on 3111)
- 1450 tests pass (6 pre-existing environmental failures in worktree)

Co-Authored-By: Claude <noreply@anthropic.com>
Merge the multi-container Docker setup into a single image:
- Dockerfile: 3-stage build (iii binary + TypeScript build + runtime)
- entrypoint.sh: based on deploy/coolify upstream, starts iii-engine in
  background then agentmemory in foreground with graceful shutdown trap
- docker-compose.yml: simplified to single agentmemory service
- iii-config.docker.yaml: removed (entrypoint generates config dynamically)

Builds agentmemory from local TypeScript source (not npm registry).
Minimal diff vs deploy/coolify upstream — only source-build vs
npm-install differences.

Co-Authored-By: Claude <noreply@anthropic.com>
…xample

These env vars are needed for the single-image Docker setup.
AGENTMEMORY_SECRET_FILE points to the persisted HMAC secret file.
AGENTMEMORY_REST_HOST sets the REST upstream host for the viewer proxy.

Co-Authored-By: Claude <noreply@anthropic.com>
- Fix 'iii run' → 'iii' (no 'run' subcommand in iii CLI)
- Fix 'SIGTERM SIGINT' → 'TERM INT' (POSIX sh requires no SIG prefix)
- Verified: container starts, health check passes, viewer responds,
  HMAC persists across restarts, no regressions

Co-Authored-By: Claude <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

@Noodle05 is attempting to deploy a commit to the rohitg00's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a36f1d44-3ca0-4f27-b84b-cfae8fd03645

📥 Commits

Reviewing files that changed from the base of the PR and between f6f9e3c and 9b81487.

📒 Files selected for processing (20)
  • .dockerignore
  • .env.example
  • Dockerfile
  • README.md
  • docker-compose.yml
  • entrypoint.sh
  • iii-config.docker.yaml
  • specs/001-mcp-stream-dockerize/spec.md
  • src/cli/connect/types.ts
  • src/cli/connect/util.ts
  • src/config.ts
  • src/index.ts
  • src/mcp/handler.ts
  • src/mcp/standalone.ts
  • src/mcp/stream-http.ts
  • src/types.ts
  • src/viewer/server.ts
  • test/mcp-stream-http.test.ts
  • test/mcp-transport-coexistence.test.ts
  • test/multi-instance-port.test.ts

📝 Walkthrough

Walkthrough

Adds an MCP Streamable HTTP JSON-RPC transport server (stream-http.ts) on a new mcpPort (default restPort + 3), extracts shared tool-call logic into handler.ts, reduces standalone.ts to thin forwarding wrappers, wires the server into boot/shutdown, introduces a full multi-stage Docker build with a first-boot entrypoint and a consolidated single-service docker-compose.yml, and ships comprehensive tests and docs.

Changes

MCP Streamable HTTP Transport and Docker Deployment

Layer / File(s) Summary
Config and type contracts for mcpPort and stream-http category
src/types.ts, src/cli/connect/types.ts, src/config.ts, src/cli/connect/util.ts, .env.example
AgentMemoryConfig gains mcpPort, ConnectAdapter.category gains "stream-http", loadConfig() derives mcpPort from AGENTMEMORY_MCP_PORT (default restPort + 3), and AGENTMEMORY_MCP_HTTP_BLOCK constant is exported for CLI connect. .env.example documents the new env vars.
Shared MCP tool handler extracted to handler.ts
src/mcp/handler.ts, src/mcp/standalone.ts
handler.ts is introduced with URL display safety, mode announcement, input normalization, per-tool argument validation, proxy dispatch to REST endpoints, local InMemoryKV fallback, generic proxy forwarding for non-local tools, and remote tools-list fetching. standalone.ts is reduced to thin forwarding wrappers.
MCP Streamable HTTP server
src/mcp/stream-http.ts
startMcpStreamServer implements a hand-rolled Node http server with bounded body reading (5 MB), Bearer auth with timing-safe compare, session map with idle eviction, and JSON-RPC routing for initialize/tools/list/tools/call/DELETE.
Boot and shutdown wiring
src/index.ts
Imports startMcpStreamServer, creates mcpServer from config.mcpPort during worker boot with a boot log, and awaits mcpServer.shutdown() in the shutdown handler.
Viewer REST proxy host made configurable
src/viewer/server.ts
proxyToRestApi() reads AGENTMEMORY_REST_HOST (defaulting to 127.0.0.1) instead of hardcoding the upstream host.
Docker packaging
Dockerfile, .dockerignore, docker-compose.yml, entrypoint.sh, iii-config.docker.yaml
Multi-stage Dockerfile with tini/gosu/openssl; entrypoint.sh initializes /data, generates and persists the HMAC secret on first boot, writes runtime iii-config.yaml, starts iii in background with TERM/INT trap, then execs the CLI via gosu. docker-compose.yml consolidates to a single agentmemory service with healthcheck and agentmemory-data volume. Workers config block is removed from iii-config.docker.yaml (now generated by entrypoint).
README Docker section and feature spec
README.md, specs/001-mcp-stream-dockerize/spec.md
README gains a Docker quickstart section. A full feature specification with user stories, edge cases, functional requirements, and success criteria is added under specs/.
MCP stream HTTP server tests (T010–T019)
test/mcp-stream-http.test.ts
~937-line Vitest suite covering initialize, tools/list, tools/call, Bearer auth, JSON-RPC error codes, session management, notification handling, HTTP method/Content-Type validation, body size limits, DELETE, and session eviction with fake timers.
Transport coexistence and port derivation tests
test/mcp-transport-coexistence.test.ts, test/multi-instance-port.test.ts
T016 validates HTTP tools match handleToolsList output; T017 validates notification handling. Port test suite extended to assert cfg.mcpPort in all derivation scenarios including explicit AGENTMEMORY_MCP_PORT pinning.

Sequence Diagram(s)

sequenceDiagram
  participant Client as MCP Client
  participant StreamHTTP as stream-http.ts (port 3114)
  participant Handler as handler.ts
  participant KV as InMemoryKV
  participant REST as agentmemory REST API

  Client->>StreamHTTP: POST /mcp initialize
  StreamHTTP->>StreamHTTP: validate auth, create session
  StreamHTTP->>Client: 200 + mcp-session-id

  Client->>StreamHTTP: POST /mcp tools/list (mcp-session-id)
  StreamHTTP->>Handler: handleToolsList(sdk, kv)
  Handler-->>StreamHTTP: { tools: [...] }
  StreamHTTP->>Client: 200 JSON-RPC result

  Client->>StreamHTTP: POST /mcp tools/call (mcp-session-id)
  StreamHTTP->>Handler: handleToolCall(name, args, sdk, kv)
  alt proxy mode
    Handler->>REST: GET/POST /agentmemory/...
    REST-->>Handler: result
  else local fallback
    Handler->>KV: read/write
    KV-->>Handler: data
  end
  Handler-->>StreamHTTP: { content: [...] }
  StreamHTTP->>Client: 200 JSON-RPC result

  Client->>StreamHTTP: DELETE (mcp-session-id)
  StreamHTTP->>StreamHTTP: remove session
  StreamHTTP->>Client: 200
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • rohitg00/agentmemory#887: Modifies ConnectAdapter category plumbing in src/cli/connect/types.ts, which this PR directly extends to include the "stream-http" category.
  • rohitg00/agentmemory#815: Modifies loadConfig() in src/config.ts to derive related ports from a REST anchor, the same pattern this PR extends to introduce mcpPort.

Suggested reviewers

  • rohitg00

🐇 A port for streams, a port for REST,
A secret sealed in /data/.hmac at rest,
tini guards the process tree with care,
gosu drops to node beyond compare—
HTTP, stdio, they coexist just right,
Hop hop, the MCP server's live tonight! 🎉

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Noodle05 Noodle05 closed this Jun 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant