Repository navigation
VS Code AI surfaces: MCP server + @modelith chat participant - #16
Merged
Merged
Conversation
Two Copilot Chat integrations, one per mode, sharing one query layer so the two frontends never drift. The spec sketched the MCP server as a Node module compiled against packages/core, but core is Python — Node can't import it, and every other integration here (LSP, canvas) runs by spawning the mdl CLI. So the server IS a Python `mdl mcp` subcommand over stdio, registered from VS Code exactly like the LSP. - packages/mcp (modelith-mcp): FastMCP stdio server with seven tools. Reads (list_entities, get_entity, search_ontology, get_model_context, validate) come from the new shared query layer; writes (create_entity, update_entity) go through mdl_core.commands.apply_command, so they take the same validation + fingerprint path as any edit. Direct-write to the checkout by design (§1). Pinned mcp>=1.2,<2 (v1 FastMCP; editor clients target v1 today). - packages/core/src/mdl_core/query.py: the shared read layer — condensed, chat-context-sized shapes with name-or-ULID entity lookup (the IR has only ULID/domain/code-set name indexes). Backs both AI frontends and `mdl model`. - CLI: `mdl mcp --repo <dir>` starts the server; `mdl model context|entities| entity` print the query layer as JSON (the chat participant's data path). - VS Code: mcpProvider.ts registers the stdio server via McpStdioServerDefinition, resolving mdl through findMdl and passing the workspace dir as --repo; guarded for the 1.99+ API so older hosts keep every other feature. chatParticipant.ts implements @modelith for Ask mode (/list, /explain, name-an-entity, summarise) over the same `mdl model` reads. Verified: real MCP stdio handshake (initialize -> tools/list -> tools/call) on the demo model; create/update entities land on disk and reflect in reads; 593 pytest pass (+13), ruff clean, extension tsc+esbuild+vsce clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vsce warned (and prompted) on every package because package.json declares Apache-2.0 but no license file shipped. Copy the repo LICENSE into vscode/ so packaging is non-interactive and the Marketplace shows the license. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A stale global `mdl` (predating the `model` command) exits non-zero and prints
Typer's usage dump to stdout, which the chat participant surfaced raw ("No such
command 'model'"). Detect that signature and tell the user to upgrade mdl and
reload, instead of leaking the usage text.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The extension and the mdl CLI install separately (Marketplace .vsix vs a user's own `uv tool`/pipx/pip install), so a stale CLI silently lacks commands the AI surfaces call — as seen when @modelith hit an old global mdl. Add a one-shot capability handshake on activation: probe `mdl model --help` (shipped with `mdl mcp`, so it proxies "new enough"). If it's missing, show ONE dismissible warning with a Reload Window action and an upgrade command inferred from HOW this mdl was resolved (uv tool / pipx / pip / project .venv / workspace uv run) — never assuming a package manager. No hardcoded version to maintain (mdl has no --version anyway); the per-command guards remain the backstop. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The handler treated the whole prompt after /explain as a literal entity name, so
"/explain explain instrument entity, everything about it" failed with "no entity
named …". Replace the naive routing with LLM-backed understanding.
The chat-participant API hands us request.model — the user's own authenticated
Copilot model. For free-text asks we now ground with the real model facts (entity
roster + condensed context + detail for any entity the prompt names, all via the
same `mdl model` query layer) and let that model answer, pinned to the facts so it
can't invent entities. It handles questions ("what connects to instrument?"),
buried names, and typos alike.
Kept deterministic and fast where it should be: `/list` and an exact-name
`/explain <name>` render templates with no LLM round-trip. And if no model is
available (or the call fails), a heuristic name match + structural summary is the
fallback — verified it extracts "instrument" from the failing prompt above — so the
participant never dead-ends.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A question like "are all entities in BCNF?" got only the entity roster and relationships — no attributes, no keys — so the model (correctly) refused to answer. Two gaps, both fixed in the shared query layer so every surface benefits: - get_entity now returns `keys` (pk / unique / alternate key groups with member columns in order). Normalization and identity questions need this; the MCP get_entity tool and `mdl model entity` gain it too. - New entities_detail() + `mdl model detail`: compact per-entity records (attributes as name:domain?, keys, relationship names) for grounding a model-wide question in ONE call. Terse and capped (default 40, chat passes 120) with total/shown/truncated, so a 150-entity model stays inside the context window instead of blowing it. The chat participant now picks its grounding by scope: a model-wide ask (normalization, which/all/every/each, or no entity named) grounds on `mdl model detail` and appends a truncation notice when the cap bites; a targeted ask grounds on the named entities in full. The deterministic entity card renders keys too. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Six of seven demo entities reported "no keys" because they mark keys with the per-attribute `role: business_key` convention rather than a first-class KeyGroup, and _keys_for only read KeyGroups. The IR documents business_key as still supported (a pk KeyGroup merely "wins over" it), so the model DOES know these keys — the query layer just wasn't surfacing them. _keys_for now synthesises an inferred pk from business_key attributes when no explicit pk KeyGroup exists, flagged `inferred: true`. All entities now expose a primary key, so @modelith can reason about them for normalization; the LLM prompt explains the inferred flag and that a full BCNF/3NF proof still needs functional dependencies the model doesn't yet capture. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
findMdl probed `.venv/bin/mdl` only under the MODEL dir, which is typically a demo/ subfolder with no venv — so when developing Modelith itself it fell through to the stale `uv tool` global on PATH, and every core change needed a global reinstall to show up in the extension. Also probe `.venv/bin/mdl` in each workspace folder, ranked above the PATH/global candidates. In the Modelith repo that resolves to the working-tree editable install, so Python edits are live with no reinstall. A normal user (model dir with its own venv, or only a global install) has no workspace .venv and is unaffected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Packaging under the default Node 16 crashed cryptically ("ReferenceError:
ReadableStream is not defined") from deep inside vsce/undici. Add a check-node
guard (engines.node >=18 declaratively, plus a preflight in build/package/publish)
that aborts with a clear message pointing at `nvm use 20` before the toolchain
runs. Verified: exits 1 with the message on Node 16, silent no-op on Node 20.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds two Copilot Chat integrations to the VS Code extension, one per mode, sharing a single query layer in
packages/coreso the two frontends never drift.Architecture note
The spec sketched the MCP server as a Node module compiled against
packages/core— but core is Python, so Node can't import it, and every other integration here (LSP, canvas) runs by spawning themdlCLI. This keeps the spec's intent (one query layer, no duplication, writes reuse validation) but implements the server as a Pythonmdl mcpsubcommand over stdio, registered from VS Code exactly like the LSP.What's included
MCP server (Agent mode) — new
packages/mcp(modelith-mcp), amdl mcpstdio server (FastMCP,mcp>=1.2,<2). Seven tools:list_entities,get_entity,search_ontology,get_model_context,validate,create_entity,update_entity. Reads via the shared query layer; writes viaapply_command(validated + fingerprint-guarded, direct-write to the checkout per spec §1). Registered viaMcpStdioServerDefinition, resolvingmdlthroughfindMdl, guarded for the 1.99+ API.@modelithchat participant (Ask mode) — intent parsed by the user's own Copilot model (request.model), grounded in real model facts (never hallucinating entities). Model-wide questions (normalization, "which entities…") ground on a compact, cappedmdl model detailso a 150-entity model stays within the context window; targeted questions ground on the named entities. Deterministic fast paths for/listand exact-name/explain; heuristic fallback when no LLM is available.Shared query layer —
packages/core/src/mdl_core/query.py: name-or-ULID entity lookup (the IR had no name index), condensed chat-sized shapes, key groups (real + inferred from the legacybusiness_keyconvention), andentities_detailfor bulk grounding. Exposed viamdl model context|entities|entity|detail.Robustness & tooling
mdlon activation and gives an install-specific upgrade hint (uv tool / pipx / pip / venv), never assuming a package manager..venv/bin/mdlover the global — so developing Modelith uses the editable install with no reinstall.ReadableStreamcrash → a clear "use Node 20" message).Verification
initialize→tools/list→tools/call) against the demo model./list,/explain, free-text questions, and entity-by-entity BCNF reasoning (keys real + inferred; honest about the FD limit).Follow-up (separate)
Functional-dependency capture in the core model (for a complete BCNF/3NF verdict) is a modeling-language addition and is being designed in its own session — out of scope here.