Local MCP server
A Model Context Protocol (MCP) server that lets an assistant on the person's
own computer call four of the suite's engines. It is a development preview:
the board's question on LLMs as an audience (Q-17, docs/board/open-questions.md)
is still open, and no decision record approves an external-assistant interface
(docs/design/roadmap-architecture.md §7). It is not part of the website build,
no route serves it, and it contacts no host.
Homepage preview and scope
The CEO's MCP marketing request and subsequent clarification of 18 October 2026 authorize a homepage section after the guided workflow and before the research-tool directory that leads with planned connections for assistants such as ChatGPT and Claude. Those names are intended future targets, not tested compatibility claims. The diagram labels its hosted connection as planned and states that ChatGPT and Claude connections are not available yet. This records the scoped permission to market the future experience; it does not approve deployment, authentication design, hosted source-data distribution or live record access/writes. Q-17 remains open.
The four implemented tools described on this page sit in a separate, closed For developers: local MCP preview disclosure on the homepage, marked Available in the local preview. They are not a guaranteed hosted launch inventory. The page adds no engine, hosted endpoint, installable package, connection control, API-key or OAuth setup, or launch date.
The homepage setup action links to this page at /technical/mcp-server, a narrow exception to D-0081's rule against public technical-documentation links authorized by that CEO request. The same action is supplied to the consultancy renderer in the landing export. The rest of the technical area's policy remains: no general public navigation, no sitemap entry, noindex, no shared caching and the existing security headers.
Public copy lives in content/apps/landing-ai.ts. src/components/suite/marketing/mcp-source.ts reads the existing tool identifiers from source at build time; no website import executes MCP tools or includes their engines. Its test compares the inventory against createServer().listTools(). LANDING_EXPORT_VERSION is 5 (D-0112 moved the storage line; aiIntegration is unchanged since version 4 added it): aiIntegration carries the planned-integration copy and explanatory steps, with implemented tools and setup nested under aiIntegration.localPreview; ai-integration is in sectionOrder, after education since D-0112. The diagram’s responseFields are readable outcome labels, not a protocol contract. See Registry and content for every exported field; the consultancy renderer must support that version before adopting it.
The homepage’s separate catalogue additions (Literature and Patent) are not additions to the MCP tool inventory. apps.items and apps.sharedTools in the landing export describe website destinations; only aiIntegration.localPreview.tools describes the existing local MCP tools. Their four-tool scope remains unchanged.
For the implemented local server, “structured results” means the JSON object serialized inside MCP text content, not the protocol's structuredContent field. Calculation replies include the method versions, references and receipt described below; protocol errors and unexpected thrown errors need not have that envelope. Local computation makes no claim about how a person's chosen assistant handles inputs or outputs.
Running it
A local repository checkout, its installed dependencies and Node.js 22.18 or later are required. From the repository root:
node scripts/mcp-server.mjs
The runner imports src/lib/mcp/index.ts directly (Node 22.18 or later strips
the types), reads newline-delimited JSON-RPC 2.0 messages on standard input and
writes responses on standard output; diagnostics go to standard error. There is
no npm script. An MCP client that launches local servers can be pointed at the
command above.
Protocol
src/lib/mcp/server.ts implements protocol revision 2024-11-05
(MCP_PROTOCOL_VERSION, src/lib/mcp/identity.ts): initialize
(capabilities { tools: {} }, server name from SUITE_NAME plus -engines,
SERVER_VERSION), notifications/initialized, ping, tools/list and
tools/call. Errors follow JSON-RPC: -32700 parse error, -32600 invalid
request, -32601 unknown method, -32602 a missing tool name or an unknown tool,
-32603 internal error. A tool that throws returns a CallToolResult with
isError: true and the message, so the client can read it. Batched requests
are not supported.
Tools
Each tool's text content is one JSON object: status, result or error,
receipt, references, and an optional workspaceUrl. status: "invalid-input" with isError: true
means the arguments cannot be used; an engine's refusal or a withheld value is
an answer (isError absent). When arguments are valid for a tool whose inputs travel in the #s= fragment (dilution_series → /calculators/app#s=…, dna_tm → /sequence/app#s=…, molecule_descriptors → /molecule/app#s=…), mcpWorkspaceUrl (src/lib/mcp/receipt.ts) attaches workspaceUrl using absoluteUrl(appHref(slug) + encodeFragmentState(state)) provided the full URL is within LINK_MAX_LENGTH (2000 characters). workspaceUrl is omitted for dose_response_4pl (since Assay never encodes raw response points in #s=), for invalid-input responses, and whenever the encoded URL would exceed LINK_MAX_LENGTH.
| Tool | Engine | Notes |
|---|---|---|
dilution_series |
Calc: src/lib/dilution/serial.ts, intermediate.ts, dmso.ts |
Serial mode defaults the pipetting and carrier thresholds to the engine's rules (PIPETTE_MIN_VOLUME, DMSO_WARN_FRACTION) and the stock solvent fraction to 1; result.inputsUsed returns each value with whether it was stated. Intermediate mode, as in Calc, needs every constraint (solvent, stock available, minimum and maximum transfer, intermediate capacity, extra volume and the four solvent fractions) and names any that are missing (MISSING_CONSTRAINTS); none is assumed. workspaceUrl opens /calculators/app#s=task=serial&… or #s=task=dilution&ip=…. |
dose_response_4pl |
Assay: src/lib/fit/analyse.ts and the reporting rules src/lib/fit/rules.ts |
Reports what the app shows. fitted only when rules.relative.shown: the relative IC50, estimates, R², Wald and profile intervals and the drawn curve. Otherwise withheld (with withheldBy and reason), refused (rule i) or no-finite-minimum, with no estimate, interval or curve. The absolute IC50 is given only when rules.absolute.shown. workspaceUrl is omitted because raw observations never travel in #s=. |
dna_tm |
Sequence: src/lib/seq/tm.ts |
Nearest-neighbour Tm with the chosen or default salt correction; the conditions used are returned. workspaceUrl opens /sequence/app#s=op=oligo-tm&seq=… when within SEQ_LINK_MAX and LINK_MAX_LENGTH. |
molecule_descriptors |
RDKit MinimalLib through src/lib/chem/descriptors.ts; Molecule's Lipinski and Veber rules |
Loads @rdkit/rdkit in Node with createRequire. workspaceUrl opens /molecule/app#s=smiles=…. |
Receipts
src/lib/mcp/receipt.ts attaches to every reply: the tool, serverVersion,
the engine methods with their versions (the methods' own id and version
constants; RDKit's release string from the loaded library; Molecule's rules,
which have no version of their own, by the Molecule registry version), and
SHA-256 digests of the arguments (inputHash) and of { status, result, error } (outputHash), each over JSON with keys sorted at every depth (workspaceUrl sits at the top level of StructuredToolResponse outside the hashed tuple so environment base URLs never alter outputHash). The
receipt holds no time, so the same arguments on the same versions give the same
receipt. It identifies a computation; it is not a signature.
Tests
src/lib/mcp/mcp.test.ts drives McpServer.handleMessage for the protocol
cases, each tool's results and refusals, the withheld unbracketed IC50 (the
Assay reviewer's 17 October 2026 case: a top concentration of 2.19 µM and a
fitted midpoint near 4 µM, previously returned as an IC50), the intermediate
constraints that are not assumed, the receipt's behaviour, and workspaceUrl round-trips against calculatorsSchema, sequenceSchema and moleculeSchema.