diff --git a/.markdownlint.json b/.markdownlint.json index 86037b36a7b8..89cc9d97fc8d 100644 --- a/.markdownlint.json +++ b/.markdownlint.json @@ -13,7 +13,7 @@ "no-space-in-code": true, "no-space-in-links": true, "no-empty-links": true, - "ol-prefix": {"style": "one_or_ordered"}, + "ol-prefix": false, "no-reversed-links": true, "reference-links-images": { "shortcut_syntax": false diff --git a/content/guides/admin-user-management/_index.md b/content/guides/admin-user-management/_index.md index 746eaed94ed6..5d68916081c6 100644 --- a/content/guides/admin-user-management/_index.md +++ b/content/guides/admin-user-management/_index.md @@ -4,9 +4,9 @@ summary: Simplify user access while ensuring security and efficiency in Docker. description: A guide for managing roles, provisioning users, and optimizing Docker access with tools like SSO and activity logs. tags: [admin] params: - featured: true + featured: false time: 20 minutes - image: + image: resource_links: - title: Overview of Administration in Docker url: /admin/ diff --git a/content/guides/github-sonarqube-sandbox/_index.md b/content/guides/github-sonarqube-sandbox/_index.md new file mode 100644 index 000000000000..a2e690d7b47c --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/_index.md @@ -0,0 +1,56 @@ +--- +title: How to build an AI-powered code quality workflow with SonarQube and E2B +linkTitle: Build an AI-powered code quality workflow +summary: Build AI-powered code quality workflows using E2B sandboxes with Docker's MCP catalog to automate GitHub and SonarQube integration. +description: Learn how to create E2B sandboxes with MCP servers, analyze code quality with SonarQube, and generate quality-gated pull requests using GitHub—all through natural language interactions with Claude. +tags: [devops] +params: + featured: true + time: 40 minutes + image: + resource_links: + - title: E2B Documentation + url: https://e2b.dev/docs + - title: Docker MCP Catalog + url: https://hub.docker.com/mcp + - title: Sandboxes + url: https://docs.docker.com/ai/mcp-catalog-and-toolkit/sandboxes/ +--- + +This guide demonstrates how to build an AI-powered code quality workflow using +[E2B sandboxes](https://e2b.dev/docs) with Docker’s MCP catalog. You’ll create +a system that automatically analyzes code quality issues in GitHub repositories +using SonarQube, then generate pull requests with fixes. + +## What you'll build + +You’ll build a Node.js script that spins up an E2B sandbox, connects GitHub and +SonarQube MCP servers, and uses Claude Code to analyze code quality and propose +improvements. The MCP servers are containerized and run as part of the E2B +sandbox. + +## What you'll learn + +In this guide, you'll learn: + +- How to create E2B sandboxes with multiple MCP servers +- How to configure GitHub and SonarQube MCP servers for AI workflows +- How to use Claude Code inside sandboxes to interact with external tools +- How to build automated code review workflows that create quality-gated +pull requests + +## Why use E2B sandboxes? + +Running this workflow in E2B sandboes provides several advantages over +local execution: + +- Security: AI-generated code runs in isolated containers, protecting your +local environment and credentials +- Zero setup: No need to install SonarQube, GitHub CLI, or manage dependencies +locally +- Scalability: Resource-intensive operations like code scanning run in the +cloud without consuming local resources + +## Learn more + +Read Docker's blog post: [Docker + E2B: Building the Future of Trusted AI](https://www.docker.com/blog/docker-e2b-building-the-future-of-trusted-ai/). diff --git a/content/guides/github-sonarqube-sandbox/customize.md b/content/guides/github-sonarqube-sandbox/customize.md new file mode 100644 index 000000000000..61b22c213434 --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/customize.md @@ -0,0 +1,181 @@ +--- +title: Customize a code quality check workflow +linkTitle: Customize workflow +summary: Adapt your GitHub and SonarQube workflow to focus on specific quality issues, integrate with CI/CD, and set custom thresholds. +description: Learn how to customize prompts for specific quality issues, filter by file patterns, set quality thresholds, and integrate your workflow with GitHub Actions for automated code quality checks. +weight: 20 +--- + +Now that you understand the basics of automating code quality workflows with +GitHub and SonarQube in E2B sandboxes, you can customize the workflow +for your needs. + +## Focus on specific quality issues + +Modify the prompt to prioritize certain issue types: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +const prompt = `Using SonarQube and GitHub MCP tools: + +Focus only on: +- Security vulnerabilities (CRITICAL priority) +- Bugs (HIGH priority) +- Skip code smells for this iteration + +Analyze "${repoPath}" and fix the highest priority issues first.`; +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +prompt = f"""Using SonarQube and GitHub MCP tools: + +Focus only on: +- Security vulnerabilities (CRITICAL priority) +- Bugs (HIGH priority) +- Skip code smells for this iteration + +Analyze "{repo_path}" and fix the highest priority issues first.""" +``` + +{{< /tab >}} +{{< /tabs >}} + +## Integrate with CI/CD + +Add this workflow to GitHub Actions to run automatically on pull requests: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```yaml +name: Automated quality checks +on: + pull_request: + types: [opened, synchronize] + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: "18" + - run: npm install + - run: npx tsx 06-quality-gated-pr.ts + env: + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} + GITHUB_OWNER: ${{ github.repository_owner }} + GITHUB_REPO: ${{ github.event.repository.name }} + SONARQUBE_ORG: your-org-key +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```yaml +name: Automated quality checks +on: + pull_request: + types: [opened, synchronize] + +jobs: + quality: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.8" + - run: pip install e2b python-dotenv + - run: python 06_quality_gated_pr.py + env: + E2B_API_KEY: ${{ secrets.E2B_API_KEY }} + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} + GITHUB_OWNER: ${{ github.repository_owner }} + GITHUB_REPO: ${{ github.event.repository.name }} + SONARQUBE_ORG: your-org-key +``` + +{{< /tab >}} +{{< /tabs >}} + +## Filter by file patterns + +Target specific parts of your codebase: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +const prompt = `Analyze code quality but only consider: +- Files in src/**/*.js +- Exclude test files (*.test.js, *.spec.js) +- Exclude build artifacts in dist/ + +Focus on production code only.`; +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +prompt = """Analyze code quality but only consider: +- Files in src/**/*.js +- Exclude test files (*.test.js, *.spec.js) +- Exclude build artifacts in dist/ + +Focus on production code only.""" +``` + +{{< /tab >}} +{{< /tabs >}} + +## Set quality thresholds + +Define when PRs should be created: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +const prompt = `Quality gate thresholds: +- Only create PR if: + * Bug count decreases by at least 1 + * No new security vulnerabilities introduced + * Code coverage does not decrease + * Technical debt reduces by at least 15 minutes + +If changes do not meet these thresholds, explain why and skip PR creation.`; +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +prompt = """Quality gate thresholds: +- Only create PR if: + * Bug count decreases by at least 1 + * No new security vulnerabilities introduced + * Code coverage does not decrease + * Technical debt reduces by at least 15 minutes + +If changes do not meet these thresholds, explain why and skip PR creation.""" +``` + +{{< /tab >}} +{{< /tabs >}} + +## Next steps + +Learn how to troubleshoot common issues. diff --git a/content/guides/github-sonarqube-sandbox/troubleshoot.md b/content/guides/github-sonarqube-sandbox/troubleshoot.md new file mode 100644 index 000000000000..4e07789c211c --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/troubleshoot.md @@ -0,0 +1,348 @@ +--- +title: Troubleshoot code quality workflows +linkTitle: Troubleshoot +summary: Resolve common issues with E2B sandboxes, MCP server connections, and GitHub/SonarQube integration. +description: Solutions for MCP tools not loading, authentication errors, permission issues, workflow timeouts, and other common problems when building code quality workflows with E2B. +weight: 30 +--- + +This page covers common issues you might encounter when building code quality +workflows with E2B sandboxes and MCP servers, along with their solutions. + +If you're experiencing problems not covered here, check the +[E2B documentation](https://e2b.dev/docs). + +## MCP tools not available + +Issue: Claude reports `I don't have any MCP tools available`. + +Solution: + +1. Verify you're using the authorization header: + +```plaintext +--header "Authorization: Bearer ${mcpToken}" +``` + +2. Check you're waiting for MCP initialization: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +await new Promise((resolve) => setTimeout(resolve, 1000)); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +await asyncio.sleep(1) +``` + +{{< /tab >}} +{{< /tabs >}} + +3. Ensure credentials are in both `envs` and `mcp` configuration: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, +}); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, +) +``` + +{{< /tab >}} +{{< /tabs >}} + +4. Verify your API tokens are valid and have proper scopes. + +## GitHub tools work but SonarQube doesn't + +Issue: GitHub MCP tools load but SonarQube tools don't appear. + +Solution: SonarQube MCP server requires GitHub to be configured simultaneously. +Always include both servers in your sandbox configuration, even if you're only +testing one. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +// Include both servers even if only using one +const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, +}); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +# Include both servers even if only using one +sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, +) +``` + +{{< /tab >}} +{{< /tabs >}} + +## Claude can't access private repositories + +Issue: "I don't have access to that repository". + +Solution: + +1. Verify your GitHub token has `repo` scope (not just `public_repo`). +2. Test with a public repository first. +3. Ensure the repository owner and name are correct in your `.env`: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```plaintext +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```plaintext +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +``` + +{{< /tab >}} +{{< /tabs >}} + +## Workflow times out or runs too long + +Issue: Workflow doesn't complete or Claude credits run out. + +Solutions: + +1. Use `timeoutMs: 0` (TypeScript) or `timeout_ms=0` (Python) for complex workflows to allow unlimited time: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +await sbx.commands.run( + `echo '${prompt}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, // No timeout + onStdout: console.log, + onStderr: console.log, + }, +); +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout_ms=0, # No timeout + on_stdout=print, + on_stderr=print, +) +``` + +{{< /tab >}} +{{< /tabs >}} + +2. Break complex workflows into smaller, focused tasks. +3. Monitor your Anthropic API credit usage. +4. Add checkpoints in prompts: "After each step, show progress before continuing". + +## Sandbox cleanup errors + +Issue: Sandboxes aren't being cleaned up properly, leading to resource exhaustion. + +Solution: Always use proper error handling with cleanup in the `finally` block: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +```typescript +async function robustWorkflow() { + let sbx: Sandbox | undefined; + + try { + sbx = await Sandbox.betaCreate({ + // ... configuration + }); + + // ... workflow logic + } catch (error) { + console.error("Workflow failed:", error); + process.exit(1); + } finally { + if (sbx) { + console.log("Cleaning up sandbox..."); + await sbx.kill(); + } + } +} +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +```python +async def robust_workflow(): + sbx = None + + try: + sbx = await AsyncSandbox.beta_create( + # ... configuration + ) + + # ... workflow logic + + except Exception as error: + print(f"Workflow failed: {error}") + sys.exit(1) + finally: + if sbx: + print("Cleaning up sandbox...") + await sbx.kill() +``` + +{{< /tab >}} +{{< /tabs >}} + +## Environment variable not loading + +Issue: Script fails with "undefined" or "None" for environment variables. + +Solution: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +1. Ensure `dotenv` is loaded at the top of your file: + +```typescript +import "dotenv/config"; +``` + +2. Verify the `.env` file is in the same directory as your script. + +3. Check variable names match exactly (case-sensitive): + +```typescript +// .env file +GITHUB_TOKEN = ghp_xxxxx; + +// In code +process.env.GITHUB_TOKEN; // Correct +process.env.github_token; // Wrong - case doesn't match +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +1. Ensure `dotenv` is loaded at the top of your file: + +```python +from dotenv import load_dotenv +load_dotenv() +``` + +2. Verify the `.env` file is in the same directory as your script. + +3. Check variable names match exactly (case-sensitive): + +```python +# .env file +GITHUB_TOKEN=ghp_xxxxx + +# In code +os.getenv("GITHUB_TOKEN") # ✓ Correct +os.getenv("github_token") # ✗ Wrong - case doesn't match +``` + +{{< /tab >}} +{{< /tabs >}} + +## SonarQube returns empty results + +Issue: SonarQube analysis returns no projects or issues. + +Solution: + +1. Verify your SonarCloud organization key is correct. +2. Ensure you have at least one project configured in SonarCloud. +3. Check that your SonarQube token has the necessary permissions. +4. Confirm your project has been analyzed at least once in SonarCloud. diff --git a/content/guides/github-sonarqube-sandbox/workflow.md b/content/guides/github-sonarqube-sandbox/workflow.md new file mode 100644 index 000000000000..ccb12dbbd441 --- /dev/null +++ b/content/guides/github-sonarqube-sandbox/workflow.md @@ -0,0 +1,1586 @@ +--- +title: Build a code quality check workflow +linkTitle: Build workflow +summary: Learn to use GitHub and SonarQube MCP servers in E2B sandboxes through progressive examples. +description: Create E2B sandboxes, discover MCP tools, test individual operations, and build complete quality-gated PR workflows. +weight: 10 +--- + +In this section, you'll build a complete code quality automation workflow +step-by-step. You'll start by creating an E2B sandbox with GitHub and +SonarQube MCP servers, then progressively add functionality until you have a +production-ready workflow that analyzes code quality and creates pull requests. + +By working through each step sequentially, you'll learn how MCP servers work, +how to interact with them through Claude, and how to chain operations together +to build powerful automation workflows. + +## Prerequisites + +Before you begin, make sure you have: + +- E2B account with [API access](https://e2b.dev/docs/api-key) +- [Anthropic API key](https://docs.claude.com/en/api/admin-api/apikeys/get-api-key) + + > [!NOTE] + > + > This example uses Claude CLI which comes pre-installed in E2B sandboxes, but you can adapt the example to work with other AI assistants of your choice. See [E2B's MCP documentation](https://e2b.dev/docs/mcp/quickstart) for alternative connection methods. + +- GitHub account with: + - A repository containing code to analyze + - [Personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with `repo` scope +- SonarCloud account with: + - [Organization](https://docs.sonarsource.com/sonarqube-cloud/administering-sonarcloud/resources-structure/organization) created + - [Project configured](https://docs.sonarsource.com/sonarqube-community-build/project-administration/creating-and-importing-projects) for your repository + - [User token](https://docs.sonarsource.com/sonarqube-server/instance-administration/security/administering-tokens) generated +- Language runtime installed: + - TypeScript: [Node.js 18+](https://nodejs.org/en/download) + - Python: [Python 3.8+](https://www.python.org/downloads/) + +> [!NOTE] +> +> This guide uses Claude's `--dangerously-skip-permissions` flag to enable +> automated command execution in E2B sandboxes. This flag bypasses permission +> prompts, which is appropriate for isolated container environments like E2B +> where sandboxes are disposable and separate from your local machine. +> +> However, be aware that Claude can execute any commands within the sandbox, +> including accessing files and credentials available in that environment. Only +> use this approach with trusted code and workflows. For more information, +> see [Anthropic's guidance on container security](https://docs.anthropic.com/en/docs/claude-code/devcontainer). + +## Set up your project + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +1. Create a new directory for your workflow and initialize Node.js: + +```bash +mkdir github-sonarqube-workflow +cd github-sonarqube-workflow +npm init -y +``` + +2. Open `package.json` and configure it for ES modules: + +```json +{ + "name": "github-sonarqube-workflow", + "version": "1.0.0", + "description": "Automated code quality workflow using E2B, GitHub, and SonarQube", + "type": "module", + "main": "quality-workflow.ts", + "scripts": { + "start": "tsx quality-workflow.ts" + }, + "keywords": ["e2b", "github", "sonarqube", "mcp", "code-quality"], + "author": "", + "license": "MIT" +} +``` + +3. Install required dependencies: + +```bash +npm install e2b dotenv +npm install -D typescript tsx @types/node +``` + +4. Create a `.env` file in your project root: + +```bash +touch .env +``` + +5. Add your API keys and configuration, replacing the placeholders with your actual credentials: + +```plaintext +E2B_API_KEY=your_e2b_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here +GITHUB_TOKEN=ghp_your_personal_access_token_here +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +SONARQUBE_ORG=your_sonarcloud_org_key +SONARQUBE_TOKEN=your_sonarqube_user_token +SONARQUBE_URL=https://sonarcloud.io +``` + +6. Protect your credentials by adding `.env` to `.gitignore`: + +```bash +echo ".env" >> .gitignore +echo "node_modules/" >> .gitignore +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +1. Create a new directory for your workflow: + +```bash +mkdir github-sonarqube-workflow +cd github-sonarqube-workflow +``` + +2. Create a virtual environment and activate it: + +```bash +python3 -m venv venv +source venv/bin/activate # On Windows: venv\Scripts\activate +``` + +3. Install required dependencies: +```bash +pip install e2b python-dotenv +``` + +4. Create a `.env` file in your project root: + +```bash +touch .env +``` + +5. Add your API keys and configuration, replacing the placeholders with your actual credentials: + +```plaintext +E2B_API_KEY=your_e2b_api_key_here +ANTHROPIC_API_KEY=your_anthropic_api_key_here +GITHUB_TOKEN=ghp_your_personal_access_token_here +GITHUB_OWNER=your_github_username +GITHUB_REPO=your_repository_name +SONARQUBE_ORG=your_sonarcloud_org_key +SONARQUBE_TOKEN=your_sonarqube_user_token +SONARQUBE_URL=https://sonarcloud.io +``` + +6. Protect your credentials by adding `.env` to `.gitignore`: + +```bash +echo ".env" >> .gitignore +echo "venv/" >> .gitignore +echo "__pycache__/" >> .gitignore +``` + +{{< /tab >}} +{{< /tabs >}} + +## Step 1: Create your first sandbox + +Let's start by creating a sandbox and verifying the MCP servers are configured correctly. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create a file named `01-test-connection.ts` in your project root: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function testConnection() { + console.log( + "Creating E2B sandbox with GitHub and SonarQube MCP servers...\n", + ); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + console.log(" Sandbox created successfully!"); + console.log(`MCP Gateway URL: ${mcpUrl}\n`); + + // Wait for MCP initialization + await new Promise((resolve) => setTimeout(resolve, 1000)); + + // Configure Claude to use the MCP gateway + console.log("Connecting Claude CLI to MCP gateway..."); + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log("\nœConnection successful! Cleaning up..."); + await sbx.kill(); +} + +testConnection().catch(console.error); +``` + +Run this script to verify your setup: + +```bash +npx tsx 01-test-connection.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create a file named `01_test_connection.py` in your project root: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def test_connection(): + print("Creating E2B sandbox with GitHub and SonarQube MCP servers...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + print(" Sandbox created successfully!") + print(f"MCP Gateway URL: {mcp_url}\n") + + # Wait for MCP initialization + await asyncio.sleep(1) + + # Configure Claude to use the MCP gateway + print("Connecting Claude CLI to MCP gateway...") + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\n Connection successful! Cleaning up...") + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(test_connection()) +``` + +Run this script to verify your setup: + +```bash +python 01_test_connection.py +``` + +{{< /tab >}} +{{< /tabs >}} + +Your output should look similar to the following example: + +```console {collapse=true} +Creating E2B sandbox with GitHub and SonarQube MCP servers... + +✓ Sandbox created successfully! +MCP Gateway URL: https://50005-xxxxx.e2b.app/mcp + +Connecting Claude CLI to MCP gateway... +Added HTTP MCP server e2b-mcp-gateway with URL: https://50005-xxxxx.e2b.app/mcp to local config +Headers: { + "Authorization": "Bearer xxxxx-xxxx-xxxx" +} +File modified: /home/user/.claude.json [project: /home/user] + +✓ Connection successful! Cleaning up... +``` + +You've just learned how to create an E2B sandbox with multiple MCP servers +configured. The `betaCreate` method initializes a cloud environment +with Claude CLI and your specified MCP servers. + +## Step 2: Discover available MCP tools + +MCP servers expose tools that Claude can call. The GitHub MCP server provides +repository management tools, while SonarQube provides code analysis tools. +By listing their tools, you know what operations are possible. + +To try listing MCP tools: + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `02-list-tools.ts`: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function listTools() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + // Wait for MCP initialization + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + console.log("\nDiscovering available MCP tools...\n"); + + const prompt = + "List all MCP tools you have access to. For each tool, show its exact name and a brief description."; + + await sbx.commands.run( + `echo '${prompt}' | claude -p --dangerously-skip-permissions`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + await sbx.kill(); +} + +listTools().catch(console.error); +``` + +Run the script: + +```bash +npx tsx 02-list-tools.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `02_list_tools.py`: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def list_tools(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + # Wait for MCP initialization + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\nDiscovering available MCP tools...\n") + + prompt = "List all MCP tools you have access to. For each tool, show its exact name and a brief description." + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(list_tools()) +``` + +Run the script: + +```bash +python 02_list_tools.py +``` + +{{< /tab >}} +{{< /tabs >}} + +In the console, you should see a list of MCP tools: + +```console {collapse=true} +Creating sandbox... + +Sandbox created +Connecting to MCP gateway... + +Discovering available MCP tools... + +I have access to the following MCP tools: + +**GitHub Tools:** +1. mcp__create_repository - Create a new GitHub repository +2. mcp__list_issues - List issues in a repository +3. mcp__create_issue - Create a new issue +4. mcp__get_file_contents - Get file contents from a repository +5. mcp__create_or_update_file - Create or update files in a repository +6. mcp__create_pull_request - Create a pull request +7. mcp__create_branch - Create a new branch +8. mcp__push_files - Push multiple files in a single commit +... (30+ more GitHub tools) + +**SonarQube Tools:** +1. mcp__get_projects - List projects in organization +2. mcp__get_quality_gate_status - Get quality gate status for a project +3. mcp__list_project_issues - List quality issues in a project +4. mcp__search_issues - Search for specific quality issues +... (SonarQube analysis tools) +``` + +## Step 3: Test GitHub MCP tools + +Let's try testing GitHub using MCP tools. Start simple by listing +repository issues. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `03-test-github.ts`: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function testGitHub() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + + console.log(`\nListing issues in ${repoPath}...\n`); + + const prompt = `Using the GitHub MCP tools, list all open issues in the repository "${repoPath}". Show the issue number, title, and author for each.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + await sbx.kill(); +} + +testGitHub().catch(console.error); +``` + +Run the script: + +```bash +npx tsx 03-test-github.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `03_test_github.py`: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def test_github(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + + print(f"\nListing issues in {repo_path}...\n") + + prompt = f'Using the GitHub MCP tools, list all open issues in the repository "{repo_path}". Show the issue number, title, and author for each.' + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(test_github()) +``` + +Run the script: + +```bash +python 03_test_github.py +``` + +{{< /tab >}} +{{< /tabs >}} + +You should see Claude use the GitHub MCP tools to list your repository's issues: + +```console {collapse=true} +Creating sandbox... +Connecting to MCP gateway... + +Listing issues in ... + +Here are the first 10 open issues in the repository: + +1. **Issue #23577**: Update README (author: user1) +2. **Issue #23575**: release-notes for Compose v2.40.1 version (author: user2) +3. **Issue #23570**: engine-cli: fix `docker volume prune` output (author: user3) +4. **Issue #23568**: Engdocs update (author: user4) +5. **Issue #23565**: add new section (author: user5) +... (continues with more issues) +``` + +You can now send prompts to Claude and interact with GitHub through +natural language. Claude decides what tool to call based on your prompt. + +## Step 4: Test SonarQube MCP tools + +Let's analyze code quality using SonarQube MCP tools. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `04-test-sonarqube.ts`: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function testSonarQube() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + console.log("\nAnalyzing code quality with SonarQube...\n"); + + const prompt = `Using the SonarQube MCP tools: + 1. List all projects in my organization + 2. For the first project, show: + - Quality gate status (pass/fail) + - Number of bugs + - Number of code smells + - Number of security vulnerabilities + 3. List the top 5 most critical issues found`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + await sbx.kill(); +} + +testSonarQube().catch(console.error); +``` + +Run the script: + +```bash +npx tsx 04-test-sonarqube.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `04_test_sonarqube.py`: + +```python +import os +import asyncio +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def test_sonarqube(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\nAnalyzing code quality with SonarQube...\n") + + prompt = """Using the SonarQube MCP tools: + 1. List all projects in my organization + 2. For the first project, show: + - Quality gate status (pass/fail) + - Number of bugs + - Number of code smells + - Number of security vulnerabilities + 3. List the top 5 most critical issues found""" + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(test_sonarqube()) +``` + +Run the script: + +```bash +python 04_test_sonarqube.py +``` + +{{< /tab >}} +{{< /tabs >}} + +> [!NOTE] +> +> This script may take a few minutes to run. + +You should see Claude output SonarQube analysis results: + +```console {collapse=true} +Creating sandbox... + +Analyzing code quality with SonarQube... + +## SonarQube Analysis Results + +### 1. Projects in Your Organization + +Found **1 project**: +- **Project Name**: project-1 +- **Project Key**: project-testing + +### 2. Project Analysis + +... + +### 3. Top 5 Most Critical Issues + +Found 1 total issues (all are code smells with no critical/blocker severity): + +1. **MAJOR Severity** - test.js:2 + - **Rule**: javascript:S1854 + - **Message**: Remove this useless assignment to variable "unusedVariable" + - **Status**: OPEN + +**Summary**: The project is in good health with no bugs or vulnerabilities detected. +``` + +You can now use SonarQube MCP tools to analyze code quality through +natural language. You can retrieve quality metrics, identify issues, +and understand what code needs fixing. + +## Step 5: Create a branch and make code changes + +Now, let's teach Claude to fix code based on quality issues discovered +by SonarQube. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `05-fix-code-issue.ts`: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function fixCodeIssue() { + console.log("Creating sandbox...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + const branchName = `quality-fix-${Date.now()}`; + + console.log("\nFixing a code quality issue...\n"); + + const prompt = `Using GitHub and SonarQube MCP tools: + + 1. Analyze code quality in repository "${repoPath}" with SonarQube + 2. Find ONE simple issue that can be confidently fixed (like an unused variable or code smell) + 3. Create a new branch called "${branchName}" + 4. Read the file containing the issue using GitHub tools + 5. Fix the issue in the code + 6. Commit the fix to the new branch with a clear commit message + + Important: Only fix issues you're 100% confident about. Explain what you're fixing and why.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log(`\nœCheck your repository for branch: ${branchName}`); + + await sbx.kill(); +} + +fixCodeIssue().catch(console.error); +``` + +Run the script: + +```bash +npx tsx 05-fix-code-issue.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `05_fix_code_issue.py`: + +```python +import os +import asyncio +import time +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def fix_code_issue(): + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + branch_name = f"quality-fix-{int(time.time() * 1000)}" + + print("\nFixing a code quality issue...\n") + + prompt = f"""Using GitHub and SonarQube MCP tools: + + 1. Analyze code quality in repository "{repo_path}" with SonarQube + 2. Find ONE simple issue that can be confidently fixed (like an unused variable or code smell) + 3. Create a new branch called "{branch_name}" + 4. Read the file containing the issue using GitHub tools + 5. Fix the issue in the code + 6. Commit the fix to the new branch with a clear commit message + + Important: Only fix issues you're 100% confident about. Explain what you're fixing and why.""" + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print(f"\n Check your repository for branch: {branch_name}") + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(fix_code_issue()) +``` + +Run the script: + +```bash +python 05_fix_code_issue.py +``` + +{{< /tab >}} +{{< /tabs >}} + +> [!NOTE] +> +> This script may take a few minutes to run. + +Claude will analyze your repository and fix a code quality issue: + +```console {collapse=true} +Creating sandbox... + +Fixing a code quality issue... + +## Task Completed Successfully! + +I've successfully analyzed the repository and fixed a code quality issue. Here's what I did: + +### 1. Analysis +Found 1 SonarQube issues in the `` repository: +- **Unused variable** (MINOR severity, line 2) +- Useless assignment (MAJOR severity, line 2) +- For-of loop suggestion (MINOR severity, lines 5-7) + +### 2. Issue Fixed +**Location:** test.js:2 +**Issue:** Unused variable declaration +**SonarQube Rule:** javascript:S1481 +**What was wrong:** The line `const unusedVariable = "this is not used";` declared a variable that was never used anywhere in the function. + +### 3. Why This Fix is Safe +- The variable was only declared and assigned, never referenced +- Removing it has zero impact on functionality +- It eliminates dead code and improves code quality +- This is a textbook example of a code smell that should be removed + +### 4. Changes Made +- Created branch: `quality-fix-xxxxx` +- Removed the unused variable declaration +- Committed with a clear message explaining the fix and referencing the SonarQube rule + +**Commit SHA:** `xxxxxxxxxxxxxxxxxxxxxxxxx` +**Branch:** https://github.com///tree/quality-fix-xxxxx + +The fix is ready for review and can be merged to improve the code quality of the repository! +``` + +You can now use GitHub and SonarQube MCP tools in the same workflow to read +files, make code changes, and commit them. + +## Step 6: Create quality-gated pull requests + +Finally, let's build the complete workflow: analyze quality, fix issues, +and create a PR only if improvements are made. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `06-quality-gated-pr.ts`: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function qualityGatedPR() { + console.log("Creating sandbox for quality-gated PR workflow...\n"); + + const sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + const branchName = `quality-improvements-${Date.now()}`; + + console.log("\nRunning quality-gated PR workflow...\n"); + + const prompt = `You are a code quality engineer. Using GitHub and SonarQube MCP tools: + + STEP 1: ANALYSIS + - Get current code quality status from SonarQube for "${repoPath}" + - Record the current number of bugs, code smells, and vulnerabilities + - Identify 1-3 issues that you can confidently fix + + STEP 2: FIX ISSUES + - Create branch "${branchName}" + - For each issue you're fixing: + * Read the file with the issue + * Make the fix + * Commit with a descriptive message + - Only fix issues where you're 100% confident the fix is correct + + STEP 3: VERIFICATION + - After your fixes, check if quality metrics would improve + - Calculate: Would this reduce bugs/smells/vulnerabilities? + + STEP 4: QUALITY GATE + - Only proceed if your changes improve quality + - If quality would not improve, explain why and stop + + STEP 5: CREATE PR (only if quality gate passes) + - Create a pull request from "${branchName}" to main + - Title: "Quality improvements: [describe what you fixed]" + - Description should include: + * What issues you fixed + * Before/after quality metrics + * Why these fixes improve code quality + - Add a comment with detailed SonarQube analysis + + Be thorough and explain your decisions at each step.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log(`\n Workflow complete! Check ${repoPath} for new pull request.`); + + await sbx.kill(); +} + +qualityGatedPR().catch(console.error); +``` + +Run the script: + +```bash +npx tsx 06-quality-gated-pr.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `06_quality_gated_pr.py`: + +```python +import os +import asyncio +import time +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def quality_gated_pr(): + print("Creating sandbox for quality-gated PR workflow...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + branch_name = f"quality-improvements-{int(time.time() * 1000)}" + + print("\nRunning quality-gated PR workflow...\n") + + prompt = f"""You are a code quality engineer. Using GitHub and SonarQube MCP tools: + + STEP 1: ANALYSIS + - Get current code quality status from SonarQube for "{repo_path}" + - Record the current number of bugs, code smells, and vulnerabilities + - Identify 1-3 issues that you can confidently fix + + STEP 2: FIX ISSUES + - Create branch "{branch_name}" + - For each issue you are fixing: + Read the file with the issue + Make the fix + Commit with a descriptive message + - Only fix issues where you are 100 percent confident the fix is correct + + STEP 3: VERIFICATION + - After your fixes, check if quality metrics would improve + - Calculate: Would this reduce bugs/smells/vulnerabilities? + + STEP 4: QUALITY GATE + - Only proceed if your changes improve quality + - If quality would not improve, explain why and stop + + STEP 5: CREATE PR (only if quality gate passes) + - Create a pull request from "{branch_name}" to main + - Title: "Quality improvements: [describe what you fixed]" + - Description should include: + What issues you fixed + Before/after quality metrics + Why these fixes improve code quality + - Add a comment with detailed SonarQube analysis + + Be thorough and explain your decisions at each step.""" + + await sbx.commands.run( + f"echo '{prompt.replace(chr(39), chr(39) + chr(92) + chr(39) + chr(39))}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print(f"\n Workflow complete! Check {repo_path} for new pull request.") + + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(quality_gated_pr()) +``` + +Run the script: + +```bash +python 06_quality_gated_pr.py +``` + +{{< /tab >}} +{{< /tabs >}} + +> [!NOTE] +> +> This script may take a few minutes to run. + +Claude will run the entire workflow, creating a quality improvement +and opening a PR in GitHub: + +```console {collapse=true} +Creating sandbox for quality-gated PR workflow... + +Running quality-gated PR workflow... + +## Complete - Code Quality Improvement Successfully Executed + +### Summary of Work Completed + +**Pull Request Created:** https://github.com///pull/1 + +--- + +### STEP 1: ANALYSIS + +**Current State:** +- **Bugs:** 0 +- **Vulnerabilities:** 0 +- **Code Smells:** 1 +- **Lines of Code:** 12 +- **All Ratings:** A (Reliability, Security, Maintainability) + +**Issues Identified (all in test.js):** +1. xxxxxxxxx - Unused variable declaration (MINOR) + +--- + +### STEP 2: FIX ISSUES + +**Branch Created:** `quality-improvements-xxxxxxxx` + +**Changes Made:** +1. **Removed unused variable** (line 2) - Eliminated dead code that served no purpose +2. **Modernized loop pattern** (lines 5-7) - Converted `for (let i = 0; i < items.length; i++)` to `for (const item of items)` + +**Commit:** xxxxxxxxxx + +--- + +### STEP 3: VERIFICATION + +**Expected Impact:** +- Code Smells: 1 → 0 (100% reduction) +- Bugs: 0 → 0 (maintained) +- Vulnerabilities: 0 → 0 (maintained) +- All quality ratings maintained at A + +--- + +### STEP 4: QUALITY GATE PASSED + +**Decision Criteria Met:** +- ✅ Reduces code smells by 100% +- ✅ No new bugs or vulnerabilities introduced +- ✅ Code is more readable and maintainable +- ✅ Follows modern JavaScript best practices +- ✅ All fixes are low-risk refactorings with no behavioral changes + +--- + +### STEP 5: CREATE PR + +**Pull Request Details:** +- **Number:** #1 +- **Title:** Quality improvements: Remove unused variable and modernize for loop +- **Branch:** quality-improvements-xxxxxxxx → main +- **URL:** https://github.com//pull/1 + +**PR Includes:** +- Comprehensive description with before/after metrics +- Detailed SonarQube analysis comment with issue breakdown +- Code comparison showing improvements +- Quality metrics table + +The pull request is now ready for review and merge! +``` + +You've now built a complete, multi-step workflow with conditional logic. +Claude analyzes quality with SonarQube, makes fixes using GitHub tools, +verifies improvements, and only creates a PR if quality actually improves. + +## Step 7: Add error handling + +Production workflows need error handling. Let's make the workflow more robust. + +{{< tabs group="language" >}} +{{< tab name="TypeScript" >}} + +Create `07-robust-workflow.ts`: + +```typescript +import "dotenv/config"; +import { Sandbox } from "e2b"; + +async function robustWorkflow() { + let sbx: Sandbox | undefined; + + try { + console.log("Creating sandbox...\n"); + + sbx = await Sandbox.betaCreate({ + envs: { + ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, + GITHUB_TOKEN: process.env.GITHUB_TOKEN!, + SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, + }, + mcp: { + githubOfficial: { + githubPersonalAccessToken: process.env.GITHUB_TOKEN!, + }, + sonarqube: { + org: process.env.SONARQUBE_ORG!, + token: process.env.SONARQUBE_TOKEN!, + url: "https://sonarcloud.io", + }, + }, + }); + + const mcpUrl = sbx.betaGetMcpUrl(); + const mcpToken = await sbx.betaGetMcpToken(); + + await new Promise((resolve) => setTimeout(resolve, 1000)); + + await sbx.commands.run( + `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, + { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, + ); + + const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; + + console.log("\nRunning workflow with error handling...\n"); + + const prompt = `Run a quality improvement workflow for "${repoPath}". + + ERROR HANDLING RULES: + 1. If SonarQube is unreachable, explain the error and stop gracefully + 2. If GitHub API fails, retry once, then explain and stop + 3. If no fixable issues are found, explain why and exit (this is not an error) + 4. If file modifications fail, explain which file and why + 5. At each step, check for errors before proceeding + + Run the workflow and handle any errors you encounter professionally.`; + + await sbx.commands.run( + `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, + { + timeoutMs: 0, + onStdout: console.log, + onStderr: console.log, + }, + ); + + console.log("\n Workflow completed"); + } catch (error) { + const err = error as Error; + console.error("\n Workflow failed:", err.message); + + if (err.message.includes("403")) { + console.error("\n Check your E2B account has MCP gateway access"); + } else if (err.message.includes("401")) { + console.error("\n Check your API tokens are valid"); + } else if (err.message.includes("Credit balance")) { + console.error("\n Check your Anthropic API credit balance"); + } + + process.exit(1); + } finally { + if (sbx) { + console.log("\n Cleaning up sandbox..."); + await sbx.kill(); + } + } +} + +robustWorkflow().catch(console.error); +``` + +Run the script: + +```bash +npx tsx 07-robust-workflow.ts +``` + +{{< /tab >}} +{{< tab name="Python" >}} + +Create `07_robust_workflow.py`: + +```python +import os +import asyncio +import sys +from dotenv import load_dotenv +from e2b import AsyncSandbox + +load_dotenv() + +async def robust_workflow(): + sbx = None + + try: + print("Creating sandbox...\n") + + sbx = await AsyncSandbox.beta_create( + envs={ + "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), + "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), + "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), + }, + mcp={ + "githubOfficial": { + "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), + }, + "sonarqube": { + "org": os.getenv("SONARQUBE_ORG"), + "token": os.getenv("SONARQUBE_TOKEN"), + "url": "https://sonarcloud.io", + }, + }, + ) + + mcp_url = sbx.beta_get_mcp_url() + mcp_token = await sbx.beta_get_mcp_token() + + await asyncio.sleep(1) + + await sbx.commands.run( + f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', + timeout=0, # Fixed: was timeout_ms + on_stdout=print, + on_stderr=print, + ) + + repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" + + print("\nRunning workflow with error handling...\n") + + prompt = f"""Run a quality improvement workflow for "{repo_path}". + + ERROR HANDLING RULES: + 1. If SonarQube is unreachable, explain the error and stop gracefully + 2. If GitHub API fails, retry once, then explain and stop + 3. If no fixable issues are found, explain why and exit (this is not an error) + 4. If file modifications fail, explain which file and why + 5. At each step, check for errors before proceeding + + Run the workflow and handle any errors you encounter professionally.""" + + await sbx.commands.run( + f"echo '{prompt}' | claude -p --dangerously-skip-permissions", + timeout=0, + on_stdout=print, + on_stderr=print, + ) + + print("\n Workflow completed") + + except Exception as error: + print(f"\n✗ Workflow failed: {str(error)}") + + error_msg = str(error) + if "403" in error_msg: + print("\n Check your E2B account has MCP gateway access") + elif "401" in error_msg: + print("\n Check your API tokens are valid") + elif "Credit balance" in error_msg: + print("\n Check your Anthropic API credit balance") + + sys.exit(1) + + finally: + if sbx: + print("\n Cleaning up sandbox...") + await sbx.kill() + +if __name__ == "__main__": + asyncio.run(robust_workflow()) +``` + +Run the script: + +```bash +python 07_robust_workflow.py +``` + +{{< /tab >}} +{{< /tabs >}} + +Claude will run the entire workflow, and if it encounters an error, respond +with robust error messaging. + +## Next steps + +In the next section, you'll customize your workflow for your needs. diff --git a/hugo_stats.json b/hugo_stats.json index 35a8c1c8985c..4907c59ce48d 100644 --- a/hugo_stats.json +++ b/hugo_stats.json @@ -134,14 +134,17 @@ "Specific-version", "Svelte", "Testcontainers-Cloud", + "TypeScript", "Ubuntu", "Ubuntu/Debian", "Unix-pipe", "Updated-Dockerfile", "Use-Docker-Init", "Use-OpenAI", + "Using-Docker-Hardened-Images", "Using-the-CLI", "Using-the-GUI", + "Using-the-official-image", "VS-Code", "Vue", "WSL-2-backend-Arm-Early-Access",