feat: add _meta support to Tool struct and JSON encoder - #108
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WalkthroughThis pull request introduces optional metadata capability to tool components throughout the Anubis framework. The implementation adds a new Sequence DiagramsequenceDiagram
participant Dev as Developer
participant Macro as Component Macro
participant Server as Server.ex
participant Tool as Tool Struct
participant JSON as JSON Encoder
Dev->>Macro: Define component with meta: %{...}
Macro->>Macro: Generate meta/0 function
Server->>Macro: mod.meta() if exported?
Macro-->>Server: Returns meta map
Server->>Tool: Pass meta to Tool struct
Tool->>Tool: Store meta field
Tool->>JSON: Serialize to JSON
JSON-->>JSON: Include _meta field if meta present
JSON-->>Dev: Return encoded JSON with metadata
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~18 minutes Suggested reviewers
Review Notes 😎Overview: Clean addition of optional metadata to tools, mirroring the existing annotations pattern nicely. The changes are incremental and well-scoped across the framework layers. Strengths:
Observations (P1–P2):
Minor: The test support file adds several new tool modules—nice breadth for future tests. No issues there. 🚀 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs). Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/anubis/server/frame.ex (1)
125-131:⚠️ Potential issue | 🟡 MinorP2: Missing
:metaintool_opttypespec. 📝The
register_tool/3implementation at line 154 usesopts[:meta], but thetool_opttype definition doesn't include it. This creates a documentation gap and may cause Dialyzer warnings if strict typing is enabled.🔧 Proposed fix
`@spec` register_tool(t(), String.t(), list(tool_opt)) :: t() when tool_opt: {:description, String.t() | nil} | {:input_schema, map() | nil} | {:output_schema, map() | nil} | {:title, String.t() | nil} | {:annotations, map() | nil} + | {:meta, map() | nil}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@lib/anubis/server/frame.ex` around lines 125 - 131, The typespec for tool options (tool_opt) is missing the :meta option used in register_tool/3; update the tool_opt union to include {:meta, map() | nil} so the `@spec` register_tool(t(), String.t(), list(tool_opt)) :: t() accurately reflects runtime usage (refer to register_tool/3 and opts[:meta] in the function body) and prevent Dialyzer/documentation mismatches.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@lib/anubis/server/frame.ex`:
- Around line 125-131: The typespec for tool options (tool_opt) is missing the
:meta option used in register_tool/3; update the tool_opt union to include
{:meta, map() | nil} so the `@spec` register_tool(t(), String.t(), list(tool_opt))
:: t() accurately reflects runtime usage (refer to register_tool/3 and
opts[:meta] in the function body) and prevent Dialyzer/documentation mismatches.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c79a9b04-f65a-415b-8ac7-0e837f859107
📒 Files selected for processing (6)
lib/anubis/server.exlib/anubis/server/component.exlib/anubis/server/component/tool.exlib/anubis/server/frame.extest/anubis/server/component/tool_meta_test.exstest/support/test_tools.ex
|
does that field addition need to have a MCP version guardrail? |
|
Not sure. I haven't dealt with MCP protocol versions. I do know that MCP apps is an official MCP extension: https://modelcontextprotocol.io/extensions/client-matrix EDIT: My understanding is that client that support he MCP apps will have to broadcast that they do. Clients that don't support it ignore the extra field and are unaffected. See the "Negotiation" section of the docs page: https://modelcontextprotocol.io/extensions/overview#negotiation |
|
it seems ok to proceed |
…b.com:zoedsoupe/anubis-mcp into refactor/phase-5-remove-dead-code-update-docs * 'refactor/phase-5-remove-dead-code-update-docs' of github.com:zoedsoupe/anubis-mcp: Fix misleading session store warning when disabled (#107) feat: add _meta support to Tool struct and JSON encoder (#108) Fix memory leak by preventing stale SSE unregister dropping active heandlers (#109) deps:(deps-dev): bump credo from 1.7.15 to 1.7.17 (#105) deps:(deps): bump telemetry from 1.3.0 to 1.4.1 (#106) deps:(deps-dev): bump styler from 1.10.1 to 1.11.0 (#101)
|
Excellent, thank you. I have had this in prod via my fork since opening the PR and it has been working great. To render a custom UI within the MCP client is fun |
🚀 Want to release this? --- ## [1.0.0](v0.17.1...v1.0.0) (2026-03-16) ### ⚠ BREAKING CHANGES * remove client base module and client macro ([#110](#110)) * **phase-3:** server re-implementation and simplification ([#96](#96)) ### Features * add _meta support to Tool struct and JSON encoder ([#108](#108)) ([6ac49d1](6ac49d1)) ### Bug Fixes * **phase-5:** remove dead code and update docs ([#104](#104)) ([eea86af](eea86af)) * regression for input/output server schema ([85f8ebb](85f8ebb)) * remove client base module and client macro ([#110](#110)) ([1f9f13c](1f9f13c)) * server examples and sse server transport ([944bafb](944bafb)) * session serializion errors ([#112](#112)) ([cb8c0e3](cb8c0e3)), closes [#60](#60) * Start SSE keepalive when first handler is registered ([#83](#83)) ([c3c01e9](c3c01e9)) * stdio server transport working ([#111](#111)) ([b331281](b331281)) ### Code Refactoring * **phase-3:** server re-implementation and simplification ([#96](#96)) ([badb0f0](badb0f0)) * **phase-4:** client extraction of handlers ([#100](#100)) ([08b98c0](08b98c0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
## Problem I have an Elixir app that provides an MCP server and I wanted to utilize the new MCP Apps extension (https://modelcontextprotocol.io/extensions/apps/overview). To do this, I needed support for adding the _meta field to tool declarations so clients know which visual resource to render alongside a tool's output. ## Solution I added a meta field to the Tool struct and wired it through the JSON encoder so _meta appears in the serialized MCP tool object. Tools can now declare `meta: %{"ui" => %{"resourceUri" => "ui://my-app/dashboard"}}` via `use Anubis.Server.Component`. ## Rationale This is the minimal change needed to support the MCP spec's _meta field — I only touched the Tool struct and its serialization path. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Release Notes * **New Features** * Tool components now support optional metadata that can be provided during tool definition and is automatically included in exported tool information and JSON responses. * **Tests** * Added comprehensive tests validating metadata functionality, including JSON encoding and callback optionality. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
🚀 Want to release this? --- ## [1.0.0](v0.17.1...v1.0.0) (2026-03-16) ### ⚠ BREAKING CHANGES * remove client base module and client macro ([#110](#110)) * **phase-3:** server re-implementation and simplification ([#96](#96)) ### Features * add _meta support to Tool struct and JSON encoder ([#108](#108)) ([3b26a1c](3b26a1c)) ### Bug Fixes * **phase-5:** remove dead code and update docs ([#104](#104)) ([2a33dfc](2a33dfc)) * regression for input/output server schema ([fbf138b](fbf138b)) * remove client base module and client macro ([#110](#110)) ([a8ec690](a8ec690)) * server examples and sse server transport ([f4d3097](f4d3097)) * session serializion errors ([#112](#112)) ([8416cef](8416cef)), closes [#60](#60) * Start SSE keepalive when first handler is registered ([#83](#83)) ([67d93bb](67d93bb)) * stdio server transport working ([#111](#111)) ([b3d42a8](b3d42a8)) ### Code Refactoring * **phase-3:** server re-implementation and simplification ([#96](#96)) ([14c4e4f](14c4e4f)) * **phase-4:** client extraction of handlers ([#100](#100)) ([dc0ea85](dc0ea85)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
Problem
I have an Elixir app that provides an MCP server and I wanted to utilize the new MCP Apps extension (https://modelcontextprotocol.io/extensions/apps/overview). To do this, I needed support for adding the _meta field to tool declarations so clients know which visual resource to render alongside a tool's output.
Solution
I added a meta field to the Tool struct and wired it through the JSON encoder so _meta appears in the serialized MCP tool object. Tools can now declare
meta: %{"ui" => %{"resourceUri" => "ui://my-app/dashboard"}}viause Anubis.Server.Component.Rationale
This is the minimal change needed to support the MCP spec's _meta field — I only touched the Tool struct and its serialization path.
Summary by CodeRabbit
Release Notes
New Features
Tests