This is maintainer documentation for the experimental lean-beam-mcp server. User setup lives in
the setup guide.
lean-beam-mcp is a stdio MCP server over the shared Beam broker runtime. It is not a raw Lean LSP
proxy and does not auto-expose editor-oriented LSP methods as agent tools.
The server prefers stateless
MCP 2026-07-28. It also supports
initialization-based MCP 2025-11-25 while clients migrate. server/discover lists the modern
revisions that clients may send in per-request metadata; legacy clients continue to start with
initialize, so 2025-11-25 is not a discovery result.
One stdio transport uses one protocol family. server/discover is a neutral compatibility probe:
after discovery, a client may still choose either family. A successful legacy initialize selects
the legacy family, while the first supported operation carrying valid modern protocol metadata
selects the modern family. Once selected, requests from the other family return JSON-RPC -32600.
Modern request metadata remains request-local: it is validated independently and never retained for
a later request.
Modern clients do not initialize a session. Every request instead includes:
{
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
"io.modelcontextprotocol/clientInfo": {
"name": "example-client",
"version": "1.0"
}
}
}The protocol version and client capabilities are required; client identity is optional. Each
successful modern result has resultType: "complete" and identifies the server in
_meta["io.modelcontextprotocol/serverInfo"]. server/discover and tools/list are public-cache
results with a one-hour TTL because their contents do not depend on a user, workspace, or prior
request. MCP 2026-07-28 deprecates Logging, so Beam writes operational logs to stderr. Discovery
still advertises logging for clients that need request-scoped, structured Lean diagnostics on the
MCP stream; those clients opt in on each request.
The application model is request-stateless in both protocol eras:
- every workspace-bound request includes an explicit workspace descriptor
- no request depends on a root or workspace selected by earlier MCP traffic
- continuation state is explicit in proof handles
- warm Lean processes, document mirrors, metrics, and diagnostics remain implementation caches
The server does not currently initiate Multi Round-Trip Requests. It recognizes the standard
inputResponses and requestState fields on tools/call, but rejects them with JSON-RPC -32602
because Beam never returned the input_required result that would authorize a continuation. If
MRTR is added later, requestState will remain continuation data for one logical request; it will
not select a workspace.
The current local descriptor is:
{
"workspace": {
"root": "/absolute/path/to/lean/project"
}
}workspace is required on beam_feedback_report, lean_drop_workspace, and every Lean operation.
The root must be an absolute path to an existing Lean/Lake project. Beam resolves it to a canonical
path and derives a private, deterministic broker cache key from that path. Canonical aliases
therefore share one runtime; clients do not choose process-local workspace ids. For this local
transport, any Lean/Lake project accessible to the MCP server process may be selected; the root is
not restricted to the directory from which the server was started.
There is no distinguished default workspace, lean-beam-mcp --root, lean_init_workspace,
lean_list_workspaces, or MCP roots/list fallback. A first ordinary request is sufficient:
{
"name": "lean_sync",
"arguments": {
"workspace": {"root": "/absolute/path/to/project"},
"path": "Main.lean"
}
}The server validates the descriptor, lazily creates or reuses its runtime, and echoes the canonical descriptor in successful Lean results:
{
"workspace": {"root": "/canonical/path/to/project"}
}This is deliberately a local-only descriptor. Remote workspaces and same-source multiple-toolchain mirrors need a broader Source/Workspace split and a transport-safe descriptor; they are not modeled as client-chosen aliases for local roots.
The descriptor selects the Lean runtime and toolchain; it is not a filesystem authorization
boundary. Relative path values resolve under the canonical workspace root, while absolute paths
are canonicalized independently of that root and may name dependency sources outside it. A remote
transport must add an explicit Source model and authorization policy rather than treating this
local descriptor as a sandbox.
The installed bin/lean-beam-mcp wrapper is the public setup path. It pairs the MCP executable with
the matching installed beam-cli and passes --beam-cli. On first use of a canonical root,
Beam/Mcp/Runtime.lean asks
beam-cli --root <root> mcp-config for the project-specific Lean command, runAt plugin, and
target-built Lake helper. The helper exchanges only JSON metadata with the MCP broker, so workspace
configuration and zero-build save traces remain owned by the selected Lean/Lake build even when the
MCP executable itself was built with a different Lean commit.
Keep bundle resolution in this CLI/runtime boundary. Normal MCP clients should pass the workspace
descriptor, not raw Lean commands or plugin paths. Source-tree tests and direct developer runs may
instead pass --lean-cmd and --lean-plugin together when the plugin has a sibling beam-daemon in
the standard build or install layout. MCP rejects either explicit flag alone, combining
--beam-cli with explicit runtime flags, and an explicit plugin without that sibling helper.
lean_drop_workspace is optional cache management, not context selection. It evicts the runtime
for its descriptor and invalidates proof handles owned by that runtime. Drop is idempotent and
returns dropped: false with reason: "notFound" when no cache exists. A later ordinary request
with the same descriptor recreates the runtime lazily. Retain the canonical descriptor when a Lean
operation, non-confidential feedback result, or drop result echoes it: cache eviction accepts that
absolute descriptor even if the project directory or its Lean/Lake markers have since disappeared.
After editing a lakefile, manifest, package override, lean-toolchain, Lean options, plugins, or
dynamic libraries, drop that workspace or restart the MCP server before the next request. Re-syncing
inside the old Lean process is not sufficient to reload workspace configuration.
The lean-beam-mcp stdio process owns its optional in-process broker runtime. MCP clients do not
start or attach to a wrapper daemon and do not need a separate lean-beam serve owner. The
first Lean operation that needs broker execution creates the runtime lazily; later descriptors share
that runtime while the broker remains authoritative for workspace membership. Feedback and cache
eviction can inspect or update an absent runtime without creating one. lean_drop_workspace evicts
one cached workspace but does not end the stdio session.
Closing stdin or reaching EOF closes MCP request admission. The server cooperatively cancels active cancellable requests, waits for every admitted request and non-cancellable workspace eviction to finish, then closes the broker runtime and all remaining backend sessions. A JSON-RPC request ID is active only until its request reaches terminal completion. The server retires that exact admission before publishing its terminal response, so a client may reuse the ID after observing the response; the completion barrier is resolved only after the terminal response write attempt finishes, including when that write fails.
CLI ingress has a different transport lifetime: a broker daemon accepts one request per socket connection, and disconnecting that connection cancels its exact broker admission. MCP carries many overlapping requests on one stdio stream and therefore owns application-level ID routing, cancellation, output serialization, and workspace-control fences. Both paths use the same broker request dispatcher and runtime teardown boundary.
- Beam/Broker/Protocol.lean owns broker request, response, handle, and stream envelopes.
- Beam/Broker/Server.lean owns workspace runtimes, document state, sessions, metrics, cancellation, and handle invalidation.
- Beam/Lean/Operation.lean owns curated Lean operations, typed inputs, schemas, and operation-to-broker adapters.
- Beam/Workspace/Protocol.lean owns the public local descriptor and broker-internal lifecycle types.
- Beam/Lean/Workspace.lean owns Lean/Lake root validation.
- Beam/Mcp/Projection.lean owns MCP tool names, descriptors, schemas, and normalized output.
- Beam/Mcp/Protocol.lean owns the current MCP JSON-RPC helpers.
- Beam/Mcp/Runtime.lean owns root-to-runtime configuration.
- Beam/Mcp/SelfCheck.lean owns the installed-wrapper self-check.
- Beam/Mcp/Server.lean owns descriptor resolution, lazy broker-runtime access, the typed protocol-family state machine, and the synchronous protocol-test seam. The broker runtime is authoritative for workspace state.
- Beam/Mcp/StdioServer.lean owns the permanent stdin reader, concurrent coordination, cancellation, cache-control barriers, and serialized output.
tools/list contains:
- process utilities:
beam_version,beam_stats - workspace-bound feedback report:
beam_feedback_report - cache eviction:
lean_drop_workspace - curated Lean operations projected from
Beam.Lean.Operation
The Lean operations include update/sync/refresh/save/close operations, runAt and explicit follow-up handle operations, hover and navigation, document/workspace symbols, goals, todo discovery, and code-action resolution. Raw LSP methods and generic broker escape hatches are intentionally absent.
Twelve observational tools advertise MCP annotations.readOnlyHint = true:
- process inspection:
beam_version,beam_stats - stateless speculation:
lean_run_at - document inspection:
lean_hover,lean_signature_help,lean_definition,lean_references,lean_document_symbols,lean_workspace_symbols,lean_goals, andlean_todo - payload resolution without edit application:
lean_code_action_resolve
The annotation means the tool does not intentionally retain or update Beam semantic state and does not write Beam-managed artifacts. Incidental cache warming can affect debug statistics, but it is implementation bookkeeping rather than a project-semantic update. The hint is advisory and is not an OS sandbox: user-supplied Lean commands and project metaprogramming may perform IO. The generated descriptions for speculative tools state that boundary directly. Other tools omit the hint. In particular, the feedback tool has an optional evidence-bundle mode that writes local files, so one static read-only annotation cannot describe every call.
Two non-read-only tools advertise annotations.destructiveHint = false because their
Beam-managed effects are only additive:
lean_run_at_handlemay retain a fresh follow-up handlelean_run_withmay retain a fresh continuation handle without consuming its parent
Two destructive lifecycle tools advertise annotations.idempotentHint = true because repeating
them has no additional Beam-managed effect:
lean_drop_workspaceleaves the cache absent and returnsdropped: falsewithreason: "notFound"when repeated before another request recreates the workspacelean_closeleaves the document closed and succeeds when it is already closed
These hints do not make the tools read-only, approval-free, or safe to parallelize with dependent
operations. Idempotence describes the resulting environment, not identical repeated responses.
All other non-read-only tools retain the protocol defaults: potentially destructive and not
idempotent. This includes beam_feedback_report, whose optional evidence-bundle mode writes local
files subject to the concurrent-writer limitation in the feedback output contract.
beam_version returns the running server identity in structuredContent. Its mcp_protocol field
is the server's preferred revision, not mutable negotiated state for the current request. Installed
runtime identities include the optional Boolean runtime_current: true means the process belongs
to the runtime selected by the install root's current link, while false means it is stale or
that the link is missing. Source-checkout identities omit this field. Invalid installed state also
includes the optional string runtime_error; the tool call still succeeds so clients can report
the broken identity. Restart an agent or MCP client for runtime_current: false. For
runtime_error, stop Beam clients and follow the error-specific
installed-runtime recovery guidance before resuming normal
work.
Direct MCP clients should call lean_update or lean_sync before snapshot-bound operations and
pass the returned version for the same descriptor and path. lean_workspace_symbols is
workspace-scoped but has no document version. lean_run_with, lean_run_with_linear, and
lean_release take an opaque handle returned by a previous handle operation. The supplied workspace
descriptor must resolve to the same private runtime identity carried by that handle. lean_goals
also requires mode: "before" or mode: "after".
Beam's source-file invariant is that Beam never applies source edits to .lean files on disk; the
client applies source edits. lean_update, lean_sync, and lean_refresh read the current saved
source from disk into Beam's LSP mirror. lean_save and lean_close_save additionally write
Lean/Lake build artifacts, never source. lean_code_action_resolve only returns a resolved action;
the client must apply any LSP WorkspaceEdit it contains.
lean_run_at, lean_run_at_handle, lean_run_with, and lean_run_with_linear are speculative.
They test supplied text against a selected document snapshot or follow-up handle without persisting
that text as source. Do not call lean_sync as a way to commit a successful probe. To keep a result,
first edit and save the Lean file with the client's normal file-editing tool. Then call lean_update
before another snapshot-bound operation, or call lean_sync when a diagnostics/readiness barrier is
needed. Both commands read the current on-disk file; neither applies or recovers speculative text.
lean_code_action_resolve takes a code_action payload previously returned by lean_todo. Clients
apply any returned LSP WorkspaceEdit themselves, then call lean_update or lean_sync again so
Beam observes the edited file and reports the new version. Use lean_sync instead of lean_update
when the client also needs the diagnostics/readiness barrier.
lean_save and lean_close_save create development checkpoints from the accepted Lean server
snapshot, including structured Lake options, dynamic libraries, and plugins already applied by the
file worker. Modules with batch-only moreLeanArgs fail with saveUnsupportedSetup; move shared
-D settings to leanOptions, or use lake build when the arguments are intentionally batch-only.
Successful checkpoints are normally sufficient for the local development loop, but MCP clients
should describe them as checkpoint success rather than batch-build or CI success. CI must separately
run lake build from clean artifacts. If no successful clean CI result is available, perform the
one-time clean local check outside MCP. See the
checkpoint contract.
beam_version and beam_stats are process-wide and accept no workspace descriptor. beam_stats
reports all currently cached broker workspaces for debugging; callers must not use it to establish
context for a later operation.
beam_feedback_report requires a descriptor to validate its local workspace target. Beam does not
upload or submit the report; the tool returns it to the MCP caller and can optionally write a local
evidence bundle. Non-confidential mode collects project context for that workspace without starting
a Lean runtime solely for feedback; if the descriptor is already cached, its in-process stats and
open files are included. Another workspace's state is not included. Confidential mode does not
collect that project-derived context and does not echo the workspace descriptor in its result.
In both supported revisions:
- malformed or unknown tools are JSON-RPC errors
- request and notification envelopes reject mixed or undeclared top-level members; unsolicited client responses are rejected because the server does not send requests
tools/callacceptsname,arguments, and_meta; the standard MRTR continuation fields are recognized but rejected until Beam can issue aninput_requiredresult- invalid inputs for known tools are MCP tool errors with
isError=true - undeclared tool-input fields, including undeclared workspace selector aliases, are rejected as
structured
invalidInputerrors rather than ignored - invalid, missing, relative, or non-project workspace roots are structured
invalidInputerrors - Lean semantic failures remain successful tool returns with Lean-specific success fields
- stale or cross-workspace handles remain structured transport/tool errors
Once a request is identified as MCP 2026-07-28, missing required per-request metadata returns
JSON-RPC -32602. server/discover is always treated as modern; another request is treated as
modern when it carries any reserved modern MCP metadata key. Before a protocol family is selected,
a bare non-discovery request remains legacy-era input because stdio has no other signal with which
to distinguish a legacy client. The same bare request returns -32602 after modern operation
traffic has selected the per-request protocol family. An unsupported modern protocol version
returns -32022 with the requested revision and the server's supported per-request revision list.
Modern initialize, ping, and logging/setLevel requests return -32601.
For MCP 2025-11-25, a well-formed initialize followed by notifications/initialized remains
required. Beam validates that the client supplied a protocol-version string, capabilities object,
and client identity before changing legacy lifecycle state. The initialize result selects Beam's
supported legacy revision, 2025-11-25; the client's proposed revision need not already match it.
The server has one permanent stdin reader and one serialized stdout sink. Ordinary tool calls may overlap, and responses may arrive out of request order. Clients must correlate exact JSON-RPC IDs; string and numeric IDs are distinct.
A request ID is owned from admission through terminal stdout flush. Reuse is valid in the modern family after that terminal point. A malformed second request using an ID that is still active is ignored so it cannot create two indistinguishable terminal responses for the same ID.
The server never sends JSON-RPC requests to the client. Under the current protocol it emits only responses and request-related notifications. In particular, descriptor resolution never invokes MCP Roots.
notifications/cancelled cooperatively cancels active broker work. If cancellation wins the
terminal race, no final response is emitted for that request. lean_drop_workspace is
non-cancellable once admitted because partial cache eviction cannot be rolled back safely. Cache
eviction is a full stream-order fence: previously admitted calls drain before the drop runs, while
later calls wait for its terminal result and may then recreate the same descriptor.
For both protocol families, the MCP coordinator binds each ordinary tool call to the exact request handle produced by broker admission. Cancellation before that binding prevents dispatch; cancellation afterward targets that handle directly. The handle becomes inert when its dispatch scope ends, so a late cancellation cannot affect a later request even if a broker client request ID is reused.
EOF is the transport shutdown in both supported revisions. lean-beam-mcp defines no private
shutdown request. This is separate from lean-beam --root ROOT stop, which sends the typed shutdown
operation directly to a Beam broker daemon.
For tools/call, clients may pass params._meta.progressToken as a string or integer. Progress
updates for one request are monotonic and precede that request's final response. Every Lean
operation tool description, plus beam_feedback_report and lean_drop_workspace, advertises this
metadata so progress tuning is visible in tools/list even though MCP places it outside the tool's
arguments object.
When a broker-backed Lean operation, beam_feedback_report, or lean_drop_workspace has no
progress token, fast calls remain quiet. If Lake setup is observed or the call is still pending
after two seconds, Beam emits at most one notifications/message event at level notice with
logger beam.status. Its data contains the exact JSON-RPC requestId, tool, state, optional path,
human message, and a
progressHint naming _meta.progressToken. This is a best-effort liveness notice, not percentage
progress or a readiness result. Legacy clients receive it under the default log level and can
suppress it by raising logging/setLevel above notice. Modern clients receive it only when that
request's
_meta["io.modelcontextprotocol/logLevel"] admits notice.
With a progress token, Beam emits one contextual preparation update followed by meaningful,
throttled Lake setup and Lean file-progress changes. It does not send separate generic starting,
preparing, and running updates, and it streams at most one terminal done=true file-progress
event. Final sync, refresh, save, and close-save results contain document_progress for clients that
do not retain notifications. It is the latest observation available when the response is constructed
and may therefore be newer than the last throttled notification or reused from already-observed
document state. Other tools, including lean_run_at, do not inherit this final metadata.
The full structuredContent object is also serialized in content[0].text, as the
MCP 2025-11-25 compatibility guidance
recommends. Those are two representations of one result; clients should render one rather than
presenting them as two messages.
Incremental Lean diagnostics are separate notifications/message events with logger
lean.diagnostic. Modern requests receive these logs only when that request includes
_meta["io.modelcontextprotocol/logLevel"]; legacy clients use global logging/setLevel. Clients
that cannot collect interleaved notifications can pass diagnostics_in_result: true to
lean_sync or lean_refresh. Set diagnostic_scope: "all" when live logs or final items should
include warnings, information, and hints rather than only errors. Asking for final replay can
intentionally repeat diagnostics already seen live; it is an alternate delivery path for clients
that cannot consume interleaved events. Silent editor-only Lean messages, such as
Goals accomplished!, are removed at reception and are never made visible by diagnostic_scope.
The structuredContent for a clean lean_sync has this semantic shape (counts are always present;
diagnostics.items appears only when requested):
{
"workspace": {"root": "/work/demo"},
"path": "Main.lean",
"version": 3,
"diagnostics": {
"counts": {"error": 0, "warning": 0, "information": 0, "hint": 0, "unknown": 0, "total": 0}
},
"readiness": {
"save_ready": true,
"reason": "ok",
"blocking_error_count": 0,
"blocking_diagnostics": [],
"blocking_messages": []
},
"document_progress": {"updates": 12, "done": true, "range_end_line": 80}
}lean_save returns artifact paths plus sync containing that same
path/version/diagnostics/readiness object. Optional backend artifacts (olean_server,
olean_private, ir, and bc) appear only when Lean produced them:
{
"workspace": {"root": "/work/demo"},
"path": "Main.lean",
"module": "Main",
"version": 3,
"source_hash": "9a9bdc9950870951",
"olean": "/work/demo/.lake/build/lib/lean/Main.olean",
"ilean": "/work/demo/.lake/build/lib/lean/Main.ilean",
"c": "/work/demo/.lake/build/ir/Main.c",
"trace": "/work/demo/.lake/build/lib/lean/Main.olean.trace",
"sync": {
"path": "Main.lean",
"version": 3,
"diagnostics": {
"counts": {"error": 0, "warning": 0, "information": 0, "hint": 0, "unknown": 0, "total": 0}
},
"readiness": {
"save_ready": true,
"reason": "ok",
"blocking_error_count": 0,
"blocking_diagnostics": [],
"blocking_messages": []
}
},
"document_progress": {"updates": 12, "done": true, "range_end_line": 80}
}lean_close_save returns { "closed": true, "saved": <save-result> }. Save and close-save
answers are closed typed shapes rather than passthrough broker JSON; unexpected artifact fields are
rejected before an MCP reply is constructed. A lean_run_at result is deliberately smaller and
never gains final document progress:
{
"workspace": {"root": "/work/demo"},
"success": true,
"messages": [],
"traces": [],
"proof_state": null,
"next_handle": null
}readiness.blocking_error_count counts save-blocking evidence and can include a command message that
has no diagnostic. It may therefore differ from diagnostics.counts.error.
MCP display behavior has four independent controls. _meta.progressToken belongs beside
arguments in the tools/call envelope. diagnostic_scope and diagnostics_in_result are ordinary
tool arguments, but only on the tools listed below. Log delivery is session-wide
logging/setLevel for legacy clients and per-request
_meta["io.modelcontextprotocol/logLevel"] for modern clients.
| Tool or family | With _meta.progressToken |
Without a token | Diagnostic arguments | Stable final result |
|---|---|---|---|---|
lean_sync, lean_refresh |
Preparation, throttled Lake setup, and file-progress updates. | One beam.status on the first setup observation or after two seconds. |
diagnostic_scope, diagnostics_in_result |
Path/version, complete diagnostic counts, readiness, document_progress, and optional diagnostic items. |
lean_save, lean_close_save |
Preparation, throttled Lake setup, and file-progress updates. | One beam.status on the first setup observation or after two seconds. |
diagnostic_scope |
Checkpoint result embedding the same sync/readiness result and document_progress; no diagnostic replay argument. |
lean_run_at, lean_run_at_handle |
Preparation, Lake setup when observed, and file progress when Lean publishes it. | One beam.status on the first setup observation or after two seconds. |
None | Run result messages, traces, proof state, and optional handle; no final document_progress or full-file diagnostic replay. |
lean_run_with, lean_run_with_linear |
Preparation and file progress when Lean publishes it. | One beam.status after two seconds. |
None | Continuation result and optional next handle. |
lean_update |
Preparation phase only. | One beam.status after two seconds. |
None | New document version and changed flag; no readiness barrier. |
lean_hover, lean_signature_help, lean_definition, lean_references, lean_document_symbols, lean_goals, lean_todo, lean_code_action_resolve |
Preparation and file progress when Lean publishes it. | One beam.status after two seconds. |
None | Operation-specific structured result. |
lean_workspace_symbols |
Preparation phase only. | One pathless beam.status after two seconds. |
None | Workspace-symbol result. |
lean_release, lean_close |
Preparation, plus file progress for release when Lean publishes it. | One beam.status after two seconds if the normally short call is delayed. |
None | Release/close result. |
lean_drop_workspace |
Preparation phase, emitted before waiting behind earlier requests. | One pathless beam.status after two seconds while the drop waits for earlier requests or eviction. |
None | Drop and handle-invalidation result. |
beam_feedback_report |
Collection phase only. | One beam.status after two seconds while collection remains pending. |
None | Rendered report and optional collected evidence. |
beam_stats |
Quiet; a supplied token is not used. | Quiet. | None | Process statistics. |
beam_version |
Quiet; a supplied token is not used. | Quiet. | None | Server identity. |
The no-token beam.status cells assume that the active log policy admits notice; otherwise those
calls remain quiet.
For lean_sync and lean_refresh, the diagnostic argument combinations are:
diagnostic_scope |
diagnostics_in_result |
Live lean.diagnostic candidates |
Final diagnostics.items |
|---|---|---|---|
omitted or "errors" |
omitted or false |
Errors only | Omitted |
omitted or "errors" |
true |
Errors only | Current errors |
"all" |
omitted or false |
Errors, warnings, information, and hints | Omitted |
"all" |
true |
Errors, warnings, information, and hints | Current errors, warnings, information, and hints |
lean_save and lean_close_save support the same diagnostic_scope live filter but do not expose
diagnostics_in_result. Diagnostic counts and readiness remain complete regardless of
these display choices.
The active log level applies after the diagnostic filter and never suppresses
notifications/progress or fields in the final result. The legacy server starts at debug, so all
otherwise-selected log events are enabled until the client changes the level. Modern requests that
omit their per-request log level receive no log notifications:
| Active minimum log level | beam.status notice |
With diagnostic_scope: "all", visible Lean diagnostic levels |
|---|---|---|
| Modern level omitted | Hidden | None |
debug |
Shown | Error, warning, information, hint |
info |
Shown | Error, warning, information |
notice |
Shown | Error, warning |
warning |
Hidden | Error, warning |
error |
Hidden | Error |
critical, alert, or emergency |
Hidden | None of the current Lean diagnostic events |
Useful presets are therefore:
- normal legacy call: omit the per-call controls; fast calls are quiet and slow Lean operations get one liveness notice under the default log level
- normal modern call with no logs: omit the per-request log level; both diagnostics and automatic status logs remain quiet
- modern no-token liveness: request log level
notice - detailed progress: add
_meta.progressToken; progress delivery is independent of diagnostic logs - quiet modern detailed progress: add
_meta.progressTokenand omit the per-request log level - rich live sync diagnostics: set the request log level to
debugand adddiagnostic_scope: "all"; add_meta.progressTokenseparately when detailed progress is also useful - rich diagnostics for a client that cannot retain notifications: also add
diagnostics_in_result: trueonlean_syncorlean_refresh - warnings/errors but no automatic status: select log level
warningand usediagnostic_scope: "all"where supported
A no-token liveness event has this shape:
{
"jsonrpc": "2.0",
"method": "notifications/message",
"params": {
"level": "notice",
"logger": "beam.status",
"data": {
"requestId": 42,
"tool": "lean_sync",
"state": "running",
"message": "lean_sync on Main.lean is still working.",
"path": "Main.lean",
"progressHint": "For detailed live updates, pass tools/call params._meta.progressToken."
}
}
}Global logging/setLevel belongs only to the legacy 2025-11-25 path. Modern 2026-07-28
requests carry their log level independently in
_meta["io.modelcontextprotocol/logLevel"]; it is not retained for later calls.
- McpProjectionTest.lean checks the curated tool surface, descriptor schemas, adapters, and normalized results.
- McpProtocolTest.lean checks JSON-RPC shapes, protocol-family routing and selection, discovery version scope, metadata validation, result/cache envelopes, descriptor decoding, lifecycle gating, progress, status logs, errors, and diagnostic forwarding.
- test-mcp-stdio.py checks real modern discovery and direct calls, malformed modern metadata, modern progress and tool-error envelopes, per-request logging, EOF teardown, legacy lifecycle compatibility, lazy first use, canonical aliases, simultaneous cold first use of distinct roots, different toolchains in one process, cross-workspace and cross-process handle rejection, scoped feedback, eviction/recreation, modern and legacy cancellation, response routing, progress, status reporting, and shutdown.
- test-mcp-http-bridge.py checks the local test-only HTTP adapter.
- test-mcp-conformance.sh runs the pinned external
2025-11-25conformance scenarios. - test-mcp-modern-sdk.sh uses a pinned release of the official
@modelcontextprotocol/clientpackage against the real stdio executable. It checks automatic and pinned modern negotiation, discovery, tool listing, an explicit-workspace Lean call, progress, request-scoped diagnostic logging, and clean process teardown. The script owns the package pin. - test-mcp-modern-conformance.sh runs pinned modern alpha
@modelcontextprotocol/conformancescenarios that apply to Beam: tool-list structure, Streamable HTTP header validation, and DNS-rebinding protection. This is deliberately labeled an alpha lane until the upstream modern suite is stable; the script owns the package and scenario pins.
The alpha suite's broad server-stateless and caching scenarios are not product-neutral today.
They require synthetic diagnostic tools such as test_missing_capability and methods that Beam
does not advertise, including prompts and resources. Beam does not add test-only tools to its
public surface to satisfy those scenarios. The protocol test, real stdio harness, and official SDK
test cover Beam's per-request metadata, stateless workspace selection, request-scoped logging, and
absence of server-to-client requests directly.
The Streamable HTTP bridge is a test adapter over the stdio product, not a separate deployment
model. Its modern mode validates the headers and HTTP statuses needed by the conformance runner,
then forwards the same JSON-RPC message to lean-beam-mcp; it does not implement a second tool
surface. Remote and load-balanced deployment requires an explicit transport-safe workspace design
and must not infer application identity from a connection.