{"id":"52e5c6eb-4ba8-487c-b981-af3eb8b11ea3","slug":"msmcp-site","kind":"mcp","name":"msmcp-site","summary":"[ msmcp.dev ] Consult your data. MSMCP is a model context protocol for mass spectrometry data enabling AI chat with your database, locally.","owner":"janusson","repoUrl":"https://github.com/janusson/msmcp-site","license":"MIT","licenseTier":"permissive","trust":"community","stars":0,"match":null,"fewStars":true,"format":"mcp","createdAt":"2026-10-10T22:49:23.000Z","addedAt":"2026-10-21T07:30:02.082Z","logoUrl":"https://avatars.githubusercontent.com/u/34246713?u=e7ee03548b43047f5f4d587bd6f69dc568a42fa6&v=4","linkOnly":false,"collection":null,"skillPath":null,"categories":["data-analytics","databases","communication"],"tags":["data-analysis","llm-tools","mass-spectrometry"],"runtime":"binary","local":true,"cloudCapable":false,"cloudCapability":"unknown","cloudReadyOnPlatform":false,"needsApiKey":false,"reviewed":false,"syncedAt":"2026-10-22T15:54:13.975Z","sample":false,"url":"https://conduits.dev/tools/msmcp-site","language":"Astro","transports":["stdio"],"install":{},"requirements":{"env":[],"upstreamMayCharge":false},"tools":null,"readme":"# msmcp-site\n\nSource for the [MSMCP](https://github.com/janusson/MSMCP) project site — a single\nlanding page for the Model Context Protocol server for computational mass\nspectrometry.\n\nMSMCP puts your own mass spectrometry data in front of an AI client: it is an\nopen-source MCP server that lets Claude, Zed or any other MCP host read your\nmzML, MGF and imzML files and work on them with real chemistry — precursor and\nisotope calculations, MS/MS library matching, QC.\n\nThe site's job is narrow: inside thirty seconds, a mass spectrometrist or a\nscientific-software developer should understand what MSMCP does, see a real\nworkflow, and find a working path to installation. It is one page — not a docs\nportal, not a blog, and not a marketing site.\n\n**Live at:** https://msmcp.dev — deployed from this repository to GitHub Pages\n(see [Deployment](#deployment))\n\n---\n\n## Stack\n\n| Piece     | Choice                                     | Why                                                                                                                                                                             |\n| --------- | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| Framework | **Astro 7** (static output)                | The same framework as the personal site, so the toolchain, conventions and deployment shape are already familiar. Static output means no runtime, no server, no database.       |\n| Styling   | **Tailwind CSS 4** via `@tailwindcss/vite` | Tokens live in `src/styles/global.css` under `@theme`, exactly as on the personal site.                                                                                         |\n| Fonts     | Inter + Source Code Pro (Google Fonts CDN) | Inter for prose; Source Code Pro carries a real 600 for the mono labels, where JetBrains Mono shipped 400/500 and the browser synthesised the bold. `preconnect` on both hosts. |\n| Scripts   | None                                       | No client-side JavaScript is shipped. The mobile layout wraps instead of hiding behind a drawer, and the figures are CSS positioned from data.                                  |\n\nNothing else. No analytics, no trackers, no cookie banner, no newsletter, no\nbackend, no contact form, no separate documentation engine.\n\n## Local development\n\nRequires Node 20+ (CI uses 24).\n\n```bash\nmake deps          # npm install, stamped so it only runs when package files change\nmake dev           # Astro dev server with HMR at http://localhost:4321\nmake build         # static build into dist/\nmake preview       # serve the production build locally\n```\n\nThe gate, in the order CI runs it:\n\n```bash\nmake check         # astro check (types) + prettier --check   — fast, no build\nmake verify        # build, then assert the built HTML        — the real gate\nmake all           # check, then verify\n```\n\n`scripts/verify-build.mjs` is a post-build contract check rather than a unit-test\nsuite, because this project has no application logic to unit-test. It asserts the\nthings that break silently on a static page making claims about software:\n\n- exactly one `<h1>`, a `lang`, and a meta description;\n- **every in-page `#anchor` resolves** — a dead nav link is the most likely\n  regression on a one-page site;\n- **all fourteen MSMCP tool names still appear**, and the tools reference has\n  exactly one `data-tool` row per tool with a call in it, so a renamed or added\n  tool cannot drift the page out of sync with the server;\n- the hero phrasing and the \"candidates, not identifications\" framing are intact;\n- no `http://` links, `target=\"_blank\"` implies `rel=\"noopener\"`, and no external\n  link points outside a small allowlist of trusted hosts;\n- no `undefined` / `NaN` / `[object Object]` template leaks, and the document stays\n  under 120 KB.\n\n## Layout checks (what the gate cannot catch)\n\n`make check` and `make verify` prove the HTML is sound. Neither can prove the page\n_looks_ right, and both were green while two real layout defects were live:\n\n- the document scrolled horizontally on a 390 px viewport (593 px wide) because a\n  `<pre>` inside a CSS grid item stretches the track — grid items default to\n  `min-width: auto`, so the item needs `min-w-0` for the block to scroll instead;\n- three pairs of words were welded together (`server-side:validate_precursor`,\n  `withscoring_method`, `belong inGitHub Issues`) because Astro trims whitespace\n  around an inline tag when the source breaks the line there — the fix is an\n  explicit `{\" \"}`.\n\nThe second class is now asserted by `scripts/verify-build.mjs`, since it is\ninvisible in the HTML source. The first is not, and cannot be: **after any layout\nchange, render the page and look at it.** It was checked in headless Chromium at\n1440×1000 and 390×844; screenshots and the render method are recorded in\n`~/Hermes/Artifacts/msmcp-site/` (`screenshots/` and `REPORT.md`).\n\n## Project layout\n\n```\n.\n├── astro.config.mjs          # `site` origin — the single source of the canonical URL\n├── Makefile                  # deps / dev / build / check / verify / all\n├── public/\n│   ├── favicon.svg\n│   └── og.png                # 1200x630 link-preview card, from scripts/make-og-card.py\n├── scripts/\n│   ├── make-og-card.py       # regenerates og.png by hand when the headline changes\n│   └── verify-build.mjs      # post-build contract checks\n├── src/\n│   ├── components/\n│   │   ├── IsotopeFigure.astro   # glucose isotope envelope, from real tool output\n│   │   ├── ReferenceFlow.astro   # the data-reference mechanism, drawn\n│   │   ├── SiteFooter.astro\n│   │   └── SiteHeader.astro\n│   ├── layouts/BaseLayout.astro  # head, meta/OG, skip link, header, footer\n│   ├── pages/index.astro         # the whole page\n│   └── styles/global.css         # @theme tokens + a handful of primitives\n└── .github/workflows/pages.yml   # gate, then deploy to GitHub Pages\n```\n\n## Deployment\n\n**Host: GitHub Pages · domain: `msmcp.dev` · DNS: Cloudflare (DNS only).**\n`.github/workflows/pages.yml` is Astro's official deployment workflow —\n`withastro/action@v6` installs, builds and uploads the site, and\n`actions/deploy-pages@v5` publishes it — with one addition: a `gate` job that\n`build` depends on. `withastro/action` installs, builds and uploads in a single\nstep, so there is no seam to run a check between its build and its upload; a\nseparate job is the only way a red gate can be kept from reaching Pages. Pull\nrequests run the gate and the build and stop before the deploy job.\n\nThe site has a build step, so Pages must publish **from GitHub Actions**, not from\na branch: the repository holds Astro sources, `dist/` is gitignored, and a branch\nsource would publish this repository's Markdown instead of the built site.\n\n### Repository settings (one-time, owner action)\n\n1. **Settings → Pages → Build and deployment → Source: GitHub Actions.**\n2. **Settings → Pages → Custom domain: `msmcp.dev`**, then Save.\n\n   Set this in the settings UI, **not** with a `CNAME` file. When the publishing\n   source is a custom GitHub Actions workflow, GitHub ignores any `CNAME` file and\n   the repository setting is the source of truth — which is why this repository\n   deliberately has no `public/CNAME`, and why adding one back would have no effect.\n\nAccount-level domain verification for `msmcp.dev` is already complete (the\n`_github-pages-challenge-janusson` TXT record is live in the zone). That is a\nseparate thing from the two settings above, and it needs nothing further.\n\n### Do not pick a starter workflow from the Pages gallery\n\nWhen you enable Pages, the **Source → GitHub Actions** control offers a searchable\ngallery of starter workflows. Choosing the Astro one commits `.github/workflows/astro.yml`\nto `main`: a second, competing deployment workflow that does not use\n`withastro/action`, hardcodes Node 20, runs no checks, and — before this branch is\nmerged — fails immediately at \"Detect package manager\", because no `package.json`\nexists on `main` yet. Two workflows deploying one Pages site is a race, not\nbelt-and-braces.\n\nSet **Source: GitHub Actions** and leave the gallery alone. This repository already\nhas the deployment workflow, and that starter file is deliberately not present: it\nwas committed on 2026-10-11 and removed by this branch.\n\n### DNS records required at Cloudflare\n\nThe zone is registered with Cloudflare and served by Cloudflare's nameservers, but\nCloudflare is **DNS only** here — GitHub Pages serves the site and GitHub's\ncustom-domain certificate provides HTTPS.\n\n`msmcp.dev` is an apex domain, so it takes `A` records; an apex cannot have a\nCNAME. GitHub also publishes IPv6 addresses and recommends an `A` record alongside\nany `AAAA`.\n\n| Type    | Name              | Value                 | Proxy    |\n| ------- | ----------------- | --------------------- | -------- |\n| `A`     | `msmcp.dev` (`@`) | `185.199.108.153`     | DNS only |\n| `A`     | `msmcp.dev` (`@`) | `185.199.109.153`     | DNS only |\n| `A`     | `msmcp.dev` (`@`) | `185.199.110.153`     | DNS only |\n| `A`     | `msmcp.dev` (`@`) | `185.199.111.153`     | DNS only |\n| `AAAA`  | `msmcp.dev` (`@`) | `2606:50c0:8000::153` | DNS only |\n| `AAAA`  | `msmcp.dev` (`@`) | `2606:50c0:8001::153` | DNS only |\n| `AAAA`  | `msmcp.dev` (`@`) | `2606:50c0:8002::153` | DNS only |\n| `AAAA`  | `msmcp.dev` (`@`) | `2606:50c0:8003::153` | DNS only |\n| `CNAME` | `www`             | `janusson.github.io`  | DNS only |\n\n**Every one of these must be DNS-only (grey cloud).** With Cloudflare's proxy in\nfront, the certificate challenge cannot reach GitHub, so GitHub cannot issue the\ncertificate for the domain, _Enforce HTTPS_ stays unavailable, and the HTTPS in\nuse would be Cloudflare's rather than GitHub's. The proxy stays off.\n\n`www.msmcp.dev` then redirects to the apex automatically: GitHub creates redirects\nbetween the apex and the `www` variant once the records for both resolve to Pages.\nNo redirect rule is needed. GitHub's documentation is explicit that `www` is the\nonly subdomain that may accompany an apex domain, and that wildcard records must\nnever be used.\n\n### HTTPS\n\n`.dev` is on the HSTS preload list, so browsers refuse plain `http://` with no\nfallback: `https://msmcp.dev` is unreachable until GitHub's certificate exists.\nGitHub issues it automatically once the records resolve; DNS changes can take up to\n24 hours. When the certificate is issued, tick **Enforce HTTPS** under\nSettings → Pages.\n\n### Verify a deploy\n\n```bash\ndig +short msmcp.dev                                     # expect the four 185.199.10x.153 addresses\ndig +short www.msmcp.dev CNAME                           # expect janusson.github.io.\ncurl -sSI https://msmcp.dev | head -3                    # expect 200 once the certificate is issued\ncurl -sSI https://www.msmcp.dev | head -3                # expect a redirect to the apex\n```\n\nThe Actions run is the other check: the `Gate`, `Build` and `Deploy` jobs appear\nseparately, and a failed gate means the build never ran and nothing was published.\n\n### Hosting history\n\nCloudflare Pages was considered, because the domain is registered at Cloudflare, and\nrejected: this repository exists in order to host the site, so a second host for the\nsame content would split the canonical URL. Cloudflare's role is DNS only — no\nPages, Workers, server or backend. `msmcp.ericjanusson.ca` was the host proposed in\nthe original brief; `msmcp.dev` is what was registered. To change the canonical\norigin, edit `site` in `astro.config.mjs` and the custom domain in\nSettings → Pages.\n\n## Maintaining the page\n\nWhere the load-bearing content lives, and what has to be re-checked when MSMCP\nchanges:\n\n| Content                                          | Lives in                                                                 | Re-check when                                                                                                                                                             |\n| ------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| The transcribed workflow, hit table, and timings | `src/pages/index.astro` — the `hits` array and the transcript markup     | `search_library` scoring, the report format, or the null model changes. The numbers came from a single recorded run (see below); they are a record, not a live benchmark. |\n| The isotope envelope                             | `src/components/IsotopeFigure.astro` — the `peaks` array                 | `annotate_isotopes` maths changes. Re-run the tool and paste its output.                                                                                                  |\n| The tool list (13 names)                         | `src/pages/index.astro` — `tools`; guarded by `scripts/verify-build.mjs` | A tool is added, renamed or removed in `src/msmcp/tools/`.                                                                                                                |\n| Install commands                                 | `src/pages/index.astro` — `installSnippet`                               | The distribution story changes (PyPI, `server.json`, a container).                                                                                                        |\n| Document links                                   | `src/pages/index.astro` — `docs`                                         | A file is renamed or moved in the MSMCP repository.                                                                                                                       |\n\n**Cardinal rule for edits:** every number and every code block on the page must be\nsomething the software actually printed or a file that actually exists. No\nbenchmark, testimonial, adoption figure or security guarantee is invented. If a\nclaim cannot be reproduced, it comes off the page rather than getting softened.\n\n## Provenance of the example on the page\n\nThe workflow in the \"Example\" section is a real run, not an illustration:\n\n- **Query:** spectrum #966 of a Bruker maXis LC–MS/MS acquisition (RT 3.795 min,\n  precursor m/z 315.2321, 220 peaks). The acquisition's filename is deliberately\n  not published on the page.\n- **Library:** MassBank's public MSP library, 63,879 spectra, read from disk\n  through the same `SecurityPolicy` as the query.\n- **Scoring:** `classical` — greedy peak matching, ±0.02 Da — with the report's own\n  FDR threshold. Top candidate 0.8309 cosine, q = 0.0125; the runner-ups are\n  progesterone, a constitutional isomer at the same precursor m/z.\n- **The ppm verdict** (0.32 ppm) was produced by calling `validate_precursor`\n  against MassBank's reference mass, and is reproducible in one tool call.\n- **The isotope envelope** is `annotate_isotopes(\"C6H12O6\")` output, transcribed\n  without rounding. Note that the MSMCP README's inline example of this tool shows\n  older values (`M+1` at 181.0721, `[M+H]+` at +1.006728 Da); the tool itself\n  prints 181.0668 and +1.007276 Da, which is what the page uses.\n\n## Accessibility and performance notes\n\n- Semantic landmarks (`header`, `main`, `nav`, `footer`), one `<h1>`, a skip link,\n  visible focus rings, and `<table>` markup with `<caption>`/`scope` for the hit\n  table rather than a styled div grid.\n- Figures carry `role=\"img\"` with a full text description, so the plotted values\n  reach a screen reader.\n- Colour contrast targets WCAG AA against the dark canvas; the palette is a dark\n  sibling of the personal site's Kanagawa Dragon tokens rather than a copy.\n- No JavaScript, no web fonts beyond two families, one HTML document (~28 KB) and\n  one small CSS file. `prefers-reduced-motion` is respected (there is no animation\n  to disable, but smooth scrolling is switched off for users who ask).\n\n---\n\n_MIT licensed — see [LICENSE](LICENSE). Copyright (c) 2026 Eric Janusson._\n"}