> ## Documentation Index
> Fetch the complete documentation index at: https://docs.barndoor.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Railway MCP Server

export default function ServerDetails({serverName: initialServerName = null}) {
  const ITEMS_PER_PAGE = 20;
  const CATALOG_SCRIPT_URL = "https://images.barndoor.ai/catalog/catalog.js";
  const CATALOG_GLOBAL = "__BARNDOOR_MCP_CATALOG__";
  const CATALOG_TIMEOUT_MS = 8000;
  const [serverName, setServerName] = useState(initialServerName);
  const [serverInfo, setServerInfo] = useState(null);
  const [allTools, setAllTools] = useState([]);
  const [tab, setTab] = useState("tools");
  const [isDark, setIsDark] = useState(() => typeof document !== "undefined" && document.documentElement.classList.contains("dark"));
  useEffect(() => {
    if (typeof document === "undefined") return;
    const root = document.documentElement;
    const observer = new MutationObserver(() => {
      setIsDark(root.classList.contains("dark"));
    });
    observer.observe(root, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => observer.disconnect();
  }, []);
  const colors = isDark ? {
    tagBg: "#16211e",
    tagText: "#e8f5f0",
    tagBorder: "#FA7A54",
    panelBorder: "#22302b",
    headerBorder: "#22302b",
    headerBg: "#0d1210",
    cellBorder: "#1c2622",
    codeBg: "#0d1210",
    codeBorder: "#22302b",
    linkText: "#60a5fa",
    inlineCodeBg: "#1c2622",
    buttonBorderDisabled: "#22302b",
    buttonBorderHover: "#33443e",
    buttonBorder: "#22302b",
    buttonBgHover: "#161f1c",
    buttonBg: "#101816",
    buttonTextDisabled: "#4a5852",
    buttonText: "#c8d4d0",
    errorText: "#f87171",
    tableBg: "#141b19",
    tableHeaderBg: "#0d1210",
    tableHeaderBorder: "#22302b",
    tableCellBorder: "#1c2622",
    mutedText: "#8a9691",
    copyButtonBorder: "#22302b",
    copyButtonBg: "#101816",
    copyButtonBgHover: "#161f1c",
    copyButtonText: "#c8d4d0",
    copyButtonSuccess: "#4ade80",
    codeBlockBg: "#0d1210",
    codeBlockText: "#c8d4d0",
    dimText: "#5a6862",
    setupCardBg: "#141b19",
    setupBorder: "#1c2622",
    accent: "#7fb5a0",
    bodyText: "#b8c4c0"
  } : {
    tagBg: "#e6f7ff",
    tagText: "#224035",
    tagBorder: "#FA7A54",
    panelBorder: "#ddd",
    headerBorder: "#ccc",
    headerBg: "#f7f7f7",
    cellBorder: "#eee",
    codeBg: "#f4f4f4",
    codeBorder: "#ddd",
    linkText: "#2563eb",
    inlineCodeBg: "#f0f0f0",
    buttonBorderDisabled: "#e5e7eb",
    buttonBorderHover: "#d1d5db",
    buttonBorder: "#e9e9e9",
    buttonBgHover: "#f3f4f6",
    buttonBg: "#ffffff",
    buttonTextDisabled: "#9ca3af",
    buttonText: "#374151",
    errorText: "#b91c1c",
    tableBg: "#ffffff",
    tableHeaderBg: "#f8fafc",
    tableHeaderBorder: "#e2e8f0",
    tableCellBorder: "#e2e8f0",
    mutedText: "#64748b",
    copyButtonBorder: "#e5e7eb",
    copyButtonBg: "white",
    copyButtonBgHover: "#f3f4f6",
    copyButtonText: "#374151",
    copyButtonSuccess: "#22c55e",
    codeBlockBg: "#f1f5f9",
    codeBlockText: "#1e293b",
    dimText: "#94a3b8",
    setupCardBg: "#ffffff",
    setupBorder: "#e9e9e9",
    accent: "#224035",
    bodyText: "#444"
  };
  const [policies, setPolicies] = useState([]);
  const [setupInstructions, setSetupInstructions] = useState([]);
  const [toolsLoading, setToolsLoading] = useState(true);
  const [policiesLoading, setPoliciesLoading] = useState(false);
  const [setupLoading, setSetupLoading] = useState(false);
  const [toolsError, setToolsError] = useState(null);
  const [policiesError, setPoliciesError] = useState(null);
  const [setupError, setSetupError] = useState(null);
  const [policiesCount, setPoliciesCount] = useState(0);
  const [setupCount, setSetupCount] = useState(0);
  const [searchTerm, setSearchTerm] = useState("");
  const [currentPage, setCurrentPage] = useState(1);
  const [policiesListFetched, setPoliciesListFetched] = useState(false);
  const [setupListFetched, setSetupListFetched] = useState(false);
  const [policiesCountFetched, setPoliciesCountFetched] = useState(false);
  const [setupCountFetched, setSetupCountFetched] = useState(false);
  const TAB_ANCHORS = {
    tools: "tools",
    policies: "policies",
    setup: "setup-instructions"
  };
  const TAB_ANCHOR_STYLE = {
    scrollMarginTop: "120px"
  };
  const tagStyle = {
    display: "inline-block",
    padding: "4px 8px",
    marginRight: "8px",
    borderRadius: "4px",
    fontSize: "0.85em",
    fontWeight: "normal",
    backgroundColor: colors.tagBg,
    color: colors.tagText,
    border: `1px solid ${colors.tagBorder}`
  };
  function timeAgo(dateString) {
    if (!dateString) return "—";
    const now = new Date();
    const past = new Date(dateString);
    const seconds = Math.floor((now - past) / 1000);
    if (isNaN(seconds) || seconds < 0) return "—";
    const intervals = [{
      label: "year",
      seconds: 31536000
    }, {
      label: "month",
      seconds: 2592000
    }, {
      label: "week",
      seconds: 604800
    }, {
      label: "day",
      seconds: 86400
    }, {
      label: "hour",
      seconds: 3600
    }, {
      label: "minute",
      seconds: 60
    }, {
      label: "second",
      seconds: 1
    }];
    for (const interval of intervals) {
      const count = Math.floor(seconds / interval.seconds);
      if (count >= 1) {
        return `${count} ${interval.label}${count > 1 ? "s" : ""} ago`;
      }
    }
    return "just now";
  }
  const summarySectionStyle = {
    padding: "20px",
    border: `1px solid ${colors.panelBorder}`,
    borderRadius: "8px"
  };
  const headerCell = {
    borderBottom: `1px solid ${colors.headerBorder}`,
    padding: "8px 12px",
    background: colors.headerBg,
    fontWeight: "bold",
    textAlign: "left"
  };
  const cell = {
    borderBottom: `1px solid ${colors.cellBorder}`,
    padding: "8px 12px"
  };
  const resolveToolType = (tool = {}) => {
    const typeValue = tool.toolType || tool.type || tool.actionType || tool.category || "";
    return typeof typeValue === "string" ? typeValue : String(typeValue || "");
  };
  const filteredTools = useMemo(() => {
    const lowerCaseSearchTerm = searchTerm.toLowerCase();
    const filtered = allTools.filter(tool => {
      const name = (tool.name || "").toLowerCase();
      const type = resolveToolType(tool).toLowerCase();
      const description = (tool.description || "").toLowerCase();
      return name.includes(lowerCaseSearchTerm) || type.includes(lowerCaseSearchTerm) || description.includes(lowerCaseSearchTerm);
    });
    filtered.sort((a, b) => {
      const nameA = (a.name || "").toLowerCase();
      const nameB = (b.name || "").toLowerCase();
      return nameA.localeCompare(nameB);
    });
    if (currentPage > Math.ceil(filtered.length / ITEMS_PER_PAGE)) {
      setCurrentPage(1);
    }
    return filtered;
  }, [allTools, searchTerm, currentPage]);
  const totalPages = Math.ceil(filteredTools.length / ITEMS_PER_PAGE);
  const paginatedTools = useMemo(() => {
    const start = (currentPage - 1) * ITEMS_PER_PAGE;
    const end = start + ITEMS_PER_PAGE;
    return filteredTools.slice(start, end);
  }, [filteredTools, currentPage]);
  const [serverData, setServerData] = useState(null);
  const [dataError, setDataError] = useState(null);
  useEffect(() => {
    if (!serverName) return;
    let alive = true;
    const name = serverName.toLowerCase();
    function loadCatalogScript() {
      if (window[CATALOG_GLOBAL]) return Promise.resolve(window[CATALOG_GLOBAL]);
      return new Promise((resolve, reject) => {
        const existing = document.querySelector(`script[data-bd-catalog]`);
        const el = existing ?? document.createElement("script");
        let settled = false;
        const done = (fn, arg) => {
          if (settled) return;
          settled = true;
          clearTimeout(timer);
          fn(arg);
        };
        const timer = setTimeout(() => done(reject, new Error(`catalog script timed out after ${CATALOG_TIMEOUT_MS}ms`)), CATALOG_TIMEOUT_MS);
        el.addEventListener("load", () => {
          if (window[CATALOG_GLOBAL]) done(resolve, window[CATALOG_GLOBAL]); else done(reject, new Error("catalog script loaded but set no global"));
        });
        el.addEventListener("error", () => done(reject, new Error("catalog script failed to load")));
        if (!existing) {
          el.src = CATALOG_SCRIPT_URL;
          el.async = true;
          el.setAttribute("data-bd-catalog", "");
          document.head.appendChild(el);
        }
      });
    }
    async function fromExport() {
      const catalog = await loadCatalogScript();
      return {
        entry: catalog[name] ?? null,
        absent: !catalog[name]
      };
    }
    (async () => {
      try {
        const {entry, absent} = await fromExport();
        if (!alive) return;
        if (absent) {
          setServerData(null);
          setDataError(`No catalog data for "${name}".`);
          return;
        }
        setServerData(entry);
        setDataError(null);
      } catch (err) {
        console.error("Published catalog unavailable:", err);
        if (alive) {
          setServerData(null);
          setDataError("Unable to load server data.");
        }
      }
    })();
    return () => {
      alive = false;
    };
  }, [serverName]);
  const wistiaId = useMemo(() => {
    return serverInfo?.demo?.split("/")?.pop()?.replace(".js", "")?.trim() || null;
  }, [serverInfo]);
  useEffect(() => {
    if (typeof window === "undefined") return;
    const pathMatch = window.location.pathname.match(/\/mcp-servers\/([^/?]+)/i);
    const fromPath = pathMatch ? pathMatch[1] : null;
    const params = new URLSearchParams(window.location.search);
    const fromQuery = params.get("name");
    const resolvedName = fromPath || fromQuery || initialServerName || "default";
    setServerName(resolvedName.toLowerCase());
  }, [initialServerName]);
  useEffect(() => {
    if (typeof window === "undefined") return;
    const hash = window.location.hash.replace("#", "");
    const nextTabKey = Object.entries(TAB_ANCHORS).find(([, anchor]) => anchor === hash)?.[0] || null;
    if (nextTabKey && nextTabKey !== tab) {
      setTab(nextTabKey);
    }
  }, [tab]);
  useEffect(() => {
    setPolicies([]);
    setPoliciesListFetched(false);
    setPoliciesCountFetched(false);
    setPoliciesCount(0);
    setSetupInstructions([]);
    setSetupListFetched(false);
    setSetupCountFetched(false);
    setSetupCount(0);
  }, [serverData]);
  useEffect(() => {
    if (!serverData) return;
    if (tab === "policies" && policies.length === 0 && !policiesLoading && !policiesListFetched) {
      readTabContent("policies", {
        setter: setPolicies,
        loadingSetter: setPoliciesLoading,
        errorSetter: setPoliciesError,
        countSetter: setPoliciesCount,
        transform: filterPublishedPolicies
      });
      setPoliciesListFetched(true);
    }
    if (tab === "setup" && setupInstructions.length === 0 && !setupLoading && !setupListFetched) {
      readTabContent("instructions", {
        setter: setSetupInstructions,
        loadingSetter: setSetupLoading,
        errorSetter: setSetupError,
        countSetter: setSetupCount
      });
      setSetupListFetched(true);
    }
  }, [tab, serverData, policies.length, policiesListFetched, policiesLoading, setupInstructions.length, setupLoading, setupListFetched]);
  useEffect(() => {
    if (!serverName) return;
    if (dataError) {
      setServerInfo(null);
      setAllTools([]);
      setToolsError(dataError);
      setToolsLoading(false);
      return;
    }
    if (!serverData) return;
    const overview = serverData.overview ?? ({});
    setServerInfo({
      name: overview["Server Name"] || serverName.toUpperCase() || "MCP Server",
      logo: overview.Logo || "https://via.placeholder.com/150?text=Logo",
      summary: overview.Summary || "No summary available.",
      howTo: overview.HowTo || "",
      category: overview.Category || "Uncategorized",
      managedBy: overview["Managed by"] || "Barndoor",
      lastUpdated: overview.lastUpdated || null,
      demo: overview.Demo || null
    });
    setAllTools(serverData.tools ?? []);
    setToolsError(null);
    setToolsLoading(false);
  }, [serverName, serverData, dataError]);
  useEffect(() => {
    if (!serverData) return;
    if (!policiesCountFetched) {
      readTabContent("policies", {
        countSetter: setPoliciesCount,
        transform: filterPublishedPolicies
      });
      setPoliciesCountFetched(true);
    }
    if (!setupCountFetched) {
      readTabContent("instructions", {
        countSetter: setSetupCount
      });
      setSetupCountFetched(true);
    }
  }, [serverData, policiesCountFetched, setupCountFetched]);
  function filterPublishedPolicies(list = []) {
    return list.filter((item = {}) => !item.unpublished);
  }
  const readTabContent = (role, {setter, loadingSetter, errorSetter, countSetter, transform} = {}) => {
    loadingSetter?.(true);
    errorSetter?.(null);
    try {
      const raw = serverData?.[role] ?? [];
      const content = transform ? transform(raw) : raw;
      setter?.(content);
      countSetter?.(content.length);
      return content;
    } finally {
      loadingSetter?.(false);
    }
  };
  const handleTabClick = (newTab, options = {
    updateURL: true
  }) => {
    setTab(newTab);
    if (!serverData) return;
    const anchorId = TAB_ANCHORS[newTab];
    if (options.updateURL && anchorId) {
      const {pathname, search} = window.location;
      window.history.replaceState({}, "", `${pathname}${search}#${anchorId}`);
    }
    if (newTab === "policies" && policies.length === 0 && !policiesLoading && !policiesListFetched) {
      readTabContent("policies", {
        setter: setPolicies,
        loadingSetter: setPoliciesLoading,
        errorSetter: setPoliciesError,
        countSetter: setPoliciesCount,
        transform: filterPublishedPolicies
      });
      setPoliciesListFetched(true);
    }
    if (newTab === "setup" && setupInstructions.length === 0 && !setupLoading && !setupListFetched) {
      readTabContent("instructions", {
        setter: setSetupInstructions,
        loadingSetter: setSetupLoading,
        errorSetter: setSetupError,
        countSetter: setSetupCount
      });
      setSetupListFetched(true);
    }
  };
  const parseMarkdown = text => {
    if (!text) return null;
    const lines = text.split("\n");
    const result = [];
    let i = 0;
    while (i < lines.length) {
      const line = lines[i];
      const isCodeFence = l => l.trim().startsWith("```") || l.trim() === "`";
      if (isCodeFence(line)) {
        const fenceContent = line.trim().startsWith("```") ? line.slice(line.indexOf("```") + 3) : "";
        const codeBlock = fenceContent.trim() ? [fenceContent] : [];
        i++;
        while (i < lines.length && !isCodeFence(lines[i])) {
          codeBlock.push(lines[i]);
          i++;
        }
        if (i < lines.length) i++;
        result.push(<pre key={`code-${result.length}`} style={{
          margin: "12px 0",
          padding: "12px",
          background: colors.codeBg,
          border: `1px solid ${colors.codeBorder}`,
          borderRadius: "4px",
          fontSize: "0.9em",
          overflowX: "auto",
          whiteSpace: "pre-wrap",
          fontFamily: 'Menlo, Monaco, "Courier New", monospace',
          color: colors.codeBlockText
        }}>
            <code>{codeBlock.join("\n")}</code>
          </pre>);
      } else if (line.trim() === "") {
        result.push(<br key={`br-${result.length}`} />);
        i++;
      } else {
        result.push(<div key={`line-${result.length}`}>
            {parseInlineMarkdown(line)}
          </div>);
        i++;
      }
    }
    return result;
  };
  const parseInlineMarkdown = text => {
    const elements = [];
    function parseSegment(segment, key) {
      if (typeof segment !== "string") return segment;
      let result = [];
      let remaining = segment;
      let keyCounter = 0;
      while (remaining) {
        let earliestMatch = null;
        let matchType = null;
        const linkMatch = (/\[([^\]]+)\]\(([^)]+)\)/).exec(remaining);
        if (linkMatch && (!earliestMatch || linkMatch.index < earliestMatch.index)) {
          earliestMatch = linkMatch;
          matchType = "link";
        }
        const boldMatch = (/\*\*([^*]+)\*\*/).exec(remaining);
        if (boldMatch && (!earliestMatch || boldMatch.index < earliestMatch.index)) {
          earliestMatch = boldMatch;
          matchType = "bold";
        }
        const codeMatch = (/`([^`]+)`/).exec(remaining);
        if (codeMatch && (!earliestMatch || codeMatch.index < earliestMatch.index)) {
          earliestMatch = codeMatch;
          matchType = "code";
        }
        const italicMatch = (/\*([^*]+)\*(?!\*)/).exec(remaining);
        if (italicMatch && (!earliestMatch || italicMatch.index < earliestMatch.index)) {
          earliestMatch = italicMatch;
          matchType = "italic";
        }
        const underitalicMatch = (/_([^_]+)_/).exec(remaining);
        if (underitalicMatch && (!earliestMatch || underitalicMatch.index < earliestMatch.index)) {
          earliestMatch = underitalicMatch;
          matchType = "underitalic";
        }
        if (!earliestMatch) {
          if (remaining) result.push(remaining);
          break;
        }
        if (earliestMatch.index > 0) {
          result.push(remaining.slice(0, earliestMatch.index));
        }
        const matchContent = earliestMatch[1];
        let element;
        switch (matchType) {
          case "link":
            element = <a key={`${key}-link-${keyCounter++}`} href={earliestMatch[2]} target="_blank" rel="noopener noreferrer" style={{
              color: colors.linkText,
              textDecoration: "underline",
              cursor: "pointer"
            }}>
                {parseSegment(matchContent, `${key}-link-${keyCounter}`)}
              </a>;
            break;
          case "bold":
            element = <strong key={`${key}-bold-${keyCounter++}`}>
                {parseSegment(matchContent, `${key}-bold-${keyCounter}`)}
              </strong>;
            break;
          case "code":
            element = <code key={`${key}-code-${keyCounter++}`} style={{
              background: colors.inlineCodeBg,
              padding: "2px 6px",
              borderRadius: "3px",
              fontFamily: 'Menlo, Monaco, "Courier New", monospace',
              fontSize: "0.9em",
              color: colors.codeBlockText
            }}>
                {matchContent}
              </code>;
            break;
          case "italic":
            element = <em key={`${key}-italic-${keyCounter++}`}>
                {parseSegment(matchContent, `${key}-italic-${keyCounter}`)}
              </em>;
            break;
          case "underitalic":
            element = <em key={`${key}-underitalic-${keyCounter++}`}>
                {parseSegment(matchContent, `${key}-underitalic-${keyCounter}`)}
              </em>;
            break;
        }
        result.push(element);
        remaining = remaining.slice(earliestMatch.index + earliestMatch[0].length);
      }
      return result.length > 0 ? result : segment;
    }
    return parseSegment(text, "inline");
  };
  function ToolsTable({tools}) {
    return <table style={{
      width: "100%",
      borderCollapse: "collapse",
      marginTop: 8
    }}>
        <thead>
          <tr>
            <th style={{
      ...headerCell,
      width: "25%"
    }}>Tool Name</th>
            <th style={{
      ...headerCell,
      width: "55%"
    }}>Tool Description</th>
            <th style={{
      ...headerCell,
      width: "20%"
    }}>Tool Type</th>
          </tr>
        </thead>
        <tbody>
          {tools.length === 0 && searchTerm === "" ? <tr>
              <td style={cell} colSpan={3}>
                No tools have been documented yet.
              </td>
            </tr> : tools.length === 0 && searchTerm !== "" ? <tr>
              <td style={cell} colSpan={3}>
                No tools match your search criteria.
              </td>
            </tr> : tools.map((tool, idx) => <tr key={idx}>
                <td style={{
      ...cell,
      width: "25%"
    }}>{tool.name || "—"}</td>
                <td style={{
      ...cell,
      width: "55%"
    }}>
                  {tool.description || "—"}
                </td>
                <td style={{
      ...cell,
      width: "20%"
    }}>
                  {resolveToolType(tool) || "—"}
                </td>
              </tr>)}
        </tbody>
      </table>;
  }
  function PaginationControls({position = "bottom"}) {
    const [hoveredButton, setHoveredButton] = useState(null);
    const startIndex = filteredTools.length === 0 ? 0 : (currentPage - 1) * ITEMS_PER_PAGE + 1;
    const endIndex = Math.min(filteredTools.length, currentPage * ITEMS_PER_PAGE);
    const showingLabel = filteredTools.length === 0 ? "No tools to display" : `Showing ${startIndex}-${endIndex} of ${filteredTools.length}`;
    const buttonStyle = (disabled, isHovered) => ({
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      padding: "0 12px",
      cursor: disabled ? "not-allowed" : "pointer",
      border: `1px solid ${disabled ? colors.buttonBorderDisabled : isHovered ? colors.buttonBorderHover : colors.buttonBorder}`,
      borderRadius: "8px",
      background: isHovered && !disabled ? colors.buttonBgHover : colors.buttonBg,
      color: disabled ? colors.buttonTextDisabled : colors.buttonText,
      width: "36px",
      height: "36px",
      minWidth: "36px",
      minHeight: "36px",
      gap: "8px",
      opacity: disabled ? 0.6 : 1,
      transition: "all 0.2s ease",
      fontSize: "16px",
      fontWeight: "500"
    });
    return <div style={{
      display: "flex",
      justifyContent: "space-between",
      alignItems: "center",
      gap: "16px",
      flexWrap: "wrap",
      marginTop: position === "top" ? "0" : "0px",
      marginBottom: position === "top" ? "0px" : "0",
      padding: "12px 0",
      borderTop: "none",
      borderBottom: position === "bottom" ? `1px solid ${colors.buttonBorderDisabled}` : "none",
      background: colors.buttonBg
    }}>
        <div style={{
      display: "flex",
      flexDirection: "column",
      gap: "4px"
    }}>
          <div style={{
      fontSize: "0.95em",
      color: colors.buttonText,
      fontWeight: 500
    }}>
            {filteredTools.length === 0 ? "No tools to display" : `Page ${currentPage} of ${totalPages}`}
          </div>
          <div style={{
      fontSize: "0.85em",
      color: colors.mutedText
    }}>
            {showingLabel}
          </div>
        </div>
        <nav style={{
      display: "flex",
      gap: "4px",
      alignItems: "center"
    }}>
          <button onClick={() => setCurrentPage(1)} disabled={currentPage === 1} title="First page" onMouseEnter={() => setHoveredButton("first")} onMouseLeave={() => setHoveredButton(null)} style={buttonStyle(currentPage === 1, hoveredButton === "first")}>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M6 2L2 8L6 14M10 2L6 8L10 14" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </button>
          <button onClick={() => setCurrentPage(prev => Math.max(1, prev - 1))} disabled={currentPage === 1} title="Previous page" onMouseEnter={() => setHoveredButton("prev")} onMouseLeave={() => setHoveredButton(null)} style={buttonStyle(currentPage === 1, hoveredButton === "prev")}>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M10 2L4 8L10 14" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </button>
          <button onClick={() => setCurrentPage(prev => Math.min(totalPages, prev + 1))} disabled={currentPage === totalPages || totalPages === 0} title="Next page" onMouseEnter={() => setHoveredButton("next")} onMouseLeave={() => setHoveredButton(null)} style={buttonStyle(currentPage === totalPages || totalPages === 0, hoveredButton === "next")}>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M6 2L12 8L6 14" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </button>
          <button onClick={() => setCurrentPage(totalPages)} disabled={currentPage === totalPages || totalPages === 0} title="Last page" onMouseEnter={() => setHoveredButton("last")} onMouseLeave={() => setHoveredButton(null)} style={buttonStyle(currentPage === totalPages || totalPages === 0, hoveredButton === "last")}>
            <svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path d="M10 2L14 8L10 14M6 2L10 8L6 14" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
          </button>
        </nav>
      </div>;
  }
  function PoliciesTable({policies, loading, error}) {
    const [copiedIndex, setCopiedIndex] = useState(null);
    if (loading) return <p>Loading Policies...</p>;
    if (error) return <p style={{
      color: colors.errorText
    }}>Error loading policies: {error}</p>;
    if (policies.length === 0) {
      return <div style={{
        paddingTop: "10px",
        paddingBottom: "10px"
      }}><p>There are no policy examples for this MCP server.</p></div>;
    }
    const gridTemplate = "minmax(100px, 0.4fr) minmax(260px, 0.8fr) minmax(360px, 1.2fr)";
    const headerRowStyle = {
      display: "grid",
      gridTemplateColumns: gridTemplate,
      background: colors.tableHeaderBg,
      borderBottom: `2px solid ${colors.tableHeaderBorder}`,
      fontWeight: 600,
      fontSize: "0.95rem"
    };
    const bodyRowStyle = {
      display: "grid",
      gridTemplateColumns: gridTemplate,
      borderBottom: `1px solid ${colors.tableCellBorder}`
    };
    const headerCellStyle = {
      padding: "14px 12px",
      lineHeight: 1.4
    };
    const cellStyle = {
      padding: "16px 12px",
      lineHeight: 1.5,
      wordBreak: "break-word",
      overflowWrap: "break-word",
      whiteSpace: "normal",
      background: colors.tableBg
    };
    const getPrettyPrintedCode = code => {
      try {
        const parsed = JSON.parse(code.trim());
        return JSON.stringify(parsed, null, 2);
      } catch (e) {
        return code.trim();
      }
    };
    const handleCopy = (code, idx) => {
      if (typeof navigator === "undefined" || !navigator.clipboard) return;
      navigator.clipboard.writeText(getPrettyPrintedCode(code)).catch(() => {});
      setCopiedIndex(idx);
      setTimeout(() => setCopiedIndex(null), 2000);
    };
    return <div style={{
      overflowX: "auto"
    }}>
        <div style={{
      width: "100%",
      minWidth: "720px",
      border: `1px solid ${colors.tableHeaderBorder}`,
      borderRadius: "12px",
      background: colors.tableBg,
      boxShadow: isDark ? "0 1px 3px rgba(0, 0, 0, 0.3)" : "0 1px 3px rgba(15, 23, 42, 0.05)"
    }}>
          <div style={headerRowStyle}>
            <div style={headerCellStyle}>Policy Title</div>
            <div style={headerCellStyle}>Policy Description</div>
            <div style={headerCellStyle}>Policy Definition</div>
          </div>

          {policies.length === 0 ? <div style={{
      padding: "20px",
      textAlign: "center",
      color: colors.mutedText
    }}>
              No policies have been documented yet.
            </div> : policies.map((policy, idx) => <div key={idx} style={bodyRowStyle}>
                <div style={{
      ...cellStyle,
      fontWeight: 500
    }}>{policy.name || "—"}</div>
                <div style={cellStyle}>{policy.description || "—"}</div>
                <div style={{
      ...cellStyle,
      padding: "12px"
    }}>
                  {policy.code ? <div style={{
      position: "relative"
    }}>
                      <button type="button" onClick={() => handleCopy(policy.code, idx)} title="Copy to clipboard" style={{
      position: "absolute",
      top: 6,
      right: 6,
      display: "inline-flex",
      alignItems: "center",
      justifyContent: "center",
      width: "32px",
      height: "32px",
      border: `1px solid ${colors.copyButtonBorder}`,
      borderRadius: "6px",
      background: colors.copyButtonBg,
      cursor: "pointer",
      zIndex: 10,
      transition: "all 0.2s ease",
      color: copiedIndex === idx ? colors.copyButtonSuccess : colors.copyButtonText
    }} onMouseEnter={e => {
      if (copiedIndex !== idx) {
        e.currentTarget.style.backgroundColor = colors.copyButtonBgHover;
      }
    }} onMouseLeave={e => {
      e.currentTarget.style.backgroundColor = colors.copyButtonBg;
    }}>
                        {copiedIndex === idx ? <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
      width: "16px",
      height: "16px"
    }}>
                            <path d="M20 6 9 17l-5-5"></path>
                          </svg> : <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{
      width: "16px",
      height: "16px"
    }}>
                            <rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect>
                            <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path>
                          </svg>}
                      </button>
                      <div style={{
      overflowX: "auto",
      maxWidth: "100%"
    }}>
                        <pre style={{
      margin: 0,
      padding: "10px",
      fontSize: "0.82rem",
      fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
      background: colors.codeBlockBg,
      borderRadius: "6px",
      whiteSpace: "pre",
      lineHeight: "1.45",
      color: colors.codeBlockText,
      minWidth: "fit-content"
    }}>
                          <code>{getPrettyPrintedCode(policy.code)}</code>
                        </pre>
                      </div>
                    </div> : <span style={{
      color: colors.dimText
    }}>—</span>}
                </div>
              </div>)}
        </div>
      </div>;
  }
  function SetupInstructions({instructions, loading, error, serverInfo}) {
    if (loading) return <p>Loading Setup Instructions...</p>;
    if (error) return <p style={{
      color: colors.errorText
    }}>Error loading instructions: {error}</p>;
    if (instructions.length === 0) {
      return <p style={{
        paddingTop: "10px",
        paddingBottom: "10px"
      }}>There are no special setup instructions for this MCP server.</p>;
    }
    const instructionItemStyle = {
      padding: "16px 0",
      borderBottom: `1px solid ${colors.setupBorder}`,
      listStyleType: "none"
    };
    const lastItemStyle = {
      ...instructionItemStyle,
      borderBottom: "none"
    };
    return <div style={{
      padding: "16px",
      background: colors.setupCardBg,
      borderRadius: "8px",
      boxShadow: isDark ? "0 1px 3px rgba(0,0,0,0.3)" : "0 1px 3px rgba(0,0,0,0.05)"
    }}>
        <h3 style={{
      borderBottom: `2px solid ${colors.accent}`,
      color: colors.accent,
      paddingBottom: "8px",
      marginBottom: "16px"
    }}>
          Setup Guide for {serverInfo.name}
        </h3>
        <ol style={{
      padding: 0,
      margin: 0,
      counterReset: "step-counter"
    }}>
          {instructions.map((item, idx) => {
      const isLast = idx === instructions.length - 1;
      const currentStyle = isLast ? lastItemStyle : instructionItemStyle;
      return <li key={idx} style={currentStyle}>
                <div style={{
        display: "flex",
        alignItems: "flex-start",
        gap: "12px"
      }}>
                  <div style={{
        background: colors.accent,
        color: isDark ? "#101816" : "white",
        borderRadius: "50%",
        width: "28px",
        height: "28px",
        display: "flex",
        alignItems: "center",
        justifyContent: "center",
        fontWeight: "bold",
        fontSize: "0.9em",
        flexShrink: 0
      }}>
                    {idx + 1}
                  </div>

                  <div>
                    {item.name && <strong style={{
        display: "block",
        color: colors.accent,
        marginBottom: "4px"
      }}>
                        {item.name}
                      </strong>}

                    <div style={{
        margin: 0,
        color: colors.bodyText,
        lineHeight: 1.6
      }}>
                      {parseMarkdown(item.description)}
                    </div>

                    {item.code && <pre style={{
        margin: "12px 0 0 0",
        padding: "12px",
        background: colors.codeBg,
        border: `1px solid ${colors.codeBorder}`,
        borderRadius: "4px",
        fontSize: "0.9em",
        overflowX: "auto",
        whiteSpace: "pre-wrap",
        color: colors.codeBlockText
      }}>
                        {item.code}
                      </pre>}
                  </div>
                </div>
              </li>;
    })}
        </ol>
      </div>;
  }
  if (!serverInfo) {
    if (toolsError && !toolsLoading) {
      return <div className="flex flex-col items-center justify-center py-32 text-center">
          <p className="text-lg text-red-600 dark:text-red-400 mb-4">
            Unable to load server details.
          </p>
          <p className="text-gray-500 dark:text-gray-400">{toolsError}</p>
        </div>;
    }
    return <div className="flex items-center justify-center py-32">
        <p className="text-lg text-gray-500 dark:text-gray-400">Loading MCP Server details...</p>
      </div>;
  }
  const lastUpdatedDate = serverInfo?.lastUpdated ? new Date(serverInfo.lastUpdated) : null;
  const hasValidLastUpdated = lastUpdatedDate && !isNaN(lastUpdatedDate.getTime());
  const displayedPoliciesCount = policies.length > 0 ? policies.length : policiesCount;
  const hasPolicies = policies.length > 0 ? true : policiesCountFetched ? policiesCount > 0 : true;
  return <div className="not-prose space-y-8">
      {serverInfo && <div className="not-prose -mx-6 sm:-mx-8 lg:mx-0">
          <div className="border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-transparent serverContentMain">
            <div className="px-6 py-12 sm:px-8 lg:px-16">
              <div className="mx-auto max-w-7xl">
                <div className="flex flex-col gap-10 lg:flex-row lg:items-start lg:gap-16">

                  {}
                  <div className="flex-1">

                    {}
                    <div className="flex items-center gap-5">
                      <img src={serverInfo.logo} alt={`${serverInfo.name} Logo`} className="h-16 w-16 rounded-xl shadow-lg ring-1 ring-black/5 serverInfoLogo" />
                      <h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-gray-100 serverInfoName">
                        {serverInfo.name}
                      </h1>
                    </div>

                    <div className="mt-4 flex flex-wrap items-center gap-3 serverInfoCapsules">
                      <span className="inline-flex items-center gap-2 rounded-lg bg-gray-100 dark:bg-gray-800 px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 ring-1 ring-gray-600/20 dark:ring-gray-400/20">
                        Secure
                      </span>
                      <span className="inline-flex items-center gap-2 rounded-lg bg-green-50 dark:bg-green-950 px-4 py-2 text-sm font-medium text-green-700 dark:text-green-400 ring-1 ring-green-600/20 dark:ring-green-400/20">
                        {serverInfo.managedBy === "Barndoor" || serverInfo.managedBy === "N/A" ? "Barndoor Managed" : serverInfo.managedBy === "Remote" ? "Official Remote" : serverInfo.managedBy}
                      </span>
                      <span className="inline-flex items-center gap-2 rounded-lg bg-blue-50 dark:bg-blue-950 px-4 py-2 text-sm font-medium text-blue-700 dark:text-blue-400 ring-1 ring-blue-600/20 dark:ring-blue-400/20">
                        {serverInfo?.category || "Uncategorized"}
                      </span>
                    </div>

                    <div className="mt-6 space-y-4 text-lg leading-8 text-gray-600 dark:text-gray-400 max-w-5xl not-prose serverInfoSummaryAndHowTo">
                      {serverInfo.summary && <>
                          <p>
                            <strong>Summary:</strong> {serverInfo.summary}
                          </p> <br /> <br /></>}

                      {serverInfo.howTo && <p>
                          <strong>How Teams Use It:</strong> {serverInfo.howTo}
                        </p>}
                    </div>

                    {wistiaId && <div className="mt-6 max-w-lg" style={{
    marginLeft: "auto",
    marginRight: "auto"
  }}>
                        <div style={{
    position: "relative",
    paddingBottom: "56.25%",
    height: 0,
    width: "100%",
    maxWidth: "680px",
    borderRadius: "12px",
    overflow: "hidden",
    boxShadow: "0 10px 25px rgba(15, 23, 42, 0.1)"
  }}>
                          <iframe src={`https://fast.wistia.net/embed/iframe/${wistiaId}?seo=false&videoFoam=true`} title={`${serverInfo.name} demo video`} allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen style={{
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%",
    border: 0
  }}></iframe>
                        </div>
                      </div>}



                  </div>
                  <div className="w-full max-w-md lg:w-auto">
                    <dl className="grid grid-cols-1 gap-6 sm:grid-cols-3 lg:grid-cols-1 lg:gap-8">
                      <div className="rounded-xl bg-gray-50 dark:bg-gray-900 px-6 py-5">
                        <dt className="text-sm font-medium text-gray-500 dark:text-gray-400">Tools</dt>
                        <dd className="mt-2 text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-100">
                          {allTools.length.toLocaleString()}
                        </dd>
                      </div>

                      {hasPolicies && <div className="rounded-xl bg-gray-50 dark:bg-gray-900 px-6 py-5">
                          <dt className="text-sm font-medium text-gray-500 dark:text-gray-400">Policies</dt>
                          <dd className="mt-2 text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-100">
                            {displayedPoliciesCount.toLocaleString()}
                          </dd>
                        </div>}



                      {}
                      {}
                    </dl>

                  </div>

                </div>
              </div>
            </div>
          



            {}
            <div className="border-b border-gray-200 dark:border-gray-800 xl:-mx-20 bg-white dark:bg-transparent">
              <div className="mint-px-6 sm:mint-px-8 lg:mint-px-16" style={{
    paddingTop: "30px",
    paddingBottom: "0px"
  }}>
                <div className="mx-auto max-w-7xl">
                  <nav className="-mb-px flex space-x-8" style={{
    marginBottom: "-1px"
  }}>
                  {[{
    id: "tools",
    label: `Tools (${allTools.length})`
  }, {
    id: "policies",
    label: "Policy Examples"
  }, {
    id: "setup",
    label: "Setup Instructions"
  }].map(item => <button key={item.id} onClick={() => handleTabClick(item.id)} className={`
                  whitespace-nowrap border-b-2 px-1 pt-4 pb-4 text-sm font-medium transition-colors
                  ${tab === item.id ? "border-blue-600 text-blue-600 dark:border-blue-400 dark:text-blue-400" : "border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 dark:text-gray-400 dark:hover:border-gray-600 dark:hover:text-gray-300"}
                `}>
                      {item.label}
                    </button>)}
                  </nav>
                </div>
              </div>
            </div>

            <div className="px-6 sm:px-8 lg:px-16">
              <div className="mx-auto max-w-7xl">


              {}
              <div id={TAB_ANCHORS.tools} style={TAB_ANCHOR_STYLE} />
              {tab === "tools" && <>
                  {toolsError && <p style={{
    color: colors.errorText,
    marginTop: 12
  }}>{toolsError}</p>}
                  {toolsLoading ? <p>Loading Tools...</p> : <>
                      <div style={{
    padding: "0 16px",
    marginTop: "5px"
  }}>
                        <PaginationControls position="top" />
                      </div>
                      <div style={{
    padding: "8px 16px"
  }}>
                        <input type="text" placeholder="Search tools by name, type, or description..." value={searchTerm} onChange={e => {
    setSearchTerm(e.target.value);
    setCurrentPage(1);
  }} style={{
    width: "calc(100% - 20px)",
    padding: "10px",
    marginTop: "0",
    border: `1px solid ${colors.codeBorder}`,
    borderRadius: "6px",
    fontSize: "1em",
    background: colors.buttonBg,
    color: colors.buttonText
  }} />
                      </div>
                      <div style={{
    padding: "0 16px"
  }}>
                        <ToolsTable tools={paginatedTools} />
                      </div>
                      {filteredTools.length > ITEMS_PER_PAGE && <div style={{
    padding: "0 16px"
  }}>
                          <PaginationControls position="bottom" />
                        </div>}
                    </>}
                </>}

              <div id={TAB_ANCHORS.policies} style={TAB_ANCHOR_STYLE} />
              {tab === "policies" && <div style={{
    paddingTop: "10px"
  }}>
                  <PoliciesTable policies={policies} loading={policiesLoading} error={policiesError} />
                </div>}

              <div id={TAB_ANCHORS.setup} style={TAB_ANCHOR_STYLE} />
              {tab === "setup" && serverInfo && <div style={{
    paddingTop: "10px",
    paddingBottom: "10px"
  }}>
                  <SetupInstructions instructions={setupInstructions} loading={setupLoading} error={setupError} serverInfo={serverInfo} />
                </div>}
              </div>
            </div>
          </div>
        </div>}

    </div>;
}

<ServerDetails serverName="railway" />
