Commit 25821a0
authored
Harden MCP pagination against cursor cycles and add go-sdk registration canaries (#4302)
This updates the go-sdk integration points highlighted in the review:
production pagination now fails fast on cyclical cursors, and
upgrade-sensitive behavior around tool registration is covered by a
stronger canary. It also clarifies transport-specific protocol/session
handling to reduce future maintenance ambiguity.
- **Pagination safety in `internal/mcp/connection.go`**
- Added cursor-cycle detection to `paginateAll` using a `seenCursors`
set.
- Returns an explicit error when a backend repeats a cursor instead of
continuing until the page cap.
- **Pagination behavior coverage in `internal/mcp/connection_test.go`**
- Added a cycle-focused test case to `TestPaginateAll` that verifies
early termination on repeated cursors.
- Adjusted the max-pages test to use unique cursors so it continues
validating the cap path independently.
- **Transport-scope clarity in `internal/mcp/http_transport.go`**
- Clarified that `MCPProtocolVersion` applies to the plain JSON-RPC
fallback path only.
- Added inline rationale in `isSessionNotFoundError` for string fallback
matching when SDK typed errors are unavailable.
- **SDK-upgrade canary for schema-validation bypass in
`internal/server/routed_test.go`**
- Extended `TestRegisterToolWithoutValidation` with a strict-schema tool
and intentionally schema-invalid arguments.
- Verifies the `registerToolWithoutValidation`/`Server.AddTool` path
still invokes handlers without SDK argument-value validation
regressions.
```go
seenCursors := make(map[string]struct{})
for pageCount := 1; cursor != ""; pageCount++ {
if _, seen := seenCursors[cursor]; seen {
return nil, fmt.Errorf("list%s: backend serverID=%s returned cyclical cursor %q", itemKind, serverID, cursor)
}
seenCursors[cursor] = struct{}{}
// fetch next page...
}
```
> [!WARNING]
>
> <details>
> <summary>Firewall rules blocked me from connecting to one or more
addresses (expand for details)</summary>
>
> #### I tried to connect to the following addresses, but was blocked by
firewall rules:
>
> - `example.com`
> - Triggering command: `/tmp/go-build2010847725/b513/launcher.test
/tmp/go-build2010847725/b513/launcher.test
-test.testlogfile=/tmp/go-build2010847725/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build2010847725/b423/vet.cfg
rotocol/go-sdk@v1.5.0/jsonrpc/jsonrpc.go -trimpath x_amd64/vet gci-lint
failed /opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet
7457537/b209/sym-atomic -lang=go1.25 x_amd64/vet -p g_.a
477457537/b287//-ifaceassert x_amd64/vet -I go-sdk/mcp 7457537/b287/
x_amd64/vet` (dns block)
> - Triggering command: `/tmp/go-build81877103/b513/launcher.test
/tmp/go-build81877103/b513/launcher.test
-test.testlogfile=/tmp/go-build81877103/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s rds/��
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_git
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_commit
-guard/target/debug/deps/rustco0oSqi/symbols.o -guard/target/debash b0d7
-guard/target/de--noprofile
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.03.rcgu.o
-gua��
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.05.rcgu/opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.06.rcgu/tmp/go-build3709670566/b506/vet.cfg
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.07.rcgu.o
-guard/target/debash -guard/target/de--norc -guard/target/de--noprofile
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.11.rcgu.o`
(dns block)
> - `invalid-host-that-does-not-exist-12345.com`
> - Triggering command: `/tmp/go-build2010847725/b495/config.test
/tmp/go-build2010847725/b495/config.test
-test.testlogfile=/tmp/go-build2010847725/b495/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build2010847725/b397/vet.cfg @v1.1.3/cpu/x86/-c=4
ggXp/9qGZEHgmklo-nolocalimports x_amd64/vet --gdwarf-5
nal/encoding/tag-atomic -o x_amd64/vet 7457�� g_.a
ache/go/1.25.9/x-ifaceassert x_amd64/vet -I /opt/hostedtoolc-atomic -I
x_amd64/vet` (dns block)
> - `nonexistent.local`
> - Triggering command: `/tmp/go-build2010847725/b513/launcher.test
/tmp/go-build2010847725/b513/launcher.test
-test.testlogfile=/tmp/go-build2010847725/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build2010847725/b423/vet.cfg
rotocol/go-sdk@v1.5.0/jsonrpc/jsonrpc.go -trimpath x_amd64/vet gci-lint
failed /opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet
7457537/b209/sym-atomic -lang=go1.25 x_amd64/vet -p g_.a
477457537/b287//-ifaceassert x_amd64/vet -I go-sdk/mcp 7457537/b287/
x_amd64/vet` (dns block)
> - Triggering command: `/tmp/go-build81877103/b513/launcher.test
/tmp/go-build81877103/b513/launcher.test
-test.testlogfile=/tmp/go-build81877103/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s rds/��
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_git
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_commit
-guard/target/debug/deps/rustco0oSqi/symbols.o -guard/target/debash b0d7
-guard/target/de--noprofile
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.03.rcgu.o
-gua��
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.05.rcgu/opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.06.rcgu/tmp/go-build3709670566/b506/vet.cfg
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.07.rcgu.o
-guard/target/debash -guard/target/de--norc -guard/target/de--noprofile
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.11.rcgu.o`
(dns block)
> - `slow.example.com`
> - Triggering command: `/tmp/go-build2010847725/b513/launcher.test
/tmp/go-build2010847725/b513/launcher.test
-test.testlogfile=/tmp/go-build2010847725/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s
/tmp/go-build2010847725/b423/vet.cfg
rotocol/go-sdk@v1.5.0/jsonrpc/jsonrpc.go -trimpath x_amd64/vet gci-lint
failed /opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet
7457537/b209/sym-atomic -lang=go1.25 x_amd64/vet -p g_.a
477457537/b287//-ifaceassert x_amd64/vet -I go-sdk/mcp 7457537/b287/
x_amd64/vet` (dns block)
> - Triggering command: `/tmp/go-build81877103/b513/launcher.test
/tmp/go-build81877103/b513/launcher.test
-test.testlogfile=/tmp/go-build81877103/b513/testlog.txt
-test.paniconexit0 -test.timeout=10m0s rds/��
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_git
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_commit
-guard/target/debug/deps/rustco0oSqi/symbols.o -guard/target/debash b0d7
-guard/target/de--noprofile
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.03.rcgu.o
-gua��
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.05.rcgu/opt/hostedtoolcache/go/1.25.9/x64/pkg/tool/linux_amd64/vet
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.06.rcgu/tmp/go-build3709670566/b506/vet.cfg
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.07.rcgu.o
-guard/target/debash -guard/target/de--norc -guard/target/de--noprofile
-guard/target/debug/deps/serde_derive-bdc7cd22a58a5141.serde_derive.12123747d8da05ed-cgu.11.rcgu.o`
(dns block)
> - `this-host-does-not-exist-12345.com`
> - Triggering command: `/tmp/go-build2010847725/b522/mcp.test
/tmp/go-build2010847725/b522/mcp.test
-test.testlogfile=/tmp/go-build2010847725/b522/testlog.txt
-test.paniconexit0 -test.timeout=10m0s abis�� cfg -I x_amd64/vet
--gdwarf-5 ce -o x_amd64/vet cfg 7457537/b449/_pkg_.a
nLJk/a4JpKmHiBmr_E514nLJk x_amd64/vet --gdwarf-5 ntio/asm/keyset -o
x_amd64/vet` (dns block)
> - Triggering command: `/tmp/go-build81877103/b522/mcp.test
/tmp/go-build81877103/b522/mcp.test
-test.testlogfile=/tmp/go-build81877103/b522/testlog.txt
-test.paniconexit0 -test.timeout=10m0s lib/��
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_git
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_commit
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_-m
lib/rustlib/x86_bash b0d7 lib/rustlib/x86_--noprofile
lib/rustlib/x86_/home/REDACTED/.rustup/toolchains/stable-x86_64-REDACTED-linux-gnu/lib/rustlib/x86_64-REDACTED-linux-gnu/lib/libminiz_oxide-2b6a8d2f6e1dc71b.rlib
lib/��
lib/rustlib/x86_64-REDACTED-linux-gnu/lib/librustc_std_workspace_alloc-76b5fe9328c1063f.rlib
lib/rustlib/x86_64-REDACTED-linux-gnu/lib/libminiz_oxide-2b6a8d2f6e1dc71b.rlib
iginal -f "$GOPATH/bin/bash -I yload_StoredInPa--noprofile
-guard/target/debug/deps/libserde_derive-bdc7cd22a58a5141.so` (dns
block)
> - Triggering command: `/tmp/go-build1985989918/b522/mcp.test
/tmp/go-build1985989918/b522/mcp.test
-test.testlogfile=/tmp/go-build1985989918/b522/testlog.txt
-test.paniconexit0 -test.timeout=10m0s /tmp�� /run/containerd/-p
herFiles,CFiles,main by/31b594e5b708d-lang=go1.25 ntime.v2.task/mogit
-guard/target/deshow
-guard/target/de7291de63ecded2ae0119c7bba93a1d428d68106a:internal/server/routed_test.go
296/log.json b2f7�� 081a786f99487854go1.25.9 y er-linux
ntime.v2.task/mo/opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/tools/linux64/java/lib/jspawnhelper
/tmp/awmg-log-te21.0.9+10-LTS --routed er-linux` (dns block)
>
> If you need me to access, download, or install something from one of
these locations, you can either:
>
> - Configure [Actions setup
steps](https://gh.io/copilot/actions-setup-steps) to set up my
environment, which run before the firewall is enabled
> - Add the appropriate URLs or hosts to the custom allowlist in this
repository's [Copilot coding agent
settings](https://github.com/github/gh-aw-mcpg/settings/copilot/coding_agent)
(admins only)
>
> </details>4 files changed
Lines changed: 67 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
610 | 610 | | |
611 | 611 | | |
612 | 612 | | |
| 613 | + | |
613 | 614 | | |
614 | 615 | | |
615 | 616 | | |
616 | 617 | | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
617 | 622 | | |
618 | 623 | | |
619 | 624 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
978 | 978 | | |
979 | 979 | | |
980 | 980 | | |
981 | | - | |
| 981 | + | |
982 | 982 | | |
983 | 983 | | |
984 | 984 | | |
985 | | - | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
986 | 990 | | |
987 | 991 | | |
988 | 992 | | |
989 | 993 | | |
990 | 994 | | |
991 | 995 | | |
992 | 996 | | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
| 1019 | + | |
993 | 1020 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
37 | 39 | | |
38 | 40 | | |
39 | 41 | | |
| |||
78 | 80 | | |
79 | 81 | | |
80 | 82 | | |
| 83 | + | |
| 84 | + | |
81 | 85 | | |
82 | 86 | | |
83 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
703 | 703 | | |
704 | 704 | | |
705 | 705 | | |
| 706 | + | |
| 707 | + | |
| 708 | + | |
| 709 | + | |
| 710 | + | |
| 711 | + | |
| 712 | + | |
| 713 | + | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
706 | 724 | | |
707 | 725 | | |
708 | 726 | | |
| |||
721 | 739 | | |
722 | 740 | | |
723 | 741 | | |
| 742 | + | |
| 743 | + | |
| 744 | + | |
| 745 | + | |
| 746 | + | |
| 747 | + | |
| 748 | + | |
| 749 | + | |
| 750 | + | |
| 751 | + | |
724 | 752 | | |
725 | 753 | | |
726 | 754 | | |
| |||
0 commit comments