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
20 changes: 10 additions & 10 deletions docs/protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
# Support for the MCP base protocol

1. [Lifecycle](#lifecycle)
1. [Discovery (`server/discover`)](#discovery-(server/discover))
1. [Per-request `_meta` keys](#per-request-meta-keys)
1. [Per-response `_meta` keys](#per-response-meta-keys)
1. [Subscriptions (`subscriptions/listen`)](#subscriptions-(subscriptions/listen))
1. [Discovery](#discovery)
1. [Per-request metadata keys](#per-request-metadata-keys)
1. [Per-response metadata keys](#per-response-metadata-keys)
1. [Subscriptions](#subscriptions)
1. [Transports](#transports)
1. [Stdio Transport](#stdio-transport)
1. [Streamable Transport](#streamable-transport)
1. [SSE Transport (legacy)](#sse-transport-(legacy))
1. [Legacy SSE Transport](#legacy-sse-transport)
1. [Custom transports](#custom-transports)
1. [Concurrency](#concurrency)
1. [Authorization](#authorization)
Expand Down Expand Up @@ -111,7 +111,7 @@ func Example_lifecycle() {
}
```

### Discovery (`server/discover`)
### Discovery

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
Expand All @@ -128,7 +128,7 @@ request. Servers implementing `2026-07-28` MUST implement it.
fails or the server does not support the latest version, the client falls back to the
legacy `initialize` handshake.

### Per-request `_meta` keys
### Per-request metadata keys

When the negotiated protocol version is `2026-07-28` or later, every request
carries these keys inside its `_meta` map (constants live in
Expand All @@ -147,7 +147,7 @@ request, and populates `clientInfo` when configured with an `*Implementation`
`ServerRequest[P].ProtocolVersion()`, `ServerRequest[P].ClientInfo()`, and
`ServerRequest[P].ClientCapabilities()`.

### Per-response `_meta` keys
### Per-response metadata keys

Under the same protocol version, servers SHOULD identify themselves on every
response. The SDK populates this
Expand All @@ -157,7 +157,7 @@ key automatically on every outgoing response:
|---|---|---|---|
| `MetaKeyServerInfo` | `io.modelcontextprotocol/serverInfo` | `*Implementation` | No |

### Subscriptions (`subscriptions/listen`)
### Subscriptions

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
Expand Down Expand Up @@ -343,7 +343,7 @@ _See [examples/server/distributed](https://github.com/modelcontextprotocol/go-sd
an example using stateless mode to implement a server distributed across
multiple processes._

### SSE Transport (legacy)
### Legacy SSE Transport

Before the streamable transport, the
[2024-11-05](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports)
Expand Down
2 changes: 1 addition & 1 deletion docs/server.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ to be notified of changes to subscribed resources. On `2026-07-28` and
later sessions the SDK delivers these notifications over a
`subscriptions/listen` stream instead of the legacy `resources/subscribe`
RPC; see [Subscriptions
(`subscriptions/listen`)](protocol.md#subscriptions-subscriptionslisten)
(`subscriptions/listen`)](protocol.md#subscriptions)
for the wire-level details.

**Server-side**:
Expand Down
10 changes: 5 additions & 5 deletions internal/docs/protocol.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ it is the client's responsibility to end the session.

%include ../../mcp/mcp_example_test.go lifecycle -

### Discovery (`server/discover`)
### Discovery

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
Expand All @@ -72,7 +72,7 @@ request. Servers implementing `2026-07-28` MUST implement it.
fails or the server does not support the latest version, the client falls back to the
legacy `initialize` handshake.

### Per-request `_meta` keys
### Per-request metadata keys

When the negotiated protocol version is `2026-07-28` or later, every request
carries these keys inside its `_meta` map (constants live in
Expand All @@ -91,7 +91,7 @@ request, and populates `clientInfo` when configured with an `*Implementation`
`ServerRequest[P].ProtocolVersion()`, `ServerRequest[P].ClientInfo()`, and
`ServerRequest[P].ClientCapabilities()`.

### Per-response `_meta` keys
### Per-response metadata keys

Under the same protocol version, servers SHOULD identify themselves on every
response. The SDK populates this
Expand All @@ -101,7 +101,7 @@ key automatically on every outgoing response:
|---|---|---|---|
| `MetaKeyServerInfo` | `io.modelcontextprotocol/serverInfo` | `*Implementation` | No |

### Subscriptions (`subscriptions/listen`)
### Subscriptions

Introduced in `2026-07-28` by
[SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575),
Expand Down Expand Up @@ -267,7 +267,7 @@ _See [examples/server/distributed](https://github.com/modelcontextprotocol/go-sd
an example using stateless mode to implement a server distributed across
multiple processes._

### SSE Transport (legacy)
### Legacy SSE Transport

Before the streamable transport, the
[2024-11-05](https://modelcontextprotocol.io/specification/2024-11-05/basic/transports)
Expand Down
2 changes: 1 addition & 1 deletion internal/docs/server.src.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ to be notified of changes to subscribed resources. On `2026-07-28` and
later sessions the SDK delivers these notifications over a
`subscriptions/listen` stream instead of the legacy `resources/subscribe`
RPC; see [Subscriptions
(`subscriptions/listen`)](protocol.md#subscriptions-subscriptionslisten)
(`subscriptions/listen`)](protocol.md#subscriptions)
for the wire-level details.

**Server-side**:
Expand Down
70 changes: 70 additions & 0 deletions internal/docs/toc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2025 The Go MCP SDK Authors. All rights reserved.
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package docs

import (
"os"
"path/filepath"
"regexp"
"strings"
"testing"
)

// Headings are slugged twice by two different tools: weave writes the %toc
// links, and the renderer (GitHub, and mkdocs on go.sdk.modelcontextprotocol.io)
// assigns the heading ids. The two agree on plain words, and disagree on
// punctuation: weave keeps "(", ")" and "/" and drops "_", the renderers drop
// the first three and keep the last. A heading carrying any of them therefore
// gets a table-of-contents entry that lands nowhere.
//
// This test reads the generated docs and checks every %toc entry against the
// ids the renderers will actually produce.

var (
headingRE = regexp.MustCompile(`(?m)^(#{1,6})\s+(.*?)\s*$`)
tocLineRE = regexp.MustCompile(`(?m)^\s*1\. \[(.*?)\]\((#.*?)\)\s*$`)
dropRE = regexp.MustCompile(`[^\w\s-]`)
spaceRE = regexp.MustCompile(`[-\s]+`)
)

// renderedID reproduces the anchor GitHub and python-markdown's toc extension
// derive from heading text: take the text, drop everything that is not a word
// character, whitespace or a hyphen, lowercase it, and join on hyphens.
func renderedID(heading string) string {
s := strings.ReplaceAll(heading, "`", "")
s = dropRE.ReplaceAllString(s, "")
s = strings.ToLower(strings.TrimSpace(s))
return spaceRE.ReplaceAllString(s, "-")
}

func TestTOCAnchorsResolve(t *testing.T) {
files, err := filepath.Glob(filepath.Join("..", "..", "docs", "*.md"))
if err != nil {
t.Fatal(err)
}
if len(files) == 0 {
t.Fatal("no generated docs found")
}
for _, file := range files {
data, err := os.ReadFile(file)
if err != nil {
t.Fatal(err)
}
text := string(data)

ids := make(map[string]bool)
for _, m := range headingRE.FindAllStringSubmatch(text, -1) {
ids[renderedID(m[2])] = true
}
for _, m := range tocLineRE.FindAllStringSubmatch(text, -1) {
label, target := m[1], strings.TrimPrefix(m[2], "#")
if !ids[target] {
t.Errorf("%s: table-of-contents entry %q points at #%s, which no heading produces "+
"(want #%s); drop (, ), / and _ from the heading",
filepath.Base(file), label, target, renderedID(label))
}
}
}
}
Loading