Rams MCP · The full engine, now in your coding agent
danny-avila on GitHub

danny-avila/librechat

Reviewed against Rams quality heuristics: accessibility, color, typography, spacing, components, motion, UX, and craft.

30 files reviewed·July 9, 2026

Top fix

Wire the dialog's accessible name to its visible heading

See the fix

Verdict

Good instincts, sloppy wiring: components get the hard accessibility calls right in concept but fumble the execution on focus, labels, and timing. The biggest risk is stacked dialogs and unconfirmed clipboard toasts creating real focus and trust failures for users.

Files Rams reviewed

client/src/components/Auth/AuthLayout.tsx

client/src/components/Chat/Input/TokenUsage/index.tsx

client/src/components/SidePanel/MCPBuilder/MCPServerDialog/index.tsx

client/src/components/Agents/AgentCard.tsx

client/src/components/Agents/AgentContact.tsx

client/src/components/Agents/AgentDetail.tsx

client/src/components/Agents/AgentDetailContent.tsx

client/src/components/Agents/AgentGrid.tsx

client/src/components/Agents/CategoryTabs.tsx

client/src/components/Agents/ErrorDisplay.tsx

client/src/components/Agents/MarketplaceAdminSettings.tsx

client/src/components/Agents/SearchBar.tsx

client/src/components/Agents/SmartLoader.tsx

client/src/components/Agents/VirtualizedAgentGrid.tsx

client/src/components/Artifacts/Artifact.tsx

client/src/components/Artifacts/ArtifactButton.tsx

client/src/components/Artifacts/ArtifactCodeEditor.tsx

client/src/components/Artifacts/ArtifactPreview.tsx

client/src/components/Artifacts/ArtifactTabs.tsx

client/src/components/Artifacts/ArtifactVersion.tsx

client/src/components/Artifacts/Artifacts.tsx

client/src/components/Artifacts/Code.tsx

client/src/components/Artifacts/DownloadArtifact.tsx

client/src/components/Audio/TTS.tsx

client/src/components/Audio/Voices.tsx

client/src/components/Auth/Footer.tsx

client/src/components/Auth/Login.tsx

client/src/components/Auth/LoginForm.tsx

client/src/components/Auth/Registration.tsx

client/src/components/Auth/RequestPasswordReset.tsx

92/100

Accessibility

4 serious
Accessibility·client/src/components/Chat/Input/TokenUsage/index.tsx:104Serious

Popover dialog suppresses focus ring with no visible replacement

`Ariakit.Popover` carries `focus:outline-none` in its className, and the comment above confirms it's suppressing the ring intentionally while `finalFocus` returns focus to the trigger on close.

Why it matters

When the dialog itself receives programmatic focus on open, a sighted keyboard user gets zero visual confirmation of where focus landed, which breaks WCAG 2.4.7 and makes the popover feel unresponsive to keyboard navigation.

Fix

Replace outline-none with a focus-visible ring on the popover container so keyboard focus stays visible.

className="z-[200] rounded-xl border border-border-medium bg-surface-secondary p-3 shadow-lg focus:outline-none"
className="z-[200] rounded-xl border border-border-medium bg-surface-secondary p-3 shadow-lg focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
Accessibility·client/src/components/Auth/AuthLayout.tsx:45Serious

"Click here" link gives screen-reader users no destination context

The invalid-token error renders `{localize('com_auth_click_here')}` as the entire visible link text, with the surrounding sentence split across separate localize calls outside the anchor.

Why it matters

Screen-reader users often pull a page's links into a standalone list; "Click here" read outside its sentence tells them nothing about where it goes or what it does, adding friction to an already stressful password-reset flow.

Fix

Give the link an aria-label that names its destination independent of surrounding text.

<a className="font-semibold text-green-600 hover:underline" href="/forgot-password">
  {localize('com_auth_click_here')}
</a>
<a
  className="font-semibold text-green-700 hover:underline"
  href="/forgot-password"
  aria-label={localize('com_auth_click_here')}
  title="Request a new password reset link"
>
  {localize('com_auth_click_here')}
</a>
Accessibility·client/src/components/Agents/AgentContact.tsx:29Serious

Mailto link's accessible name never tells screen readers it's an email action

The `<a href={`mailto:${contact.email}`}>` renders only `{label}` as its visible and accessible text, which is just the contact's name or bare email string, with no indication the link triggers an email client.

Why it matters

Screen reader users often navigate by a links list out of context; a link named only "Jane Doe" gives no clue what activating it does, which fails WCAG 2.4.4 link-purpose guidance.

Fix

Add an aria-label that states the action, e.g. 'Email {name or address}', while keeping the visible text unchanged.

<a href={`mailto:${contact.email}`} className="text-primary hover:underline">
  {label}
</a>
<a
  href={`mailto:${contact.email}`}
  className="text-primary hover:underline"
  aria-label={`${localize('com_agents_contact')}: ${label}`}
>
  {label}
</a>
Accessibility·client/src/components/Agents/AgentDetail.tsx:140Serious

Dialog has no accessible name wired to its visible heading

`OGDialogContent` renders a plain `<h2>` for the agent's name but never links it back to the dialog via `aria-labelledby` (and there's no `DialogTitle` primitive in sight).

Why it matters

Screen reader users opening this dialog hear an unnamed dialog instead of "[agent name] details", so they lose immediate context for what just opened.

Fix

Give the dialog container an aria-labelledby pointing at an id on the visible h2.

<OGDialogContent ref={dialogRef} className="max-h-[90vh] w-11/12 max-w-lg overflow-y-auto">
<OGDialogContent ref={dialogRef} aria-labelledby="agent-detail-title" className="max-h-[90vh] w-11/12 max-w-lg overflow-y-auto">
98/100

Typography

1 serious
Typography·client/src/components/Agents/AgentCard.tsx:83Serious

Truncated agent name and description have no way to reveal the full text

The agent name uses `line-clamp-2` and the description uses `line-clamp-2 md:line-clamp-5`, both with no `title` attribute or expand affordance. - `Label` (name): `line-clamp-2` - `<p>` (description): `line-clamp-2 md:line-clamp-5` Longer agent names or descriptions are cut off with no way for a sighted user to recover the hidden text (the dialog opens the full detail view, but nothing on the card signals that).

Why it matters

Users scanning a grid of agent cards can't tell whether a clipped name or description is complete, and have no lightweight way to check without opening the dialog for every card.

Fix

Add a native `title` attribute carrying the full text so truncated content is recoverable on hover.

<Label className="line-clamp-2 text-base font-semibold text-text-primary md:text-lg">
  {agent.name}
</Label>
<Label
  className="line-clamp-2 text-base font-semibold text-text-primary md:text-lg"
  title={agent.name}
>
  {agent.name}
</Label>

Want this on your repo?

Rams reviews your next PR automatically and posts inline fix suggestions.

Review my public repo for free
98/100

UX

1 serious
UX·client/src/components/SidePanel/MCPBuilder/MCPServerDialog/index.tsx:165Serious

"Copied to clipboard" toast fires before the copy actually happens

In the redirect URI copy button's `onClick`, `showToast({ message: localize('com_ui_copied_to_clipboard') })` runs immediately, then `copyLink(setIsCopying)` is called after.

Why it matters

If the clipboard write fails (permissions, insecure context, browser quirk), the user already saw a success toast and has no way to know the copy didn't happen. They'll paste a stale or empty value and lose trust in the field.

Fix

Await the copy result and only show the success toast after copyLink resolves; show an error state if it rejects.

onClick={() => {
  if (isCopying) return;
  showToast({ message: localize('com_ui_copied_to_clipboard') });
  copyLink(setIsCopying);
}}
onClick={async () => {
  if (isCopying) return;
  try {
    await copyLink(setIsCopying);
    showToast({ message: localize('com_ui_copied_to_clipboard') });
  } catch {
    showToast({ message: localize('com_ui_copy_failed'), status: 'error' });
  }
}}

Color

No issues found

Spacing

No issues found

Components

No issues found

Motion

No issues found

Craft

No issues found

Working well

  • The comments on `finalFocus` and the popover focus behavior explain the *why* (screen readers need to enter and announce the breakdown, the composer's global focus logic would otherwise steal it) rather than narrating the code line by line. That's the difference between a comment that earns its keep and filler.
  • The copy button keeps a constant aria-label (`com_ui_copy_link`) while swapping the visible icon between `Copy` and `CopyCheck` based on `isCopying`. Icon-only toggle controls should announce their fixed purpose rather than flipping label text, which matches the recommended pattern for state-driven icon swaps.
  • The config gate in `TokenUsage` (returning null until `startupConfig` loads and checking `contextUsage === false`) prevents both a flash of the indicator and a wasted token-config query on disabled deployments. That's the right place to short-circuit: before any hook or query fires, not after render.
  • The delete trigger's aria-label uses `com_ui_delete_mcp_server_name` with the actual server title, not a generic "Delete" label. This is the right pattern for icon-only destructive controls: a screen reader user hears exactly what will be deleted before activating it, without needing sighted context.

Score your own repo with Rams.

Free on public repos. Rams reviews every pull request for accessibility, color, type, spacing, components, motion, UX, and craft, and posts inline fixes.