siyuan-note/siyuan · error

tools/list exceeded pages

Error message

tools/list exceeded %d pages

What it means

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.

Solutions

  1. Increase maxMCPToolListPages if the tool count legitimately exceeds the cap
  2. Fix or upgrade the MCP server so it terminates pagination (empty NextCursor on the final page)
  3. Reduce the number of tools exposed by the server or increase its page size
  4. Check server logs to confirm cursors are actually advancing rather than being freshly minted each page

Example fix

// before: const maxMCPToolListPages = 10 // too small
// after: const maxMCPToolListPages = 100 // accommodate large tool registries
Defensive patterns

Strategy: try-catch

Validate before calling

null

Type guard

null

Try / catch

conn, err := listAllMCPTools(ctx, listPage)
if err != nil {
    if strings.Contains(err.Error(), "exceeded") {
        logging.LogWarnf("mcp: pagination cap hit: %v", err)
        return
    }
    return err
}

Prevention

When it happens

Trigger: connectOneServer calls listAllMCPTools; the server returns a valid distinct NextCursor on every page for maxMCPToolListPages consecutive requests.

Common situations: 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.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/bf2c68a7bee28084. Report an issue: GitHub.

Appendix: source

Thrown at kernel/mcp/client/mcp.go:368

	for page := 0; page < maxMCPToolListPages; page++ {
		result, err := listPage(ctx, params)
		if err != nil {
			return nil, err
		}
		if result == nil {
			return nil, fmt.Errorf("tools/list returned an empty response")
		}
		allTools = append(allTools, result.Tools...)
		if result.NextCursor == "" {
			return allTools, nil
		}
		if _, exists := seenCursors[result.NextCursor]; exists {
			return nil, fmt.Errorf("tools/list repeated cursor %q", result.NextCursor)
		}
		seenCursors[result.NextCursor] = struct{}{}
		params = &mcp.ListToolsParams{Cursor: result.NextCursor}
	}
	return nil, fmt.Errorf("tools/list exceeded %d pages", maxMCPToolListPages)
}

func sanitizedServerNameCollision(server conf.MCPServer) bool {
	mcpMu.Lock()
	defer mcpMu.Unlock()
	sanitizedName := sanitize(server.Name)
	for _, configured := range mcpServers {
		if configured.ID != server.ID && sanitize(configured.Name) == sanitizedName {
			return true
		}
	}
	return false
}

func mcpToolName(server conf.MCPServer, toolName string, collision bool) string {
	name := "mcp_" + sanitize(server.Name) + "_" + sanitize(toolName)
	if !collision && len(name) <= maxMCPToolNameLen {
		return name

View on GitHub (pinned to 9f775e8a12)