Summary
The tool macro in ClaudeCode.MCP.Server generates nested modules that lack a working description/0 callback, causing Anubis.Server.Component.get_description/1 to return "" instead of the description text.
Reproduction
MIX_ENV=test mix run -e '
IO.inspect(function_exported?(ClaudeCode.TestTools.Add, :description, 0))
# => false
IO.inspect(Anubis.Server.Component.get_description(ClaudeCode.TestTools.Add))
# => "" (expected: "Add two numbers")
'
Root Cause
In lib/claude_code/mcp/server.ex, the tool macro's quote block (lines 177-196) sets @moduledoc before use Anubis.Server.Component:
quote do
defmodule Module.concat(__MODULE__, unquote(module_name)) do
@moduledoc unquote(description_text) # line 179: set @moduledoc
use Anubis.Server.Component, type: :tool # line 180: __using__ captures @moduledoc
@impl true
def description, do: unquote(description_text) # line 188: define callback
end
end
Two issues:
-
@moduledoc capture timing: Anubis's __using__ macro defines def __description__, do: @moduledoc (component.ex line 51). This snapshots @moduledoc at the point use expands, but the @moduledoc assignment at line 179 may not have taken effect yet in the compilation pipeline.
-
description/0 not exported: Despite @impl true + def description at line 188, function_exported?(Add, :description, 0) returns false. The description/0 callback is declared as @optional_callbacks in Anubis.Server.Component.Tool (line 201), so no default is generated. The def description in the macro should provide the implementation, but it's not surviving compilation.
Impact
Anubis.Server.Component.get_description/1 returns "" for all tool modules
- Test
"have description from @moduledoc" in server_test.exs:58 fails
- This appears as a flaky CI failure because the test asserts on different modules each run (
Add one run, Greet the next), but the underlying issue is deterministic — all modules are affected
Observed CI Failures
Suggested Fix
Swap the ordering so use comes before @moduledoc, or set description purely through the description/0 callback without relying on @moduledoc:
# Option A: move use before @moduledoc
defmodule Module.concat(__MODULE__, unquote(module_name)) do
use Anubis.Server.Component, type: :tool
@moduledoc unquote(description_text)
# ...
end
# Option B: don't use @moduledoc at all, rely solely on description/0
defmodule Module.concat(__MODULE__, unquote(module_name)) do
use Anubis.Server.Component, type: :tool
@impl true
def description, do: unquote(description_text)
# ...
end
Related
Summary
The
toolmacro inClaudeCode.MCP.Servergenerates nested modules that lack a workingdescription/0callback, causingAnubis.Server.Component.get_description/1to return""instead of the description text.Reproduction
Root Cause
In
lib/claude_code/mcp/server.ex, thetoolmacro'squoteblock (lines 177-196) sets@moduledocbeforeuse Anubis.Server.Component:Two issues:
@moduledoccapture timing: Anubis's__using__macro definesdef __description__, do: @moduledoc(component.ex line 51). This snapshots@moduledocat the pointuseexpands, but the@moduledocassignment at line 179 may not have taken effect yet in the compilation pipeline.description/0not exported: Despite@impl true+def descriptionat line 188,function_exported?(Add, :description, 0)returnsfalse. Thedescription/0callback is declared as@optional_callbacksinAnubis.Server.Component.Tool(line 201), so no default is generated. Thedef descriptionin the macro should provide the implementation, but it's not surviving compilation.Impact
Anubis.Server.Component.get_description/1returns""for all tool modules"have description from @moduledoc"inserver_test.exs:58failsAddone run,Greetthe next), but the underlying issue is deterministic — all modules are affectedObserved CI Failures
get_description(Add) == ""(expected"Add two numbers")get_description(Greet) == ""(expected"Greet a user")Suggested Fix
Swap the ordering so
usecomes before@moduledoc, or set description purely through thedescription/0callback without relying on@moduledoc:Related
tool/3→tool/2DSL change was introduced