# `Snodo.Router`
[🔗](https://github.com/joshrotenberg/snodo/blob/v0.2.0/lib/snodo/router.ex#L1)

An immutable registry and synchronous protocol-neutral dispatcher.

The router owns no process, connection, session, or application state.

`dispatch/5` accepts the immutable execution options the server holds:
`:schema_validator` and the optional `:authorization` policy. `Snodo.Server`
supplies both from `Snodo.Server.Runtime`; calling the router directly with
neither runs neither.

# `operation`

```elixir
@type operation() ::
  :tools_list
  | {:tools_call, String.t()}
  | :resources_list
  | :resource_templates_list
  | {:resource_read, String.t()}
  | :prompts_list
  | {:prompt_get, String.t()}
  | :completion_complete
  | term()
```

# `t`

```elixir
@type t() :: %Snodo.Router{
  prompts: %{optional(String.t()) =&gt; module()},
  resource_names: %{optional(String.t()) =&gt; module()},
  resource_templates: %{optional(String.t()) =&gt; module()},
  resources: %{optional(String.t()) =&gt; module()},
  tools: %{optional(String.t()) =&gt; module()}
}
```

# `completion_capable?`

```elixir
@spec completion_capable?(t()) :: boolean()
```

Returns whether any registered prompt or resource template supports completion.

# `dispatch`

```elixir
@spec dispatch(t(), operation(), map(), Snodo.Context.t(), keyword()) ::
  {:ok, Snodo.Result.t()} | {:error, Snodo.Error.t()}
```

Runs one operation against the registered components.

Operations and the `params` each reads:

  * `:tools_list`, `:prompts_list`, `:resources_list`, and
    `:resource_templates_list` - the whole sorted catalog, less any
    component the authorization policy refuses in the `:discovery` phase.
    `params` is ignored. Pagination and cache hints are applied later by
    `Snodo.Server`.
  * `{:tools_call, name}` - calls the tool with `params["arguments"]`
    (default `%{}`).
  * `{:resource_read, uri}` - reads the direct resource or the one
    template that matches `uri`. Template variables are merged into
    `params` before `c:Snodo.Resource.read/2` runs.
  * `{:prompt_get, name}` - renders the prompt with `params["arguments"]`,
    a map of strings to strings.
  * `:completion_complete` - completes a prompt or resource template
    argument from the `completion/complete` params.

Any other operation returns a -32601 error.

Returns `{:ok, %Snodo.Result{}}` or `{:error, %Snodo.Error{}}`:

  * An unknown name or URI, an `"arguments"` value of the wrong shape, or a
    missing required prompt argument is a -32602 error.
  * A missing required tool argument, tool arguments the schema validator
    rejects, and a tool that returns `{:error, reason}` without an
    `Snodo.Error` produce `{:ok, result}` with a `Snodo.Result.error/2`
    result.
  * An `Snodo.Error` returned by a component, or by the authorization
    policy in the `:invocation` phase, is returned unchanged.
  * A component that raises, exits, or returns an invalid value is a
    -32603 error.

Options:

  * `:schema_validator` - the `Snodo.Schema.Validator` module that checks
    tool arguments and structured output. Defaults to
    `Snodo.Schema.Validator.Passthrough`.
  * `:authorization` - a `Snodo.Authorization` policy module,
    `{module, options}`, or `nil`, as `Snodo.Server.Runtime.new/1`
    accepts. Defaults to `nil`, which allows everything. Any other value
    raises `ArgumentError`.

# `list_prompts`

```elixir
@spec list_prompts(t()) :: [Snodo.Prompt.Definition.t()]
```

Returns the definition of every registered prompt, sorted by name.

No authorization policy is applied.

# `list_resource_templates`

```elixir
@spec list_resource_templates(t()) :: [Snodo.Resource.Definition.t()]
```

Returns the definition of every resource template, sorted by URI template.

No authorization policy is applied.

# `list_resources`

```elixir
@spec list_resources(t()) :: [Snodo.Resource.Definition.t()]
```

Returns the definition of every direct resource, sorted by URI.

No authorization policy is applied.

# `list_tools`

```elixir
@spec list_tools(t()) :: [Snodo.Tool.Definition.t()]
```

Returns the definition of every registered tool, sorted by name.

No authorization policy is applied.

# `new`

```elixir
@spec new() :: t()
```

Returns an empty router.

# `register_prompt`

```elixir
@spec register_prompt(t(), module()) :: t()
```

Registers a prompt module under the name in its definition.

Registering the same module again returns the router unchanged. Raises
`ArgumentError` when the module is not a valid `Snodo.Prompt` or another
module already registered the name.

# `register_resource`

```elixir
@spec register_resource(t(), module()) :: t()
```

Registers a resource module.

A definition with a `:uri` registers a direct resource under that URI. A
definition with a `:uri_template` registers a resource template, which
answers any URI its `c:Snodo.Resource.matches?/1` accepts. Registering the
same module again returns the router unchanged.

Raises `ArgumentError` when the module is not a valid `Snodo.Resource`, or
when its name, URI, or URI template is already registered by another
module. A direct URI that an existing template matches, and a template that
matches an existing direct URI, are also refused.

# `register_tool`

```elixir
@spec register_tool(t(), module()) :: t()
```

Registers a tool module under the name its `c:Snodo.Tool.name/0` returns.

The module must implement `Snodo.Tool` with a valid definition. Registering
the same module again returns the router unchanged.

Raises `ArgumentError` when the module cannot be loaded, does not export the
`Snodo.Tool` callbacks, returns an invalid definition, or uses a name that
another module already registered.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
