Jido.MCP

Hex.pmHex DocsCILicenseWebsiteEcosystemDiscord

jido_mcp integrates MCP servers into the Jido ecosystem using anubis_mcp directly.

Features

Installation

def deps do
  [
    {:jido_mcp, "~> 0.1"}
  ]
end

Endpoint Configuration

config :jido_mcp, :endpoints,
  github: %{
    transport: {:streamable_http, [base_url: "http://localhost:8080", mcp_path: "/mcp"]},
    client_info: %{name: "my_app", version: "1.0.0"},
    protocol_version: "2025-06-18",
    capabilities: %{},
    timeouts: %{request_ms: 30_000}
  },
  local_fs: %{
    transport: {:stdio, [command: "uvx", args: ["mcp-server-filesystem"]]},
    client_info: %{name: "my_app", version: "1.0.0"}
  }

Supported transports in v1:

For Streamable HTTP, :url or a :base_url containing a non-root path is normalized to Anubis' :base_url + :mcp_path option shape.

Endpoint config may also be loaded through an MFA callback:

config :jido_mcp, :endpoints, {MyApp.MCPConfig, :endpoints, []}

The callback must return a map/keyword endpoint declaration or {:ok, endpoints}.

Runtime endpoints can be registered after application start:

{:ok, endpoint} =
  Jido.MCP.Endpoint.new(:github, %{
    transport: {:streamable_http, [base_url: "http://localhost:8080", mcp_path: "/mcp"]},
    client_info: %{name: "my_app", version: "1.0.0"}
  })

{:ok, ^endpoint} = Jido.MCP.register_endpoint(endpoint)

Runtime registration is process-local, rejects duplicate endpoint ids, and starts the MCP client only when the endpoint is first used.

Runtime Endpoint Lifecycle

{:ok, endpoint} =
  Jido.MCP.Endpoint.new(:runtime_demo, %{
    transport: {:streamable_http, [base_url: "http://localhost:8080/mcp"]},
    client_info: %{name: "my_app"}
  })

{:ok, ^endpoint} = Jido.MCP.register_endpoint(endpoint)
{:ok, tools} = Jido.MCP.list_tools(:runtime_demo)

{:ok, _removed} = Jido.MCP.unregister_endpoint(:runtime_demo)

For config changes at runtime, unregister then register the updated endpoint.

Consume MCP APIs

{:ok, tools} = Jido.MCP.list_tools(:github)
{:ok, called} = Jido.MCP.call_tool(:github, "search_issues", %{"query" => "label:bug"})

{:ok, resources} = Jido.MCP.list_resources(:github)
{:ok, content} = Jido.MCP.read_resource(:github, "repo://owner/name/README")

{:ok, prompts} = Jido.MCP.list_prompts(:github)
{:ok, prompt} = Jido.MCP.get_prompt(:github, "release_notes", %{"version" => "1.2.0"})

All calls return normalized envelopes:

Jido Actions + Plugin

Actions

Plugin

defmodule MyApp.Agent do
  use Jido.Agent,
    name: "assistant",
    plugins: [
      {Jido.MCP.Plugins.MCP,
        %{
          default_endpoint: :github,
          allowed_endpoints: [:github, :local_fs]
          # or allowed_endpoints: :all
        }}
    ]
end

allowed_endpoints defaults to [] (deny-all) when omitted. Set it to :all to allow all currently configured/runtime-registered endpoints.

Signal routes:

To update the plugin default endpoint at runtime, emit mcp.endpoint.default.set with %{endpoint_id: "github"} (or nil/omitted to clear).

Jido.AI Sync

Jido.MCP.JidoAI.Actions.SyncToolsToAgent discovers remote tools and creates proxy Jido.Action modules, then registers them on a running Jido.AI.Agent.

Jido.MCP.JidoAI.Actions.UnsyncToolsFromAgent removes previously synced proxies.

Tool sync uses deterministic MCP readiness: endpoint calls wait on Anubis.Client.await_ready/2 before executing.

Plugin route support:

Host Runtime Orchestration (Signals)

Host projects are expected to orchestrate runtime endpoint lifecycle and tool sync explicitly using plugin signals.

Recommended signal sequences:

Example signal payloads:

# mcp.endpoint.register
%{
  endpoint_id: "runtime_demo",
  endpoint: %{
    transport: {:streamable_http, [base_url: "http://localhost:8080/mcp"]},
    client_info: %{name: "my_app"}
  }
}

# mcp.ai.sync_tools
%{
  endpoint_id: "runtime_demo",
  agent_server: :my_ai_agent,
  replace_existing: true,
  prefix: "mcp_"
}

# mcp.ai.unsync_tools
%{endpoint_id: "runtime_demo", agent_server: :my_ai_agent}

# mcp.endpoint.unregister
%{endpoint_id: "runtime_demo"}

Expose Jido As MCP Server

Server module

defmodule MyApp.MCPServer do
  use Jido.MCP.Server,
    name: "my-app",
    version: "1.0.0",
    publish: %{
      tools: [MyApp.Actions.SearchIssues],
      resources: [MyApp.MCP.Resources.ReleaseNotes],
      prompts: [MyApp.MCP.Prompts.CodeReview]
    }
end

Publication is explicit allowlist only.

Supervision

children =
  Jido.MCP.Server.server_children(MyApp.MCPServer,
    transport: :streamable_http
  )

Router (streamable HTTP)

forward "/mcp", Anubis.Server.Transport.StreamableHTTP.Plug,
  Jido.MCP.Server.plug_init_opts(MyApp.MCPServer)

Resource and Prompt Behaviours

Implement those behaviours for items listed in publish.resources and publish.prompts.

Testing

mix test