From 5239434f5277987f235a92579e3623d04c64968b Mon Sep 17 00:00:00 2001 From: Debasish Bose Date: Sat, 12 Sep 2026 17:28:19 +1000 Subject: [PATCH 1/2] Set a commit identity on a catalog checkout branch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit checkout_branch produces a fresh clone with no local git identity, and its docstring promises the checkout is "ready to propose from". On a host without a global git config (CI runners, containers) a commit into it fails with exit 128 ("empty ident name") — which is why test_a_proposal_pushes_back_to_the_models_own_repo failed in GitHub Actions but passed on developer machines that have a global identity. Set a local user.name/user.email on the checkout (the same identity publish() already commits under), so a commit into a materialised proposal branch works regardless of the host's global git config. Pre-existing latent bug, surfaced by a CI run; unrelated to the docs/MCP changes. Verified by reproducing the CI environment locally (GIT_CONFIG_GLOBAL=/dev/null, no GIT_AUTHOR_* set): the catalog suite fails before the fix and passes after. Co-Authored-By: Claude Opus 4.8 --- packages/catalog/src/mdl_catalog/git_backend.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/catalog/src/mdl_catalog/git_backend.py b/packages/catalog/src/mdl_catalog/git_backend.py index c205ff1..3e19e11 100644 --- a/packages/catalog/src/mdl_catalog/git_backend.py +++ b/packages/catalog/src/mdl_catalog/git_backend.py @@ -187,6 +187,12 @@ def checkout_branch(self, entry: CatalogEntry, branch: str) -> Path: raise MaterializeNotSupported( f"could not create branch {branch} in {entry.model}: {out.strip()[:200]}" ) + # A fresh clone inherits no commit identity; on a host without a global + # git config (CI, a container) a commit into this checkout would fail with + # exit 128. The docstring promises it's "ready to propose from", so make it + # so — set a local identity, matching the one publish() commits under. + self.runner.run(["config", "user.name", self.author_name], repo_root) + self.runner.run(["config", "user.email", self.author_email], repo_root) return model_dir def materialize(self, entry: CatalogEntry, *, refresh: bool = False) -> Path: From 36968a78e1425c3b01da29231341d138f2ea5242 Mon Sep 17 00:00:00 2001 From: Debasish Bose Date: Sat, 12 Sep 2026 17:28:29 +1000 Subject: [PATCH 2/2] Create modelith-vscode-ai-spec.md --- modelith-vscode-ai-spec.md | 138 +++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 modelith-vscode-ai-spec.md diff --git a/modelith-vscode-ai-spec.md b/modelith-vscode-ai-spec.md new file mode 100644 index 0000000..45fade0 --- /dev/null +++ b/modelith-vscode-ai-spec.md @@ -0,0 +1,138 @@ +# Modelith — VS Code MCP Server + Chat Participant Spec + +Two separate extension integrations, aimed at Copilot Chat's two different +modes. Both ship inside the Modelith VS Code extension; neither requires +the user to create or edit a config file. + +--- + +## 1. Bundled MCP server (Agent mode) + +Registers automatically on extension activation — no `.vscode/mcp.json` +needed. + +**Registration:** + +```jsonc +// package.json +"contributes": { + "mcpServerDefinitionProviders": [ + { "id": "modelith.mcp", "label": "Modelith" } + ] +} +``` + +```typescript +// extension.ts — activate() +context.subscriptions.push( + vscode.lm.registerMcpServerDefinitionProvider('modelith.mcp', { + provideMcpServerDefinitions: async () => { + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + return [ + new vscode.McpStdioServerDefinition( + 'modelith', 'node', + [context.asAbsolutePath('dist/mcp/server.js'), '--repo', workspaceRoot] + ) + ]; + } + }) +); +``` + +- Bundle the server module inside the extension's own `dist/`, compiled + directly against `packages/core`. Do not shell out to a separately + installed `mdl` CLI — that would reintroduce an install step through the + back door and defeat the point of bundling. +- Pass the active workspace folder explicitly via `--repo`. A + programmatically registered server has no implicit repo scope the way a + committed `.vscode/mcp.json` does — `provideMcpServerDefinitions` must + read it from `vscode.workspace.workspaceFolders` itself. +- Guard the registration call. `registerMcpServerDefinitionProvider` only + exists from VS Code 1.99+. Use + `vscode.lm?.registerMcpServerDefinitionProvider` (optional chaining) or + set `engines.vscode: "^1.99.0"`. An unguarded call has been observed + crashing extension activation entirely on older hosts, taking down the + LSP and canvas features alongside MCP, not just MCP itself. + +**Tools to expose** (writes reuse the LSP's existing validation path, no +separate write logic): + +| Tool | Purpose | +|---|---| +| `list_entities(subject_area?)` | Grounding — what already exists | +| `get_entity(name)` | Full entity detail incl. relationships, `ontology_refs` | +| `search_ontology(text, layer?)` | Proxies the `OntologyResolver` interface | +| `create_entity(draft)` | Direct local write, validated before commit | +| `update_entity(name, changes)` | Direct local write, validated before commit | +| `get_model_context()` | Condensed model summary sized for a chat context window | +| `validate(entity)` | Inline lint feedback before anything is written | + +- Direct local write is intentional here, not PR-gated — this is the + engineer's own checkout, a different trust boundary than the SME app. +- Tools only function when Copilot Chat is in **Agent mode** — tool + invocation is architecturally a plan/act/observe loop that Ask mode + doesn't have, so there's no mode-detection code to write. Document the + requirement (e.g. first-run notification) rather than trying to work + around it. + +--- + +## 2. `@modelith` chat participant (Ask mode) + +Separate extension point, aimed specifically at Ask mode, where MCP tools +structurally cannot run (no agentic loop to invoke them from). + +**Registration:** + +```jsonc +// package.json +"contributes": { + "chatParticipants": [{ + "id": "modelith.chat", + "name": "modelith", + "fullName": "Modelith", + "description": "Ask about entities, relationships, and ontology alignment in this model." + }] +} +``` + +```typescript +// extension.ts — activate() +const participant = vscode.chat.createChatParticipant('modelith.chat', handler); +``` + +```typescript +const handler: vscode.ChatRequestHandler = async ( + request: vscode.ChatRequest, + context: vscode.ChatContext, + stream: vscode.ChatResponseStream, + token: vscode.CancellationToken +) => { + // parse request.prompt / request.command, call the same core query + // functions the MCP tools in §1 use, stream the answer back +}; +``` + +- The handler is **solely responsible** for the response once invoked — + no blended reasoning with Copilot's own tool-picking the way Agent-mode + tools work. Implement basic intent handling directly rather than + expecting the model to route sub-tasks for you. +- Optional slash commands for common asks, e.g. `@modelith /explain + pension_warehouse`. +- **Share the query layer with §1, don't duplicate it.** Both the MCP + tools and this handler should call the same underlying functions in + `packages/core` (`list_entities`, `get_entity`, ontology resolution, + etc.) — one query layer, two frontends. This keeps behavior consistent + regardless of which mode the user happens to be in, and avoids having + to reconcile two implementations later when one drifts from the other. + +## Build order + +1. Confirm/stabilize the core query functions both integrations will call + (`list_entities`, `get_entity`, `get_model_context`, ontology search) — + build this once in `packages/core`, not inside either integration. +2. MCP server (§1) — wraps the query layer as tools, adds the write path + (`create_entity`/`update_entity`/`validate`). +3. `@modelith` chat participant (§2) — wraps the same query layer as a + request handler. Built last and on top of §1's query layer specifically + so there's nothing left to duplicate.