All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[1.0.0] - Unreleased
Stable 1.0 will preserve the behavior of the final modern-preferred release candidate after its minimum seven-day soak and the mixed-cluster rollback drill complete. No wire-design or public-API changes are planned between that RC and stable 1.0.
[1.0.0-rc.8] - Unreleased
This narrow follow-up candidate preserves rc.7's MCP/ACP wire behavior and
:prefer_modern default while adding real adapter lifecycle evidence, fixing
Pi configuration isolation, and reducing duplicated internal code. Publishing
rc.8 starts the final-candidate soak from the rc.8 artifact.
Fixed
- The Pi adapter now consistently honors
:agent_dirwhile discovering settings, prompts, and sessions, allowing tests and embedded callers to avoid the developer's real Pi state. Slash-command input hints are normalized to the ACP object shape before advertisement. - ACP adapter examples now use
ExMCP.ACP.AdapterTransport, matching the actual Claude SDK, Codex, and Pi adapter lifecycle.
Added
- Opt-in, credential-free lifecycle tests launch the real Claude Code, Codex, and Pi CLIs through their adapters. The tests initialize, create/list/close a session, and shut down without sending a prompt or making an LLM request.
- Post-1.0 maintenance and 2.0 roadmap notes now track adapter decomposition, pure functional cores, dependency-cycle cleanup, configuration ownership, and the evidence required before considering separate MCP and ACP packages.
Changed
- Subprocess environment isolation, positive-integer option handling, and symlink-aware workspace containment now use shared internal helpers with focused characterization tests; public options and security defaults are unchanged.
- Internal planning, audit, migration, coverage, and release-history documents remain in the repository but are no longer installed in the Hex source package or rendered as normal HexDocs guides. The user-facing architecture, configuration, transport, security, troubleshooting, ACP, DSL, migration, and getting-started guides remain packaged.
Release evidence
- See the repository-only rc.8 release record.
[1.0.0-rc.7] - 2026-08-13
This release candidate packages the post-rc.6 transport lifecycle, ACP, and
security hardening work that requires a fresh modern-preferred soak before
stable 1.0. The application default remains :prefer_modern. Set
protocol_mode: :legacy_only to preserve the legacy protocol era. Exact rc.5
wire and session behavior still requires package rollback to 1.0.0-rc.5;
rc.7 continues to enforce server-issued sessions and newer lifecycle/security
rules.
Fixed
- Legacy GET SSE streams now persist events before delivery and replay events
published during a connection gap after
Last-Event-ID. Normal stream disconnects retain the MCP session until explicit deletion or expiry, and bounded event retention continues to keep the newest events after repeated evictions. - ACP Pi managed event handling now correctly processes multi-chunk agent streams without dropping or duplicating partial updates.
- CI compatibility follow-ups after the 2026-08-12 security harden: invalid MRTR identities fail closed, public docs remain compatible with Elixir 1.17, an unreachable Elixir 1.20 clause was removed, and tagged test fixtures align with server-issued sessions and strict schema/privacy behavior.
- SSE cleanup tests are race-safe under concurrent teardown.
Security
- MCP and ACP protocol implementations were hardened across lifecycle, identity, authorization, network, resource, and privacy boundaries. See the 2026-08-12 security audit.
- Legacy Streamable HTTP sessions are server-issued only, identity-bound for their lifetime, initialize-once, and capacity-capped. Duplicate JSON-RPC request IDs are rejected before dispatch.
- OAuth metadata and MCP HTTP destinations use pinned, hostname-verifying clients with DNS revalidation, public-address checks, and bounded redirects.
- Adapter and MCP/ACP subprocesses use a closed environment by default:
inherited variables are cleared before a minimal runtime baseline and
explicit values are restored. Callers that require the legacy behavior can
opt into
environment_policy: :inheritoradapter_opts[:environment_policy] == :inherit. - Resource, frame, queue, handshake, and replay paths enforce byte/count/ deadline limits with slow-consumer handling. Peer-facing errors omit internal details; logs and telemetry use structural summaries.
Added
- Added a canonical ExMCP 2.0 roadmap covering runtime ownership, bounded handler scheduling, state/replay adapters, public API consolidation, deprecation removals, and the compatibility gate for 1.x backports.
- Additive
ExMCP.SessionManager.append_event/3for store-owned, monotonically increasing SSE event IDs used by persist-before-delivery.
Documentation and release evidence
- Release packaging for
1.0.0-rc.7, including the rc.7 release record and an updated public API delta note for post-rc.6 changes.
[1.0.0-rc.6] - 2026-08-05
This is the first published dual-era release candidate and starts the
modern-preferred soak for stable 1.0. New connections try MCP 2026-07-28 first
and retain evidence-based fallback to every legacy revision. Set
protocol_mode: :legacy_only for the exact rc.5 wire path.
MCP wire protocol changes
- MCP 2026-07-28 lands before stable 1.0 — New connections prefer the modern
protocol era and fall back according to the configured protocol mode. The
:prefer_legacy,:legacy_only, and:modern_onlymodes provide explicit rollout and rollback controls. A connection is pinned to one era after negotiation; it never silently downgrades after application traffic starts. - Modern initialization and discovery — Modern sessions use the 2026-07-28
initialization shape and
server/discover, including extension metadata. Existing revisions remain supported throughout 1.x. - Stateless Streamable HTTP routing — Modern requests use body-derived
Mcp-Method,Mcp-Params, andMcp-Param-*headers, per-request or subscription POST-owned SSE streams, and modern GET/DELETE semantics. The deprecated two-endpoint HTTP+SSE transport remains opt-in for legacy peers. - Multi-round tool results — Tools, prompts, and resource reads can return
input-required results with sealed
requestState; clients dispatch input and continue the operation under bounded round, expiry, replay, principal, and key-rotation controls. - Subscriptions and cache metadata —
subscriptions/listenreplaces the modern resource-only flow and supports bounded local storage plus optional clustered PubSub fanout. Result envelopes validateresultType,ttlMs, andcacheScope; 1.0 deliberately does not store or reuse cached responses. - Tasks extension — Store-backed handlers advertise and implement
io.modelcontextprotocol/tasks; clients advertise it only when explicitly configured. Legacy task APIs remain available on legacy protocol revisions. - Protocol deprecations are compatibility features in 1.x — Roots, sampling, logging, the old resource-subscription methods, and legacy HTTP+SSE remain available where their negotiated revision supports them, with documented migration paths.
ExMCP public API compatibility
- No rc.5 public surface was removed — The compiled API comparison found no removed module, exported function/arity, callback, named type, or struct field. Existing handler callbacks are widened for MRTR and existing structs only gain fields. See the rc.5-to-1.0 API diff.
- Modern APIs are additive — Discovery, request context, MRTR, subscriptions, Tasks-extension, era control, modern HTTP, and OAuth security APIs are added alongside the rc.5 surface.
- Deprecated library APIs remain for all of 1.x —
ExMCP.Server.Tools,ExMCP.Server.Tools.Simplified, companion modules, and deprecated content transformation stubs target removal in 2.0.0, not a 1.x release. - OAuth state ownership is intentionally hardened —
OAuthFlow.start_authorization_flow/1rejects caller-supplied:state, generates a single-use value, and returns it in the bound transaction. This is the one public input-behavior change identified by the rc.5 API audit; callers should retain the returned transaction instead of supplying state.
Operations and security
- Modern-preferred release default — The application default is now
:prefer_modern. Servers accept both eras and advertise 2026-07-28 first; clients probe withserver/discoverand initialize only after positive legacy evidence. Explicit modes are unchanged. - Patched HTTP dependency stack — Mint, Mint WebSocket, Plug, Plug Cowboy,
Cowboy, Cowlib, HPAX, Decimal, and related runtime dependencies are updated
to remove every high-severity lockfile advisory. CI now gates on
mix hex.auditand a real Hex package build. The two remaining upstream Cowlib medium/low advisories are explicitly acknowledged because Plug and Cowboy validate response headers and ExMCP does not call the affected cookie encoder; every new advisory still fails the build. - Bounded modern state — Era observations, MRTR continuations/replay data, subscription queues, filters, lifetimes, and task records have explicit bounds and cleanup behavior.
- Migration telemetry is privacy-safe — Era selection/fallback/downgrade, MRTR failures, ambiguous reissue, and subscription reconnect/pressure events use bounded classifications and exclude headers, arguments, response content, request state, credentials, and raw subscription identifiers.
- OAuth 2026 hardening — Metadata fetching is address-pinned and SSRF-resistant; transactions are atomic and single-use; credentials are issuer- and authorization-context-bound; CIMD replaces DCR where available. Modern POST-owned request streams now apply the configured provider token, handle a bounded 401 refresh plus optional 403 scope step-up, and merge the resulting token/provider state back only while that stream still owns the request.
Documentation and release evidence
- Protocol-era guidance is consistent across README, HexDocs, and guides — Public HTTP and callback documentation now distinguishes stateless MCP 2026-07-28 behavior from legacy sessions, GET streams, resumability, and independent server-to-client requests. Troubleshooting covers modern discovery, metadata, routing headers, result envelopes, and expected 405s.
- Release evidence ships with the package — The MCP coverage matrix, rc.5-to-1.0 API diff, and 2026 migration plan are packaged and linked from HexDocs. The development checklist records the modern conformance, RC soak, and mixed-cluster rollback gates, and CI treats ExDoc warnings as failures.
- Official SDK v2 interop is release-gating — Four isolated CI lanes exercise
ExMCP as client and server over stdio and Streamable HTTP against the official
TypeScript MCP v2 client/server packages pinned at
2.0.0. They negotiate2026-07-28exactly and cover discovery, request context, result envelopes, MRTR, subscriptions, routing headers, POST-owned SSE, and stateless sessions; legacy SDK interop remains independently gated.
Security
- OAuth metadata fetches are SSRF-hardened — CIMD, Protected Resource Metadata,
OIDC/RFC 8414 authorization-server metadata, and JWKS retrieval now require HTTPS and share a
bounded, address-pinned fetcher. It rejects any private, loopback, link-local, reserved or mixed
DNS result; re-resolves every redirect; blocks cross-origin redirects unless an exact HTTPS
origin is configured; and forwards no credentials or application headers. Legacy URL-only
custom metadata clients are replaced by
get(uri, approved_address, opts)so adapters cannot silently re-resolve after validation. - OAuth transactions are atomic and single-use — Library-started authorization-code flows now generate their own 256-bit state, reject reserved parameter overrides, retain only state/code digests in a bounded supervised transaction store, and bind callback issuer, PKCE, authorization code, and exact redirect URI through one-time transitions shared by both MCP protocol eras. Concurrent callback/redemption attempts have one winner; an ambiguous token request cannot replay its code. OAuth failure logs and telemetry redact credential fields, cookies, authorization values, and URL query/fragment data.
- OAuth credentials are authorization-server bound — MCP 2026-07-28 pre-registered clients require an exact
credential_issuer; discovered metadata, callbacks, and persisted registrations use one no-normalization issuer comparison. The new pluggableExMCP.Authorization.CredentialStorepartitions registrations by issuer + client ID and tokens by the complete authorization context, rejects unkeyed legacy records pending explicit migration, and redacts stored secrets from inspection. DCR credentials are reused only within the same issuer partition and a changed AS triggers registration in a new partition.
[1.0.0-rc.5] - 2026-08-04
This release closes the findings of a full-codebase audit (architecture, security, tests, tooling). Behavior changes are listed under Breaking Changes below.
Security
- Real DNS-rebinding protection in
ExMCP.HttpPlug— Origin validation previously allowed a missing or emptyOrigin, and its same-origin fallback comparedOriginagainst the attacker-controlledHostheader, so the check passed in exactly the scenario it was meant to stop. Host is now validated against an allow-list (:allowed_hosts) before routing, mismatches get421 Misdirected Request, and the same-origin fallback is gone.ExMCP.Plugs.DnsRebindingno longer default-allows0.0.0.0and now parses bracketed IPv6 hosts correctly ([::1]:8080). - Configured TLS options are actually applied to HTTPS POSTs —
ExMCP.Transport.HTTP.build_ssl_options/1returned two different shapes; on the POST path the result was spliced into:httpchttp_options as a flat list, which:httpcrejects and ignores (Invalid option {verify,verify_peer} ignored). Every configured TLS setting — client certificates for mTLS, privatecacerts,versions— was silently dropped on each request. The builder now returns one shape, wrapped as{:ssl, opts}at each call site, and default client TLS addscustomize_hostname_checkso wildcard certificates verify correctly. - JWT expiry is now enforced —
ExMCP.Authorization.JWTaccepted tokens with noexpclaim, or with a non-numericexp.expis now required and must be numeric (opt out withrequire_exp: false),nbf/iattypes are validated when present, and a:leewayoption (default 30s) covers clock skew. The asymmetric-only algorithm allow-list is unchanged. - Client-supplied session IDs are validated —
mcp-session-id(and legacyx-session-id) values are checked for format and length instead of being accepted and echoed back unbounded. - Consent expiry contract is explicit —
ExMCP.Security.Consentaccepted a bare integer expiry as a monotonic value, so a handler returning unix seconds granted near-permanent consent. Handlers now returnDateTime,{:ttl, _},{:unix, _}, or{:monotonic, _}; implausible values fail closed. Fixes a related unit bug where:consent_ttl(milliseconds in config) was passed to handlers as seconds. - Security enforcement flags are honored —
enable_token_passthrough_preventionandenable_user_consent_validationwere read nowhere; they now gate their checks inExMCP.Transport.SecurityGuardand are type-validated. - Removed a hostname-verification stub —
ExMCP.Internal.Security.verify_hostname/3always returned:valid_peer; wiring it as averify_funwould have silently disabled hostname checking. Removed, and the module's divergent second TLS-options builder now delegates to the canonical one.verify: :verify_nonenow logs a warning.
Added
- Session-scoped HTTP resource subscriptions — Streamable-HTTP subscriptions
are retained across POST and SSE requests, tracked independently per client
session in supervised ETS indexes, and removed on explicit termination or
TTL expiry.
ExMCP.Server.notify_resource_update/1broadcasts updates to every connected subscriber without routing through a singleton mailbox. - Canonical protocol method registry —
ExMCP.Protocol.Methodsnow owns method names, version bounds, message kinds, and dispatcher handlers. Server dispatch, HTTP message processing, request processing, version gating, and message-format metadata all derive from it. - Pre-2.0 compatibility guardrails — committed wire-capability fixtures and characterization tests pin protocol method tables, public error codes, the omitted-version initialize default, and elicitation callback routing across all four supported protocol revisions.
Client auto-reconnection —
ExMCP.Clientnow automatically reconnects when the transport closes unexpectedly, as the documentation always promised. Pending requests still fail with a connection error, then the client re-establishes the transport and MCP handshake with exponential backoff and jitter (defaults: initial 1s, multiplier 2, cap 60s, up to 10 attempts). Newstart_link/1options::reconnect(defaulttrue),:max_reconnect_attempts, and:reconnect_backoff(:initial/:max/:multiplier). Emits[:ex_mcp, :client, :reconnect, :attempt | :success | :error | :timeout]telemetry. Explicitdisconnect/1/stop/2never triggers reconnection; requests made while reconnecting return{:error, :not_connected}.- Client health checks — On persistent transports the client sends a protocol
pingon an interval and treats an unanswered ping as transport loss, handing off to the reconnect path. (The previous health-check timer fired but performed no check.) - Working progress tokens —
call_tool/4now accepts:progress_token, which was previously ignored; it is sent as_meta.progressToken, and request-level_metais merged into the arguments map handed tohandle_call_tool/3, asExMCP.Server.Handlerhas always documented. Long-running tools can now actually report progress. - Shared server dispatch — New
ExMCP.Server.Dispatch,ExMCP.Server.ResultNormalizer, andExMCP.Server.HandlerBridgegive the HandlerServer, stdio, HTTP, and request-processor paths one method table and one result/error shape. stdio gainedcompletion/complete,resources/subscribe,resources/unsubscribe,roots/list,logging/setLevel, resource templates, and thetasks/*methods it was missing.
Fixed
- Claude streamed text is emitted once — Claude SDK terminal assistant text no longer repeats content already emitted through text deltas. Multiple non-streamed text blocks and distinct identical assistant messages remain intact.
- Codex structured approvals are preserved — Structured command-execution,
file-change, and permission responses no longer crash string conversion or
lose provider fields. Decisions are validated against the advertised
availableDecisionsbefore they are returned. - Codex file-change snapshots and legacy patches agree — Current ordered
changessnapshots are mapped in full, while legacy nestedpatchand flatdiff/text/deltapayloads retain their previous behavior. - Raw Handler field names use MCP wire casing — Shared result normalization
maps idiomatic fields including
:input_schema,:output_schema,:mime_type, and:uri_templateto their protocol names on every server dispatch path. - HTTP Handler timeout is configurable —
ExMCP.HttpPlugnow exposes:handler_call_timeout(default 10 seconds) and threads it into the existing MessageProcessor deadline. This server-side deadline is independent of client request and SSE timeouts. - URL-mode elicitation reaches the URL callback —
elicitation/createrequests withmode: "url"now dispatch tohandle_url_elicitation/3when implemented. This is the release's one intentional behavior change: handlers implementing both elicitation callbacks will now receive URL-mode requests in the URL callback. Form-only handlers retain a compatibility fallback, receive the URL payload instead of an empty schema, and get a once-per-handler warning. - Compliance coverage includes 2025-11-25 — the generated legacy compliance matrix now derives versions from the canonical registry, requires an explicit handler for every version, and carries inherited feature coverage through the newest legacy revision.
- Transport
close/1no longer leaks processes — HTTP and stdio calledProcess.exit(pid, :normal)on processes that do not trap exits (a no-op), leaving the SSE client GenServer and the stdio reader running after close. - Async POST state and crashes — The async POST task's updated transport state (session-ID rotation, refreshed OAuth token) was discarded by the client, so session and auth updates were lost between requests; the task was also unmonitored, so a crash hung the pending request until timeout.
- SSE handler and session-registry lifetime — The SSE session table was created lazily inside a Cowboy request process, so every registration vanished when that request ended; it is now owned by a supervised
ExMCP.HttpPlug.SessionRegistry. SSE handlers stop on conn-owner:EXITand clean up interminate/2instead of leaking with heartbeats still firing. - Handler crashes and timeouts return JSON-RPC errors —
ExMCP.MessageProcessorran a per-request handler GenServer whose exits were not caught, so a crash or a call timeout killed the request process and the client got no response at all. Exits are caught and mapped to-32603. - Handler errors are no longer reported as "Method not found" — Any
{:error, _}or exit from a custom method previously became-32601; only genuinely unknown methods do now. - Batch rejection follows the spec across versions — Batch support was gated on the exact string
"2025-06-18", so 2025-11-25 sessions still accepted JSON-RPC batches. Gating now uses version ordering. - Protocol-version defaults unified —
ExMCP.Plugs.ProtocolVersion,MessageProcessor,MethodHandlers, and the defaulthandle_initializedisagreed on the default and supported-version list; all now readExMCP.Internal.VersionRegistry. logging/setLevelreaches the handler on the HTTP path instead of being answered with a canned success.- Internal detail no longer leaks in error responses —
inspect(reason)was embedded in JSON-RPC error data and messages; detail is logged, clients get generic messages. - Circuit breaker and reliability wrapper — A raising function killed the
CircuitBreakerGenServer through its task link, andClientWrapper.execute_with_reliabilityspawned unbounded unsupervised tasks while never applying the configured breaker. Both fixed; the wrapper takes:max_concurrency(default 100). - Retry policy —
add_jitter/1raised via:rand.uniform(0)for sub-4ms delays, and every exception was treated as retryable. - Handshake timeouts — Client connect and stdio receive had no timeout, so
start_link/1could hang forever ininit/1against an unresponsive server; they now return{:error, :handshake_timeout}. - Request bookkeeping — Requests with a caller-supplied
:timeoutleaked their pending-request entry forever. ExMCP.SessionManageris no longer lazily started linked to an HTTP request process (it is supervised by the application);HttpPlugfails fast with a clear message instead.clientInfoversion — The MCP handshake advertised a hardcoded"0.8.0"; it is now derived from the application version.- Dead code removed —
HandlerServer's unreachable:start_message_loop(which evaluatedself()insidespawn_linkand would have messaged itself), the unused stdio:bufferfield, and an unreachable branch in the client's connect path. - Test isolation —
ExMCP.ApplicationTest,ProgressTrackerMinimalTest, and the stdio isolation test stopped the:ex_mcpapplication without restarting it, taking supervised singletons down for every test that ran afterwards. - stdio handshake no longer kills the spawned server — The bounded handshake wait ran the transport's blocking
receive_message/1inside a temporary task, which took ownership of the port; when that task exited, OTP closed the port and terminated the spawned program, so the very next send failed with{:send_failed, :badarg}.ExMCP.Transport.Stdionow exposes a timeout-awarereceive_message/2that runs in the owning process.
Changed
- Protocol versions have one source of truth —
VersionNegotiatorandExMCP.Typesdelegate version lists/scalars toVersionRegistry; unknown-version capability/message-format fallbacks now warn while preserving their existing return values. - Error codes have one numeric source — duplicate JSON-RPC constants and atom maps now delegate to
ExMCP.Protocol.ErrorCodes. Future header/capability/version codes are additive; the legacy three-way-32002collision is documented and unchanged. Test scaffolding out of production code —
HttpPlug's:test_modebranch is replaced by:sse_mode(:stream|:oneshot) resolved ininit/1, SSE conn duck-typing by a:conn_moduleinjection point, andHandlerServer's cancellation-state poking by a:cancellation_trackerbehaviour.- Dev tooling out of the published package — Mix tasks (
test.suite,test.tags,test.cleanup,check_skip_tags,mcp.sync_spec) andExMCP.SpecSyncmoved todev/, so they no longer ship on Hex or appear in dependents'mix help.ExMCP.Testing.*remains a supported public test kit. - CI — Integration, performance/stress/slow, and Node interop suites now actually run (they were excluded by default and included by no job); the three identical per-version compliance jobs are collapsed; coverage is broadened;
_buildis cached. - Tests —
Process.sleep-based synchronization reduced from 296 to 142 occurrences, with the remainder documented where the delay is the thing under test.
Removed
- Pre-2.0 dead validation paths — removed the uncalled method-version validator, its unreachable session-version batch clause, and the empty 2025-03-26 method-gating branch.
- Dead client state machine modules — Removed
ExMCP.Client.StateMachine,ExMCP.Client.Transitions, andExMCP.Client.States(unreferenced since the client adapter layer was removed before 1.0; their reconnect/backoff behavior now lives inExMCP.Client), along with their tests, the state-machine-only test transport helper, and the now-unusedgen_state_machinedependency. mox— Removed from dependencies; it was imported by a single test that used no mock.
Breaking Changes
Batch replies are consistently tagged —
ExMCP.Client.batch_request/3replied{:ok, results}on success but a bare list on disconnect. It now always returns{:ok, results} | {:error, reason}.ExMCPfacade returns tagged tuples — The convenience functions inExMCPreturned bare values on success and rescued every exception into "Client not responding". They now follow the{:ok, _} | {:error, _}convention used everywhere else and rescue narrowly.ExMCP.Client.connect/2returns errors instead of throwing — Invalid transport configuration now yields{:error, {:invalid_transport_config, reason}}.- JWT validation requires
exp— Tokens without a numericexpare rejected unlessrequire_exp: falseis passed. - Consent handler expiry values — Bare integers are still read as monotonic for compatibility, but implausible values (in the past, or more than 365 days out) now fail closed; prefer the explicit
{:ttl, _}/{:unix, _}/DateTimeforms. Handlers now receive:consent_ttlin seconds (it was mistakenly passed as milliseconds). ExMCP.Internal.Security.verify_hostname/3removed, andapply_security/2now includescacertsandcustomize_hostname_checkin its TLS options.ExMCP.Security.Validation.validate_localhost_binding/1rejects unrecognised binding shapes instead of passing them through ({0,0,0,0}no longer validates as localhost).ExMCP.Server.Transportno longer passes the ignoredtools:key and defaultscors_enabledtofalse, matchingHttpPlug.- Server transports may return
{:ok, state, response}— TheExMCP.Transportbehaviour'ssend_message/2type was widened to admit the 3-tuple the HTTP transport already returned (a widening; existing implementations still conform).
Deprecated
ExMCP.Server.ToolsAPI —ExMCP.Server.Tools,ExMCP.Server.Tools.Simplified, and companion modules (Builder,Helpers,Registry,ResponseNormalizer,ASTValidator) are deprecated, retained throughout 1.x, and planned for removal in 2.0.0.use ExMCP.Server.Toolsanduse ExMCP.Server.Tools.Simplifiedemit compile-time warnings. Migrate toExMCP.Server.Handler+ExMCP.Server.DSL(see the DSL guide and migration guide).- Image processing stubs — Compress/resize/thumbnail/encoding conversion helpers under
ExMCP.Content.Transformer,ExMCP.Content.Builders, and related Validation pipelines are deprecated for removal in 2.0.0. They were never required by MCP/ACP (which only define image content blocks: base64 + MIME). UseExMCP.Content.image/2for protocol content; do image processing in the application if needed. remove_metadata/ face-color analysis stubs — Same deprecation class; not protocol features.
Improved
- Protocol sync verification (2026-07-11) — Confirmed alignment with MCP 2025-11-25, the latest stable revision on that date, and ACP major v1. Official conformance re-run: 39/39 server and 226/226 client (
@modelcontextprotocol/conformance@0.1.16core suite). All-versions server/client suites green for 2025-11-25 and 2025-06-18. Refreshed localdocs/mcp-specs/2025-11-25viamix mcp.sync_spec --force. Documented that the then-unreleased MCP 2026-07-28 draft/RC was intentionally not implemented yet. - SSE bidirectional stability —
ExMCP.Server.SSESessionwaits for a live GET stream (and sole-session fallback), drops dead ETS registrations, cleans up on stream exit, and tolerates int/string request-id echoes. Conformance harness frees port 3099 between runs so elicitation/sampling tools no longer flake with “Server did not request elicitation/sampling”. - DSL compile errors —
ExMCP.Server.DSLnow raisesCompileErrors with file/line and actionable messages for missing handlers, duplicate tool/resource/prompt ids, wrong instructions per declaration kind, unknown instructions (with suggestions such asinputSchema→input_schema), non-literal values, and invalid param types. - DSL docs and examples — Documented param types, compile-time checks, and
ToolResultscoping in the DSL guide and quick start; examples note theToolResultalias. - 1.0 cleanup — Documented API stability (README /
ExMCPmoduledoc); fixed stale agent docs (CLAUDE.md) that still referenced removed client adapters; HexDocs module groups now feature core APIs and group deprecated Tools modules; content modules clarify protocol vs experimental helpers; client list-changed notifications emit telemetry instead of bare TODOs; removed emptylibdirectories and finished docs audit plan file; pruned dead.dialyzer_ignore.exsentries. - Content schema validation —
ExMCP.Content.SchemaValidator.validate_schema/2andExMCP.Content.Validation.validate_schema/2now validate with ExJsonSchema. - Unicode NFC —
Sanitizer.normalize_unicode/1and related Validation sanitization use:unicode.characters_to_nfc_binary/1.
Fixed
- ACP initialize lifecycle —
ExMCP.ACP.Clientaccepts a bounded:initialize_timeout, applies it to the full initialize handshake, and closes the receiver and transport when initialization fails so spawned stdio agents do not outlive a failed client start. - Bare
:arrayparam types —param :name, :arrayno longer silently maps to a string schema. Use{:array, :string}(or another item type). Examples (advanced_dsl_server,weather_service) were updated accordingly. - Empty-list param defaults —
default: []is preserved in generated JSON Schema properties. - Honest image transform behaviour — Deprecated resize no longer pretends to change width/height without processing pixels.
[1.0.0-rc.4] - 2026-07-08
Added
- HTTP handler initialization context —
ExMCP.HttpPlugnow accepts:handler_optsas a static term,Plug.Connfunction,Plug.Connplus decoded request function, or MFA tuple, and passes the resolved value into temporary handler GenServers. This lets Phoenix/Plug pipelines pass verified request context such as authenticated users, tenants, or signed request data into handlerinit/1.
Fixed
- MessageProcessor handler startup —
ExMCP.MessageProcessornow starts temporary handler GenServers with the configured handler options and consistently assignsserver_infobefore dispatching initialize requests. - MessageProcessor handler reply normalization —
ExMCP.MessageProcessor.MethodHandlersnow accepts map-shaped list results and GenServer-style three-tuple replies from handler processes, preserving compatibility with existing handlers while returning MCP tool errors as successfulisErrortool results. - Release docs — Updated active installation snippets and Phoenix request-context examples for the rc.4 release line.
[1.0.0-rc.3] - 2026-07-03
Fixed
- State machine test determinism — Removed an idle-timeout race from the state machine test transport so successful handshakes are not sampled after the fake transport has already disconnected.
[1.0.0-rc.2] - 2026-07-03
Changed
- ACP protocol updates — Added current ACP request cancellation support, propagated prompt
messageIdvalues through client/agent/protocol helpers, and refreshed adapter chunk handling for upstream-compatible message streaming. - Codex ACP upstream sync — Synced
ExMCP.ACP.Adapters.Codexwithagentclientprotocol/codex-acpv1.1.0 behavior, including upstream auth ids (api-key,chat-gpt, and opt-ingateway),read-only/agent/agent-full-accessmodes, app-serversession/deletethrough thread archive, additional workspace directories, upstream prompt content shapes, HTTP MCPhttp_headers, per-turn model/mode/reasoning/fast-mode application,/status, richer terminal/MCP metadata, usage updates, guardian review, image view, and fuzzy file search events. - Claude ACP upstream sync — Synced
ExMCP.ACP.Adapters.ClaudeSDKwith compatibleagentclientprotocol/claude-agent-acpv0.55.0 behavior, including Claude Agent SDK 0.3.198 launch metadata, initialize-aware terminal/gateway auth method advertisement,auth.logoutcapability, upstreammodeconfig option id, fast-mode and agent config controls when advertised by the SDK, MCP slash-command prompt rewriting, resource-link plus embedded-context prompt shapes, HTTP image prompt support, tool-call-before-permission ordering, Bash terminal metadata, result usage updates, and richer model/agent config option metadata. - Pi ACP upstream sync — Synced
ExMCP.ACP.Adapters.Piwith compatiblesvkozak/pi-acpv0.0.31 behavior, including upstream-compatible Pi RPC spawn arguments, per-sessionmodelandthought_levelconfig options, config-option sync updates after model/thinking changes, and Pi session listing that prefers latest message activity over later metadata-only timestamps. - MCP conformance tooling — Updated the draft conformance runner pin to the latest published alpha while keeping stable conformance runs on the published
latestrunner.
Fixed
- Claude final-result fallback —
ExMCP.ACP.Adapters.ClaudeSDKnow emits anagent_message_chunkfrom Claude SDKsession_resulttext when the SDK completes without streaming message chunks, so ACP clients that accumulate streamed text do not receive an empty answer.
Breaking Changes
- Codex ACP mode IDs —
ExMCP.ACP.Adapters.Codexnow only accepts upstream codex-acp-compatible mode IDs:read-only,agent, andagent-full-access. The olderautoandfull-accessids are no longer accepted, along with the priorsuggest,auto-edit, andfull-autoaliases.
[1.0.0-rc.1] - 2026-06-15
Changed
- BEAM-local MCP cleanup — Removed the public
ExMCP.Nativedirect dispatcher stack,ExMCP.Service, andExMCP.ServiceRegistry.*. BEAM-local MCP now usestransport: :beamwith a server pid, preserving MCP initialization, capabilities, request ids, and tool/resource/prompt semantics. - Examples and docs refresh — Updated README, guides, getting-started docs, and examples to use
ExMCP.Server.HandlerplusExMCP.Server.DSL; removed stale debug/test examples and the placeholder OAuth full-flow script.
Removed
- Optional Horde dependency — Removed the old optional Horde dependency and registry wiring because the Native dispatcher API no longer exists.
- Legacy Claude ACP adapter — Removed
ExMCP.ACP.Adapters.Claude; Claude Code integrations now useExMCP.ACP.Adapters.ClaudeSDK. - Unused public cleanup modules — Removed
ExMCP.ClientConfigEnhanced,ExMCP.ClientConfig.Macros, and the old distributedExMCP.Transport.Beam.*cluster namespace. The active BEAM-local MCP path remainstransport: :beam.
[1.0.0-rc.0] - 2026-06-08
Added
- Claude SDK ACP adapter — Added
ExMCP.ACP.Adapters.ClaudeSDK, a new Claude Code adapter that uses the SDK-compatible stream-json control protocol with permission bridging, partial tool-call lifecycle events, SDK interrupt cancellation, runtime model/mode/effort config, richer status updates, plan updates, and SDK launch environment support. - Claude SDK session store — Added pure Elixir helpers for Claude Code's SDK session store, including SDK-compatible project key derivation, JSONL metadata extraction, sidechain filtering, transcript reads, ACP
session/list, disk-backedsession/fork, and validatedsession/delete. - Codex ACP parity — Expanded
ExMCP.ACP.Adapters.Codexwith app-server-backedsession/list,session/load/resume replay,session/close, embedded-context prompts, ACP HTTP/stdio MCP descriptor forwarding, Codex auth methods, runtime model/reasoning config updates,session/set_model, Codex model catalog state frommodel/list, slash commands (/compact,/init,/review,/review-branch,/review-commit,/logout), and permission request bridging for Codex approval prompts. - Pi ACP parity — Reworked
ExMCP.ACP.Adapters.Piaround ACP-native session setup/load/list, terminal auth advertisement, model state andsession/set_model, thinking levels throughsession/set_mode, prompt queuing, image/resource/audio prompt normalization, built-in and markdown slash commands, structured tool updates, edit diffs, session replay, and a pure Elixir Pi session map helper. - ACP session fork support — Added unstable
session/forkprotocol/client/agent/adapter-bridge support, includingExMCP.ACP.Client.fork_session/4, native agenthandle_fork_session/3, adapterfork_session/2, and capability auto-advertisement. - Adapter bridge direct replies — Adapter implementations can now return direct JSON-RPC results, emit ACP messages before direct replies, emit messages from notifications, or reply while also writing to the subprocess, so adapted agents can own session IDs, replay history, and runtime config responses without bridge-synthesized placeholders.
- Adapter-managed bridge mode — Adapters can now return
:adapter_managedfromcommand/1, own persistent subprocess Ports directly, receive forwarded process messages throughhandle_adapter_message/2, and clean up resources throughshutdown/1. - Adapter auth method advertisement — ACP adapters can implement
auth_methods/1so the bridge initialize response reflects implemented authentication methods instead of always returning an empty list.
Changed
- ACP docs — Updated README and the ACP guide to recommend
ClaudeSDKfor new Claude Code integrations while keeping the legacyClaudestream-json adapter documented. - Claude SDK session capabilities — The new adapter now advertises live session setup/load/resume/fork/close plus disk-backed
session/listandsession/deletesupport for Claude Code's local SDK store.session/loadnow replays persisted JSONL transcript entries as ACP updates before returning. - Claude SDK MCP capabilities — Replaced legacy
"mcpServers" => trueadvertisement with official ACPmcpCapabilitiesand an ExMCP-only_meta.ex_mcp.mcpCapabilities.beamflag for BEAM-local MCP transport negotiation. - Claude SDK prompt lifecycle — Claude SDK prompts now queue while another prompt is active and drain in FIFO order after the active prompt result; queued prompts for a cancelled/closed/deleted session receive cancelled prompt responses.
- Codex adapter session safety — Codex prompt, cancel, mode, and config requests now require an explicit known
sessionIdinstead of falling back to the last active thread. Codex-specific session metadata now lives under_meta.ex_mcp.codexinstead of a non-spec rootmetadatafield. - Codex app-server event coverage — The Codex adapter now handles current app-server camelCase item variants (
commandExecution,fileChange,mcpToolCall,dynamicToolCall,webSearch,imageGeneration), reasoning summary deltas, plan/status/goal/compaction notifications, and richer replay of loaded turn history. Unsupported dynamic tool calls, request-user-input prompts, ChatGPT token refresh, and attestation requests are rejected explicitly instead of being treated as approval prompts. - Pi adapter ACP surface — Pi-specific control now uses ACP methods and slash commands instead of custom extension methods.
session/newandsession/loadwait for Pi state/model/command responses instead of returning bridge-generated placeholder sessions, andsession/listscans Pi JSONL sessions with optional cwd filtering. - Pi adapter remaining parity — Pi now runs through adapter-managed subprocesses, supports
session/resume,session/close, andsession/delete, returns paginatedsession/listresult maps withnextCursor, merges global/project Pi settings, filters skill commands from settings, emits startup/prelude context, adds/changelog, improves slash-command result messages, and maps prompt-time auth failures to ACP auth-required errors. - Pi destructive defaults — Pi
session/deleteremoves ExMCP session-map state by default and only deletes backing Pi JSONL files whendelete_session_files: trueis configured. Pi registry update notices are opt-in throughupdate_notice: trueorPI_ACP_UPDATE_NOTICE=true. - Adapter bridge set_model behavior —
AdapterBridgenow returns method-not-found when an adapter skipssession/set_model, instead of synthesizing a successful empty result for an unimplemented model change.
Fixed
- HexDocs package contents — Included the public guide docs in the Hex package and updated ExDoc module grouping so the 1.0 RC documentation builds against current public modules.
Breaking Changes
- MCP server DSL compatibility removal — Removed the legacy
use ExMCP.Servermacro,deftool/defresource/defpromptdeclarations, generated getter APIs (get_tools/0,get_resources/0,get_prompts/0),ExMCP.DSL.*modules, and the oldExMCP.Server.start_linkhelper. Server implementations should useExMCP.Server.Handlerdirectly, optionally combined withExMCP.Server.DSL, and start handler processes withMyServer.start_link/1,ExMCP.Server.HandlerServer.start_link/1, orExMCP.start_server/1. - MCP handler server rename — Renamed
ExMCP.Server.LegacytoExMCP.Server.HandlerServerand removed migration-onlyMessageProcessordispatcher/handler facades for direct/genserver legacy server modes. - Server notification spelling — Removed the legacy
notify_resource_updatedalias. UseExMCP.Server.notify_resource_update/2. - Refactor compatibility helpers removed — Removed unused
ExMCP.Server.RefactorHelpers, old DSL-orientedExMCP.ContentHelpers,ExMCP.ErrorHelpers, and transportsend/recvcompatibility aliases. UseExMCP.Protocol.ResponseBuilder,ExMCP.Content,ExMCP.Error, and transportsend_message/2/receive_message/1respectively. - Root protocol wrapper removed — Removed deprecated
ExMCP.Protocol. Low-level protocol construction/parsing remains internal; public protocol utility modules underExMCP.Protocol.*remain available. - Client adapter switching layer removed — Removed
ExMCP.Client.Adapter,ExMCP.Client.LegacyAdapter,ExMCP.Client.StateMachineAdapter,ExMCP.Client.Wrapper, andExMCP.Client.Configuration. UseExMCP.Clientas the supported public client API. - Handler init arguments are explicit —
ExMCP.Server.HandlerServerno longer passes leftover transport/system options tohandler.init/1. Usehandler_args: termwhen a handler needs initialization data. - Transport aliases removed — Removed public
transport: :sseandtransport: :native. Usetransport: :http, use_sse: truefor HTTP SSE streaming andtransport: :beamfor BEAM-local MCP transport. BEAM transport now carries MCP-shaped maps/lists as Elixir terms without JSON serialization. - ACP BEAM MCP metadata renamed — ExMCP ACP metadata now advertises BEAM-local MCP support only as
_meta.ex_mcp.mcpCapabilities.beam; the previousnativemetadata flag and:native_mcpbuilder option were removed. - ACP adapter session listing callback — Custom ACP adapters should implement
list_sessions(params, state)instead oflist_sessions(state). The bridge now forwards decodedsession/listparams directly to adapters and no longer carries the legacy arity. - Codex ACP mode IDs —
ExMCP.ACP.Adapters.Codexnow only accepts upstream codex-acp-compatible mode IDs:read-only,agent, andagent-full-access. The oldsuggest,auto-edit,auto,full-auto, andfull-accessaliases were removed. - Adapter authenticate behavior —
AdapterBridgeno longer returns{}forauthenticatewhen an adapter skips the request. Unimplemented auth now returns method-not-found or invalid-params, while adapters that write to their subprocess can produce the eventual auth response themselves. - Pi extension methods removed —
ExMCP.ACP.Adapters.Pino longer implements_ex_mcp.pi/*or legacypi/*control methods. Usesession/set_model,session/set_mode,session/load,session/list, or prompt slash commands such as/compact,/export,/session,/name,/steering, and/follow-up.
[0.12.0] - 2026-06-07
Added
- ACP functional cores — Added focused ACP modules for capabilities, lifecycle params, envelopes, metadata, map helpers, name/value normalization, and adapter event normalization. These keep protocol transformation logic pure and easier to test while preserving the existing facade APIs.
- ACP interoperability coverage — Expanded integration fixtures and tests for official ACP TypeScript libraries, including everything-style agent/client flows, prompt lifecycle events, auth/logout, permissions, session list/resume/close/delete, and rich content blocks.
Changed
- ACP stable spec alignment — Updated ACP protocol encoding/decoding, session lifecycle params, prompt/session updates, capability builders, MCP server config shapes, extension method names, and adapter output to match the current stable ACP schema.
- ACP adapter behavior — Claude, Codex, and Pi adapters now produce normalized stable update events for tool calls, tool call progress, usage, plan/config/mode/session info, and streaming message chunks. Deprecated Pi extension aliases are still accepted, but only
_ex_mcp.pi/*methods are advertised. - Claude adapter permission defaults — The Claude adapter no longer defaults to
--permission-mode bypassPermissions; it now inherits the Claude CLI default unless a permission mode is configured explicitly. Permission mode atoms now map to Claude CLI's real mode names. - HTTP Plug internals — Extracted request parsing, session resolution, CORS/origin checks, response shaping, and SSE handling into a smaller functional core so side effects stay at the Plug boundary.
Fixed
- Official ACP library compatibility — Fixed response result shapes, session id propagation, update discriminators, error responses, prompt capability handling, and adapter bridge request handling that prevented clean interop with official ACP SDK clients and agents.
- MCP HTTP/SSE edge cases — Hardened streamable HTTP and SSE parsing, session manager selection, response conversion, and structured output wrapping across conformance and integration paths.
- Client state-machine tests — Reworked timing-sensitive client tests to use telemetry-driven synchronization instead of sleeps.
Security
- MCP HTTP hardening — OAuth and HTTP guard paths now fail closed on invalid auth state, validate scopes consistently, preserve configured session managers, enforce origin/CORS decisions at the boundary, and keep request body handling bounded.
- ACP adapter hardening — Adapter process startup now avoids leaking configured API keys into child environments unless explicitly required, and adapter error responses are normalized before crossing the ACP boundary.
[0.11.0] - 2026-06-05
Added
- ACP Claude adapter —
sessionIdon prompt response result —ExMCP.ACP.Adapters.Claudenow includes"sessionId"in the prompt response result map whenstate.session_idis non-nil. The field sits alongside the existing optional"thinking"blocks and is omitted entirely when no session id is available, so downstream consumers can rely onMap.has_key?(result, "sessionId")as an absence signal. Backwards-compatible — callers that ignore it keep working unchanged. Useful for callers that need to correlate a prompt response with the underlying Claude SDK session, drive--resumethemselves, or attach the session id to audit / telemetry.
[0.10.1] - 2026-05-29
Fixed
- CI test determinism — Explicitly load target modules before API export assertions so tests do not depend on execution order.
[0.10.0] - 2026-05-29
Added
- Native ACP agents —
ExMCP.ACP.AgentandExMCP.ACP.Agent.Handlerlet Elixir applications implement the agent side of the Agent Client Protocol over the same transports as the ACP client. - ACP agent facade helpers —
ExMCP.ACP.start_agent/1,run_agent/1, and streaming helpers provide a symmetrical API for building controllers and agents. - ACP examples — Added an end-to-end native Elixir ACP echo agent and controller under
examples/acp. - ACP cross-SDK interop fixtures — Added TypeScript SDK agent/client fixtures plus ExMCP integration tests that cover both directions of ACP controller/agent interoperability.
- ACP everything-style interop coverage — Added broad ACP fixtures that exercise auth/logout, session lifecycle, prompt/cancel, mode/config updates, permission requests, filesystem requests, terminal requests, session updates, and rich content blocks.
- ACP
usage_updatehelpers — Added protocol, type, and agent helper APIs for emitting stable context-window usage updates.
Changed
- ACP documentation — Updated README, examples, and the ACP guide to cover both controller-side and agent-side protocol support.
- ACP capabilities — Updated ACP interop agents to use the official schema shape for session capability declarations.
Fixed
- ACP streamed prompt isolation —
ExMCP.ACP.Clientnow accumulates streamedagent_message_chunktext only while a matching prompt is pending, preventing out-of-band updates such assession/loadchunks from leaking into the next prompt result.
[0.9.2] - 2026-05-28
Added
- ACP Registry helpers —
ExMCP.ACP.Registrycan fetch, parse, search, and buildnpxcommands from the public ACP Registry. - ACP handler runner — Agent-originated requests and session update handlers now run outside the ACP client process, so slow permission, file, terminal, or update handlers cannot block streamed updates or prompt completion.
- MCP 2025-11-25 conformance coverage — Conformance scripts and tests cover the then-latest supported MCP revision and updated server behaviors.
Changed
- ACP stable spec alignment — Updated ACP method names, content/resource builders, permission responses, terminal delegation, config options, prompt capabilities, session capabilities, and adapter update shapes to match the current stable ACP v1 schema.
- Adapter update normalization — Claude, Codex, and Pi adapters now emit stable
agent_thought_chunk,tool_call, andtool_call_updateshapes for core streaming and tool lifecycle events. - Prompt text handling —
Client.prompt/4now folds streamedagent_message_chunktext into the returned result when agents stream the answer instead of returning inline text.
Fixed
- Permission cancellation —
Client.cancel/2now replies to pendingsession/request_permissionrequests with the requiredcancelledoutcome without waiting for a blocked handler. - MCP protocol edge cases — Updated method handling and version registry tests for newly covered MCP conformance cases.
[0.9.1] - 2026-04-11
Added
HttpPlug: cached body support viaconn.assigns[:raw_body]— Upstream plugs (e.g., signature-verification auth pipelines) can now pre-read the request body and stash it inconn.assigns[:raw_body]. The HTTP plug checks for a cached body before callingread_body/1, avoiding the empty-body issue that occurs when the underlying adapter has already been consumed by an upstream plug.Backwards compatible: callers that don't pre-read the body see no change in behavior. The new helper falls through to
read_body/1whenraw_bodyis absent.Use case: enables HTTP-authentication plugs that need to verify the body bytes (e.g., per-request request signing) before ExMCP processes the request, without forcing the auth plug to patch ExMCP, swap the conn adapter, or replace
ExMCP.HttpPlugentirely.
[0.9.0] - 2026-03-18
Added
- Pi ACP Adapter — Full adapter for the Pi coding agent (badlogic/pi-mono) with 25 RPC commands and 14 event types
- Text/thinking streaming, tool execution lifecycle, auto-compaction/retry events
- Extension UI request/response bridge for dialog flows
- Session persistence via
--sessionflag, session directory scanning forsession/list - Image support with data-url prefix stripping
- 6 config options routed to native Pi RPC (model, thinking_level, auto_compaction, auto_retry, steering_mode, follow_up_mode)
- ACP Spec Compliance — All stabilized ACP features now implemented
session/listmethod (stabilized March 9, 2026)- All 8 official session update types:
user_message_chunk,agent_message_chunk,tool_call_update,plan_update,available_commands_update,config_option_update,current_mode_update,session_info_update - Content blocks:
audio,resource_link,resource(in addition to existingtext,image) sessionCapabilitiesin agent capabilities- ACP error codes:
-32000(auth_required),-32002(resource_not_found) - Terminal request routing in Client (
terminal/*methods delegated to handler)
- Adapter Behaviour Extensions — 3 new optional callbacks
modes/0— declare supported operational modes (advertised in initialize response)config_options/0— declare supported config options (advertised in initialize response)list_sessions/1— return available sessions forsession/list
- AdapterBridge Enhancements
session/listhandler with adapter delegation or empty fallbacksession/set_modehandler with synthesized OK responsesession/set_config_optionhandler routed through adaptersauthenticatehandler with synthesized OK response (RFD draft scaffolding)- Initialize response includes modes, configOptions, sessionCapabilities from adapter callbacks
- Authentication Scaffolding — protocol authentication encoding,
Client.authenticate/3, andTypes.auth_required_code/0 - Plan Mode Builders —
Types.plan_entry/3,Types.plan_update/2for structured plan updates
Changed
- Claude Adapter — Zed-parity tool introspection
- Context-aware tool titles: "Read lib/app.ex (10-29)", "Search: defmodule"
- Structured metadata:
kind(read/write/execute/search/think),locations(file:line for jump-to-source),content(diff/terminal/text) - Tool calls now use spec-compliant
tool_call_update(was non-standardtool_call) - Tool results include
completed/failedstatus - Project-relative display paths when cwd is known
- Usage streaming notification emitted before final result
- System event and rate_limit_event forwarding as status notifications
- Richer stop reason classification (end_turn, max_tokens, tool_use, error)
- Declares
config_options(model, thinking_budget)
- Codex Adapter — Tool call lifecycle and enrichments
- Tool call notifications:
item/createdwithfunction_calltype - Tool completion:
item/completedfor function_call, function_call_output, patch types - Command execution lifecycle (started/outputDelta/completed)
- Web search events (started/completed)
- Image content in prompts
- Session resume via
session/load→thread/startwith threadId - Status notification on turn/completed
- Declares
modes(suggest, auto-edit, full-auto) andconfig_options(model)
- Tool call notifications:
- Pi Adapter — Enhanced tool result parsing matching pi-acp reference
- Content blocks, diff details, stdout/stderr/exitCode formatting
- Replaces simpler
extract_tool_contentwith fullextract_tool_result_text
MCP Protocol Conformance — 100% Client and Server
- Official MCP Conformance — 223/223 client checks, 39/39 server checks (0 failures, 0 warnings)
- Full OAuth 2.1 Authorization Code Flow with PKCE (
ExMCP.Authorization.FullOAuthFlow)- Protected Resource Metadata discovery (RFC 9728) with path-based and root fallback
- OIDC/OAuth AS metadata discovery with 4 URL patterns (RFC 8414)
- Dynamic Client Registration (RFC 7591)
- Local redirect server for authorization code callback
- Token endpoint auth method selection (client_secret_basic, client_secret_post, none)
- Client ID Metadata Document support (CIMD)
- Scope negotiation from WWW-Authenticate header and PRM scopes_supported
- Scope step-up on 403 insufficient_scope
- Resource mismatch validation (RFC 8707)
- HTTP Transport OAuth Integration
- Automatic 401/403 → OAuth discovery → token → retry
- Auth loop protection (prevents infinite retry)
- Unified FullOAuthFlow for both pre-existing credentials and browser auth
- SSE POST response parsing with retry field extraction
- SSE forced reconnection for pending tool results
- Last-Event-ID propagation on reconnection
- Elicitation Support
ExMCP.Client.ElicitationHandler— configurable auto-accept/decline- Capability-aware routing (method-not-found when not declared)
ExMCP.Testing.SchemaGenerator— generate test values from JSON Schema
- Server-Side SSE Sessions (
ExMCP.Server.SSESession)- Bidirectional SSE for server→client requests (elicitation, sampling)
- ETS-based pending request tracking
- GET SSE stream registration and event loop
- DNS Rebinding Protection (
ExMCP.Plugs.DnsRebinding) - MessageProcessor Fixes
deep_stringify_keysfor all handler response paths (atom→string keys)- Initialize response normalization for Handler path
- tools/call result wrapping in
{content: [...]}format logging/setLevelandcompletion/completehandlers- Default protocol version updated to
2025-11-25
- Test Infrastructure
scripts/test.sh— saves output on every runscripts/conformance.sh— runs official MCP conformance frameworkcapture_log: true— logs shown on test failures- Expected-failures baseline for CI
- HTTP Transport Improvements
- URL auto-splitting (extract endpoint from URL path)
:ssetransport alias → HTTP with use_sse: true (was broken)- SSE receive loop stability (waiting_for_session, not_supported_in_sync_mode)
- 405 handling for GET SSE (graceful fallback)
- SSE retry field parsing and timing buffer
Removed
- Misleading
supportedModesfrom Claude and Codex capabilities (removed features that weren't actually implemented)
[0.8.4] - 2026-03-10
Fixed
AdapterTransportreceiver loop now uses:infinitytimeout (was 30s) forAdapterBridge.receive_message/2. This prevents spuriousreceiver_exitederrors when CLI agents (Claude, Codex) take longer than 30 seconds to produce their first output line — common during complex reasoning or multi-turn tool use. The timeout is now configurable viareceive_timeout:in transport opts.
[0.8.3] - 2026-03-09
Added
- Claude adapter handles multi-turn tool use sequences (
assistant(thinking)→assistant(tool_use)→user(tool_result)→assistant(text)→result). Emitstool_callandtool_resultsession updates for observability.
[0.8.2] - 2026-03-09
Fixed
- BREAKING: Comprehensive ACP spec conformance audit — align all method names, field names, and message structures with the ACP specification
session/promptparams key is"prompt"(not"content") per specinitializerequest uses"clientCapabilities"(not"capabilities")initializeresponse reads"agentCapabilities"(not"capabilities")- Method names use snake_case:
session/set_mode,session/set_config_option,session/request_permission - File system methods:
fs/read_text_file/fs/write_text_file(notsession/fileRead/session/fileWrite) session/updatenotifications use nested"update"object with"sessionUpdate"discriminator (not flat"kind")- Text updates use
"sessionUpdate": "agent_message_chunk"with content block (not"kind": "text") - Permission options use
"optionId"field (not"id") - Permission response is flat
{"outcome": "selected", "optionId": "..."}(not wrapped) fs/write_text_fileresponse returnsnullresult (not empty map)- Image content blocks use
"mimeType"(not"mediaType") - Plan entries use
"content"/"priority"(not"id"/"title") - Capabilities restructured to match spec (
loadSession,promptCapabilities,mcp,fs,terminal)
[0.8.1] - 2026-03-09
Fixed
- ACP
session/promptmessage uses correct"content"param key instead of"prompt"to match the ACP specification
[0.8.0] - 2026-03-08
Added
- Agent Client Protocol (ACP) Support -- Full implementation of the Agent Client Protocol for controlling coding agents programmatically
ExMCP.ACPfacade module for quick client startupExMCP.ACP.ClientGenServer for managing ACP agent connections over stdioExMCP.ACP.Protocolfor ACP-specific JSON-RPC 2.0 message encoding (integer protocol versions, ACP method names)ExMCP.ACP.Typeswith type specifications and builder functions for ACP messagesExMCP.ACP.Client.Handlerbehaviour for handling session events (updates, permission requests, file access)ExMCP.ACP.Client.DefaultHandlerimplementation that auto-allows permissions
- ACP Adapter System for non-native agents
ExMCP.ACP.Adapterbehaviour for protocol translation between ACP and agent-native formatsExMCP.ACP.AdapterBridgeGenServer bridge managing adapted agent subprocessesExMCP.ACP.AdapterTransporttransport implementation delegating to the adapter bridgeExMCP.ACP.Adapters.Claude-- Adapter for Claude Code CLI (NDJSON stream-json protocol)ExMCP.ACP.Adapters.Codex-- Adapter for Codex CLI (app-server JSON-RPC protocol)
- ACP Session Management -- Create, resume, prompt, cancel, and configure sessions
session/new,session/load,session/prompt,session/cancelmethodssession/set_mode,session/set_config_optionfor runtime agent configuration- Streaming session updates via notifications
- Bidirectional communication for permission and file access requests
- ACP Documentation -- New ACP Guide with usage examples and adapter development instructions
[0.7.4] - 2026-02-14
Fixed
- Fixed compile warnings for users without Horde installed --
ExMCP.ServiceRegistry.Hordenow usesapply/3for allHorde.Registrycalls to avoid compile-time "module is not available" warnings - Removed 15 dead test files left over from DSL migration (eliminates ExUnit
test_load_filterswarning)
[0.7.3] - 2026-02-13
Added
- OAuth Client Credentials with JWT Authentication (
private_key_jwt) -- RFC 7523 Section 2.2 client assertions as an alternative to client secrets for machine-to-machine auth - Enterprise-Managed Authorization (ID-JAG) -- RFC 8693 token exchange + RFC 7523 JWT bearer grants for enterprise SSO flows
- JWT Infrastructure (
ExMCP.Authorization.JWT) -- General-purpose JWT module wrapping JOSE for key management, signing, verification, and claims validation - Client Assertion Module (
ExMCP.Authorization.ClientAssertion) -- Build and verify JWT client assertions for token endpoint authentication - Discovery Flow (
ExMCP.Authorization.DiscoveryFlow) -- Full 401-to-discovery-to-auth orchestrator supporting bothclient_secretandprivate_key_jwtmethods - Token Exchange (
ExMCP.Authorization.TokenExchange) -- RFC 8693 token exchange for swapping ID tokens for ID-JAG tokens - JWT Bearer Grant (
ExMCP.Authorization.JWTBearerAssertion) -- RFC 7523 Section 2.1 JWT bearer authorization grants - ID-JAG Creation and Validation (
ExMCP.Authorization.IdJag) -- Create and validate ID-JAG JWTs withtyp="oauth-id-jag+jwt" - ID-JAG Server Handler (
ExMCP.Authorization.IdJagHandler) -- Server-side processing of JWT bearer grants containing ID-JAG tokens - Enterprise Flow (
ExMCP.Authorization.EnterpriseFlow) -- Client-side enterprise SSO orchestrator (OIDC -> token exchange -> JWT bearer grant) - Extended
OAuthFlowwithclient_credentials_jwt_flow/1for private_key_jwt auth - Extended
HTTPClientmetadata parsing withtoken_endpoint_auth_methods_supported,token_endpoint_auth_signing_alg_values_supported,issuer,jwks_uri, andissued_token_type - Extended
Validatorwith JWT bearer and token exchange grant type validation - Extended
AuthorizationServerMetadatawith auth method metadata fields - Extended
TokenManagerwithauth_methodawareness (:client_secret,:private_key_jwt,:enterprise_idjag) - Added
{:jose, "~> 1.11"}dependency for JWT operations - Pluggable Service Registry (
ExMCP.ServiceRegistry) -- Registry abstraction withLocal(built-inRegistry, zero deps) andHordeadapters forExMCP.Native ExMCP.ServiceRegistry.Local-- Default adapter using Elixir's built-inRegistryfor single-node service discoveryExMCP.ServiceRegistry.Horde-- Distributed adapter wrappingHorde.Registryfor cross-node clusters (opt-in)
Changed
Hordeis now fully optional -- default service registry uses Elixir's built-inRegistrywith zero extra dependenciesExMCP.Nativeuses pluggable registry viaExMCP.ServiceRegistry.adapter()instead of hardcodedHorde.Registry- Application supervision tree starts the configured registry adapter's child specs instead of hardcoded Horde processes
Fixed
- All examples updated to use correct DSL syntax (
meta do+input_schema) -- previously used invalid syntax that would fail to compile - Removed unnecessary Horde references from examples and getting-started guides
- Updated all documentation to present DSL (
use ExMCP.Server) as the primary server API - User Guide rewritten to lead with DSL examples; low-level Handler API preserved as one reference section
[0.7.2] - 2026-02-12
Fixed
- Aligned Tools DSL
handle_call_toolwith Handler behaviour arity - Resolved CI failures in compliance test and dialyzer
- Made
ConsentCache.clear/0synchronous to fix test isolation race condition - Eliminated Elixir 1.19 type warnings in handler bridge
- Fixed
@before_compileordering for GenServer bridge in Elixir 1.19 - Injected GenServer bridge via
__using__macro for HttpPlug compatibility - Suppressed dialyzer pattern_match warnings in generated GenServer bridge at the source
- Fixed
@behaviourvsusein handler20250618 compliance test (Elixir 1.17 compat)
[0.7.0] - 2026-02-11
Added
- MCP Protocol Version 2025-11-25 Support - Added the then-latest protocol version with full spec compliance
- Streamable HTTP Spec Compliance - Client and server now fully comply with MCP Streamable HTTP spec:
- Server provides session ID (not client); first POST omits
Mcp-Session-Idheader Accept: application/json, text/event-streamheader sent on requests- SSE GET handled on same endpoint as POST (not
/sse) mcp-protocol-versionheader included in all responses- POST responses return 200 with JSON body even when SSE is enabled
- Server provides session ID (not client); first POST omits
- TypeScript MCP SDK Interop Tests - Verified interoperability with the official TypeScript MCP SDK
- Agent Simulation Integration Tests - Integration tests with MockLLM for testing agent workflows
- <code>mix mcp.sync_spec</code> Task - Automated task for syncing MCP protocol specifications
- Conformance Test Suites - Automated conformance tests for all 4 protocol versions (2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25)
- Client State Machine Adapter - Refactored client using GenStateMachine with:
- Formal state transitions with guards
- State-specific data structures
- Comprehensive telemetry events for observability
- Enhanced reconnection logic with exponential backoff
- Integration with
ExMCP.ProgressTracker
- Structured error types with
ExMCP.Errormodule - Comprehensive telemetry instrumentation:
[:ex_mcp, :request, :start/stop]events[:ex_mcp, :tool, :start/stop]events[:ex_mcp, :resource, :read, :start/stop]events[:ex_mcp, :prompt, :get, :start/stop]events
- Bidirectional communication for MCP server-to-client requests
- Comprehensive protocol version validation
Changed
- BREAKING: Refactored internal architecture of
ExMCP.Servermodule- Split monolithic 1,488-line module into focused components:
ExMCP.Protocol.ResponseBuilder- Response formattingExMCP.Protocol.RequestTracker- Request lifecycle managementExMCP.Protocol.RequestProcessor- Request routing and handlingExMCP.Server.Transport.Coordinator- Transport managementExMCP.DSL.CodeGenerator- DSL macro code generation
- Public API remains unchanged - 100% backward compatible
- Split monolithic 1,488-line module into focused components:
- Replaced deprecated
preferred_cli_envwithcli/0callback - Reduced cyclomatic complexity in
TestTransportandReliability.Supervisor
Fixed
- DSL type narrowing warnings for unreachable clauses (closes #3)
- Test isolation issues in session and property tests
- Conformance test alignment with MCP spec
- ETF deserialization security in BEAM transport
- Security guard robustness against malformed consent handlers
- Flaky security test race conditions
- Test infrastructure race conditions
- HTTP transport communication reliability
- Replaced unsafe
String.to_atomusage with safe alternatives (atom exhaustion prevention) - All compiler warnings in test files resolved
Removed
- 22 stale planning/internal docs from root directory
- 16 stale docs and 5 stale subdirectories from
docs/ - Non-existent
USER_GUIDE.mdandEXTENSIONS.mdfrom hex package file list
Security
- Prevented atom exhaustion attacks by using string keys instead of dynamic atom creation
- Enhanced ETF deserialization security in BEAM transport
[0.6.0] - 2025-06-26
🎉 Major Release: Production-Ready ExMCP
This release represents the completion of an 18-week comprehensive test remediation and enhancement project that transformed ExMCP from alpha software into a production-ready MCP implementation. 100% MCP protocol compliance achieved across all supported protocol versions.
🏆 18-Week Project Achievements
📊 Quantitative Results:
- 100% MCP Compliance: All 270/270 compliance tests passing across 3 protocol versions
- Complete Protocol Support: 2024-11-05, 2025-03-26, and 2025-06-18 MCP specifications
- High Performance: <10ms average latency, >100 ops/sec throughput, ~15μs native BEAM calls
- Comprehensive Testing: 8 test suites with 95%+ coverage and organized tagging strategy
- Security Implementation: OAuth 2.1, TLS/SSL, comprehensive audit logging
- Documentation: 80+ documentation files with complete guides and examples
🛡️ Enterprise-Grade Reliability:
- Circuit breaker pattern for automatic failure detection and recovery
- Configurable retry policies with exponential backoff
- Health monitoring and connection recovery
- Performance baselines with regression detection
- Comprehensive security audit with OAuth 2.1 compliance
⚡ Performance & Scalability:
- Native BEAM service dispatcher with zero serialization overhead
- Cross-node distributed service discovery via Horde.Registry
- Performance profiling infrastructure with baseline establishment
- Concurrent load testing and throughput optimization
- Memory efficiency optimization for production workloads
Added
Comprehensive Python MCP SDK Interoperability Examples
- Complete bidirectional integration between ExMCP (Elixir) and Python MCP SDK
- Elixir → Python Integration:
elixir_to_python_stdio.ex- Elixir clients connecting to Python subprocess serverselixir_to_python_http.ex- Elixir clients connecting to Python HTTP servers with load balancing and failover
- Python → Elixir Integration:
python_clients/elixir_client.py- Python clients connecting to Elixir servers via stdioelixir_servers_for_python.ex- Elixir servers with rich schemas designed for Python clients
- Python MCP Server Examples:
python_mcp_servers/calculator_server.py- Full stdio MCP server with history and statisticspython_mcp_servers/http_server.py- FastAPI-based HTTP MCP server with REST endpoints
- Hybrid Architecture Example:
hybrid_architecture.ex- Production-ready architecture combining Native Elixir (~15μs), Python stdio (~1-5ms), and Python HTTP (~5-20ms) services- ServiceRegistry for managing multi-language service types
- HybridOrchestrator with intelligent routing and automatic failover
- Performance-based service selection and load balancing
- Complete Documentation:
- Comprehensive setup instructions and prerequisites
- Performance comparisons across transport types
- Cross-language JSON-RPC compatibility examples
- Production deployment patterns and best practices
Native Service Dispatcher Migration
- Migrated 30+ example files from non-existent
:beamtransport to Native Service Dispatcher pattern - Updated examples to use
ExMCP.Servicemacro for automatic service registration - Enhanced
ExMCP.Nativecalls with zero serialization overhead for ultra-high performance - Fixed references to internal modules now in
ExMCP.Internal.*namespace - Updated all BEAM transport examples to use Horde.Registry for service discovery
- Migrated 30+ example files from non-existent
Comprehensive Test Tagging Strategy
- Implemented test tagging system based on ex_llm approach for efficient test execution
- Created <code>mix test.suite</code> task with predefined test suites: unit, compliance, integration, transport, security, performance, all, ci
- Created <code>mix test.tags</code> task to list all available tags and descriptions
- Added 100+ test files with appropriate module tags for categorization
- Default exclusions for fast development: integration, external, slow, performance tests excluded by default
- Test categories:
:unit,:integration,:compliance,:security,:performance,:transport, feature-specific tags - Transport-specific tags:
:beam,:http,:stdiowith requirement tags:requires_beam,:requires_http,:requires_stdio - Feature tags:
:progress,:roots,:resources,:prompts,:protocol,:cancellation,:batch,:logging - Development tags:
:slow,:wip,:skip,:manual_onlyfor test lifecycle management - Reduced default test run time from ~30s to ~5s while maintaining full test coverage
- Updated test tags from
:sseto:httpto align with MCP "Streamable HTTP transport" naming convention - Removed unused
ExMCP.Test.MockSSEServermodule and cleaned up references
Enhanced Compliance Test Organization
- Extracted MCP protocol compliance tests from implementation-specific test files
- Created 7 new compliance test files by extracting tests from non-compliance files:
cancellation_compliance_test.exs- Cancellation protocol validationversion_negotiation_compliance_test.exs- Version negotiation complianceroots_compliance_test.exs- Roots functionality protocol compliancesecurity_compliance_test.exs- MCP security requirements
- All 241 compliance tests now centralized in
test/ex_mcp/compliance/directory - Updated compliance test statistics: 218 passing, 0 failing, 23 skipped
- Created comprehensive documentation:
TAGGING_STRATEGY.md,TAGGING_IMPLEMENTATION.md,EXTRACTION_LOG.md
Configurable SSE Endpoint
- HTTP transport now supports custom endpoint configuration via
:endpointoption - Defaults to "/mcp/v1" for backward compatibility
- Handles trailing slashes and empty endpoints properly
- Example:
ExMCP.Client.start_link(transport: :http, url: "http://localhost", endpoint: "/custom/api")
- HTTP transport now supports custom endpoint configuration via
Progress Token and _meta Field Support
- Added
_metafield support to all MCP request methods in Protocol module - Extended Client API to accept
:metaoption for all methods - Progress tokens can now be passed via
meta: %{"progressToken" => token} - Backward compatibility maintained for
:progress_tokenoption incall_tool/4 - All protocol methods now support arbitrary metadata passthrough
- Server handlers receive _meta in tool arguments (for tools/call) or params (for other methods)
- Added
OAuth 2.1 Authorization Framework (MCP 2025-03-26 specification)
- Full OAuth 2.1 implementation with:
- Authorization Code Flow with mandatory PKCE (RFC 7636)
- Client Credentials Flow for service-to-service authentication
- Authorization Server Metadata Discovery (RFC 8414)
- Dynamic Client Registration (RFC 7591)
- Protected Resource Metadata Discovery (RFC 9728 draft)
- Token Management:
- Automatic token refresh with configurable window
- Token rotation for public clients
- Token validation and introspection support
- Secure token storage in GenServer state
- Security Features:
- PKCE S256 code challenge method required for all authorization code flows
- HTTPS enforcement for all OAuth endpoints (except localhost)
- No tokens in URLs - all tokens in headers
- Bearer token authentication for HTTP transports
- Integration:
ExMCP.Authorizationmodule for OAuth flowsExMCP.Authorization.TokenManagerfor automatic token lifecycleExMCP.Authorization.PKCEfor code challenge generation/verification- Transport-level OAuth support for HTTP streaming and WebSocket
- Comprehensive test coverage: 217+ passing OAuth tests
- Full OAuth 2.1 implementation with:
Production-Grade Reliability Framework
- Circuit Breaker Integration: Automatic failure detection and recovery across all transports
- Enhanced Retry Policies: Configurable retry strategies with exponential backoff for all MCP operations
- Health Monitoring: Real-time transport and connection health tracking
- Connection Recovery: Automatic reconnection with intelligent backoff strategies
- Error Recovery: Comprehensive error handling and graceful degradation
- Reliability Testing: 100+ integration tests for circuit breakers, retry policies, and health monitoring
Performance Infrastructure & Benchmarking
- Performance Profiling Utility: Comprehensive metrics collection including execution time, memory usage, GC statistics
- Baseline Establishment: Performance baselines stored for regression detection across all operations
- Benchmark Test Suites: 7 comprehensive test suites covering basic operations, payload scaling, concurrent load, throughput
- Performance Regression Detection: Automated comparison against established baselines
- Memory Efficiency Tracking: Detailed memory delta monitoring and optimization
- Throughput Optimization: Benchmarked >100 ops/sec for basic operations with concurrent client support
Comprehensive Testing Framework
- 8 Organized Test Suites: Unit, compliance, integration, transport, security, performance, CI, and comprehensive suites
- Advanced Test Tagging: Efficient test execution with 15+ tags for categorization and selection
- Cross-Transport Testing: Comprehensive compatibility tests across stdio, HTTP, SSE, and Native BEAM transports
- Integration Test Framework: End-to-end scenario validation with real component testing
- Process Cleanup Automation: Automated test environment cleanup for reliable test execution
- CI/CD Integration: Complete automated testing pipeline with quality gates
Cancellation Protocol Implementation (MCP specification compliance)
- Full support for
notifications/cancelledmessages - Client-side cancellation API:
ExMCP.Client.send_cancelled/3 - Request tracking:
ExMCP.Client.get_pending_requests/1 - Automatic cleanup of cancelled in-progress requests
- Validation that initialize request cannot be cancelled per spec
- Proper handling of race conditions and late cancellations
- Comprehensive test coverage with 12 passing tests
- Full support for
Logging Control Implementation (MCP specification compliance)
logging/setLevelrequest handler with RFC 5424 syslog levels- Full integration with Elixir's Logger system
ExMCP.Loggingmodule for centralized logging management- Automatic log level conversion between MCP and Elixir formats
- Structured logging via
notifications/message - Security features:
- Automatic sanitization of sensitive data (passwords, tokens, keys)
- Rate limiting support
- Configurable logger names
- Comprehensive test coverage with 33 passing tests
MCP Specification Compliance Updates
- Initialize request batch validation - prevents
initializefrom being part of JSON-RPC batch per spec - Audio content type support with
ExMCP.Contentmodule and examples - Completions capability declaration with
hasArgumentsandvaluesfields - Enhanced HTTP transport flexibility:
- Session management with
Mcp-Session-Idheader - Non-streaming mode for single JSON responses
- Configurable endpoint (defaults to
/mcp/v1) - Resumability support with Last-Event-ID
- Session management with
- Security requirements enforcement:
- Origin validation for DNS rebinding protection
- HTTPS enforcement for non-localhost deployments
- Localhost binding security checks
- Enhanced
SecureServermodule with all security features
- Initialize request batch validation - prevents
Fixed
🔧 18-Week Remediation Project Fixes
Phase 1: Critical Infrastructure (Weeks 1-4)
- Transport Configuration: Fixed transport selection and configuration inconsistencies
- Response Migration: Resolved ExMCP.Response struct vs map access patterns throughout codebase
- Error Protocol: Standardized isError/is_error field handling across all protocol versions
- Connection State: Fixed connection status tracking and state machine transitions
Phase 2: Protocol Compliance (Weeks 5-8)
- Protocol Methods: Implemented missing MCP protocol methods for 100% coverage
- Message Field Normalization: Fixed camelCase/snake_case field access inconsistencies
- Pagination Standardization: Resolved cursor handling and nextCursor field presence issues
- Resource Operations: Fixed resource read protocol compliance and resource/prompt operations
Phase 3: Reliability & Performance (Weeks 9-12)
- Transport Behaviors: Standardized transport implementations and error handling patterns
- Connection Validation: Implemented consistent connection validation across all transports
- Message Format: Standardized message format handling and protocol encoding/decoding
Phase 4: Advanced Features & Testing (Weeks 13-16)
- Cross-Transport Compatibility: Fixed DSL server integration and response format issues
- Performance Profiling: Resolved JSON serialization issues in performance metrics storage
- HTTP Transport Communication: Identified and documented HTTP/SSE client-server communication issues
- Integration Framework: Fixed test infrastructure and end-to-end scenario validation
Phase 5: Documentation & Validation (Weeks 17-18)
- Documentation Completeness: Updated 80+ documentation files for consistency and accuracy
- Security Validation: Confirmed OAuth 2.1 compliance and security audit requirements
- Final Validation: Verified 100% MCP compliance maintained across all protocol versions
Other Fixes
- All Credo code quality issues resolved (0 issues)
- Logger metadata warnings fixed with proper configuration
- Dialyzer type checking issues resolved across all modules
- Memory leaks and process cleanup issues in test environment
- Performance regression detection false positives
- Security audit logging configuration for production environments
[0.5.0] - 2025-05-28
Breaking Changes
- Removed
:ssetransport identifier - Use:httpinstead for Streamable HTTP transport - Renamed SSE references - All documentation and APIs now use "HTTP streaming" or "Streamable HTTP" terminology
Fixed
- Logging Notification Method Name - Changed from
notifications/logtonotifications/messageto match MCP specification exactly
Added
Current MCP Specification (2025-03-26) Features
- OAuth 2.1 Authorization Support
- Full OAuth 2.1 implementation with PKCE support
- Automatic token refresh before expiration
- TokenManager GenServer for token lifecycle management
- Authorization error handling for 401/403 responses
- Request interceptor for automatic header injection
- Integration with HTTP transport for seamless auth
- Example demonstrating OAuth-protected MCP servers
- Enhanced Streamable HTTP Transport
- Automatic reconnection with exponential backoff
- Built-in keep-alive mechanism (30-second heartbeat)
- Support for Last-Event-ID header for event resumption with HTTP streaming
- Improved connection stability and error recovery
Draft MCP Specification Features (Experimental)
- Structured Tool Output (Draft feature - not in MCP 2025-03-26)
- Tools can define
outputSchemain their schema - Tool results can include
structuredContentalongside regular content - Marked with "Draft feature" comments in code
- Tools can define
- Logging Level Control (Draft feature - not in MCP 2025-03-26)
- Added
logging/setLevelhandler implementation Client.set_log_level/3for adjusting server log verbosityhandle_set_log_level/2callback in server handlers
- Added
- Security Best Practices Implementation (Draft specification)
- Token validation with audience checking (prevents confused deputy)
- Client registration and accountability system
- Consent management for dynamic client registration
- Request audit trail maintenance
- Trust boundary enforcement
- SecureServer module with built-in security features
- Security supervisor for managing security components
Other Enhancements
- Lifecycle Management Improvements
- Improved BEAM transport server lifecycle (supports reconnections)
- Dynamic client capability building based on handler
- Protocol version validation and negotiation
- Client Roots Tests and Examples (MCP specification compliance)
- Comprehensive tests for client roots functionality
- Root demo showing client-server root exchange
- Server tools for requesting and analyzing client roots
- Default handler providing current directory as root
- Protocol compliance verification for roots/list requests
- Note: Client roots functionality already fully implemented
- Progress Notifications Tests and Examples (MCP specification compliance)
- Integration tests for progress tracking in long-running operations
- Progress demo server showing various progress patterns
- Support for string and integer progress tokens
- Progress updates with current/total values
- Note: Progress notifications already fully implemented
- Server Utilities Tests (MCP specification compliance)
- Comprehensive test coverage for pagination across all list operations
- Completion utility tests for prompt and resource references
- Logging utility verification with all severity levels
- Cursor-based pagination with proper error handling
- Note: All utilities (completion, logging, pagination) already fully implemented
- Tools Feature Tests (MCP specification compliance)
- Comprehensive test coverage for existing tools functionality
- Tests for tool discovery, invocation, and error handling
- Verification of isError flag support for tool execution errors
- Batch tool request testing
- Multiple content type support (text, image)
- Progress token support verification
- Note: Tools functionality including isError support already fully implemented
- Resources Feature Tests and Examples (MCP specification compliance)
- Comprehensive test coverage for existing resources functionality
- Example server demonstrating various resource types (text, JSON, binary)
- Support for resource subscriptions and update notifications
- Resource templates for dynamic URI patterns
- Pagination support for resource listing
- Multiple URI schemes (file://, config://, data://, db://)
- Note: Resources functionality was already fully implemented
- Prompts Feature Tests and Examples (MCP specification compliance)
- Comprehensive test coverage for existing prompts functionality
- Example server demonstrating various prompt patterns
- Support for parameterized prompts with required/optional arguments
- Pagination support for prompt listing
- Dynamic prompt list changes with notifications
- Note: Prompts functionality was already fully implemented
- Ping Utility Tests and Examples (MCP specification compliance)
- Comprehensive test coverage for existing ping functionality
- Health check pattern examples and best practices
- Bidirectional ping demonstration
- Connection monitoring and verification patterns
- Performance measurement examples
- Note: Ping functionality was already fully implemented
- Request Cancellation Support (MCP specification compliance)
- Complete implementation of
notifications/cancelledmethod - Client and server can cancel in-progress requests
- Automatic request tracking and resource cleanup
- Initialize request cannot be cancelled (as per spec)
- Graceful handling of unknown/completed requests
- Malformed cancellation notification validation
- Comprehensive test coverage and example implementation
- Complete implementation of
- OAuth 2.1 Authorization Support (MCP specification compliance)
- Complete OAuth 2.1 implementation with PKCE support
- Authorization code flow with mandatory PKCE for security
- Client credentials flow for application-to-application communication
- Server metadata discovery (RFC 8414)
- Dynamic client registration (RFC 7591)
- Token validation and introspection
- HTTPS enforcement with localhost development support
- Comprehensive test coverage for all authorization flows
- Enhanced protocol version negotiation (MCP specification compliance)
- Handlers now receive client's protocol version in params
- Servers can check client version and propose alternatives
- Comprehensive documentation and examples for version negotiation
- Full test coverage for various negotiation scenarios
Changed
- BREAKING: Renamed SSE transport to HTTP transport (MCP specification update)
ExMCP.Transport.SSEis nowExMCP.Transport.HTTP- Use
transport: :httpinstead oftransport: :sse - Transport identifier
:sseis now:http(:ssestill works for compatibility) - Updated documentation to reflect "Streamable HTTP" terminology from MCP spec 2025-03-26
- All tests and examples updated to use new naming
[0.4.0] - 2025-05-27
Added
- Tool execution error reporting with
isErrorflag (MCP specification compliance)- Handlers can return
{:ok, %{content: [...], isError: true}, state}for tool errors - Distinguishes between protocol errors and tool execution errors
- Full test coverage demonstrating proper error handling
- Handlers can return
- Pagination support for list methods (MCP specification compliance)
- Added cursor parameter to
list_tools,list_resources, andlist_prompts - Server handlers now return optional
nextCursorfor paginated results - Client API changed to accept options keyword list for cursor and timeout
- Full test coverage for pagination functionality
- Added cursor parameter to
- JSON-RPC batch request support (MCP specification compliance)
batch_request/3client method for sending multiple requests as a batch- Server automatically handles batch requests and returns batch responses
- Full integration tests demonstrating batch functionality
- Bi-directional communication support (server-to-client requests)
- New
ExMCP.Client.Handlerbehaviour for handling server requests - Server can ping clients with
ping/2 - Server can request client roots with
list_roots/2 - Server can request client to sample LLM with
create_message/3 - Client automatically advertises capabilities when handler is provided
- New
- Human-in-the-loop (HITL) interaction support
ExMCP.Approvalbehaviour for implementing approval flowsExMCP.Client.DefaultHandlerwith built-in approval supportExMCP.Approval.Consolefor terminal-based approval prompts- Approval required for LLM sampling requests and responses
- Support for approving, denying, or modifying requests/responses
- Full test coverage with approval and HITL integration tests
- WebSocket transport implementation (client-side only)
- Support for ws:// and wss:// protocols
- Automatic ping/pong frame handling
- Full integration with ExMCP transport system
- TLS/SSL support for secure connections
- Comprehensive security features across all transports
- New
ExMCP.Securitymodule for unified security configuration - Authentication support: Bearer tokens, API keys, Basic auth, custom headers, node cookies
- HTTP streaming transport: Origin validation, CORS headers, security headers
- WebSocket transport: Authentication headers, TLS configuration
- BEAM transport: Process-level authentication, node cookie support
- TLS/SSL configuration with certificate validation
- Mutual TLS support for HTTP streaming and WebSocket transports
- Comprehensive security documentation in docs/SECURITY.md
- New
- Native format support for BEAM transport
:jsonformat (default) maintains MCP compatibility:nativeformat for direct Elixir term passing between processes- Configurable via
:formatoption in connect/accept
- HTTP test server for streaming testing
- Implemented with Plug and Cowboy
- Supports Server-Sent Events connections and message endpoints
- Request tracking for test assertions
- Proper Server-Sent Events streaming with keep-alive
Changed
- BREAKING: Client list methods now take options keyword list instead of timeout
list_tools(client, timeout)→list_tools(client, opts \\ [])list_resources(client, timeout)→list_resources(client, opts \\ [])list_prompts(client, timeout)→list_prompts(client, opts \\ [])- Options include
:timeoutand:cursorfor pagination
- BREAKING: Server handler callbacks for list methods now include cursor parameter
handle_list_tools(state)→handle_list_tools(cursor, state)handle_list_resources(state)→handle_list_resources(cursor, state)handle_list_prompts(state)→handle_list_prompts(cursor, state)- All must return 4-tuple with optional
next_cursor
- SSE transport endpoint is now configurable (was hardcoded to /mcp/v1)
- BEAM transport now supports both JSON and native Elixir term formats
Fixed
- Dialyzer type errors in WebSocket and BEAM transports
- BEAM transport connection format to support security authentication
- Test compatibility issues with new security features
Documentation
- Added comprehensive security guide (docs/SECURITY.md)
- Added local copy of MCP specification (docs/mcp-llms-full.txt)
- Updated TASKS.md with detailed compliance status
[0.3.0] - 2025-05-26
Added
- Protocol version updated to "2025-03-26" (latest MCP specification)
- Roots capability for URI-based resource boundaries
list_roots/2client methodhandle_list_roots/1server callbacknotify_roots_changed/1for dynamic root updates
- Resource subscription support
subscribe_resource/3andunsubscribe_resource/3client methodshandle_subscribe_resource/2andhandle_unsubscribe_resource/2server callbacks
- Resource templates support
list_resource_templates/2client methodhandle_list_resource_templates/1server callback
- Enhanced protocol method support
ping/2for connection health checkscomplete/4for completion/autocomplete featuressend_cancelled/3for request cancellation notificationslog_message/4for structured logging
- Tool annotations (readOnlyHint, destructiveHint, idempotentHint, openWorldHint)
- Audio content support (
audio_contenttype) - Embedded resource support in content
- Pagination support with cursor/nextCursor
- RFC-5424 compliant logging levels
- Progress tokens now use
_progressTokenin_metaas per spec - Comprehensive USER_GUIDE.md documentation
- API_REFERENCE.md with complete module documentation
- Enhanced examples for all new features
Changed
- BREAKING: JSON field names now use camelCase to match official MCP schema
mime_type→mimeTypeprogress_token→_progressToken(in_meta)- All response fields follow camelCase convention
- BREAKING: ModelHint is now an object with optional
namefield (was array) - Type specifications completely rewritten to match official schema
- Updated all documentation to reflect protocol version 2025-03-26
- Enhanced capabilities type to include roots and other new features
Fixed
- Protocol compliance with official MCP schema
- Missing cancellation and logging notification handlers
- Type definitions for multimodal content
- Progress token parameter location (now in
_meta._progressToken)
[0.2.0] - 2025-05-26
Added
- Sampling/createMessage support for LLM integrations
- Change notifications (resources, tools, prompts)
- Progress notifications with token support
- Comprehensive BEAM transport examples
- Code quality tooling (Credo, Dialyzer, Sobelow, ExCoveralls)
- Git hooks for pre-commit and pre-push checks
- GitHub Actions CI/CD pipeline
Changed
- BREAKING: Simplified BEAM transport architecture to Native BEAM transport
- Removed complex TCP-based BEAM transport modules (
ExMCP.Transport.Beam.Server,Client, etc.) - Implemented
ExMCP.Transport.Nativefor direct process communication - Added Registry-based service discovery and registration
- Improved performance: ~15μs local calls vs previous TCP overhead
- Note: Requires migration from old TCP-based API to new service registration pattern
- Removed complex TCP-based BEAM transport modules (
Fixed
- BEAM transport now properly supports server-initiated notifications
- Documentation discrepancies between claimed and actual features
- Server handler callback specs for sampling support
[0.1.0] - 2025-05-25
Added
- Initial release of ExMCP
- Complete Model Context Protocol implementation
- Protocol encoder/decoder for JSON-RPC messages
- Client implementation with automatic reconnection
- Server implementation with handler behaviour
- stdio transport for process communication
- SSE (Server-Sent Events) transport for HTTP streaming
- BEAM transport for native Erlang/Elixir communication
- Tool discovery and execution
- Resource listing and reading
- Prompt management
- Server manager for multiple connections
- Server discovery (npm packages, local directories)
- Request/response correlation
- Concurrent request handling
- Error handling and validation
- Type specifications throughout
Features
- Full MCP specification compliance
- Multiple transport layer support (stdio, Streamable HTTP with optional SSE, BEAM)
- Both client and server implementations
- Extensible architecture
- Supervision tree integration