Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7634bd1
mcp: remove stale darwin-x64 package, fix Windows target triple
bpowers Mar 13, 2026
a442e2d
mcp: add publishConfig and repository to wrapper package.json
bpowers Mar 13, 2026
c206507
mcp: include publishConfig and repository in generated platform packages
bpowers Mar 13, 2026
5e0e11e
mcp: update build script integration test for publishConfig, reposito…
bpowers Mar 13, 2026
e67df9e
mcp: add Dockerfile.cross for local cross-compilation toolchain
bpowers Mar 13, 2026
58e59bd
mcp: add cross-build.sh for local multi-platform binary builds
bpowers Mar 13, 2026
b6d7406
mcp: ignore cross-build dist/ output
bpowers Mar 13, 2026
a8605d6
mcp: fix smoke test to detect binary execution failures
bpowers Mar 13, 2026
2bf51b1
mcp: add GitHub Actions workflow for npm release
bpowers Mar 13, 2026
d5ed87f
doc: update CLAUDE.md files for mcp-npm-release
bpowers Mar 13, 2026
70551c7
mcp: add missing test coverage from code review feedback
bpowers Mar 13, 2026
336c91f
doc: add human test plan for MCP npm release
bpowers Mar 13, 2026
b52d48d
doc: add implementation plans for mcp-npm-release
bpowers Mar 13, 2026
f1332ca
mcp: address review feedback for npm release pipeline
bpowers Mar 13, 2026
0fd4619
mcp: tighten publish guards, pin Rust toolchain in CI
bpowers Mar 13, 2026
d629003
mcp: fix smoke test on arm64 Linux, clean up Dockerfile comment
bpowers Mar 13, 2026
97fd37b
mcp: add zig version sync test, fix windows dev hint
bpowers Mar 13, 2026
0347928
mcp: include rust-toolchain.toml in CI cache key
bpowers Mar 13, 2026
0e01667
mcp: clean root-owned dist files via Docker before rebuild
bpowers Mar 13, 2026
dcddb09
mcp: add --locked to release builds in CI
bpowers Mar 13, 2026
498e716
mcp: fix docker cleanup glob, use structured YAML in zig test
bpowers Mar 13, 2026
e52125d
mcp: cache cargo install metadata, add --locked to cross-build
bpowers Mar 13, 2026
ac870b0
mcp: address review feedback for release pipeline
bpowers Mar 20, 2026
3f5adc2
mcp: address PR review feedback
bpowers Mar 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
232 changes: 232 additions & 0 deletions .github/workflows/mcp-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
name: MCP npm Release

on:
push:
tags:
- 'mcp-v*'
workflow_dispatch:

permissions:
contents: read

jobs:
validate:
name: Validate version
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v4

- name: Extract and validate version
id: version
run: |
CARGO_VERSION=$(grep '^version = ' src/simlin-mcp/Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
echo "Cargo.toml version: $CARGO_VERSION"

if [[ "$GITHUB_REF" == refs/tags/mcp-v* ]]; then
TAG_VERSION="${GITHUB_REF_NAME#mcp-v}"
echo "Tag version: $TAG_VERSION"
if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
echo "::error::Tag version ($TAG_VERSION) does not match Cargo.toml ($CARGO_VERSION)"
exit 1
fi
fi

echo "version=$CARGO_VERSION" >> "$GITHUB_OUTPUT"

build:
name: Build ${{ matrix.artifact }}
needs: validate
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-musl
os: ubuntu-latest
artifact: mcp-linux-x64
binary: simlin-mcp
use-zigbuild: true
- target: aarch64-unknown-linux-musl
os: ubuntu-latest
artifact: mcp-linux-arm64
binary: simlin-mcp
use-zigbuild: true
- target: x86_64-pc-windows-gnu
os: ubuntu-latest
artifact: mcp-win32-x64
binary: simlin-mcp.exe
use-zigbuild: true
- target: aarch64-apple-darwin
os: macos-latest
artifact: mcp-darwin-arm64
binary: simlin-mcp
use-zigbuild: false
steps:
- uses: actions/checkout@v4

- name: Install Rust toolchain
run: |
rustup show
rustup target add ${{ matrix.target }}

- name: Cache Cargo artifacts
uses: actions/cache@v4
with:
path: |
~/.cargo/bin
~/.cargo/.crates.toml
~/.cargo/.crates2.json
~/.cargo/registry
~/.cargo/git
target
key: cargo-mcp-${{ matrix.os }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock', 'rust-toolchain.toml') }}
restore-keys: |
cargo-mcp-${{ matrix.os }}-${{ matrix.target }}-

- name: Install Zig
if: matrix.use-zigbuild
uses: mlugg/setup-zig@v2
with:
# Keep in sync with src/simlin-mcp/Dockerfile.cross
version: '0.15.2'

- name: Install cargo-zigbuild
if: matrix.use-zigbuild
run: cargo install --locked cargo-zigbuild@0.22

- name: Build (zigbuild)
if: matrix.use-zigbuild
run: cargo zigbuild -p simlin-mcp --locked --release --target ${{ matrix.target }}

- name: Build (native)
if: ${{ !matrix.use-zigbuild }}
run: cargo build -p simlin-mcp --locked --release --target ${{ matrix.target }}

- name: Upload binary
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: target/${{ matrix.target }}/release/${{ matrix.binary }}
if-no-files-found: error
retention-days: 1

publish-platform:
name: Publish platform packages
needs: [validate, build]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/mcp-v')
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4

# Intentionally omit registry-url: it injects a token-based .npmrc that
# conflicts with npm's OIDC provenance flow. Trusted publishing handles auth.
- uses: actions/setup-node@v4
with:
node-version: '22'

- run: npm install -g npm@latest

- uses: actions/download-artifact@v4
with:
path: artifacts/

- name: Prepare platform packages
working-directory: src/simlin-mcp
run: |
bash build-npm-packages.sh

cp ../../artifacts/mcp-linux-x64/simlin-mcp npm/@simlin/mcp-linux-x64/bin/
cp ../../artifacts/mcp-linux-arm64/simlin-mcp npm/@simlin/mcp-linux-arm64/bin/
cp ../../artifacts/mcp-win32-x64/simlin-mcp.exe npm/@simlin/mcp-win32-x64/bin/
cp ../../artifacts/mcp-darwin-arm64/simlin-mcp npm/@simlin/mcp-darwin-arm64/bin/

chmod +x npm/@simlin/mcp-linux-x64/bin/simlin-mcp
chmod +x npm/@simlin/mcp-linux-arm64/bin/simlin-mcp
chmod +x npm/@simlin/mcp-darwin-arm64/bin/simlin-mcp

- name: Publish @simlin/mcp-linux-x64
working-directory: src/simlin-mcp/npm/@simlin/mcp-linux-x64
run: |
VERSION=$(jq -r .version package.json)
if npm view "@simlin/mcp-linux-x64@$VERSION" version > /dev/null 2>&1; then
echo "Already published @simlin/mcp-linux-x64@$VERSION, skipping"
else
npm publish --provenance --access public
fi

- name: Publish @simlin/mcp-linux-arm64
working-directory: src/simlin-mcp/npm/@simlin/mcp-linux-arm64
run: |
VERSION=$(jq -r .version package.json)
if npm view "@simlin/mcp-linux-arm64@$VERSION" version > /dev/null 2>&1; then
echo "Already published @simlin/mcp-linux-arm64@$VERSION, skipping"
else
npm publish --provenance --access public
fi

- name: Publish @simlin/mcp-win32-x64
working-directory: src/simlin-mcp/npm/@simlin/mcp-win32-x64
run: |
VERSION=$(jq -r .version package.json)
if npm view "@simlin/mcp-win32-x64@$VERSION" version > /dev/null 2>&1; then
echo "Already published @simlin/mcp-win32-x64@$VERSION, skipping"
else
npm publish --provenance --access public
fi

- name: Publish @simlin/mcp-darwin-arm64
working-directory: src/simlin-mcp/npm/@simlin/mcp-darwin-arm64
run: |
VERSION=$(jq -r .version package.json)
if npm view "@simlin/mcp-darwin-arm64@$VERSION" version > /dev/null 2>&1; then
echo "Already published @simlin/mcp-darwin-arm64@$VERSION, skipping"
else
npm publish --provenance --access public
fi

publish-wrapper:
name: Publish @simlin/mcp
needs: [validate, publish-platform]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/mcp-v')
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4

# Intentionally omit registry-url: trusted publishing handles auth (see publish-platform)
- uses: actions/setup-node@v4
with:
node-version: '22'

- run: npm install -g npm@latest

- name: Update wrapper version
working-directory: src/simlin-mcp
run: |
VERSION="${{ needs.validate.outputs.version }}"
jq --arg v "$VERSION" '
.version = $v |
.optionalDependencies = (
.optionalDependencies | to_entries | map(.value = $v) | from_entries
)
' package.json > package.json.tmp && mv package.json.tmp package.json

echo "Updated wrapper package.json:"
cat package.json

- name: Publish @simlin/mcp
working-directory: src/simlin-mcp
run: |
VERSION=$(jq -r .version package.json)
if npm view "@simlin/mcp@$VERSION" version > /dev/null 2>&1; then
echo "Already published @simlin/mcp@$VERSION, skipping"
else
npm publish --provenance --access public
fi
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,4 @@ __pycache__/
# simlin-mcp npm packaging artifacts
/src/simlin-mcp/vendor
/src/simlin-mcp/npm
/src/simlin-mcp/dist
28 changes: 14 additions & 14 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,20 @@ With Simlin you can iterate on strategy and policy faster than you can in the re

This is a monorepo without external users -- breaking changes are OK if tests pass. Exception: protobuf files must follow standard versioning (we have a DB with serialized instances).

| Component | Language | Description | Docs |
|---------------------|----------|--------------------------------------------------|-------------------------------------------|
| `src/simlin-engine` | Rust | Compiles, type-checks, and simulates SD models | [CLAUDE.md](/src/simlin-engine/CLAUDE.md) |
| `src/libsimlin` | Rust | Flat C FFI to simlin-engine (WASM, CGo, C/C++) | [CLAUDE.md](/src/libsimlin/CLAUDE.md) |
| `src/simlin-mcp` | Rust | MCP server for viewing and editing models | [CLAUDE.md](/src/simlin-mcp/CLAUDE.md) |
| `src/engine` | TypeScript | Promise-based TypeScript API for WASM engine | [CLAUDE.md](/src/engine/CLAUDE.md) |
| `src/core` | TypeScript | Shared data models and common utilities | [CLAUDE.md](/src/core/CLAUDE.md) |
| `src/diagram` | TypeScript | React model editor and visualization toolkit | [CLAUDE.md](/src/diagram/CLAUDE.md) |
| `src/app` | TypeScript | Full-featured SD application | [CLAUDE.md](/src/app/CLAUDE.md) |
| `src/server` | TypeScript | Express.js backend (Firebase Auth, Firestore) | [CLAUDE.md](/src/server/CLAUDE.md) |
| `src/xmutil` | C++/Rust | Vensim-to-XMILE converter (test-only) | -- |
| `src/simlin-cli` | Rust | CLI for simulation/conversion (testing/debugging) | [CLAUDE.md](/src/simlin-cli/CLAUDE.md) |
| `src/pysimlin` | Python/Rust | Python bindings for the simulation engine | [CLAUDE.md](/src/pysimlin/CLAUDE.md) |
| `website` | TypeScript | Rspress-based documentation site | [CLAUDE.md](/website/CLAUDE.md) |
| Component | Language | Description | Docs |
|---------------------|-------------|---------------------------------------------------|-------------------------------------------|
| `src/simlin-engine` | Rust | Compiles, type-checks, and simulates SD models | [CLAUDE.md](/src/simlin-engine/CLAUDE.md) |
| `src/libsimlin` | Rust | Flat C FFI to simlin-engine (WASM, CGo, C/C++) | [CLAUDE.md](/src/libsimlin/CLAUDE.md) |
| `src/simlin-mcp` | Rust/JS | MCP server for AI assistants (`@simlin/mcp` npm) | [CLAUDE.md](/src/simlin-mcp/CLAUDE.md) |
| `src/engine` | TypeScript | Promise-based TypeScript API for WASM engine | [CLAUDE.md](/src/engine/CLAUDE.md) |
| `src/core` | TypeScript | Shared data models and common utilities | [CLAUDE.md](/src/core/CLAUDE.md) |
| `src/diagram` | TypeScript | React model editor and visualization toolkit | [CLAUDE.md](/src/diagram/CLAUDE.md) |
| `src/app` | TypeScript | Full-featured SD application | [CLAUDE.md](/src/app/CLAUDE.md) |
| `src/server` | TypeScript | Express.js backend (Firebase Auth, Firestore) | [CLAUDE.md](/src/server/CLAUDE.md) |
| `src/xmutil` | C++/Rust | Vensim-to-XMILE converter (test-only) | -- |
| `src/simlin-cli` | Rust | CLI for simulation/conversion (testing/debugging) | [CLAUDE.md](/src/simlin-cli/CLAUDE.md) |
| `src/pysimlin` | Python/Rust | Python bindings for the simulation engine | [CLAUDE.md](/src/pysimlin/CLAUDE.md) |
| `website` | TypeScript | Rspress-based documentation site | [CLAUDE.md](/website/CLAUDE.md) |

The XMILE specification (`docs/reference/xmile-v1.0.html`) is a crucial reference for simulation concepts, array/subscript notation, and equation syntax.

Expand Down
20 changes: 20 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading