Skip to content

[WidgetsMCP]: First look at the open targets widgets-mcp - #998

Open
carcruz wants to merge 82 commits into
mainfrom
cc-mcp-ui
Open

[WidgetsMCP]: First look at the open targets widgets-mcp#998
carcruz wants to merge 82 commits into
mainfrom
cc-mcp-ui

Conversation

@carcruz

@carcruz carcruz commented Jul 9, 2026

Copy link
Copy Markdown
Member

[WidgetsMCP]: First look at the open targets widgets-mcp

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist:

  • I have commented my code, particularly in hard-to-understand areas
  • My changes generate no new warnings
  • I have made corresponding changes to the documentation

carcruz added 29 commits July 21, 2026 14:59
Migrates from react-router-dom v6.30.4 to the unified react-router
package (v7.18.1) across apps/platform, apps/docs, packages/ui, and
packages/sections. react-router-dom was retired in favor of the
unified package; v8 was skipped since it requires React 19, which
would force an unrelated major upgrade. Usage is purely declarative
(BrowserRouter/Routes/Route/hooks), so this is a straightforward
import-path migration with no behavioral changes.

Also fixes tech debt surfaced while verifying this change (confirmed
unrelated to react-router, predating this branch): adds the
repo-wide-missing @types/lodash package, declares 3dmol and lodash
as explicit dependencies where they were used but undeclared
(relying on accidental hoisting), and restores skipLibCheck to true
to stop type-checking third-party packages' broken .d.ts files.
It's Yarn Berry's internal resolution cache, regenerated on every
install, and not needed since this repo doesn't use Zero-Installs
(nodeLinker: node-modules, no committed .yarn/cache).
Applies the existing lazy-wrapper pattern (already used by
Downloads/API/Projects pages) to the 8 remaining eager pages:
Disease, Drug, Target, Evidence, Variant, Study, CredibleSet, and
Analysis. Each gets a thin Wrapper that lazy-imports the real page
behind a Suspense boundary; App.tsx itself is unchanged since it
already imports pages through their directory index.

HomePage and NotFoundPage stay eager intentionally: HomePage is the
default landing route (lazy-loading it would add a loading flash for
the most common entry point), and NotFoundPage is rendered inline
without a Suspense boundary in a couple of data pages.

Cuts the main platform bundle from ~9MB (2.66MB gzip) to ~2.6MB
(767KB gzip); heavy dependencies like cytoscape (5.2MB) and the
associations network view now load on demand instead of upfront.
CI run for this PR showed the disease page header test consistently
timing out at the default 5000ms expect() timeout - Playwright's own
page snapshot at failure time showed the page still on its loading
spinner, not a broken page (other tests confirm the same page renders
correctly with more time).

Root cause: page components are now code-split (previous commit), so
the first navigation to any of the 8 newly-lazy pages needs to fetch
an extra JS chunk before rendering, on top of normal query latency.
That's normally trivial (~5-12KB gzipped) but a cold Netlify PR
preview plus CI runner contention pushed it past 5s here. The same
profile-page-header-text assertion pattern is used across multiple
page specs (disease, target, drug, etc.), so this is scoped as a
global default rather than a single-test patch.
…outer-v7

# Conflicts:
#	apps/docs/package.json
#	apps/platform/package.json
#	packages/sections/package.json
#	packages/ui/package.json
#	yarn.lock
… analysis, and implement lazy loading for Viewer component
Both were importing from the deprecated @mui/styles path, which also
meant their theme type wasn't augmented with MUI's palette/spacing
(surfaced as spurious "Property 'palette' does not exist" tsc errors).
Part of removing @mui/styles ahead of an MUI v6/v7 upgrade.
Same fix as packages/ui: these files imported useTheme from the
deprecated @mui/styles path instead of @mui/material/styles.
Same fix as packages/ui and packages/sections: these files imported
useTheme/styled from the deprecated @mui/styles path instead of
@mui/material/styles.
Replaces sectionStyles.ts (makeStyles/JSS) with colocated
SectionItem.styles.ts using @mui/material/styles' styled(). Drops
three style keys (cardHeader, cardHeaderAction, loadingPlaceholder)
that were defined but never applied anywhere.

Preserves the exact current rendering, including two pre-existing JSS
cascade quirks: avatarHasData's `!important` background always beat
avatarError's non-important one, and descriptionHasData's rule was
declared after descriptionError's, so it always won at equal
specificity. In practice only the title ever visually turned "error"
colored; avatar and description did not. Left as-is here rather than
fixed, since this migration is scoped to be a pure refactor — worth a
follow-up decision on whether to make all three consistent.
Replaces summaryStyles.ts (makeStyles/JSS) with colocated
SummaryItem.styles.ts using @mui/material/styles' styled(). The old
hover cascade ("&:hover $titleHasData" etc., JSS class-ref selectors
with no sx equivalent) is reproduced via emotion's component-selector
interpolation, referencing the sibling styled components directly in
StyledCard's hover block.

Drops classes.cardError, which was referenced in SummaryItem's
classNames() call but never existed in summaryStyles.ts - a
pre-existing dead reference (confirmed by a matching tsc error that's
now gone). Also drops the cardContent/content style keys, which were
defined but never applied anywhere.

Preserves a pre-existing JSS specificity quirk: subheaderError was
applied unconditionally (not gated on the actual error value) and its
rule came after subheaderHasData's, so it always won at equal
specificity - the subheader was always rendered in the error color.
Left as-is per the same "pure refactor" rule as the Section migration.
Without shouldForwardProp, styled("div") forwards unknown props to the
DOM by default, which would leak an "error" attribute onto the
rendered element and trigger a React warning.
Replaces tableStyles.ts's makeStyles hooks (tableStyles,
globalSearchStyles) with styled() components, consumed by Table.tsx,
TableHeader.tsx, TableRow.tsx, and GlobalFilter.tsx.

Drops Table's untyped `classes` prop (classes.root/classes.table
merged via classNames) in favor of typed `containerSx`/`tableSx`
props. One real external consumer existed - PublicationsList.tsx in
packages/sections, which used classes.root to zero out the
container's marginTop - migrated to `containerSx={{ marginTop: 0 }}`
and its own single-purpose makeStyles hook removed as a drive-by
(it existed only to produce that one class).

Also drops TableHeader's `headerClasses.headerSpan` reference, which
was never defined in tableStyles.ts and was already a no-op (JSS
resolved it to undefined, silently dropped by classnames() as a
falsy positional arg) - confirmed by a matching tsc error that's now
gone. And drops the never-applied `tooltipCardContent` style key.

Verified &:first-child/&:last-child in the header/body cell styles
still target the correct DOM element: the Hidden wrapper around each
cell renders a bare React.Fragment (no DOM node) when visible, so
TableCell remains a direct, adjacent child - no behavior change from
preserving these selectors verbatim.
Tooltip.tsx was the one real exception to the rest of this migration:
it called makeStyles inline in the render body, merged with a `style`
prop, for genuine per-instance overrides rather than static theming.

Traced actual usage: only classes.tooltip and classes.tooltipIcon were
ever applied to any element - tooltipBadge and tooltipArrow were
defined but never used. Of the two real (non-empty) `style` callers,
only ClinicalRecordDrawer.tsx's { tooltipIcon: {...} } shape ever hit
a key the base object actually had (lodash's merge() deep-merges
matching keys). DirectionalityDrawer.tsx's { background: "red" } had
no matching key, so it was appended as an inert sibling property -
already a no-op today, preserved as such rather than "fixed".

Replaces the base tooltip/tooltipIcon styles with a styled(MUITooltip)
targeting .MuiTooltip-tooltip, and the per-instance tooltipIcon
override with a plain inline style spread on the <sup> element.
ArrowTurnDownLeft, BrokenSearchIcon, EllsWrapper, PlotContainer,
PlotContainerSection, ClinvarStars: static/single-instance makeStyles
usages converted to inline style/sx props (raw svg/div elements get
plain `style`, MUI components get `sx`; ClinvarStars' color moves to
FontAwesomeIcon's native `color` prop via useTheme).
Chip, NewChip, ChipList: converted to styled() since these are
reusable atoms rendered many times per page (ChipList renders per
item in a .map()). StyledChip needs `as typeof Chip` to preserve
MUI's polymorphic `component` prop typing, which styled() otherwise
drops.

LongList, LongText, OtLongText: single-instance styles converted to
sx (Typography/Box), since each style key maps to exactly one JSX
element per render.
Link.tsx is the highest-leverage file in this batch (rendered
hundreds of times per page), so it gets styled() with shouldForwardProp
filtering the footer/hasTooltip boolean props, rather than sx. Variant
priority (footer > tooltip > default) is preserved to match the old
JSS declaration-order precedence when multiple conditions overlap.

TepLink, CopyUrlButton: MUI Tooltip/Snackbar slot overrides (classes
prop) converted to styled() targeting the stable .MuiTooltip-tooltip /
.MuiSnackbarContent-* global class names.

XRefLinks, EmailLink: single-instance styles converted to sx/styled
as appropriate (EmailLink's <a> needs :hover, so styled("a") rather
than plain inline style).
…ckages/ui

DirectionOfEffectIcon, DataDownloader: MUI Tooltip/Snackbar slot
overrides converted to styled() targeting stable global class names.
GlobalSearchIcon: single fontSize override moved to a shared style
const passed via native `style` prop (raw FontAwesomeIcon, not an MUI
component). Highlights, LoadingBackdrop, OpenTargetsTitle:
single-instance styles converted to sx.
…es/ui

PublicationActionsTooltip: withStyles -> styled(Tooltip) targeting
.MuiTooltip-tooltip.

PublicationsDrawer: two makeStyles hooks removed. sourceDrawerStyles
had 5 dead keys (AccordionExpanded/AccordionRoot/AccordionSubtitle/
AccordionTitle/summaryBoxRoot) never referenced anywhere in this
file - dropped. listComponentStyles was never called as a hook at
all; its only reference was `listComponentStyles.AccordionSubtitle`,
a static property read on the hook function itself (always
undefined) - confirmed by a matching tsc error that's now gone.
Deleted the whole dead hook and the dead className reference.

PublicationSummary: createStyles wrapper removed (folded into this
file's full conversion instead of a separate import-only fix, since
it still had real makeStyles usage). Dropped its unused `fileLabel`
key.

PublicationWrapper: dropped its unused `matchTable` key.

Both PublicationSummary/PublicationWrapper's Box `sx` callbacks use
theme.palette.grey directly since MUI's spacing-string shorthand
doesn't apply to `background`.
…packages/ui

DirectionalityDrawer, KnownDrugsSourceDrawer: same drawer pattern as
PublicationsDrawer (Paper/Drawer/Typography slot overrides -> styled()
targeting stable MUI global class names). DirectionalityDrawer's
Accordion/summary classes were dead here (never used); dropped, unlike
KnownDrugsSourceDrawer's identical-looking keys which ARE used and get
a real styled(Accordion) with a &.Mui-expanded selector.

Footer: 5 separate makeStyles hooks (one per sub-component) converted
to sx/styled/inline style depending on instance count. Dropped the
unused linkContainer key. LicenseCC0's two Link instances need
styled(Link) since our Link component doesn't accept sx, only
className - styled() works generically here since Link forwards
className.

GlobalSearchListHeader: sectionHeader/label divs converted to Box+sx.
…es/ui

Header: dropped unused mainIconContainer key (superseded by the
existing iconHeaderStyles/iconTextStyles sx objects already in the
file), converted the rest to sx/color props.

HeaderMenu: menuLink applies to our custom Link component, which only
forwards className (not sx), so it needs styled(Link) rather than sx.

NavBar: the highest-risk file here (rendered on every page, the
navbar class carried the classic JSS-vs-MUI-defaults !important).
Converted to styled(AppBar) with a homepage boolean prop. Dropped 4
dead keys (flex, menuList, navLogo, navSearch) never referenced.
MenuExternalLink no longer takes `classes` as a prop - it's a
NavBar-local subcomponent, no external callers.

Page (both packages/ui/src/components/Page.tsx and
packages/ui/src/pages/Page.tsx - near-duplicate files) and EmptyPage:
converted to Box/Grid sx. EmptyPage drops an unused marginTop key and
an empty no-op messageLogoContainer key, and reuses one hiddenMobileSx
object across its two responsive elements via sx array syntax.
carcruz and others added 27 commits July 26, 2026 02:50
Bumped in all 7 workspaces that declare it directly (ui, sections,
platform, docs, and the 3 shared @ot/* packages - ot-config, ot-constants,
ot-utils were missed in the initial audit and would have caused a
duplicate v5/v6 MUI install if left behind). Added a react-is resolution
pin (^18.3.1) per MUI's v6 upgrade guide for React 18 projects.

Ran the official v6 codemods (styled, sx-prop, theme-v6) as a sanity
check; reverted their output - the styled codemod introduced two real
TS2304 "cannot find name 'hasData'" bugs in SummaryItem.styles.ts by
incompletely migrating ternary conditionals to the new variants API,
and touched 30 other files for a purely optional modernization (v5-style
conditional styled() already works unchanged in v6). Not worth the risk
for zero functional benefit.

Verified via a strict before/after tsc diff per package (stashing the
package.json/lockfile changes to get a true v5 baseline): zero new
errors in ui, sections, or platform. Every diff is either a cosmetic
MUI-internal type rename (e.g. an inlined union now named `Placement`)
or a pre-existing broken file (HeatmapTable.tsx) picking up the same
cosmetic rename. Full turbo build, biome check, and a 9-page Playwright
smoke test (home, target, disease, drug, variant, study, credible-set,
downloads, api) all clean.
Bumped in all 7 workspaces (same set as the v6 bump). Two required
changes per MUI's v7 upgrade guide:

- Renamed Grid -> GridLegacy across all 43 files using the classic
  xs=/md=/item prop API (grep for `<Grid\b` found 43, not the 36 an
  earlier single-line-import regex caught - 7 files use multi-line
  import statements that missed the first pass, including
  BaselineExpressionTable.tsx, caught via a before/after tsc diff
  showing a genuine new Grid-prop type error). No prop-level changes -
  this is a mechanical rename deferring the real Grid v2 migration
  (xs={12} -> size={{xs:12}}) to the eventual shadcn migration, where
  Grid gets replaced entirely anyway.
- Replaced <Hidden> (fully removed in v7) in TableHeader.tsx/TableRow.tsx.
  Its breakpoint props came from column.hidden, which is never set
  anywhere in the codebase - the wrapper was a permanent no-op, so it's
  simply removed rather than reimplemented.

One real v7 behavior change found via the browser smoke test (not
tsc): Dialog's `role` prop is now runtime-validated to "dialog" |
"alertdialog" only (PropTypes.oneOf, not just a TS type). Fixed
GlobalSearchDialog's intentional role="searchbox" (for accessibility)
by moving it to slotProps.paper.role, which lands on the same DOM
element without going through Dialog's own restricted prop.

Verified via strict before/after tsc diff per package (stashing to
get a true v6 baseline): every remaining diff is either the expected
Hidden-fix line shift or a cosmetic MUI-internal type rename
(Variant -> TypographyVariant, TypographyOptions ->
TypographyVariantsOptions, etc.) for already-broken pre-existing code.
Full turbo build, biome check, and a 9-page Playwright smoke test all
clean - including catching and fixing a stale Vite dependency-cache
issue (GridLegacy import failing at runtime despite tsc passing,
resolved by clearing node_modules/.vite and restarting).
sx's theme-path string resolution ("grey.50" -> theme.palette.grey[50])
applies to backgroundColor/bgcolor, not the generic `background`
shorthand. The original JSS migration (7e9aca8) wrote `background:
"grey.50"` instead of `backgroundColor`, which happened to still resolve
under MUI v5/v6 but silently stopped working under v7 - "grey.50" isn't
valid CSS for the `background` property, so it was dropped, leaving the
page background transparent (showing white body) instead of the
intended light grey. Confirmed by diffing against production, which
still renders the correct #fafafa background. Same bug in both Page.tsx
files (packages/ui/src/pages and packages/ui/src/components - a known
near-duplicate pair).
apps/platform already had this declaration (src/documentNode.d.ts) but
packages/ui and packages/sections didn't - each has its own separate
tsconfig, so the declaration doesn't carry over between them. Without
it, TypeScript (and therefore VS Code's hover/intellisense) can't
resolve any `import X from "./Y.gql"`, showing "Cannot find module"
on every single one - 148 files in sections, 7 in ui - each also
cascading into extra implicit-any errors on the resulting untyped
import elsewhere in the same file.

Mirrors platform's existing file exactly. Confirmed via tsc: 0 "Cannot
find module *.gql" errors remaining in either package (was 148+7),
~105 fewer total errors per package from the cascading any-noise
clearing up too.
…imports

Adds a facade layer in packages/ui for common @mui/material primitives
(Box, Typography, GridLegacy, etc.) so consumers import from "ui" instead
of "@mui/material" directly, making a future design-system swap (shadcn)
a single-point change. Pilot: packages/sections' 82 affected files moved
over; apps/platform and apps/docs left for a follow-up pass. Chip/Button/
Tooltip/Link intentionally excluded — ui already has custom wrapped
versions under those names.
ui already exports custom Chip/Button/Tooltip/Link under those bare
names, so raw MUI usages are re-exported as MuiChip/MuiButton/
MuiTooltip/MuiLink instead — collision-free, local identifiers
unchanged (import aliasing absorbs the rename, no JSX touched).

Follow-up: audit these ~19 raw usages against the custom wrappers'
prop APIs for possible consolidation (design consistency).
…ersions

Chip: widened ui's Chip prop type from a hand-picked subset (label:
ReactElement only) to Omit<MuiChipProps, "variant"|"size">, so it accepts
the full MUI API (color, sx, clickable, onClick, ReactNode label) while
still forcing the outlined/small look. Swapped the 7 sections call sites
whose props already matched (variant="outlined" size="small" exactly) to
use it, dropping the now-redundant props. Bonus: this also fixed 2
pre-existing type errors in ProfileChipList.tsx, which was already
fighting the old, too-strict label type.

3 Chip call sites (evidence/OTEncore, evidence/OTValidation, common/
Literature/Entities) and common/Bibliography/Body.tsx keep raw MuiChip —
their chips are filled/medium by default, a different design already in
place, not a fit for the fixed outlined/small contract.

Tooltip: OntologyTooltip.tsx had copy-pasted ui's Tooltip theme override
CSS verbatim. Exported the underlying styled component (StyledMUITooltip)
from ui instead of re-declaring it.

Button: audited but left alone — ui's custom Button hardcodes
`border: none`, which would strip the visible border off every
variant="outlined"/"contained" button (2 of 6 call sites use those).
Not a safe swap without changing the custom Button first.

Link: the 2 raw MuiLink imports found in the earlier facade pass turned
out to be pre-existing dead imports (already flagged by tsc as unused,
unrelated to this migration) — deleted them.
Rounds out the swap-point migration started in packages/sections: moved
apps/platform's 96 @mui/material-importing files onto the ui barrel.
Surfaced real gaps the sections-only census missed (multi-line imports
undercounted several names) — added Card*, Dialog*, Menu*, List*,
Accordion*, Stepper family, Divider, Stack, Snackbar, Radio*, Switch,
Select, Modal, Popover, NativeSelect, FormHelperText, Slide to the
facade; retroactively swept packages/sections too so both packages share
one complete list.

Chip/Button/Tooltip/Link raw usages aliased (MuiChip/MuiButton/
MuiTooltip/MuiLink) same as the sections pass — the collision/consistency
review from that pass (Button's border:none incompatibility, etc.)
applies here too and is deferred the same way.

Verified: tsc --noEmit on apps/platform, packages/sections, packages/ui
all produce byte-identical error sets before/after (line-shift only);
yarn build:platform succeeds.
ui's custom Button hardcoded border:none, which silently strips the
visible border off any variant="outlined"/"contained" button. Confirmed
via its one real pre-existing consumer (DataUploader.tsx), whose
variant="outlined" Back/Upload buttons have been rendering borderless
in production — a pre-existing bug, not introduced by this branch.

With that fixed, swapped 21 of the 26 sections/platform files carrying
raw MuiButton onto the real Button — all had zero startIcon/endIcon
usage, so the only remaining behavioral difference (Button's
".MuiButton-startIcon { fontSize: 14px !important }") is a no-op for
them. Bonus: ColumnOptionsMenu.tsx had locally re-declared that exact
CSS rule (plus a redundant border:none) on top of the raw import —
deleted the duplicate wrapper and used Button directly.

Left on raw MuiButton (5 files): EuropePmc/Publication.tsx builds its
own styled(Button) with explicit startIcon sizing; RunHistorySidebar,
StandaloneGeneInput, and ExportButtons pass startIcon on
outlined/small-or-medium buttons where the 14px override isn't
guaranteed to be visually identical to current rendering. Consolidating
those needs a visual check, not a mechanical swap.
Button's ".MuiButton-startIcon { fontSize: 14px !important }" was the
same class of bug as the border:none one fixed earlier: correct only for
size="medium" (whose default font-size is 14px anyway), wrong for
size="small" (13px) and size="large" (15px). Confirmed a no-op for the
one real consumer (DataUploader.tsx never sets size), so removing it
changes nothing today and fixes small/large going forward. Button is
now a genuine no-op wrapper around MuiButton — exactly the swap point
this facade is for.

With that gone, the remaining 4 raw-MuiButton call sites held back
last commit (EuropePmc/Publication, RunHistorySidebar,
StandaloneGeneInput, ExportButtons) have zero behavioral difference
from the real Button, so consolidated them too. All 26 Button call
sites across sections/platform now use the real ui Button.
Chip's variant/size were forced via Omit<MuiChipProps, "variant"|"size">,
which blocked the 4 remaining raw-MuiChip sites — they rely on MUI's
native filled/medium defaults for selectable/interactive chips, a
genuinely different design than the outlined/small "tag chip" look this
component was built for. Forcing them onto Omit-typed Chip would have
silently converted them.

Changed ChipProps to plain MuiChipProps (no Omit) and made the compact
sizing (height 20px, tight margins) conditional on the *resolved*
variant/size being outlined+small, computed inside styled()'s callback
rather than baked into the base class. variant="outlined" size="small"
in the component body are now defaults, not forced — {...props} already
spread last, so passing either explicitly overrides them; the 7
already-consolidated sites are unaffected since they don't pass either.

Swapped the last 4 sites (evidence/OTEncore, evidence/OTValidation,
common/Literature/Entities, common/Bibliography/Body) onto the real
Chip, adding explicit variant="filled" size="medium" (or size="medium"
alone where variant was already outlined) to each call site — since
they previously relied on MUI's implicit defaults by omission, and
Chip's own default is now outlined/small, that implicit reliance had to
become explicit to preserve their current look.

All 15 Chip call sites across sections/platform now use the real ui
Chip. Verified: tsc byte-identical, yarn build:platform green.
Same treatment as the sections pass: swapped all raw MuiChip imports to
the real ui Chip across apps/platform (ActiveFiltersPanel,
NoveltyInlinePanel, RunHistorySidebar, AnalysisForm, ResultsPlotlySunburst,
ResultsTable, SunburstFilters, ResultsTreeView, EnrichmentMapDetailsModal,
EnrichmentMapLegend, EnrichmentMapControls, DownloadsCard, DownloadsTags,
DownloadsFilter, HomePageSuggestions, ProjectsFilter, ProjectCard).

Most of these omitted variant (relying on MUI's native "filled" default)
or omitted size (relying on "medium"). Since Chip's own defaults are now
"outlined"/"small", every call site that previously relied on the
implicit MUI default had that made explicit (variant="filled" and/or
size="medium") to preserve its current look — 30+ individual Chip
instances touched, all mechanical prop additions, no new visual
behavior.

This batch had denser sx pixel-tuning than sections (height/fontSize
overrides on top of variant+size), so it depends on MUI's sx-over-styled
precedence to still apply the caller's own sizing on top of Chip's
conditional compact styling — standard, well-documented MUI cascade
behavior, not independently visually verified in this session (user
opted to proceed on that basis rather than screenshot-verify).

tsc byte-identical before/after aside from one pre-existing, unrelated
type error in HomePageSuggestions.tsx whose message text reordered
(same underlying error, not new). yarn build:platform green.
Chip was a plain function component, so it couldn't receive a ref.
common/Literature/Entities.tsx wraps each selectable-entity Chip in
<Grow>, which attaches a ref to its child to measure/animate it — that
combination threw in the browser once Entities.tsx was swapped from
raw MuiChip (which does forward its ref) onto the ui Chip, breaking
target profile's Bibliography section (reported: crashing on
localhost). Verified via Playwright against the running dev server:
error was "Invalid prop `children` supplied to `ForwardRef(Grow2)`",
traced through EntitiesToSelect -> Entities -> Grow -> Chip.

Wrapped Chip in forwardRef, forwarding to the underlying styled MUI
Chip. Re-verified in the browser: bibliography section (entity
selector chips, filters, publication list) now renders correctly on
BRAF's target profile.
The app theme (ot-config's theme.ts) forces a 1px grey border onto every
MuiButton root by default, regardless of variant. That border reads fine
on most buttons but not on AOTF's toolbar triggers (Advanced filters,
Column options, Upload diseases, Export) — a dense row of icon+label
dropdown-style buttons where the border looks noisy.

Added ButtonNoBorder (styled(Button) with border: none) alongside
Button/ButtonPrimary in ui's Button.tsx, plus an optional `noBorder`
prop on PopoverButton that swaps its base component. Applied it to all
4 flagged buttons: FacetsSearch and ExportMenu (both via PopoverButton's
new prop), ColumnOptionsMenu and DataUploader's upload trigger (direct
component swap). AnalysisMenu, the 5th button in the same row, also
uses PopoverButton but wasn't in scope — left with the default border.

Verified visually against the running dev server: all 4 buttons render
borderless, rest of the toolbar unaffected.
Replaced the noBorder boolean + internal branch with an `as` prop that
takes the actual button component (Button/ButtonNoBorder/...) — callers
now pass the real variant instead of a leaky flag that only encoded one
of them.

Also changed ButtonNoBorder itself: border is transparent by default
(same 1px reserved, so no layout shift) and turns visible on hover,
rather than being permanently invisible — gives the button a hover
affordance instead of looking dead. Verified default/hover states
against the running dev server.
The "Ot" prefix on custom ui components had no consistent rule — some
components had it (OtCodeBlock, OtPopper, OtBtnGroup...), most didn't
(Chip, Button, Tooltip, Table). Dropped it from the 7 components where
it was safe to do so with zero naming collision against the rest of
the barrel: BtnGroup, CodeBlock, CopyToClipboard, InvalidResultFilters,
Popper (aliased its own MuiPopper import to avoid self-shadowing),
ScoreLinearBar, and GenomicLocation (file was already unprefixed, only
the export alias carried "Ot").

Explicitly NOT renamed:
- OtAsyncTooltip: real infrastructure for the separate MCP widgets
  server repo, which likely imports "ui" directly — renaming would be
  a breaking change for a consumer outside this monorepo.
- OtTable/OtTableSSP: "Table" is already taken by the older MUI-table
  implementation (Table/DataTable) that OtTable's Tanstack-based
  replacement is mid-migration away from (63 vs 34 call sites) — a
  real convergence question, not a rename, and out of scope here.

Also merged LongText + OtLongText (near-identical, only real diff was
default label text) into one LongText with an optional `displayText`
prop. The two had a subtle structural difference beyond the default
text — LongText always prepends "... " before the bracketed link,
OtLongText didn't — preserved via the prop's presence/absence rather
than flattening them, so both existing behaviors survive unchanged.
Found and fixed one more deep-import consumer bypassing the barrel
(ClinicalRecordDrawer.tsx re missed by the barrel-only grep sweep, now
passes displayText="...show more" explicitly to preserve its exact
prior look).

Verified: tsc byte-identical/improved (a few pre-existing type errors
got fixed as a side effect — LongText's `variant` prop was oddly
required despite always having a default), yarn build:platform green,
and manually confirmed in the browser (Downloads page: cards use the
merged LongText, Schema modal uses CodeBlock, Access Data modal uses
BtnGroup — all render correctly, zero console errors).
Removed packages/ui from biome.json's exclusion list — it had no lint
or format safety net until now. Ran `biome check --write` (safe fixes
only): reformatted + reorganized imports across 158 files, dropping
violations from 250 errors/392 warnings to 37 errors/297 warnings.

Verified zero behavior change: tsc --noEmit output identical aside from
cosmetic reordering (two named exports' error messages swapped source
file order after imports got resorted), yarn build:platform green.

37 errors remain, spanning noDangerouslySetInnerHtml (4, security-
sensitive), useUniqueElementIds (12), a11y issues (9), useValidTypeof
(2 errors + 16 warnings — some may be real typo bugs, needs a look),
noUnreachable (3), and a few others — none auto-fixable, all need
actual code review rather than mechanical fixes. 297 warnings, mostly
useExhaustiveDependencies (121, real risk to blind-fix — can silently
introduce infinite render loops) and noExplicitAny (95). Left for a
follow-up pass.
Enabling biome surfaced 37 errors that would fail CI. Rather than
silence them via rule-severity overrides, just excluding packages/ui
from biome's scope again (like before this branch) — same as the
original config, single-line diff. The auto-formatted/reorganized code
from the previous commit stays as-is (real improvement, just not
CI-enforced yet); actually fixing the remaining lint debt and turning
the check back on is follow-up work, not part of this PR.
Adds a new MCP (Model Context Protocol) server that exposes Open Targets
data visualisation widgets as MCP App tools for use in Claude Desktop.

- Express HTTP server with Streamable HTTP MCP transport
- Widget tools auto-generated from the sections registry (target, disease,
  drug, evidence, credible-set, variant, study entities)
- Server-side GraphQL prefetch via ot_fetch_widget_data tool, working
  around Claude Desktop bug #696 that strips structuredContent from
  tool-result notifications
- IIFE widget bundles built with Vite, inlined into HTML shells served
  as MCP App resources
- Molecular structure widget with AlphaFold 3D viewer (manual entry)
- Dockerfile for Cloud Run deployment (max-instances=1 for session
  stickiness); stdio transport for local Claude Desktop use via mcp-remote
- Updated SectionDef to remove prefetch-related properties and directFetch flag.
- Modified SECTION_REGISTRY to reflect changes in widget definitions.
- Simplified widget derivation logic in index.ts, removing prefetch handling.
- Adjusted molecularStructureWidget to fetch data directly from the iframe.
- Cleaned up types related to prefetching in types.ts.
- Removed unused prefetch data handling and fetch interceptor logic in createWidgetEntry.tsx.
carcruz added 2 commits July 30, 2026 15:57
CI's --immutable install failed since polished was added to
apps/mcp-widgets-server/package.json but the lockfile was never
regenerated to match.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants