{"record":{"id":"bf2c68a7bee28084","repo":"siyuan-note/siyuan","slug":"tools-list-exceeded-d-pages","errorCode":null,"errorMessage":"tools/list exceeded %d pages","messagePattern":"tools/list exceeded (.+?) pages","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/client/mcp.go","lineNumber":368,"sourceCode":"\tfor page := 0; page < maxMCPToolListPages; page++ {\n\t\tresult, err := listPage(ctx, params)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif result == nil {\n\t\t\treturn nil, fmt.Errorf(\"tools/list returned an empty response\")\n\t\t}\n\t\tallTools = append(allTools, result.Tools...)\n\t\tif result.NextCursor == \"\" {\n\t\t\treturn allTools, nil\n\t\t}\n\t\tif _, exists := seenCursors[result.NextCursor]; exists {\n\t\t\treturn nil, fmt.Errorf(\"tools/list repeated cursor %q\", result.NextCursor)\n\t\t}\n\t\tseenCursors[result.NextCursor] = struct{}{}\n\t\tparams = &mcp.ListToolsParams{Cursor: result.NextCursor}\n\t}\n\treturn nil, fmt.Errorf(\"tools/list exceeded %d pages\", maxMCPToolListPages)\n}\n\nfunc sanitizedServerNameCollision(server conf.MCPServer) bool {\n\tmcpMu.Lock()\n\tdefer mcpMu.Unlock()\n\tsanitizedName := sanitize(server.Name)\n\tfor _, configured := range mcpServers {\n\t\tif configured.ID != server.ID && sanitize(configured.Name) == sanitizedName {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc mcpToolName(server conf.MCPServer, toolName string, collision bool) string {\n\tname := \"mcp_\" + sanitize(server.Name) + \"_\" + sanitize(toolName)\n\tif !collision && len(name) <= maxMCPToolNameLen {\n\t\treturn name","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/mcp/client/mcp.go#L350-L386","documentation":"listAllMCPTools caps pagination at maxMCPToolListPages pages as a safety valve. If after that many tools/list round-trips the server still returns a NextCursor, the client gives up with this error instead of paging unboundedly. It usually means the tool list is genuinely huge or the server keeps issuing new cursors indefinitely.","triggerScenarios":"connectOneServer calls listAllMCPTools; the server returns a valid distinct NextCursor on every page for maxMCPToolListPages consecutive requests.","commonSituations":"A server exposing thousands of tools with small page sizes, a server generating fresh (never-terminating) cursors each call, or a malicious/buggy server forcing endless pagination.","solutions":["Increase maxMCPToolListPages if the tool count legitimately exceeds the cap","Fix or upgrade the MCP server so it terminates pagination (empty NextCursor on the final page)","Reduce the number of tools exposed by the server or increase its page size","Check server logs to confirm cursors are actually advancing rather than being freshly minted each page"],"exampleFix":"// before: const maxMCPToolListPages = 10 // too small\n// after: const maxMCPToolListPages = 100 // accommodate large tool registries","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"null","tryCatchPattern":"conn, err := listAllMCPTools(ctx, listPage)\nif err != nil {\n    if strings.Contains(err.Error(), \"exceeded\") {\n        logging.LogWarnf(\"mcp: pagination cap hit: %v\", err)\n        return\n    }\n    return err\n}","preventionTips":["Keep per-server tool counts reasonable; disable unneeded tools server-side","Set page sizes generously on the server so few pages are needed","Size maxMCPToolListPages relative to the largest registry you legitimately serve"],"tags":["mcp","pagination","limit-exceeded","protocol"],"backgroundTag":"value-out-of-range","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}