Tama.Dex.MCP (tama_dex v0.1.0)

Copy Markdown View Source

The search and execute MCP tools, as definitions and handlers.

tama-dex runs no MCP server and doesn't depend on one. A host's server advertises two tools built from search_tool/0 and execute_tool/0, and calls handle_search/3 and handle_execute/4 from them. Each definition is a map with name, title, description, input_schema and output_schema (JSON Schema 2020-12 maps with string keys) and annotations (MCP tool hints).

Callers

A caller is any map or struct whose scopes field holds the caller's verified scope list, such as TamaMCP.Context. The handlers check every operation against those scopes, so a host's own scope on the search or execute tool is only an outer gate, never per-operation authorization. handle_execute/4 passes the whole caller to the executor as call.context.

Responses

Handlers return JSON-safe maps with string keys, matching the tool's output schema, for the host to send as structured content:

  • search: %{"results" => results, "truncated" => boolean}.
  • execute: %{"cassette", "operation_id", "source", "result"}; or, for the executor's own valid error, %{"cassette", "operation_id", "source", "operation_error", "dispatched" => true}.
  • Either tool, on failure: %{"error" => %{"kind", "dispatched", "message"}}, with "details" when the kind has any. See Tama.Dex.Error for the kinds execute returns. Arguments that don't match the tool's input schema give "invalid_arguments" before any lookup.

An error with "dispatched" => true means the operation may have taken effect: reconcile its state rather than retrying.

A TamaMCP host

TamaMCP.Tool takes literal schemas, so a host builds its two tool modules with a macro that injects the definitions at compile time:

defmodule MyHost.DexTool do
  defmacro __using__(opts) do
    tool = apply(Tama.Dex.MCP, Keyword.fetch!(opts, :tool), [])

    quote do
      use TamaMCP.Tool,
        scopes: unquote(Keyword.fetch!(opts, :scopes)),
        title: unquote(tool.title),
        description: unquote(tool.description),
        annotations: unquote(Map.to_list(tool.annotations))

      raw_input_schema(unquote(Macro.escape(tool.input_schema)))
      raw_output_schema(unquote(Macro.escape(tool.output_schema)))
    end
  end

  def respond(structured) do
    failed? = Map.has_key?(structured, "error") or Map.has_key?(structured, "operation_error")
    build = if failed?, do: &TamaMCP.Response.tool_error/1, else: &TamaMCP.Response.success/1

    {:ok,
     build.(
       structured_content: structured,
       content: [TamaMCP.Response.text(Jason.encode!(structured))]
     )}
  end
end

defmodule MyHost.Tools.Search do
  use MyHost.DexTool, tool: :search_tool, scopes: ["ops.search"]

  @impl TamaMCP.Tool
  def call(arguments, context) do
    MyHost.Dex
    |> Tama.Dex.MCP.handle_search(arguments, context)
    |> MyHost.DexTool.respond()
  end
end

defmodule MyHost.Tools.Execute do
  use MyHost.DexTool, tool: :execute_tool, scopes: ["ops.execute"]

  @impl TamaMCP.Tool
  def call(arguments, context) do
    MyHost.Dex
    |> Tama.Dex.MCP.handle_execute(arguments, context, MyHost.executors())
    |> MyHost.DexTool.respond()
  end
end

The server registers them as tool MyHost.Tools.Search, name: "search" and tool MyHost.Tools.Execute, name: "execute".

Summary

Functions

The execute tool definition.

Handles an execute tool call: runs Tama.Dex.execute/5 with the caller's scopes, the host's executors (see Tama.Dex.Executor) and the caller as call.context.

Handles a search tool call: searches the operations the caller's scopes allow, using the tool's arguments.

The search tool definition.

Types

caller()

@type caller() :: %{:scopes => [String.t()], optional(atom()) => term()}

definition()

@type definition() :: %{
  name: String.t(),
  title: String.t(),
  description: String.t(),
  input_schema: map(),
  output_schema: map(),
  annotations: %{required(atom()) => boolean()}
}

Functions

execute_tool()

@spec execute_tool() :: definition()

The execute tool definition.

handle_execute(index, arguments, caller, executors)

@spec handle_execute(Tama.Dex.Index.t(), term(), caller(), map()) :: map()

Handles an execute tool call: runs Tama.Dex.execute/5 with the caller's scopes, the host's executors (see Tama.Dex.Executor) and the caller as call.context.

Returns the result, the executor's own valid error with "dispatched" => true, or an "error" object. Raises ArgumentError when caller has no scopes list or executors isn't a map.

handle_search(index, arguments, caller)

@spec handle_search(Tama.Dex.Index.t(), term(), caller()) :: map()

Handles a search tool call: searches the operations the caller's scopes allow, using the tool's arguments.

Returns %{"results" => results, "truncated" => boolean}, with the result fields of Tama.Dex.search/3, or an "error" object. Raises ArgumentError when caller has no scopes list.

search_tool()

@spec search_tool() :: definition()

The search tool definition.