- Fix
Plug.Conn.AlreadySentErrorwhen a second SSE GET arrives for an existing session. The conflict response (409 -32000) is now returned cleanly without attempting to write streaming headers on the sent conn.
- Defend from potential elicitation replication lag
- Track Elicitations to ensure duplicate requests are not sent
- Fix invalid response when client request an invalid resource_uri
- Elicitation requests can use the POSTs connection. This should fix hung-up elicitations.
- Phoenix.Tracker can take some time to replicate, so add retries when session metadata is not available
- Improve cross-nodes tests
- Add additional Logging when dispatching requests to unalive PIDs
- Catch exits due to calling unalive PIDs (thanks @davydog187)
- Fix Phantom.Tracker
- Fixup dialyzer specs
- Providing nil to binary response content (eg, image, audio) will now raise instead of encoding
<<>>.
-
Add
Phantom.Stdioadapter for local-only clients (e.g. Claude Desktop). Add{Phantom.Stdio, router: MyApp.MCP.Router}to your supervision tree. SeePhantom.Stdiofor more details. -
Add
Phantom.Iconsupport for server info, tools, and prompts per MCP 2025-11-25 specification. Icons can be set at the router level withuse Phantom.Router, icons: [...]or per-tool/prompt. -
Server now declares support for MCP spec
2025-11-25. -
Elicitation support is fully implemented.
Phantom.Session.elicit/3now blocks until the client responds (with configurable timeout) and works across both HTTP and stdio transports. SeePhantom.Elicit. -
Phantom.Trackernow works withoutphoenix_pubsubfor stdio transport, falling back to process dictionary for session metadata. -
Fixed bugs with rendering embedded_resources
-
New tool DSL with
doblock to provide input schemas using an Ecto.Schema-like syntax. For example, before you had to manually write the JSONSchema input schema:tool :validated_echo_tool, description: "Echo with validation", input_schema: %{ required: ~w[message], properties: %{ message: %{type: "string", description: "Foo bar"}, count: %{type: "integer", description: "Foo bar"}, tags: %{type: "array", items: %{type: :string}, description: "Foo bar"} } }
But now you can declare it with a
doblock:tool :validated_echo_tool, description: "Echo with validation" do field :message, :string, required: true, description: "Foo bar" field :count, :integer, default: 1, description: "Foo bar" field :tags, {:array, :string}, description: "Foo bar" end
The
doblock also supports nested maps, custom validators, and all JSON Schema types. The old map-basedinput_schemasyntax continues to work. SeePhantom.Tool.JSONSchemafor more info.
-
Breaking When using
Phantom.Plug, pass theconnto the router connect callback instead of a map with params and headers keys. Upgrade and make this adjustment in your connect callback:# Before def connect(session, context) do %{params: params, headers: headers} = context # ... end # After def connect(session, conn) do %{query_params: params, req_headers: headers} = conn # ... end
- Fixup Cache key mismatch
- Fixup updating state in async returns
- Fixup running without
phoenix_pubsub
- Fix error message referring to wrong arity.
- Allow nil origin when Plug options is set to
origins: :all. - Better error handling when
Phantom.Trackeris not in the supervision tree. Phantom.MCP will now emit a Logger warning when Phantom.Tracker can be used, but is not in the supervision tree. - Fix terminate bug introduced in 0.3.1
- Add
[:phantom, :plug, :request, :terminate]telemetry event. - Improve docs
- Move logging functions from
Phantom.SessionintoPhantom.ClientLogger. - Rename
Phantom.Trackerfunctions to be clearer and more straightforward. - Consolidate distributed logic into
Phantom.Trackersuch as PubSub topics. - Add ability to add tools, prompts, resources in runtime easily. You can call
Phantom.Cache.add_tool(router_module, tool_spec). The spec can be built withPhantom.Tool.build/1, the function takes a very similar shape to the corresponding macro fromPhantom.MCP.Router. This will also trigger notifications to clients of tool or prompt list updates. - Handle paginatin for 100+ tools and prompts.
- Change
connect/2callback to receive request headers and query params from the Plug adapter. The signature is now%{headers: list({header, value}), params: map()}where before it was justlist({header, value}). Phantom.Tool.build,Phantom.Prompt.buildandPhantom.ResourceTemplate.buildnow do more and thePhantom.Routermacros do less. This is so runtime can have a consistent experience with compiled declarations. For example, you mayPhantom.ResourceTemplate.build(...)with the same arguments as you would with the router macros, and then callPhantom.Cache.add_resource_template(...)and have the same affect as using theresource ...macro in aPhantom.Routerrouter.- Fixed building tool annontations.
- Fixed resource subscription response and implemented unsubscribe method.
- Improve documentation
- Fix the
initializerequest status code and headers. In 0.2.2 it worked with mcp-inspector but not with Claude Desktop or Zed. Now it works with all.
- Fix the
initializerequest. It should have kept the SSE stream open. - Fix bugs
- Fix default
list_resources/2callback and default implementation.
Phantom MCP released!