Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions .changeset/brave-donkeys-listen.md

This file was deleted.

12 changes: 0 additions & 12 deletions .changeset/cancelled-request-id-zero.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/codemod-string-literal-imports.md

This file was deleted.

5 changes: 4 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,8 @@
"ignore": [
"@modelcontextprotocol/examples",
"@mcp-examples/*"
]
],
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
"onlyUpdatePeerDependentsWhenOutOfRange": true
}
}
10 changes: 0 additions & 10 deletions .changeset/dpop-client-tokens.md

This file was deleted.

5 changes: 5 additions & 0 deletions .changeset/loopback-localhost-subdomains.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@modelcontextprotocol/client': patch
---

Treat hostnames ending in `.localhost` as loopback for the SEP-2207 token-endpoint https guard (RFC 6761 §6.3), so host-based multi-tenant local OAuth works. The SDK does not resolve the name itself: `*.localhost` reaches the local machine only if the system resolver follows RFC 6761.
9 changes: 0 additions & 9 deletions .changeset/no-cancel-notification-for-initialize.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/oauth-header-spread-order.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/plenty-plums-sip.md

This file was deleted.

38 changes: 0 additions & 38 deletions .changeset/propagate-save-tokens-errors-after-refresh.md

This file was deleted.

31 changes: 0 additions & 31 deletions .changeset/request-body-size-limit.md

This file was deleted.

33 changes: 0 additions & 33 deletions .changeset/require-protocol-version-header-on-modern-post.md

This file was deleted.

22 changes: 0 additions & 22 deletions .changeset/scope-challenge-server.md

This file was deleted.

9 changes: 0 additions & 9 deletions .changeset/tasks-mcp-name-header.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/migration/upgrade-to-v2.md
Original file line number Diff line number Diff line change
Expand Up @@ -1241,7 +1241,7 @@ rejection now throws `RegistrationRejectedError` (carrying `status`, `body`,
`exchangeAuthorization()`, `refreshAuthorization()`, `fetchToken()`, and the Cross-App
Access helpers throw `InsecureTokenEndpointError` when the token endpoint is not
`https:` (loopback `localhost` / `127.0.0.1` / `::1` exempt). `auth()` surfaces this on
`https:` (loopback `localhost` / `*.localhost` / `127.0.0.1` / `::1` exempt). `auth()` surfaces this on
every path including refresh — switch any plain-`http:` AS on a non-loopback host to
TLS; there is no opt-out. Storage confidentiality of `refresh_token` remains your
`saveTokens()` implementation's responsibility.
Expand Down
18 changes: 18 additions & 0 deletions docs/serving/stdio.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,28 @@ process.on('SIGINT', () => {

`close()` resolves once the instance the factory built and the underlying transport are both shut down.

A signal handler is one path to teardown; the pipe itself is the other. When the client closes its end (stdin reaches end-of-file), the transport closes itself and the connection tears down automatically — no signal handler needed. Requests still in flight at that moment are aborted and never answered (EOF means the client is gone), so a client that wants answers keeps stdin open until it has read them. A server that holds nothing else keeping the event loop alive then exits on its own. If yours does hold a keep-alive handle — a timer, a connection pool, a file watcher — release it when the connection closes so the process can exit:

```ts source="../../examples/guides/serving/stdio.examples.ts#serveStdio_releaseKeepAlive"
serveStdio(() => {
const server = new McpServer({ name: 'notes', version: '1.0.0' });

// A handle that keeps the event loop alive: a heartbeat timer, a
// connection pool, a file watcher, ...
const heartbeat = setInterval(() => console.error('notes server: still serving'), 60_000);

// Release it when the connection closes, so the process can exit.
server.server.onclose = () => clearInterval(heartbeat);

return server;
});
```

## Recap

- `serveStdio(factory)` is the stdio entry point: it owns the transport and calls your factory to build the instance that serves the connection.
- stdout is the protocol channel; log with `console.error`.
- One `console.log` puts a line no JSON-RPC parser accepts into the stream the host parses.
- `npx @modelcontextprotocol/inspector <command>` exercises a stdio server without configuring it in a host.
- The returned `StdioServerHandle`'s `close()` tears down the pinned instance and the transport.
- When the client closes the pipe (stdin EOF) the connection tears down by itself; release your own keep-alive handles on close so the process can exit.
Loading
Loading