# `AttestoMCP.Server.Test`
[🔗](https://github.com/XukuLLC/attesto_mcp_server/blob/v2.8.0/lib/attesto_mcp_server/test.ex#L1)

Helpers for exercising a registered catalog in host test suites.

Each helper builds a JSON-RPC request, encodes and decodes it as JSON,
dispatches it through the supervised server, and returns the complete
JSON-RPC response map. Requests take the same operation-scope,
definition-scope, definition `authorize`, schema, handler, cache-hint, and
final wire-output path as transport dispatch. Handlers are never called
directly.

    response =
      AttestoMCP.Server.Test.call_tool(
        MyApp.MCP,
        "get_item",
        %{"id" => "item-7"},
        principal: principal,
        scopes: ["items.read"],
        host_context: %{account_id: "acct-1"}
      )

    assert %{"result" => %{"structuredContent" => %{"id" => "item-7"}}} = response

The same request path is available for the other primitives:

    AttestoMCP.Server.Test.read_resource(MyApp.MCP, "file:///docs/item-7")
    AttestoMCP.Server.Test.get_prompt(MyApp.MCP, "summarize", %{"topic" => "release"})
    AttestoMCP.Server.Test.complete(MyApp.MCP, %{"type" => "ref/prompt", "name" => "summarize"},
      %{"name" => "topic", "value" => "rel"})
    AttestoMCP.Server.Test.discover(MyApp.MCP)
    AttestoMCP.Server.Test.list_tools(MyApp.MCP, cursor: next_cursor)

`request/4` sends any other method through the same builder.

## Boundaries

The helpers start after transport authentication. `:principal`, `:tenant`,
`:scopes`, and `:host_context` represent an already authenticated request.
They do not verify a token, DPoP proof, mTLS certificate, HTTP header, or
Plug parser order, and they do not establish HTTP header/body mirroring,
legacy session negotiation, stdio framing, or disconnect behaviour. Keep
separate Plug and stdio tests for those boundaries.

## Options

Every helper accepts:

  * `:principal` (default `"test-principal"`), `:tenant`, `:scopes`
    (default `[]`), and `:host_context`: the authenticated handler context.
    These are never copied into protocol parameters.
  * `:request_id`: a stable response ID.
  * `:protocol_version`: the request revision, default `"2026-07-28"`.
  * `:client_capabilities`: the modern request's declared capabilities.
  * `:meta`: application metadata for `params["_meta"]`, which handlers see
    as `context.request_meta`. The helper generates the protocol version and
    client capability keys; supplying either in `:meta` raises.
  * `:request_state` and `:input_responses`: modern multi-round retry fields
    for `call_tool/4`, `read_resource/3`, `get_prompt/4`, and `request/4`.
  * `:timeout`: a positive request timeout in milliseconds, still capped by
    the server's `:max_request_timeout`.

List helpers also accept `:cursor`, and `complete/4` accepts
`:completion_context` (the request's `context` member).

Protocol failures, including denied definitions, invalid schemas, missing
prompt arguments, invalid completion references, invalid cursors, handler
failures, and invalid outputs, are returned as the server's `"error"` or
`"result"` members. Malformed helper options and values that cannot be
represented as JSON raise `ArgumentError` because they are test setup
errors.

# `option`

```elixir
@type option() ::
  {:principal, term()}
  | {:tenant, term()}
  | {:scopes, [String.t()]}
  | {:host_context, map()}
  | {:request_id, String.t() | integer()}
  | {:protocol_version, String.t()}
  | {:client_capabilities, map()}
  | {:meta, map()}
  | {:request_state, String.t()}
  | {:input_responses, map()}
  | {:cursor, String.t()}
  | {:completion_context, map()}
  | {:timeout, pos_integer()}
```

# `call_tool`

```elixir
@spec call_tool(AttestoMCP.Server.API.server(), String.t(), map(), [option()]) ::
  map()
```

Calls one registered tool and returns its complete JSON-RPC response map.

The default protocol revision is `2026-07-28`, the default principal is
`"test-principal"`, and the default scope list is empty. Use `:request_id`
when a stable response ID is useful in an assertion. `:client_capabilities`
supplies the modern request's declared capabilities, including capabilities
needed by a tool that returns an interactive request.

# `complete`

```elixir
@spec complete(AttestoMCP.Server.API.server(), map(), map(), [option()]) :: map()
```

Requests completions for a prompt or resource-template reference.

`ref` is the wire reference, for example
`%{"type" => "ref/prompt", "name" => "summarize"}`, and `argument` is
`%{"name" => name, "value" => partial_value}`.

# `discover`

```elixir
@spec discover(AttestoMCP.Server.API.server(), [option()]) :: map()
```

Sends modern `server/discover`.

# `get_prompt`

```elixir
@spec get_prompt(AttestoMCP.Server.API.server(), String.t(), map(), [option()]) ::
  map()
```

Retrieves a prompt with string arguments.

# `list_prompts`

```elixir
@spec list_prompts(AttestoMCP.Server.API.server(), [option()]) :: map()
```

Lists one page of prompts.

# `list_resource_templates`

```elixir
@spec list_resource_templates(AttestoMCP.Server.API.server(), [option()]) :: map()
```

Lists one page of resource templates.

# `list_resources`

```elixir
@spec list_resources(AttestoMCP.Server.API.server(), [option()]) :: map()
```

Lists one page of static resources.

# `list_tools`

```elixir
@spec list_tools(AttestoMCP.Server.API.server(), [option()]) :: map()
```

Lists one page of tools. Pass `:cursor` for a continuation.

# `read_resource`

```elixir
@spec read_resource(AttestoMCP.Server.API.server(), String.t(), [option()]) :: map()
```

Reads a static or templated resource through the production resolver.

# `request`

```elixir
@spec request(AttestoMCP.Server.API.server(), String.t(), map(), [option()]) :: map()
```

Sends any request method through the shared builder.

`params` must not contain `_meta`, `requestState`, or `inputResponses`; use
the `:meta`, `:request_state`, and `:input_responses` options so the helper
can keep protocol fields separate from application metadata.

---

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