# Annotated message trace

Canonical URL: https://vercel-mcp-reference.vercel.app/internals/message-trace/
Markdown: https://vercel-mcp-reference.vercel.app/internals/message-trace.md
Audience: engineer, architect. MCP spec version: 2026-07-28. Last reviewed: 2026-08-26. Status: stable.

**TL;DR:** The [internals overview](https://vercel-mcp-reference.vercel.app/internals/overview/) describes the stateless per-request model in prose; this page shows the **actual [JSON-RPC](https://vercel-mcp-reference.vercel.app/glossary/#json-rpc) frames**, in order, for one complete exchange over the [Streamable HTTP transport](https://vercel-mcp-reference.vercel.app/glossary/#streamable-http-transport) under the 2026-07-28 revision: optional [discovery](https://vercel-mcp-reference.vercel.app/glossary/#discovery), a list, a [tool](https://vercel-mcp-reference.vercel.app/glossary/#tool) call, and the `input_required` retry loop, each annotated field by field and header by header. There is no `initialize` frame, no session id, and no shutdown: every request stands alone.

> **A normative trace, checked against the deployable stack.** The frames below follow the published 2026-07-28 spec pages (Streamable HTTP, Versioning, the base-protocol overview, and MRTR) and are the shape a client sending modern headers gets back from a deployed example. Wire status: the pinned stack (`mcp-handler` 2.1.1 on `@modelcontextprotocol/server` 2.0.0) serves the 2026-07-28 contract natively over Streamable HTTP and falls back to stateless 2025-11-25 Streamable HTTP for legacy clients; the SDK `Client` defaults to that legacy handshake unless you opt in to modern version negotiation, so the examples' in-memory test suites exercise only the legacy path. Which of the two shapes you capture therefore depends on the client, not the server; [What the deployed stack emits](#what-the-deployed-stack-emits) shows both, and the cache-hint values in frames 2 and 4 are illustrative (see the note there).

The subject is the repo's `examples/minimal-server` (in the repository) (one `echo` tool) deployed as a [Vercel Function](https://vercel-mcp-reference.vercel.app/glossary/#vercel-function), so every client frame below is the body of an HTTP POST to the MCP endpoint at `/api/mcp`, and every server frame comes back in an HTTP response. Unlike a stdio trace, the HTTP layer is load-bearing here: three required headers mirror the body so the edge can route without parsing it, and status codes carry half the error semantics.

## How to read a frame

Every MCP message is JSON-RPC 2.0 and is one of three shapes:

- **Request**: has an `id` *and* a `method` (and `params`, which always includes `_meta`). Expects a matching response.
- **Response**: has the same `id` and either a `result` or an `error`. No `method`. Every `result` carries a required `resultType`: `"complete"` or `"input_required"`.
- **Notification**: has a `method` but **no `id`**, so there is no response. Fire-and-forget.

`→` is client to server; `←` is server to client. Correlate a request with its response by `id`, never by arrival order.

On Streamable HTTP, one more rule matters: **every client frame is its own HTTP POST** to the single MCP endpoint. There is no persistent pipe and no session stitching the POSTs together; what makes them one "conversation" is only that the same client sent them.

## The HTTP layer

The headers that carry the protocol (per the 2026-07-28 Streamable HTTP spec):

| Header | Direction | Rule |
|---|---|---|
| `Accept: application/json, text/event-stream` | client → server | The client **MUST** list both content types on every POST. |
| `Content-Type: application/json` | client → server | The POST body is a single JSON-RPC request or notification (never a response). |
| `MCP-Protocol-Version: 2026-07-28` | client → server | **Required on every POST**, and it **MUST** match `io.modelcontextprotocol/protocolVersion` in the body's `_meta`. Mismatch or absence gets `400` with `HeaderMismatch` (`-32020`). |
| `Mcp-Method` | client → server | **Required on all requests.** Mirrors the JSON-RPC `method` so intermediaries can route without body inspection. |
| `Mcp-Name` | client → server | **Required on `tools/call`, `resources/read`, `prompts/get`.** Mirrors `params.name` or `params.uri`; non-ASCII values use the `=?base64?...?=` sentinel encoding. |
| `Mcp-Param-{Name}` | client → server | Present when a tool schema property carries an `x-mcp-header` annotation; mirrors that argument value. |
| `Origin` | client → server | The server **MUST** validate it; if present and invalid, it **MUST** respond `403 Forbidden` (the DNS-rebinding defense). |

There is deliberately no session header in this table. `Mcp-Session-Id` was removed in 2026-07-28; a modern server ignores it if a legacy client sends one.

Status codes to expect: `200` (a response body follows, as one JSON object or an SSE stream scoped to this request), `202` (notification accepted, empty body), `400` (header validation failed `-32020`, required `_meta` fields missing `-32602`, unsupported protocol version `-32022`, or missing client capability `-32021`; the JSON-RPC error body tells you which), `403` (Origin rejected), `404` with a `-32601` error body (method not implemented; a bare 404 with no modern error body means you are probably talking to a legacy server or the wrong URL), `405` (GET or DELETE on the MCP endpoint; both are correct behavior now that the listening stream and session termination are gone).

Header names are case-insensitive in HTTP. The spec prints `MCP-Protocol-Version` and `Mcp-Method`; wire captures may show any casing. Do not write a case-sensitive matcher. Header *values* (method names, tool names) are case-sensitive.

## Flow at a glance

```mermaid
sequenceDiagram
    participant C as Client
    participant S as MCP endpoint on Vercel
    C->>S: POST server/discover (optional)
    S-->>C: 200 DiscoverResult
    C->>S: POST tools/list
    S-->>C: 200 tool catalog with ttlMs and cacheScope
    C->>S: POST tools/call echo
    S-->>C: 200 result (resultType complete)
    note over C,S: no initialize, no session id, nothing to shut down
```

## The trace

### 1 → `server/discover` (request, optional)

```http
POST /api/mcp HTTP/1.1
Host: minimal-server-demo.vercel.app
Content-Type: application/json
Accept: application/json, text/event-stream
Origin: https://inspector.example.com
MCP-Protocol-Version: 2026-07-28
Mcp-Method: server/discover
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "server/discover",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "ExampleHost", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
```

The client's first frame, and already a template for every frame after it. `_meta` carries the two **required** keys (`protocolVersion`, matching the header exactly, and `clientCapabilities`, empty here: this host offers no `elicitation`, `sampling`, or `roots`) plus the recommended `clientInfo`. Servers **MUST** implement `server/discover`; calling it is the client's choice. A client that skips it just sends its real first request and handles `UnsupportedProtocolVersionError` if the version does not line up.

### 2 ← `server/discover` result (response)

```http
HTTP/1.1 200 OK
Content-Type: application/json
```

```json
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "resultType": "complete",
    "supportedVersions": ["2026-07-28"],
    "capabilities": { "tools": { "listChanged": true } },
    "ttlMs": 3600000,
    "cacheScope": "public",
    "_meta": {
      "io.modelcontextprotocol/serverInfo": { "name": "minimal-server", "version": "0.1.0" }
    }
  }
}
```

> **Illustrative cache hints.** The `ttlMs` and `cacheScope` values in this frame and in frame 4 show what a server that configures cache hints advertises. `examples/minimal-server/` sets none, and the installed SDK then emits `ttlMs: 0` and `cacheScope: "private"` (the safe default: never cache, never share). To advertise the values shown, set `cacheHints` in the `createMcpHandler` server options, or `cacheHint` on an individual `registerResource` call.

Same `id: 1`: this is the response to frame 1, delivered as a plain JSON body (a server answers each POSTed request with either `Content-Type: application/json` or an SSE stream; the client **MUST** support both). Note the fields that used to live in the `initialize` result: supported versions, `capabilities` (this server offers tools and nothing else), and the server's identity, now riding `_meta` as `serverInfo`, which servers **SHOULD** attach to **every** result. `resultType: "complete"` is the required result tag, and `ttlMs`/`cacheScope` make the freshness contract explicit: cache this for an hour, and a shared cache may hold it (illustrative values; see the note above). `serverInfo` is self-reported and unverified; display it, log it, never authorize on it.

### 3 → `tools/list` (request)

```http
POST /api/mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/list
```

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "ExampleHost", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
```

Discovery proper. The same `_meta` block rides along, because the server is entitled to forget frame 1 ever happened: on Vercel this POST may land on a different function instance, and the protocol now guarantees that is fine. `Mcp-Method` mirrors the method for the edge; no `Mcp-Name` here because `tools/list` is not one of the three name-carrying methods.

### 4 ← `tools/list` result (response)

```json
{
  "jsonrpc": "2.0",
  "id": 2,
  "result": {
    "resultType": "complete",
    "tools": [
      {
        "name": "echo",
        "description": "Echo a message back to the caller.",
        "inputSchema": {
          "type": "object",
          "properties": { "message": { "type": "string" } },
          "required": ["message"]
        }
      }
    ],
    "ttlMs": 60000,
    "cacheScope": "public",
    "_meta": {
      "io.modelcontextprotocol/serverInfo": { "name": "minimal-server", "version": "0.1.0" }
    }
  }
}
```

The server's current catalog, in the deterministic order the spec now asks for (one tool makes that easy). Each tool carries an `inputSchema` (JSON Schema 2020-12 by default) that the client uses to validate arguments and render UI; this one round-trips from the `zod` schema in `examples/minimal-server` (in the repository) (`inputSchema: z.object({ message: z.string() })`). Expect serializer noise in real captures: SDKs may add keys such as `additionalProperties` or `$schema` around the shape shown here. The `ttlMs`/`cacheScope` pair is **required** on all five list-and-read methods (the values shown are illustrative; an unconfigured server emits `0` and `"private"`); a client honoring it will not re-fetch this catalog for a minute unless a `notifications/tools/list_changed` arrives on a listen stream. A large catalog would paginate with `nextCursor`.

### 5 → `tools/call` (request)

```http
POST /api/mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: echo
```

```json
{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "echo",
    "arguments": { "message": "hello" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientInfo": { "name": "ExampleHost", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
```

Invocation. `name` selects the tool; `arguments` must satisfy its `inputSchema`. This is the first frame where `Mcp-Name` is required, and it **MUST** equal `params.name`; a WAF can now rate-limit or block calls to a specific tool without reading a byte of body, and the server **MUST** reject any header/body disagreement with `-32020`. On Vercel this POST is one function invocation with a `maxDuration` budget; see [Serverless sessions](https://vercel-mcp-reference.vercel.app/internals/serverless-sessions/) for what that means for slow tools.

### 6 ← `tools/call` result (response)

```
event: message
data: {"jsonrpc":"2.0","id":3,"result":{"resultType":"complete","content":[{"type":"text","text":"hello"}],"isError":false,"_meta":{"io.modelcontextprotocol/serverInfo":{"name":"minimal-server","version":"0.1.0"}}}}
```

The result, this time as an SSE event: the server chose a stream for this request (both framings remain legal per request). Before the final response event, the server **MAY** interleave request-scoped notifications (progress, and log messages if this request had set `io.modelcontextprotocol/logLevel` in `_meta`) on this same stream; the final response **SHOULD** terminate it. What it must not do anymore is send its own JSON-RPC *requests* here: server-initiated requests left the protocol with 2026-07-28. `content` is the unstructured array (`text` here, but also `image`, `audio`, `resource_link`, embedded `resource`); a tool with an `outputSchema` would also return `structuredContent`. `isError` is **optional, default false**: a *tool* failure comes back as `isError: true` content the model can read and self-correct on, which is distinct from a JSON-RPC `error` response, reserved for *protocol* failures such as an unknown tool name (`-32602`). Render that content as untrusted input either way; see [Tool result rendering](https://vercel-mcp-reference.vercel.app/client-side/tool-result-rendering/).

### 7 ← when the server needs input: `resultType: "input_required"`

The `echo` tool never needs more than its arguments, so this frame pair is illustrated with a hypothetical `create_issue` tool on a client that declared `"elicitation": { "form": {} }` in its per-request capabilities. Instead of pausing the call and sending an `elicitation/create` *request* (the pre-2026 shape), the server **ends** the call with an interim result:

```json
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "issue_title": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "What should the issue be titled?",
          "requestedSchema": {
            "type": "object",
            "properties": { "title": { "type": "string" } },
            "required": ["title"]
          }
        }
      }
    },
    "requestState": "opaque-server-blob"
  }
}
```

### 8 → the retry (request)

```json
{
  "jsonrpc": "2.0",
  "id": 5,
  "method": "tools/call",
  "params": {
    "name": "create_issue",
    "arguments": { "repo": "acme/site" },
    "inputResponses": {
      "issue_title": { "action": "accept", "content": { "title": "Fix the login redirect" } }
    },
    "requestState": "opaque-server-blob",
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": { "elicitation": { "form": {} } }
    }
  }
}
```

This is the [MRTR pattern](https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr) in two frames. The rules that make it safe and serverless-friendly: the retry is an **independent request with a new `id`**; the client **MUST** answer every key in `inputRequests` and echo `requestState` byte-for-byte without inspecting it; and the server **MUST** treat the returned `requestState` as attacker-controlled input, integrity-protecting it whenever it influences authorization or logic. Nothing about the original call is held in server memory between frames 7 and 8, which is exactly why the pattern works when every frame is a fresh function invocation. Sampling and roots ride the same rails, with `CreateMessageRequest` and `ListRootsRequest` objects in the map.

### 9 → the listening stream (optional)

```http
POST /api/mcp HTTP/1.1
Content-Type: application/json
Accept: application/json, text/event-stream
MCP-Protocol-Version: 2026-07-28
Mcp-Method: subscriptions/listen
```

```json
{
  "jsonrpc": "2.0",
  "id": 6,
  "method": "subscriptions/listen",
  "params": {
    "notifications": { "toolsListChanged": true },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {}
    }
  }
}
```

```
event: message
data: {"jsonrpc":"2.0","method":"notifications/subscriptions/acknowledged","params":{"_meta":{"io.modelcontextprotocol/subscriptionId":6},"notifications":{"toolsListChanged":true}}}
```

The 2025-11-25 GET stream is gone (GET now correctly gets `405`); a client that wants change notifications opts in with a `subscriptions/listen` POST whose *response stream stays open*. The server **MUST** acknowledge first, echoing the filter subset it will honor, and every notification on the stream carries `io.modelcontextprotocol/subscriptionId` (the JSON-RPC id of the listen request) in `_meta`. Streams are not resumable: if this connection drops, the client re-issues `subscriptions/listen` as a new request. On Vercel, an open listen stream is an open function invocation, with everything that implies for `maxDuration`.

### 10 → shutdown

There is nothing to shut down. No session was created, so there is no DELETE (a modern server answers `405`), no session id to expire, and no way for the server to "end the conversation" beyond closing its streams. When the client is done, it stops sending requests. Cancelling one in-flight request is transport-level: the client closes that request's SSE response stream, and the server **MUST** treat the close as cancellation.

## What a trace reveals

A captured trace makes the common failures obvious:

- **`400` with `-32020` in the body** → header/body mismatch or a missing required header (`MCP-Protocol-Version`, `Mcp-Method`, `Mcp-Name`). Look for proxies rewriting headers.
- **`400` with `-32602` mentioning `_meta`** → the request is missing `io.modelcontextprotocol/protocolVersion` or `io.modelcontextprotocol/clientCapabilities`; usually a legacy client talking to a modern-only server.
- **`400` with `-32022`** → version negotiation working as designed. Read `error.data.supported` and retry at a mutually supported version.
- **`400` with `-32021`** → the server needed a client capability (elicitation, sampling, roots) this specific request did not declare. Capabilities are per request now; check the `_meta` of the failing frame, not your connection setup.
- **`403` on the first POST** → the server rejected your `Origin`. That is the DNS-rebinding defense doing its job; fix the client, not the check.
- **`404` with a `-32601` error body** → the method is not implemented on a modern server. A bare `404` with no JSON-RPC body means a legacy server or a wrong URL.
- **`405` on GET or DELETE** → correct modern behavior: no listening stream, no session termination.
- **`-32602` "Unknown tool" on `tools/call`** → the tool name is wrong, or the catalog changed; re-run `tools/list`.
- **A result with no `resultType`** → an earlier-protocol server; clients **MUST** treat it as `"complete"`.
- **A `-32002` error** → a legacy server's resource-not-found; modern servers use `-32602` for that.
- **A JSON parse error on a response** → the client assumed `application/json` but got an SSE stream. Both are legal on a POST response; parse by `Content-Type`, not by hope.

## What the deployed stack emits

The trace above is the published 2026-07-28 contract, and it is also what this repo's deployable stack (`mcp-handler` 2.1.1 over `@modelcontextprotocol/server` 2.0.0, verified 2026-08-26) serves: the handler speaks 2026-07-28 natively and falls back to stateless 2025-11-25 Streamable HTTP for older clients, so one endpoint answers both eras. What you capture depends on which era the client speaks.

**A client sending modern headers** gets the frames above. Reproduce frame 1 with `curl` against a deployed example or `npm run dev` (the body's `_meta` and the headers must agree, or the answer is `400` with `-32020`):

```sh
curl -sS -X POST https://<deployment>/api/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H 'MCP-Protocol-Version: 2026-07-28' \
  -H 'Mcp-Method: server/discover' \
  -d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28","io.modelcontextprotocol/clientCapabilities":{}}}}'
```

The answer is frame 2: `resultType: "complete"`, `supportedVersions: ["2026-07-28"]` (the server package's `SUPPORTED_MODERN_PROTOCOL_VERSIONS`), `serverInfo` in `_meta`, and the cache hints the server is configured with (`ttlMs: 0`, `cacheScope: "private"` for `minimal-server`, which configures none; see the note under frame 2). `tools/list` and `tools/call` with the matching `Mcp-Method` and `Mcp-Name` headers return frames 4 and 6; a `tools/call` missing `Mcp-Name` gets `400` with `-32020`.

**A default SDK `Client`** gets the legacy shape instead. `@modelcontextprotocol/client` 2.0.0 defaults `versionNegotiation.mode` to `'legacy'`, so `connect()` opens with an `initialize` request at `protocolVersion: "2025-11-25"` followed by `notifications/initialized`; capabilities are exchanged there rather than in per-request `_meta`, results carry no `resultType` (treat them as `"complete"`, per the spec's compatibility rule), list results carry no `ttlMs`/`cacheScope`, single responses still arrive as SSE frames like frame 6, and there is **no session id even on this path**: the fallback transport is constructed with session generation unset, so no `Mcp-Session-Id` is issued and DELETE has nothing to terminate (see [Serverless sessions](https://vercel-mcp-reference.vercel.app/internals/serverless-sessions/)). Opt a client into the modern frames with `versionNegotiation: { mode: 'auto' }` (probe `server/discover`, fall back to `initialize` on `-32601`) or by pinning the version.

The spec's versioning page defines exactly this coexistence: a dual-era client probes and falls back, a dual-era server answers whichever handshake arrives. Note the limit for tests: a bare `McpServer` over `InMemoryTransport` answers `server/discover` with `-32601`, so the examples' vitest suites only ever see the legacy shape, and assertions on `resultType`, `ttlMs`, or `cacheScope` belong in an HTTP-level check against the handler, not in the in-memory suites. See [Testing](https://vercel-mcp-reference.vercel.app/testing/).

## The same frames over stdio

On the [stdio transport](https://vercel-mcp-reference.vercel.app/glossary/#stdio-transport) (local dev, CLI hosts) the frames above are byte-for-byte the same JSON, but the HTTP layer disappears: each message is one newline-delimited JSON object on the child process pipe, with no headers and no status codes. The mirrored-header machinery (`Mcp-Method`, `Mcp-Name`, `Mcp-Param-*`) simply does not exist there, `notifications/cancelled` returns as the cancellation signal (there is no per-request stream to close), and the backward-compatibility probe is `server/discover` itself: a modern answer means a modern server, anything else means fall back to `initialize`. The pipe is still not a session: the spec is explicit that connection identity carries no conversational state. See [Transports](https://vercel-mcp-reference.vercel.app/internals/transports/) for the full comparison.

## Related

- [MCP internals overview](https://vercel-mcp-reference.vercel.app/internals/overview/) - the stateless model this trace makes concrete.
- [Transports](https://vercel-mcp-reference.vercel.app/internals/transports/) - the POST semantics, required headers, streaming, and Origin rule in full.
- [Serverless sessions](https://vercel-mcp-reference.vercel.app/internals/serverless-sessions/) - how per-request statelessness and explicit handles map onto Vercel Functions.
- [Capability primitives](https://vercel-mcp-reference.vercel.app/internals/primitives/) - the methods appearing in these frames, and the full MRTR treatment.
- [Testing](https://vercel-mcp-reference.vercel.app/testing/) - how to drive a server in-memory with no HTTP at all, and why that path stays on the legacy handshake.

## Bibliography

- Model Context Protocol Specification, *Streamable HTTP*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http>
- Model Context Protocol Specification, *Versioning and Compatibility*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/basic/versioning>
- Model Context Protocol Specification, *Overview (base protocol, `_meta`, error codes)*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/basic/index>
- Model Context Protocol Specification, *Discovery (`server/discover`)*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/server/discover>
- Model Context Protocol Specification, *Multi Round-Trip Requests*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/mrtr>
- Model Context Protocol Specification, *Subscriptions*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/basic/patterns/subscriptions>
- Model Context Protocol Specification, *Tools*, version 2026-07-28 - <https://modelcontextprotocol.io/specification/2026-07-28/server/tools>
- JSON-RPC 2.0 Specification - <https://www.jsonrpc.org/specification>
- Vercel Documentation, *Deploy MCP servers to Vercel* - <https://vercel.com/docs/mcp/deploy-mcp-servers-to-vercel>
- mcp-handler, source repository - <https://github.com/vercel/mcp-handler>
- mcp-handler README, *Protocol Support* (2.1.1: 2026-07-28 served natively, stateless 2025-era fallback, HTTP+SSE removed) - <https://github.com/vercel/mcp-handler#protocol-support>
- @modelcontextprotocol/client on the npm registry (2.0.0: `versionNegotiation.mode` defaults to `legacy`) - <https://registry.npmjs.org/@modelcontextprotocol/client>
