From 6f6144e9642416e2fcbe042366a671b018604a3d Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Wed, 5 Aug 2026 16:41:22 -0400 Subject: [PATCH 1/6] Require UI changesets to also bump contracts-mcp for MCP App HTML (#836) --- .changeset/config.json | 18 +++++++-- .../fix-governor-crosschain-dropdown.md | 6 +++ .claude/skills/changeset/SKILL.md | 1 + .github/workflows/changeset.yml | 2 + .github/workflows/version.yml | 3 ++ CONTRIBUTING.md | 5 ++- package.json | 2 +- scripts/release/check-mcp-ui-changeset.mjs | 37 +++++++++++++++++++ scripts/release/format-changelog.mjs | 6 ++- 9 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 .changeset/fix-governor-crosschain-dropdown.md create mode 100644 scripts/release/check-mcp-ui-changeset.mjs diff --git a/.changeset/config.json b/.changeset/config.json index a1e6e616e..4c1c9d360 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -12,8 +12,18 @@ "access": "public", "baseBranch": "master", "updateInternalDependencies": "patch", - "ignore": [ - "@openzeppelin/wizard-cairo-alpha", - "ui" - ] + "privatePackages": { + "version": true, + "tag": false + }, + "changedFilePatterns": [ + "**", + "!src/cairo_alpha/**", + "!src/polkadot/**", + "!api/**", + "!public/**", + "!scripts/deno/**", + "!src/**/App.svelte" + ], + "ignore": ["@openzeppelin/wizard-cairo-alpha"] } diff --git a/.changeset/fix-governor-crosschain-dropdown.md b/.changeset/fix-governor-crosschain-dropdown.md new file mode 100644 index 000000000..676580f01 --- /dev/null +++ b/.changeset/fix-governor-crosschain-dropdown.md @@ -0,0 +1,6 @@ +--- +'ui': patch +'@openzeppelin/contracts-mcp': patch +--- + +Fix Governor Cross-Chain Execution appearing as an empty dropdown in the MCP App UI. diff --git a/.claude/skills/changeset/SKILL.md b/.claude/skills/changeset/SKILL.md index 8180602a1..46ee76dfa 100644 --- a/.claude/skills/changeset/SKILL.md +++ b/.claude/skills/changeset/SKILL.md @@ -30,6 +30,7 @@ First line is a high-level summary without leading dash (PR number gets appended 5. **Multiple packages**: Multiple packages can share a changeset file with different bump levels in the frontmatter. Use separate files only when packages need unrelated descriptions. 6. **Bump levels**: Follow semver based on current package version. `x.y.z` (>=1.0.0): major for breaking, minor for features, patch for fixes. `0.x.y`: minor for breaking, patch for features/fixes. `0.0.x`: patch for everything. 7. **New unpublished packages**: Still need a changeset to bump the initial version in package.json and for the changes to appear in the resulting changelog. +8. **UI that ships in MCP Apps**: `ui` is private (versioned in-repo, not published to npm). MCP App HTML is built from `ui` when `@openzeppelin/contracts-mcp` is published. Changes to Wizard controls or other UI that ships in MCP Apps need **one changeset listing both** `ui` and `@openzeppelin/contracts-mcp`. Web-only UI (Cairo Alpha, Polkadot, `App.svelte`) does not need a changeset. Never version `ui` in a changeset without also listing `@openzeppelin/contracts-mcp`. ## Steps diff --git a/.github/workflows/changeset.yml b/.github/workflows/changeset.yml index 4a4ecac92..ea900ab32 100644 --- a/.github/workflows/changeset.yml +++ b/.github/workflows/changeset.yml @@ -26,3 +26,5 @@ jobs: uses: ./.github/actions/setup - name: Check changeset run: npx changeset status --since=origin/${{ github.base_ref }} + - name: Check UI changesets include contracts-mcp + run: node scripts/release/check-mcp-ui-changeset.mjs origin/${{ github.base_ref }} diff --git a/.github/workflows/version.yml b/.github/workflows/version.yml index bad20d1a0..ae826ecef 100644 --- a/.github/workflows/version.yml +++ b/.github/workflows/version.yml @@ -4,6 +4,9 @@ on: push: branches: - master + # Skip pushes that only affect the Netlify UI deployment. If changes affect + # contract-specific controls which are part of the packaged HTML files for MCP Apps, + # those PRs also add a .changeset file, so this workflow still runs. paths-ignore: - 'packages/ui/**' diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4c9829099..a07a31754 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -96,5 +96,8 @@ From the `packages/mcp` directory: As a contributor, we ask that you fork this repository, work on your own fork and then submit pull requests. The pull requests will be reviewed and eventually merged into the main repo. See ["Fork-a-Repo"](https://help.github.com/articles/fork-a-repo/) for how this works. ### Adding Changesets -If your PR modifies code generation logic under `packages/core`, you will need to add changesets for the relevant packages to summarize the changes. The PR's `Changeset` GitHub check will give an error if this condition is not satisfied. +Published packages (`packages/core/*`, `packages/common`, `packages/cli`, `packages/mcp`) use [Changesets](https://github.com/changesets/changesets) for versioning. The PR's `Changeset` GitHub check fails when a changed publishable package has no changeset. Use the `ignore-changeset` label only when a bump is genuinely not needed. + +The `ui` package is private (Netlify + MCP App source) and is versioned in-repo but not published. MCP App HTML is built from `ui` when `@openzeppelin/contracts-mcp` is published. If your PR changes Wizard controls or other UI that ships in MCP Apps, add **one changeset that lists both** `ui` and `@openzeppelin/contracts-mcp`. Web-only UI (for example Cairo Alpha, Polkadot, or the web Wizard `App.svelte` shells) does not require a changeset. + - To add a changeset: from the root directory, run `yarn changeset` diff --git a/package.json b/package.json index 7ce24e546..6ad086e74 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "devDependencies": { "@changesets/changelog-github": "^0.5.1", "@changesets/cli": "^2.29.2", + "@changesets/read": "^0.6.5", "@eslint/js": "^9.21.0", "concurrently": "^9.1.2", "eslint": "^9.33.0", @@ -49,4 +50,3 @@ "typescript-eslint": "^8.29.0" } } - diff --git a/scripts/release/check-mcp-ui-changeset.mjs b/scripts/release/check-mcp-ui-changeset.mjs new file mode 100644 index 000000000..0b118087b --- /dev/null +++ b/scripts/release/check-mcp-ui-changeset.mjs @@ -0,0 +1,37 @@ +#!/usr/bin/env node + +/** + * Fail if a changeset lists `ui` but not `@openzeppelin/contracts-mcp`. + * + * `ui` is private (in-repo version only). App HTML is published only as part of + * contracts-mcp, so a ui-only changeset must not land. + * + * `changeset status` already requires a `ui` changeset when UI sources change. + */ + +import getChangesets from '@changesets/read'; + +const UI = 'ui'; +const MCP = '@openzeppelin/contracts-mcp'; +const sinceRef = process.argv[2]; +const missing = []; + +for (const changeset of await getChangesets(process.cwd(), sinceRef)) { + const names = new Set(changeset.releases.map(release => release.name)); + if (names.has(UI) && !names.has(MCP)) { + missing.push(changeset.id); + } +} + +if (missing.length === 0) { + process.exit(0); +} + +console.error( + `These changesets version \`${UI}\` without \`${MCP}\`:\n` + + missing.map(id => ` - .changeset/${id}.md`).join('\n') + + `\n\nList both packages so MCP App HTML is published with the UI change.\n` + + `Example frontmatter:\n` + + `---\n'${UI}': patch\n'${MCP}': patch\n---`, +); +process.exit(1); diff --git a/scripts/release/format-changelog.mjs b/scripts/release/format-changelog.mjs index 2f45dda52..14ac451b1 100644 --- a/scripts/release/format-changelog.mjs +++ b/scripts/release/format-changelog.mjs @@ -2,7 +2,7 @@ // Adjusts the format of the changelog that changesets generates. -import { readFileSync, writeFileSync } from 'fs'; +import { existsSync, readFileSync, writeFileSync } from 'fs'; import { join } from 'path'; import { getSupportedLanguageInCoreSubfolder } from '../language-input.mjs'; @@ -10,6 +10,9 @@ function formatChangelog(dir) { console.log(`Formatting changelog for ${dir}...`); const changelogPath = join(dir, 'CHANGELOG.md'); + if (!existsSync(changelogPath)) { + return; + } const changelog = readFileSync(changelogPath, 'utf8'); @@ -43,3 +46,4 @@ for (const languageFolder of languageFolders) { } formatChangelog('./packages/common'); formatChangelog('./packages/mcp'); +formatChangelog('./packages/ui'); From 6eb89bc05c71433761a343783e2882a791d1af84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 5 Aug 2026 20:56:02 +0000 Subject: [PATCH 2/6] Prepare Release (#837) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Eric Lau --- .changeset/fix-governor-crosschain-dropdown.md | 6 ------ packages/mcp/CHANGELOG.md | 4 ++++ packages/mcp/package.json | 2 +- packages/ui/CHANGELOG.md | 6 ++++++ packages/ui/package.json | 2 +- 5 files changed, 12 insertions(+), 8 deletions(-) delete mode 100644 .changeset/fix-governor-crosschain-dropdown.md create mode 100644 packages/ui/CHANGELOG.md diff --git a/.changeset/fix-governor-crosschain-dropdown.md b/.changeset/fix-governor-crosschain-dropdown.md deleted file mode 100644 index 676580f01..000000000 --- a/.changeset/fix-governor-crosschain-dropdown.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'ui': patch -'@openzeppelin/contracts-mcp': patch ---- - -Fix Governor Cross-Chain Execution appearing as an empty dropdown in the MCP App UI. diff --git a/packages/mcp/CHANGELOG.md b/packages/mcp/CHANGELOG.md index 8564b8a27..09d5a6288 100644 --- a/packages/mcp/CHANGELOG.md +++ b/packages/mcp/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog +## 0.6.1 (2026-08-05) + +- Fix Governor Cross-Chain Execution appearing as an empty dropdown in the MCP App UI. ([#836](https://github.com/OpenZeppelin/contracts-wizard/pull/836)) + ## 0.6.0 (2026-07-31) - **Breaking changes**: Renamed the `erc7984` tool to `confidential-erc7984`. ([#828](https://github.com/OpenZeppelin/contracts-wizard/pull/828)) diff --git a/packages/mcp/package.json b/packages/mcp/package.json index a4e19de7f..4450106c0 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@openzeppelin/contracts-mcp", - "version": "0.6.0", + "version": "0.6.1", "description": "OpenZeppelin Contracts MCP Server", "license": "AGPL-3.0-only", "repository": "https://github.com/OpenZeppelin/contracts-wizard", diff --git a/packages/ui/CHANGELOG.md b/packages/ui/CHANGELOG.md new file mode 100644 index 000000000..866ef976a --- /dev/null +++ b/packages/ui/CHANGELOG.md @@ -0,0 +1,6 @@ +# Changelog + + +## 0.0.1 (2026-08-05) + +- Fix Governor Cross-Chain Execution appearing as an empty dropdown in the MCP App UI. ([#836](https://github.com/OpenZeppelin/contracts-wizard/pull/836)) diff --git a/packages/ui/package.json b/packages/ui/package.json index 29186cfa3..d0f01ebb6 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "ui", - "version": "0.0.0", + "version": "0.0.1", "private": true, "scripts": { "prepare": "echo \"Nothing to prepare\"", From 14420e6734bf14b9f3f45ee3d2f460bdbc2561bf Mon Sep 17 00:00:00 2001 From: Eric Lau Date: Thu, 6 Aug 2026 22:59:36 -0400 Subject: [PATCH 3/6] Shrink MCP App HTML by sharing language templates and bundling the Zama icon (#838) --- .changeset/shrink-mcp-apps.md | 9 ++ packages/mcp/src/apps/register.test.ts | 95 ++++++++++--- packages/mcp/src/apps/register.ts | 130 ++++++++++++++++-- packages/mcp/src/server.test.ts | 78 ++++++++++- packages/ui/public/icons/zama.png | Bin 912 -> 0 bytes packages/ui/rollup.mcp-apps.config.mjs | 19 ++- packages/ui/scripts/package-mcp-apps.mjs | 37 ++++- packages/ui/src/common/icons/ZamaIcon.svelte | 7 + .../src/confidential/ERC7984Controls.svelte | 3 +- .../ui/src/mcp-apps/entries/cairo-account.ts | 4 - .../ui/src/mcp-apps/entries/cairo-custom.ts | 4 - .../ui/src/mcp-apps/entries/cairo-erc1155.ts | 4 - .../ui/src/mcp-apps/entries/cairo-erc20.ts | 4 - .../ui/src/mcp-apps/entries/cairo-erc721.ts | 4 - .../ui/src/mcp-apps/entries/cairo-governor.ts | 4 - .../ui/src/mcp-apps/entries/cairo-multisig.ts | 4 - .../ui/src/mcp-apps/entries/cairo-vesting.ts | 4 - packages/ui/src/mcp-apps/entries/cairo.ts | 4 + .../mcp-apps/entries/confidential-erc7984.ts | 4 - .../ui/src/mcp-apps/entries/confidential.ts | 4 + .../src/mcp-apps/entries/solidity-account.ts | 4 - .../src/mcp-apps/entries/solidity-custom.ts | 4 - .../src/mcp-apps/entries/solidity-erc1155.ts | 4 - .../ui/src/mcp-apps/entries/solidity-erc20.ts | 4 - .../src/mcp-apps/entries/solidity-erc721.ts | 4 - .../src/mcp-apps/entries/solidity-governor.ts | 4 - .../ui/src/mcp-apps/entries/solidity-rwa.ts | 4 - .../mcp-apps/entries/solidity-stablecoin.ts | 4 - packages/ui/src/mcp-apps/entries/solidity.ts | 4 + .../src/mcp-apps/entries/stellar-fungible.ts | 4 - .../src/mcp-apps/entries/stellar-governor.ts | 4 - .../mcp-apps/entries/stellar-non-fungible.ts | 4 - .../mcp-apps/entries/stellar-stablecoin.ts | 4 - .../ui/src/mcp-apps/entries/stellar-vault.ts | 4 - packages/ui/src/mcp-apps/entries/stellar.ts | 4 + .../ui/src/mcp-apps/entries/stylus-erc1155.ts | 4 - .../ui/src/mcp-apps/entries/stylus-erc20.ts | 4 - .../ui/src/mcp-apps/entries/stylus-erc721.ts | 4 - packages/ui/src/mcp-apps/entries/stylus.ts | 4 + .../ui/src/mcp-apps/entries/uniswap-hooks.ts | 4 +- packages/ui/src/mcp-apps/kind-placeholder.ts | 6 + packages/ui/src/mcp-apps/mount.ts | 19 ++- packages/ui/tailwind.mcp-apps.config.js | 60 ++++++++ 43 files changed, 445 insertions(+), 142 deletions(-) create mode 100644 .changeset/shrink-mcp-apps.md delete mode 100644 packages/ui/public/icons/zama.png create mode 100644 packages/ui/src/common/icons/ZamaIcon.svelte delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-account.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-custom.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-erc1155.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-erc20.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-erc721.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-governor.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-multisig.ts delete mode 100644 packages/ui/src/mcp-apps/entries/cairo-vesting.ts create mode 100644 packages/ui/src/mcp-apps/entries/cairo.ts delete mode 100644 packages/ui/src/mcp-apps/entries/confidential-erc7984.ts create mode 100644 packages/ui/src/mcp-apps/entries/confidential.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-account.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-custom.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-erc1155.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-erc20.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-erc721.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-governor.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-rwa.ts delete mode 100644 packages/ui/src/mcp-apps/entries/solidity-stablecoin.ts create mode 100644 packages/ui/src/mcp-apps/entries/solidity.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stellar-fungible.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stellar-governor.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stellar-non-fungible.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stellar-stablecoin.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stellar-vault.ts create mode 100644 packages/ui/src/mcp-apps/entries/stellar.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stylus-erc1155.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stylus-erc20.ts delete mode 100644 packages/ui/src/mcp-apps/entries/stylus-erc721.ts create mode 100644 packages/ui/src/mcp-apps/entries/stylus.ts create mode 100644 packages/ui/src/mcp-apps/kind-placeholder.ts create mode 100644 packages/ui/tailwind.mcp-apps.config.js diff --git a/.changeset/shrink-mcp-apps.md b/.changeset/shrink-mcp-apps.md new file mode 100644 index 000000000..1e5cef045 --- /dev/null +++ b/.changeset/shrink-mcp-apps.md @@ -0,0 +1,9 @@ +--- +'ui': patch +'@openzeppelin/contracts-mcp': patch +--- + +Shrink MCP App package size by sharing one HTML template per language. +- Inject contract kind at serve time instead of shipping near-duplicate HTML per tool. +- Scope MCP App Tailwind content away from web-only Wizard shells. +- Bundle the Zama logo so it renders in self-contained MCP Apps. diff --git a/packages/mcp/src/apps/register.test.ts b/packages/mcp/src/apps/register.test.ts index 50abbf98e..f8df848cf 100644 --- a/packages/mcp/src/apps/register.test.ts +++ b/packages/mcp/src/apps/register.test.ts @@ -1,7 +1,16 @@ import test from 'ava'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { testMcpInfo } from '../helpers.test'; -import { appResourceUri, readAppHtml, registerWizardAppTool, RESOURCE_MIME_TYPE, wizardAppResult } from './register'; +import { + appResourceUri, + getToolAppSpec, + MCP_KIND_PLACEHOLDER, + readAppHtml, + registerWizardAppTool, + RESOURCE_MIME_TYPE, + TOOL_APP_SPECS, + wizardAppResult, +} from './register'; import { registerSolidityERC20 } from '../solidity/tools/erc20'; test('solidity-erc20 registers UI metadata', t => { @@ -12,25 +21,42 @@ test('solidity-erc20 registers UI metadata', t => { t.is(tool._meta?.['ui/resourceUri'], appResourceUri('solidity-erc20')); }); -test('MCP App HTML artifacts exist for Wizard-backed tools', async t => { - // One tool per language; registerWizardAppTool fails closed for the rest at server start. - const tools = [ - 'solidity-erc20', - 'solidity-erc721', - 'cairo-erc20', - 'stellar-fungible', - 'stylus-erc20', - 'confidential-erc7984', - 'uniswap-hooks', - ]; +test('TOOL_APP_SPECS entries have distinct per-tool URIs and known overrides', t => { + const tools = Object.keys(TOOL_APP_SPECS); + t.true(tools.length >= 26, `expected at least 26 tools, got ${tools.length}`); for (const tool of tools) { + const spec = getToolAppSpec(tool); + t.truthy(spec.template, `${tool} missing template`); + t.truthy(spec.kind, `${tool} missing kind`); + t.is(appResourceUri(tool), `ui://openzeppelin/${tool}.html`); + } + t.is(TOOL_APP_SPECS['solidity-rwa']?.kind, 'RealWorldAsset'); + t.is(TOOL_APP_SPECS['uniswap-hooks']?.kind, 'Hooks'); +}); + +test('MCP App HTML artifacts exist and inject kind for Wizard-backed tools', async t => { + const tools = Object.keys(TOOL_APP_SPECS).sort(); + for (const tool of tools) { + const spec = getToolAppSpec(tool); const html = await readAppHtml(tool); t.true(html.includes(''), `${tool} missing doctype`); t.true(html.includes('