
# Logging

> `logging/setLevel`, legacy only.

> [!NOTE]
> `logging/setLevel` and the Logging feature as a whole were deprecated in `2026-07-28`. Modern clients set the level per request via `_meta["io.modelcontextprotocol/logLevel"]` — see [request-scoped streaming](/guide/modern/streaming).

Legacy clients set a server-wide log level once, and the server pushes `notifications/message` at or above that level over the [SSE stream](/guide/legacy/sse).

```ts
defineMcpHandler({
  name: "my-server",
  version: "1.0.0",
  onLogLevelSet(level, event) {
    console.log(`Client requested log level: ${level}`);
  },
  tools: [/* ... */],
});
```

The level is validated against the eight MCP levels before your callback runs:

`debug` · `info` · `notice` · `warning` · `error` · `critical` · `alert` · `emergency`

Declaring `onLogLevelSet` is what advertises `logging: {}` in the `initialize` capabilities. The callback is a notification hook, not bookkeeping: `h3-mcp` already records the level per session, so nothing you write has to.

> [!IMPORTANT]
> Log messages are sent to the client. Treat them as output for a third party: no credentials, no tokens, no internal hostnames or stack traces. Everything that applies to [error messages](/guide/basics/errors#messages-are-not-forwarded) applies here.

To actually send a log message, use `event.context.mcp.log()` — the same call on both eras. It gates on the level in force (here, the one this session set) and needs no configuration:

```ts
event.context.mcp?.log?.("warning", { retries: 3 }, "importer");
```

On legacy the message travels on the request's own SSE response, so the client must send
`Accept: text/event-stream` with the call. Anything logged below the requested level, or by a
request whose client asked for no logs at all, is dropped.

:read-more{to="/guide/modern/streaming" title="Request-scoped streaming"}

To advertise the `logging` capability to modern clients on `server/discover`, set `logging: true`.
