diff --git a/.eslintignore b/.eslintignore index 25aee502..f3af2557 100644 --- a/.eslintignore +++ b/.eslintignore @@ -3,6 +3,6 @@ dist coverage *.log **/*.js -packages/client/src/remote-dom/iframe-bundle.ts +sdks/typescript/client/src/remote-dom/iframe-bundle.ts examples \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index efb0938e..6ee80eed 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,26 +7,48 @@ on: pull_request: branches: - main + release: + types: [published] jobs: - build_lint_test: + filter_changed_paths: runs-on: ubuntu-latest - strategy: - matrix: - node-version: [22.x] + outputs: + ts_client_files: ${{ steps.filter.outputs.ts_client_files }} + ts_server_files: ${{ steps.filter.outputs.ts_server_files }} + ruby_sdk_files: ${{ steps.filter.outputs.ruby_sdk_files }} + example_files: ${{ steps.filter.outputs.example_files }} steps: - - name: Checkout repository - uses: actions/checkout@v4 + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v2 + id: filter + with: + filters: | + ts_client_files: + - 'sdks/typescript/client/**' + ts_server_files: + - 'sdks/typescript/server/**' + ruby_sdk_files: + - 'sdks/ruby/**' + example_files: + - 'examples/**' + + js_build_and_test: + needs: filter_changed_paths + if: needs.filter_changed_paths.outputs.ts_client_files == 'true' || needs.filter_changed_paths.outputs.ts_server_files == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v2 with: version: 10 - - name: Setup Node.js ${{ matrix.node-version }} + - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: ${{ matrix.node-version }} + node-version: 22.x cache: 'pnpm' - name: Install dependencies @@ -36,30 +58,17 @@ jobs: run: pnpm lint - name: Test - run: pnpm test - - - name: Build libraries (excluding docs) - run: pnpm --filter=!@mcp-ui/docs build + run: pnpm test:ts - - name: Build docs - if: matrix.node-version == '20.x' # Build docs only on one Node version to save time - run: pnpm --filter=@mcp-ui/docs build + - name: Build + run: pnpm build - publish: - needs: build_lint_test + ruby_sdk_test: + needs: filter_changed_paths + if: needs.filter_changed_paths.outputs.ruby_sdk_files == 'true' runs-on: ubuntu-latest - # Run on pushes to the main branch. semantic-release will determine if a release is needed. - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && !startsWith(github.head_commit.message, 'chore(release):') && !(contains(join(github.event.commits.*.files_changed, ','), 'examples/') || contains(join(github.event.commits.*.files_changed, ','), 'docs/')) - permissions: - contents: write # Needed to push new version tags, commit changelog/package.json updates - issues: write # Needed to create/comment on release-related issues (optional, but good practice) - id-token: write # Required for publishing to NPM with OIDC steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - # Fetch all history for all tags and branches so semantic-release can analyze commits - fetch-depth: 0 + - uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v2 @@ -69,29 +78,110 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 22.x # Use a Node version compatible with semantic-release + node-version: 22.x cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build all packages - run: pnpm --filter="./packages/*" build + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler: latest + bundler-cache: true + working-directory: 'sdks/ruby' - - name: List contents of client dist - run: | - echo "--- Listing packages/client/dist ---" - ls -R packages/client/dist || echo "packages/client/dist not found or empty" - echo "--- End of packages/client/dist ---" + - name: Lint + run: bundle exec rubocop + working-directory: 'sdks/ruby' - - name: List contents of server dist - run: | - echo "--- Listing packages/server/dist ---" - ls -R packages/server/dist || echo "packages/server/dist not found or empty" - echo "--- End of packages/server/dist ---" + - name: Run tests + run: pnpm test:ruby + + release_ts_client: + needs: [js_build_and_test] + if: github.ref == 'refs/heads/main' && needs.filter_changed_paths.outputs.ts_client_files == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + id-token: write + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: { version: 10 } + - name: Setup Node.js + uses: actions/setup-node@v4 + with: { node-version: 22.x, cache: 'pnpm' } + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Release + working-directory: sdks/typescript/client + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npx semantic-release - - name: Semantic Release + release_ts_server: + needs: [js_build_and_test] + if: github.ref == 'refs/heads/main' && needs.filter_changed_paths.outputs.ts_server_files == 'true' + runs-on: ubuntu-latest + permissions: + contents: write + issues: write + id-token: write + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: { version: 10 } + - name: Setup Node.js + uses: actions/setup-node@v4 + with: { node-version: 22.x, cache: 'pnpm' } + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Release + working-directory: sdks/typescript/server env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Provided by Actions, used by @semantic-release/github and @semantic-release/git - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # Your NPM token, used by @semantic-release/npm + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} run: npx semantic-release + + release_ruby_sdk: + name: Release Ruby SDK + needs: [ruby_sdk_test] + if: github.ref == 'refs/heads/main' && needs.filter_changed_paths.outputs.ruby_sdk_files == 'true' + runs-on: ubuntu-latest + environment: release + permissions: + contents: write # to push commits and tags + id-token: write # for trusted publishing + issues: write # to comment on issues + pull-requests: write # to comment on pull requests + steps: + - uses: actions/checkout@v4 + with: { fetch-depth: 0 } + - name: Setup pnpm + uses: pnpm/action-setup@v2 + with: { version: 10 } + - name: Setup Node.js + uses: actions/setup-node@v4 + with: { node-version: 22.x, cache: 'pnpm' } + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.2' + bundler: latest + bundler-cache: true + working-directory: sdks/ruby + - name: Release + working-directory: sdks/ruby + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npx semantic-release \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5a2e9210..afb19e9d 100644 --- a/.gitignore +++ b/.gitignore @@ -132,10 +132,10 @@ docs/src/.vitepress/dist docs/src/.vitepress/cache # Monorepo specific -/packages/**/dist -/packages/**/coverage -/apps/**/dist -/apps/**/coverage +/sdks/**/dist +/sdks/**/coverage +/examples/**/dist +/examples/**/coverage # OS generated files # ###################### diff --git a/.releaserc.json b/.releaserc.json index c41e309e..ad730909 100644 --- a/.releaserc.json +++ b/.releaserc.json @@ -7,14 +7,14 @@ [ "@semantic-release/npm", { - "pkgRoot": "packages/client", + "pkgRoot": "sdks/typescript/client", "npmPublish": true } ], [ "@semantic-release/npm", { - "pkgRoot": "packages/server", + "pkgRoot": "sdks/typescript/server", "npmPublish": true } ], @@ -36,8 +36,8 @@ "assets": [ "CHANGELOG.md", "package.json", - "packages/client/package.json", - "packages/server/package.json", + "sdks/typescript/client/package.json", + "sdks/typescript/server/package.json", "pnpm-lock.yaml" ], "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 00000000..be94e6f5 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +3.2.2 diff --git a/README.md b/README.md index 8ad0da8f..f44601f1 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@

Server Version Client Version + Ruby Server SDK Version MCP Documentation

@@ -34,10 +35,11 @@ ## 💡 What's `mcp-ui`? -`mcp-ui` is a TypeScript SDK comprising two packages: +`mcp-ui` is a collection of SDKs comprising: -* **`@mcp-ui/server`**: Utilities to generate UI resources (`UIResource`) on your MCP server. -* **`@mcp-ui/client`**: UI components (e.g., ``) to render the UI resources and handle their events. +* **`@mcp-ui/server` (TypeScript)**: Utilities to generate UI resources (`UIResource`) on your MCP server. +* **`@mcp-ui/client` (TypeScript)**: UI components (e.g., ``) to render the UI resources and handle their events. +* **`mcp_ui_server` (Ruby)**: Utilities to generate UI resources on your MCP server in a Ruby environment. Together, they let you define reusable UI snippets on the server side, seamlessly and securely render them in the client, and react to their actions in the MCP host environment. @@ -93,7 +95,7 @@ It accepts the following props: #### HTML (`text/html` and `text/uri-list`) -Rendered using the `` component, which displays content inside an `