WardrikMCP (wardrik_mcp v0.1.0-rc.1)

Copy Markdown

Gives WardrikCore agents the tools of any MCP server.

Start it with the servers it connects to:

children = [
  {WardrikMCP,
   name: MyApp.MCP,
   servers: [
     github: [
       transport: {:stdio, command: "github-mcp-server", args: ["stdio"]},
       only: ["search_issues", "create_issue"]
     ],
     linear: [
       transport:
         {:streamable_http,
          url: "https://mcp.linear.app/mcp",
          headers: %{"authorization" => "Bearer " <> System.fetch_env!("LINEAR_TOKEN")}}
     ]
   ]}
]

Then give an agent their tools as a WardrikCore.Capability:

WardrikCore.start_agent(
  agent_id: "triage",
  model: "anthropic:claude-haiku-4-5",
  capabilities: [{WardrikMCP, name: MyApp.MCP, servers: [:github, :linear]}]
)

The model sees each tool as github__search_issues, with the server's own description and JSON Schema. See WardrikMCP.Server for every server option.

Use it when the tools an agent needs already exist as an MCP server.

Connections

Each server connects on its own. A server that's down offers no tools and doesn't block the others. It's retried with backoff, up to a minute between tries. When a server says its tools changed, they're listed again.

Annotations

Each tool carries the server's hints as WardrikCore.Tool annotations: read_only, destructive, idempotent and open_world. A hint the server doesn't give isn't set.

  • A read-only tool is also idempotent.
  • A tool is gated unless it's read-only. The server's gated option overrides this.
  • Answers are labelled "outside" unless the server is trusted.

Errors

A call that fails transiently raises WardrikMCP.Unavailable. This covers a dropped connection, a timeout or a server error. The tool runner then retries read-only and idempotent tools. See WardrikCore.Retry.

An error the tool itself returns is passed to the model as the call's error.

Telemetry

[:wardrik_mcp, :tools] is emitted each time a server's tools change.

  • Measurements: :count, the number of tools the server offers. 0 while it's down.
  • Metadata: :mcp, the WardrikMCP name, and :server, the server's name.

Summary

Functions

Adds the server name at runtime.

Returns a specification to start this module under a supervisor.

Re-reads the servers in the store now, picking up those added or removed on other nodes.

Removes the server name and closes every connection to it.

Returns the names of all servers, sorted.

Starts WardrikMCP and connects to each of its servers.

Returns the tools of the named servers.

Functions

add(mcp, name, opts)

@spec add(atom(), String.t(), keyword()) :: :ok | {:error, String.t()}

Adds the server name at runtime.

Takes the same options as a server in :servers, but only as data, not functions. With a :store, every node reaches it.

:ok =
  WardrikMCP.add(MyApp.MCP, "linear",
    transport: {:streamable_http, url: "https://mcp.linear.app/mcp"},
    credential_name: "linear"
  )

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

reconcile(mcp)

@spec reconcile(atom()) :: :ok

Re-reads the servers in the store now, picking up those added or removed on other nodes.

remove(mcp, name)

@spec remove(atom(), String.t()) :: :ok | {:error, :not_found | String.t()}

Removes the server name and closes every connection to it.

Returns {:error, :not_found} if there's no such server, and {:error, reason} for a server given in :servers.

servers(mcp)

@spec servers(atom()) :: [String.t()]

Returns the names of all servers, sorted.

start_link(opts)

@spec start_link(keyword()) :: Supervisor.on_start()

Starts WardrikMCP and connects to each of its servers.

WardrikMCP.start_link(
  name: MyApp.MCP,
  servers: [github: [transport: {:stdio, command: "github-mcp-server", args: ["stdio"]}]],
  store: store,
  check: &MyApp.Egress.public/1
)

Options

  • :name - required. The name agents and functions reach it by.
  • :servers - servers to start with, as a keyword list of name and options. See WardrikMCP.Server. [] unless set.
  • :store - a WardrikStore for servers added at runtime. Without it, add/3 only adds a server on this node.
  • :reconcile - milliseconds between re-reads of the store, or :infinity. 60_000 unless set.
  • :check - a function called on each URL of a server added at runtime. It returns :ok or {:error, reason}. It's called when the server is added and each time a connection starts. With a check, runtime servers must use HTTP; commands are refused. Use one when users supply the URLs.

tools(context, opts)

Returns the tools of the named servers.

Options

  • :name - required. The WardrikMCP name.
  • :servers - required. A server name or a list of them, as :github or ["github", "linear"].