diff --git a/content/guides/genai-pdf-bot.md b/content/guides/genai-pdf-bot.md deleted file mode 100644 index 6588d06595..0000000000 --- a/content/guides/genai-pdf-bot.md +++ /dev/null @@ -1,534 +0,0 @@ ---- -title: PDF analysis and chat -description: Containerize generative AI (GenAI) apps using Docker -keywords: python, generative ai, genai, llm, neo4j, ollama, langchain -summary: | - Learn how to build a PDF bot for parsing PDF documents and generating - responses using Docker and generative AI. -aliases: - - /guides/use-case/genai-pdf-bot/ - - /guides/genai-pdf-bot/containerize/ - - /guides/genai-pdf-bot/develop/ -params: - tags: [ai] - time: 20 minutes ---- - - -The generative AI (GenAI) guide teaches you how to containerize an existing GenAI application using Docker. In this guide, you’ll learn how to: - -- Containerize and run a Python-based GenAI application -- Set up a local environment to run the complete GenAI stack locally for development - -Start by containerizing an existing GenAI application. - -## Containerize a generative AI application - -### Prerequisites - -> [!NOTE] -> -> GenAI applications can often benefit from GPU acceleration. Currently Docker Desktop supports GPU acceleration only on [Windows with the WSL2 backend](/manuals/desktop/features/gpu.md#using-nvidia-gpus-with-wsl2). Linux users can also access GPU acceleration using a native installation of the [Docker Engine](/manuals/engine/install/_index.md). - -- You have installed the latest version of [Docker Desktop](/get-started/get-docker.md) or, if you are a Linux user and are planning to use GPU acceleration, [Docker Engine](/manuals/engine/install/_index.md). Docker adds new features regularly and some parts of this guide may work only with the latest version of Docker Desktop. -- You have a [git client](https://git-scm.com/downloads). The examples in this section use a command-line based git client, but you can use any client. - -### Overview - -This section walks you through containerizing a generative AI (GenAI) application using Docker Desktop. - -> [!NOTE] -> -> You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) demo applications. - -### Get the sample application - -The sample application used in this guide is a modified version of the PDF Reader application from the [GenAI Stack](https://github.com/docker/genai-stack) demo applications. The application is a full stack Python application that lets you ask questions about a PDF file. - -The application uses [LangChain](https://www.langchain.com/) for orchestration, [Streamlit](https://streamlit.io/) for the UI, [Ollama](https://ollama.ai/) to run the LLM, and [Neo4j](https://neo4j.com/) to store vectors. - -Clone the sample application. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: - -```console -$ git clone https://github.com/craig-osterhout/docker-genai-sample -``` - -You should now have the following files in your `docker-genai-sample` directory. - -```text -├── docker-genai-sample/ -│ ├── .gitignore -│ ├── app.py -│ ├── chains.py -│ ├── env.example -│ ├── requirements.txt -│ ├── util.py -│ ├── LICENSE -│ └── README.md -``` - -### Create Docker assets - -Now that you have an application, you can create the necessary Docker assets to -containerize it. - -> [!TIP] -> -> [Gordon](/ai/gordon/), Docker's AI assistant, can generate Docker assets for your project. Ask Gordon to create a Dockerfile, Compose file, and `.dockerignore` tailored to your application. - -Create the following files in your `docker-genai-sample` directory. - -```dockerfile {collapse=true,title=Dockerfile} -# syntax=docker/dockerfile:1 - -# Comments are provided throughout this file to help you get started. -# If you need more help, visit the Dockerfile reference guide at -# https://docs.docker.com/go/dockerfile-reference/ - -ARG PYTHON_VERSION=3.11.4 -FROM python:${PYTHON_VERSION}-slim as base - -# Prevents Python from writing pyc files. -ENV PYTHONDONTWRITEBYTECODE=1 - -# Keeps Python from buffering stdout and stderr to avoid situations where -# the application crashes without emitting any logs due to buffering. -ENV PYTHONUNBUFFERED=1 - -WORKDIR /app - -# Create a non-privileged user that the app will run under. -# See https://docs.docker.com/go/dockerfile-user-best-practices/ -ARG UID=10001 -RUN adduser \ - --disabled-password \ - --gecos "" \ - --home "/nonexistent" \ - --shell "/sbin/nologin" \ - --no-create-home \ - --uid "${UID}" \ - appuser - -# Download dependencies as a separate step to take advantage of Docker's caching. -# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. -# Leverage a bind mount to requirements.txt to avoid having to copy them into -# into this layer. -RUN --mount=type=cache,target=/root/.cache/pip \ - --mount=type=bind,source=requirements.txt,target=requirements.txt \ - python -m pip install -r requirements.txt - -# Switch to the non-privileged user to run the application. -USER appuser - -# Copy the source code into the container. -COPY . . - -# Expose the port that the application listens on. -EXPOSE 8000 - -# Run the application. -CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0", "--server.port=8000"] -``` - -```yaml {collapse=true,title=compose.yaml} -# Comments are provided throughout this file to help you get started. -# If you need more help, visit the Docker Compose reference guide at -# https://docs.docker.com/go/compose-spec-reference/ - -# Here the instructions define your application as a service called "server". -# This service is built from the Dockerfile in the current directory. -# You can add other services your application may depend on here, such as a -# database or a cache. For examples, see the Awesome Compose repository: -# https://github.com/docker/awesome-compose -services: - server: - build: - context: . - ports: - - 8000:8000 - -# The commented out section below is an example of how to define a PostgreSQL -# database that your application can use. `depends_on` tells Docker Compose to -# start the database before your application. The `db-data` volume persists the -# database data between container restarts. The `db-password` secret is used -# to set the database password. You must create `db/password.txt` and add -# a password of your choosing to it before running `docker compose up`. -# depends_on: -# db: -# condition: service_healthy -# db: -# image: postgres -# restart: always -# user: postgres -# secrets: -# - db-password -# volumes: -# - db-data:/var/lib/postgresql/data -# environment: -# - POSTGRES_DB=example -# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password -# expose: -# - 5432 -# healthcheck: -# test: [ "CMD", "pg_isready" ] -# interval: 10s -# timeout: 5s -# retries: 5 -# volumes: -# db-data: -# secrets: -# db-password: -# file: db/password.txt -``` - -```text {collapse=true,title=".dockerignore"} -# Include any files or directories that you don't want to be copied to your -# container here (e.g., local build artifacts, temporary files, etc.). -# -# For more help, visit the .dockerignore file reference guide at -# https://docs.docker.com/go/build-context-dockerignore/ - -**/.DS_Store -**/__pycache__ -**/.venv -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/bin -**/charts -**/docker-compose* -**/compose.y*ml -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md -``` - -You should now have the following contents in your `docker-genai-sample` -directory. - -```text -├── docker-genai-sample/ -│ ├── .dockerignore -│ ├── .gitignore -│ ├── app.py -│ ├── chains.py -│ ├── compose.yaml -│ ├── env.example -│ ├── requirements.txt -│ ├── util.py -│ ├── Dockerfile -│ ├── LICENSE -│ └── README.md -``` - -To learn more about these files, see the following: - -- [Dockerfile](../../../reference/dockerfile.md) -- [.dockerignore](../../../reference/dockerfile.md#dockerignore-file) -- [compose.yaml](/reference/compose-file/_index.md) - -### Run the application - -Inside the `docker-genai-sample` directory, run the following command in a -terminal. - -```console -$ docker compose up --build -``` - -Docker builds and runs your application. Depending on your network connection, it may take several minutes to download all the dependencies. You'll see a message like the following in the terminal when the application is running. - -```console -server-1 | You can now view your Streamlit app in your browser. -server-1 | -server-1 | URL: http://0.0.0.0:8000 -server-1 | -``` - -Open a browser and view the application at [http://localhost:8000](http://localhost:8000). You should see a simple Streamlit application. The application may take a few minutes to download the embedding model. While the download is in progress, **Running** appears in the top-right corner. - -The application requires a Neo4j database service and an LLM service to -function. If you have access to services that you ran outside of Docker, specify -the connection information and try it out. If you don't have the services -running, continue with this guide to learn how you can run some or all of these -services with Docker. - -In the terminal, press `ctrl`+`c` to stop the application. - -### Summary - -In this section, you learned how you can containerize and run your GenAI -application using Docker. - -### Next steps - -In the next section, you'll learn how you can run your application, database, and LLM service all locally using Docker. - -## Use containers for generative AI development - -### Prerequisites - -Complete [Containerize a generative AI application](#containerize-a-generative-ai-application). - -### Overview - -In this section, you'll learn how to set up a development environment to access all the services that your generative AI (GenAI) application needs. This includes: - -- Adding a local database -- Adding a local or remote LLM service - -> [!NOTE] -> -> You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) demo applications. - -### Add a local database - -You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service. In addition, you'll specify an environment variables file to load the database connection information rather than manually entering the information every time. - -To run the database service: - -1. In the cloned repository's directory, rename `env.example` file to `.env`. - This file contains the environment variables that the containers will use. -2. In the cloned repository's directory, open the `compose.yaml` file in an IDE or text editor. -3. In the `compose.yaml` file, add the following: - - - Add instructions to run a Neo4j database - - Specify the environment file under the server service in order to pass in the environment variables for the connection - - The following is the updated `compose.yaml` file. All comments have been removed. - - ```yaml{hl_lines=["7-23"]} - services: - server: - build: - context: . - ports: - - 8000:8000 - env_file: - - .env - depends_on: - database: - condition: service_healthy - database: - image: neo4j:5.11 - ports: - - "7474:7474" - - "7687:7687" - environment: - - NEO4J_AUTH=${NEO4J_USERNAME}/${NEO4J_PASSWORD} - healthcheck: - test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"] - interval: 5s - timeout: 3s - retries: 5 - ``` - - > [!NOTE] - > - > To learn more about Neo4j, see the [Neo4j Official Docker Image](https://hub.docker.com/_/neo4j). - -4. Run the application. Inside the `docker-genai-sample` directory, - run the following command in a terminal. - - ```console - $ docker compose up --build - ``` - -5. Access the application. Open a browser and view the application at [http://localhost:8000](http://localhost:8000). You should see a simple Streamlit application. Note that asking questions to a PDF will cause the application to fail because the LLM service specified in the `.env` file isn't running yet. - -6. Stop the application. In the terminal, press `ctrl`+`c` to stop the application. - -### Add a local or remote LLM service - -The sample application supports both [Ollama](https://ollama.ai/) and [OpenAI](https://openai.com/). This guide provides instructions for the following scenarios: - -- Run Ollama in a container -- Run Ollama outside of a container -- Use OpenAI - -While all platforms can use any of the previous scenarios, the performance and -GPU support may vary. You can use the following guidelines to help you choose the appropriate option: - -- Run Ollama in a container if you're on Linux, and using a native installation of the Docker Engine, or Windows 10/11, and using Docker Desktop, you - have a CUDA-supported GPU, and your system has at least 8 GB of RAM. -- Run Ollama outside of a container if you're on an Apple silicon Mac. -- Use OpenAI if the previous two scenarios don't apply to you. - -Choose one of the following options for your LLM service. - -{{< tabs >}} -{{< tab name="Run Ollama in a container" >}} - -When running Ollama in a container, you should have a CUDA-supported GPU. While you can run Ollama in a container without a supported GPU, the performance may not be acceptable. Only Linux and Windows 11 support GPU access to containers. - -To run Ollama in a container and provide GPU access: - -1. Install the prerequisites. - - For Docker Engine on Linux, install the [NVIDIA Container Toolkit](https://github.com/NVIDIA/nvidia-container-toolkit). - - For Docker Desktop on Windows 10/11, install the latest [NVIDIA driver](https://www.nvidia.com/Download/index.aspx) and make sure you are using the [WSL2 backend](/manuals/desktop/features/wsl/_index.md#turn-on-docker-desktop-wsl-2) -2. Add the Ollama service and a volume in your `compose.yaml`. The following is - the updated `compose.yaml`: - - ```yaml {hl_lines=["24-38"]} - services: - server: - build: - context: . - ports: - - 8000:8000 - env_file: - - .env - depends_on: - database: - condition: service_healthy - database: - image: neo4j:5.11 - ports: - - "7474:7474" - - "7687:7687" - environment: - - NEO4J_AUTH=${NEO4J_USERNAME}/${NEO4J_PASSWORD} - healthcheck: - test: - [ - "CMD-SHELL", - "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1", - ] - interval: 5s - timeout: 3s - retries: 5 - ollama: - image: ollama/ollama:latest - ports: - - "11434:11434" - volumes: - - ollama_volume:/root/.ollama - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: all - capabilities: [gpu] - volumes: - ollama_volume: - ``` - - > [!NOTE] - > - > For more details about the Compose instructions, see [Turn on GPU access with Docker Compose](/manuals/compose/how-tos/gpu-support.md). - -3. Add the ollama-pull service to your `compose.yaml` file. This service uses - the `docker/genai:ollama-pull` image, based on the GenAI Stack's - [pull_model.Dockerfile](https://github.com/docker/genai-stack/blob/main/pull_model.Dockerfile). - The service will automatically pull the model for your Ollama - container. The following is the updated section of the `compose.yaml` file: - - ```yaml {hl_lines=["12-17"]} - services: - server: - build: - context: . - ports: - - 8000:8000 - env_file: - - .env - depends_on: - database: - condition: service_healthy - ollama-pull: - condition: service_completed_successfully - ollama-pull: - image: docker/genai:ollama-pull - env_file: - - .env - # ... - ``` - -{{< /tab >}} -{{< tab name="Run Ollama outside of a container" >}} - -To run Ollama outside of a container: - -1. [Install](https://github.com/jmorganca/ollama) and run Ollama on your host - machine. -2. Update the `OLLAMA_BASE_URL` value in your `.env` file to - `http://host.docker.internal:11434`. -3. Pull the model to Ollama using the following command. - ```console - $ ollama pull llama2 - ``` - -{{< /tab >}} -{{< tab name="Use OpenAI" >}} - -> [!IMPORTANT] -> -> Using OpenAI requires an [OpenAI account](https://platform.openai.com/login). OpenAI is a third-party hosted service and charges may apply. - -1. Update the `LLM` value in your `.env` file to - `gpt-3.5`. -2. Uncomment and update the `OPENAI_API_KEY` value in your `.env` file to - your [OpenAI API key](https://help.openai.com/en/articles/4936850-where-do-i-find-my-api-key). - -{{< /tab >}} -{{< /tabs >}} - -### Run your GenAI application - -At this point, you have the following services in your Compose file: - -- Server service for your main GenAI application -- Database service to store vectors in a Neo4j database -- (optional) Ollama service to run the LLM -- (optional) Ollama-pull service to automatically pull the model for the Ollama - service - -To run all the services, run the following command in your `docker-genai-sample` -directory: - -```console -$ docker compose up --build -``` - -If your Compose file has the ollama-pull service, it may take several minutes for the ollama-pull service to pull the model. The ollama-pull service will continuously update the console with its status. After pulling the model, the ollama-pull service container will stop and you can access the application. - -Once the application is running, open a browser and access the application at [http://localhost:8000](http://localhost:8000). - -Upload a PDF file, for example the [Docker CLI Cheat Sheet](https://docs.docker.com/get-started/docker_cheatsheet.pdf), and ask a question about the PDF. - -Depending on your system and the LLM service that you chose, it may take several -minutes to answer. If you are using Ollama and the performance isn't -acceptable, try using OpenAI. - -### Summary - -In this section, you learned how to set up a development environment to provide -access all the services that your GenAI application needs. - -Related information: - -- [Dockerfile reference](../../../reference/dockerfile.md) -- [Compose file reference](/reference/compose-file/_index.md) -- [Ollama Docker image](https://hub.docker.com/r/ollama/ollama) -- [Neo4j Official Docker Image](https://hub.docker.com/_/neo4j) -- [GenAI Stack demo applications](https://github.com/docker/genai-stack) - -### Next steps - -See samples of more GenAI applications in the [GenAI Stack demo applications](https://github.com/docker/genai-stack). diff --git a/content/guides/github-sonarqube-sandbox.md b/content/guides/github-sonarqube-sandbox.md deleted file mode 100644 index 8756972aaf..0000000000 --- a/content/guides/github-sonarqube-sandbox.md +++ /dev/null @@ -1,2140 +0,0 @@ ---- -title: How to build an AI-powered code quality workflow with SonarQube and E2B -linkTitle: AI-powered code quality -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. -keywords: sonarqube, e2b, sandboxes, mcp, github, code quality, ai workflow, claude -aliases: - - /guides/github-sonarqube-sandbox/customize/ - - /guides/github-sonarqube-sandbox/troubleshoot/ - - /guides/github-sonarqube-sandbox/workflow/ -params: - tags: [cicd] - time: 40 minutes - image: ---- - - -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/). - -## Build a code quality check workflow - -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. - -## Customize a code quality check workflow - -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@{{% param "checkout_action_version" %}} - - uses: actions/setup-node@v5 - with: - node-version: "24" - - 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@{{% param "checkout_action_version" %}} - - uses: actions/setup-python@v6 - with: - python-version: "3.14" - - 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. - -## Troubleshoot code quality workflows - -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. - - ```typescript - // typescript - await new Promise((resolve) => setTimeout(resolve, 1000)); - ``` - - ```python - # python - await asyncio.sleep(1) - ``` - -3. Ensure credentials are in both `envs` and `mcp` configuration: - - ```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", - }, - }, - }); - ``` - - ```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", - }, - }, - ) - ``` - -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/rag-ollama.md b/content/guides/rag-ollama.md deleted file mode 100644 index 17650e4871..0000000000 --- a/content/guides/rag-ollama.md +++ /dev/null @@ -1,282 +0,0 @@ ---- -description: Containerize RAG application using Ollama and Docker -keywords: python, generative ai, genai, llm, ollama, rag, qdrant -title: Build a RAG application using Ollama and Docker -linkTitle: RAG Ollama application -summary: | - This guide demonstrates how to use Docker to deploy Retrieval-Augmented - Generation (RAG) models with Ollama. -aliases: - - /guides/use-case/rag-ollama/ - - /guides/rag-ollama/containerize/ - - /guides/rag-ollama/develop/ -params: - tags: [ai] - time: 20 minutes ---- - - -The Retrieval Augmented Generation (RAG) guide teaches you how to containerize an existing RAG application using Docker. The example application is a RAG that acts like a sommelier, giving you the best pairings between wines and food. In this guide, you’ll learn how to: - -- Containerize and run a RAG application -- Set up a local environment to run the complete RAG stack locally for development - -Start by containerizing an existing RAG application. - -## Containerize a RAG application - -### Overview - -This section walks you through containerizing a RAG application using Docker. - -> [!NOTE] -> You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) demo applications. - -### Get the sample application - -The sample application used in this guide is an example of RAG application, made by three main components, which are the building blocks for every RAG application. A Large Language Model hosted somewhere, in this case it is hosted in a container and served via [Ollama](https://ollama.ai/). A vector database, [Qdrant](https://qdrant.tech/), to store the embeddings of local data, and a web application, using [Streamlit](https://streamlit.io/) to offer the best user experience to the user. - -Clone the sample application. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: - -```console -$ git clone https://github.com/mfranzon/winy.git -``` - -You should now have the following files in your `winy` directory. - -```text -├── winy/ -│ ├── .gitignore -│ ├── app/ -│ │ ├── main.py -│ │ ├── Dockerfile -| | └── requirements.txt -│ ├── tools/ -│ │ ├── create_db.py -│ │ ├── create_embeddings.py -│ │ ├── requirements.txt -│ │ ├── test.py -| | └── download_model.sh -│ ├── docker-compose.yaml -│ ├── wine_database.db -│ ├── LICENSE -│ └── README.md -``` - -### Containerizing your application: Essentials - -Containerizing an application involves packaging it along with its dependencies into a container, which ensures consistency across different environments. Here’s what you need to containerize an app like Winy : - -1. Dockerfile: A Dockerfile that contains instructions on how to build a Docker image for your application. It specifies the base image, dependencies, configuration files, and the command to run your application. - -2. Docker Compose File: Docker Compose is a tool for defining and running multi-container Docker applications. A Compose file allows you to configure your application's services, networks, and volumes in a single file. - -### Run the application - -Inside the `winy` directory, run the following command in a -terminal. - -```console -$ docker compose up --build -``` - -Docker builds and runs your application. Depending on your network connection, it may take several minutes to download all the dependencies. You'll see a message like the following in the terminal when the application is running. - -```console -server-1 | You can now view your Streamlit app in your browser. -server-1 | -server-1 | URL: http://0.0.0.0:8501 -server-1 | -``` - -Open a browser and view the application at [http://localhost:8501](http://localhost:8501). You should see a simple Streamlit application. - -The application requires a Qdrant database service and an LLM service to work properly. If you have access to services that you ran outside of Docker, specify the connection information in the `docker-compose.yaml`. - -```yaml -winy: - build: - context: ./app - dockerfile: Dockerfile - environment: - - QDRANT_CLIENT=http://qdrant:6333 # Specifies the url for the qdrant database - - OLLAMA=http://ollama:11434 # Specifies the url for the ollama service - container_name: winy - ports: - - "8501:8501" - depends_on: - - qdrant - - ollama -``` - -If you don't have the services running, continue with this guide to learn how you can run some or all of these services with Docker. -Remember that the `ollama` service is empty; it doesn't have any model. For this reason you need to pull a model before starting to use the RAG application. All the instructions are in the following page. - -In the terminal, press `ctrl`+`c` to stop the application. - -### Summary - -In this section, you learned how you can containerize and run your RAG -application using Docker. - -### Next steps - -In the next section, you'll learn how to properly configure the application with your preferred LLM model, completely locally, using Docker. - -## Use containers for RAG development - -### Prerequisites - -Complete [Containerize a RAG application](#containerize-a-rag-application). - -### Overview - -In this section, you'll learn how to set up a development environment to access all the services that your generative RAG application needs. This includes: - -- Adding a local database -- Adding a local or remote LLM service - -> [!NOTE] -> You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) demo applications. - -### Add a local database - -You can use containers to set up local services, like a database. In this section, you'll explore the database service in the `docker-compose.yaml` file. - -To run the database service: - -1. In the cloned repository's directory, open the `docker-compose.yaml` file in an IDE or text editor. - -2. In the `docker-compose.yaml` file, you'll see the following: - - ```yaml - services: - qdrant: - image: qdrant/qdrant - container_name: qdrant - ports: - - "6333:6333" - volumes: - - qdrant_data:/qdrant/storage - ``` - - > [!NOTE] - > To learn more about Qdrant, see the [Qdrant Official Docker Image](https://hub.docker.com/r/qdrant/qdrant). - -3. Start the application. Inside the `winy` directory, run the following command in a terminal. - - ```console - $ docker compose up --build - ``` - -4. Access the application. Open a browser and view the application at [http://localhost:8501](http://localhost:8501). You should see a simple Streamlit application. - -5. Stop the application. In the terminal, press `ctrl`+`c` to stop the application. - -### Add a local or remote LLM service - -The sample application supports both [Ollama](https://ollama.ai/). This guide provides instructions for the following scenarios: - -- Run Ollama in a container -- Run Ollama outside of a container - -While all platforms can use any of the previous scenarios, the performance and -GPU support may vary. You can use the following guidelines to help you choose the appropriate option: - -- Run Ollama in a container if you're on Linux, and using a native installation of the Docker Engine, or Windows 10/11, and using Docker Desktop, you - have a CUDA-supported GPU, and your system has at least 8 GB of RAM. -- Run Ollama outside of a container if running Docker Desktop on a Linux Machine. - -Choose one of the following options for your LLM service. - -{{< tabs >}} -{{< tab name="Run Ollama in a container" >}} - -When running Ollama in a container, you should have a CUDA-supported GPU. While you can run Ollama in a container without a supported GPU, the performance may not be acceptable. Only Linux and Windows 11 support GPU access to containers. - -To run Ollama in a container and provide GPU access: - -1. Install the prerequisites. - - For Docker Engine on Linux, install the [NVIDIA Container Toolkilt](https://github.com/NVIDIA/nvidia-container-toolkit). - - For Docker Desktop on Windows 10/11, install the latest [NVIDIA driver](https://www.nvidia.com/Download/index.aspx) and make sure you are using the [WSL2 backend](/manuals/desktop/features/wsl/_index.md#turn-on-docker-desktop-wsl-2) -2. The `docker-compose.yaml` file already contains the necessary instructions. In your own apps, you'll need to add the Ollama service in your `docker-compose.yaml`. The following is - the updated `docker-compose.yaml`: - - ```yaml - ollama: - image: ollama/ollama - container_name: ollama - ports: - - "8000:8000" - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - ``` - - > [!NOTE] - > For more details about the Compose instructions, see [Turn on GPU access with Docker Compose](/manuals/compose/how-tos/gpu-support.md). - -3. Once the Ollama container is up and running it is possible to use the `download_model.sh` inside the `tools` folder with this command: - - ```console - . ./download_model.sh - ``` - -Pulling an Ollama model can take several minutes. - -{{< /tab >}} -{{< tab name="Run Ollama outside of a container" >}} - -To run Ollama outside of a container: - -1. [Install](https://github.com/jmorganca/ollama) and run Ollama on your host - machine. -2. Pull the model to Ollama using the following command. - - ```console - $ ollama pull llama2 - ``` - -3. Remove the `ollama` service from the `docker-compose.yaml` and update properly the connection variables in `winy` service: - - ```diff - - OLLAMA=http://ollama:11434 - + OLLAMA= - ``` - -{{< /tab >}} -{{< /tabs >}} - -### Run your RAG application - -At this point, you have the following services in your Compose file: - -- Server service for your main RAG application -- Database service to store vectors in a Qdrant database -- (optional) Ollama service to run the LLM - service - -Once the application is running, open a browser and access the application at [http://localhost:8501](http://localhost:8501). - -Depending on your system and the LLM service that you chose, it may take several -minutes to answer. - -### Summary - -In this section, you learned how to set up a development environment to provide -access all the services that your GenAI application needs. - -Related information: - -- [Dockerfile reference](/reference/dockerfile.md) -- [Compose file reference](/reference/compose-file/_index.md) -- [Ollama Docker image](https://hub.docker.com/r/ollama/ollama) -- [GenAI Stack demo applications](https://github.com/docker/genai-stack) - -### Next steps - -See samples of more GenAI applications in the [GenAI Stack demo applications](https://github.com/docker/genai-stack). diff --git a/content/manuals/desktop/features/mcp-catalog-and-toolkit/_index.md b/content/manuals/desktop/features/mcp-catalog-and-toolkit/_index.md index 892d274a97..56381adfb8 100644 --- a/content/manuals/desktop/features/mcp-catalog-and-toolkit/_index.md +++ b/content/manuals/desktop/features/mcp-catalog-and-toolkit/_index.md @@ -39,10 +39,6 @@ grid: description: Common questions about MCP security, credentials, and server verification icon: shield-check link: /ai/mcp-catalog-and-toolkit/faqs/ - - title: E2B sandboxes - description: Cloud sandboxes for AI agents with built-in MCP Catalog access - icon: cloud - link: /ai/mcp-catalog-and-toolkit/e2b-sandboxes/ --- {{< summary-bar feature_name="Docker MCP Catalog and Toolkit" >}} diff --git a/content/manuals/desktop/features/mcp-catalog-and-toolkit/catalog.md b/content/manuals/desktop/features/mcp-catalog-and-toolkit/catalog.md index 36263e3051..0163c9a7fa 100644 --- a/content/manuals/desktop/features/mcp-catalog-and-toolkit/catalog.md +++ b/content/manuals/desktop/features/mcp-catalog-and-toolkit/catalog.md @@ -19,11 +19,6 @@ to your [profiles](/manuals/desktop/features/mcp-catalog-and-toolkit/profiles.md them from the catalog. Each server runs as an isolated container, making it portable and consistent across different environments. -> [!NOTE] -> E2B sandboxes now include direct access to the Docker MCP Catalog, giving -> developers access to over 200 tools and services to seamlessly build and run -> AI agents. For more information, see [E2B Sandboxes](e2b-sandboxes.md). - ## What's in the catalog The Docker MCP Catalog includes: diff --git a/content/manuals/desktop/features/mcp-catalog-and-toolkit/e2b-sandboxes.md b/content/manuals/desktop/features/mcp-catalog-and-toolkit/e2b-sandboxes.md deleted file mode 100644 index df0f9d90c9..0000000000 --- a/content/manuals/desktop/features/mcp-catalog-and-toolkit/e2b-sandboxes.md +++ /dev/null @@ -1,453 +0,0 @@ ---- -url: /ai/mcp-catalog-and-toolkit/e2b-sandboxes/ -title: E2B sandboxes -description: Cloud-based secure sandboxes for AI agents with built-in Docker MCP Gateway integration -keywords: E2B, cloud sandboxes, MCP Gateway, AI agents, MCP Catalog -aliases: - - /ai/mcp-catalog-and-toolkit/sandboxes/ ---- - -[E2B](https://e2b.dev/) provides secure cloud sandboxes for AI agents with direct access to Docker's [MCP Catalog](https://hub.docker.com/mcp), a collection of 200+ tools from publishers including GitHub, Notion, and Stripe. - -When you create an E2B sandbox, you specify which MCP tools it should access. E2B launches these tools and provides access through the Docker MCP Gateway. - -## Example: Using GitHub and Notion MCP server - -This example demonstrates how to connect multiple MCP servers in an E2B sandbox. You'll analyze data in Notion and create GitHub issues using Claude. - -### Prerequisites - -Before you begin, make sure you have the following: - -- [E2B account](https://e2b.dev/docs/quickstart) with API access -- Anthropic API key for Claude - - > [!NOTE] - > This example uses Claude Code, which is pre-installed in E2B sandboxes. - > However, 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. - -- Node.js 18+ installed on your machine -- Notion account with: - - A database containing sample data - - [Integration token](https://www.notion.com/help/add-and-manage-connections-with-the-api) -- GitHub account with: - - A repository for testing - - Personal access token with `repo` scope - -### Set up your environment - -Create a new directory and initialize a Node.js project: - -```console -$ mkdir mcp-e2b-quickstart -$ cd mcp-e2b-quickstart -$ npm init -y -``` - -Configure your project for ES modules by updating `package.json`: - -```json -{ - "name": "mcp-e2b-quickstart", - "version": "1.0.0", - "type": "module", - "scripts": { - "start": "node index.js" - } -} -``` - -Install required dependencies: - -```console -$ npm install e2b dotenv -``` - -Create a `.env` file with your credentials: - -```console -$ cat > .env << 'EOF' -E2B_API_KEY=your_e2b_api_key_here -ANTHROPIC_API_KEY=your_anthropic_api_key_here -NOTION_INTEGRATION_TOKEN=ntn_your_notion_integration_token_here -GITHUB_TOKEN=ghp_your_github_pat_here -EOF -``` - -Protect your credentials: - -```console -$ echo ".env" >> .gitignore -$ echo "node_modules/" >> .gitignore -``` - -### Create an E2B sandbox with MCP servers - -{{< tabs group="" >}} -{{< tab name="Typescript">}} - -Create a file named `index.ts`: - -```typescript -import "dotenv/config"; -import { Sandbox } from "e2b"; - -async function quickstart(): Promise { - console.log("Creating E2B sandbox with Notion and GitHub MCP servers...\n"); - - const sbx: Sandbox = await Sandbox.create({ - envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY as string, - }, - mcp: { - notion: { - internalIntegrationToken: process.env - .NOTION_INTEGRATION_TOKEN as string, - }, - githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN as string, - }, - }, - }); - - const mcpUrl = sbx.getMcpUrl(); - const mcpToken = await sbx.getMcpToken(); - - console.log("Sandbox created successfully!"); - console.log(`MCP Gateway URL: ${mcpUrl}\n`); - - // Wait for MCP initialization - await new Promise((resolve) => setTimeout(resolve, 1000)); - - // Connect Claude to MCP gateway - console.log("Connecting Claude 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("\nConnection successful! Cleaning up..."); - await sbx.kill(); -} - -quickstart().catch(console.error); -``` - -Run the script: - -```console -$ npx tsx index.ts -``` - -{{< /tab >}} -{{< tab name="Python">}} - -Create a file named `index.py`: - -```python -import os -import asyncio -from dotenv import load_dotenv -from e2b import Sandbox - -load_dotenv() - -async def quickstart(): - print("Creating E2B sandbox with Notion and GitHub MCP servers...\n") - - sbx = await Sandbox.beta_create( - envs={ - "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), - }, - mcp={ - "notion": { - "internalIntegrationToken": os.getenv("NOTION_INTEGRATION_TOKEN"), - }, - "githubOfficial": { - "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), - }, - }, - ) - - 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) - - # Connect Claude to MCP gateway - print("Connecting Claude to MCP gateway...") - - def on_stdout(output): - print(output, end='') - - def on_stderr(output): - print(output, end='') - - await sbx.commands.run( - f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', - timeout_ms=0, - on_stdout=on_stdout, - on_stderr=on_stderr - ) - - print("\nConnection successful! Cleaning up...") - await sbx.kill() - -if __name__ == "__main__": - try: - asyncio.run(quickstart()) - except Exception as e: - print(f"Error: {e}") - -``` - -Run the script: - -```console -$ python index.py -``` - -{{< /tab >}} -{{}} - -You should see: - -```console -Creating E2B sandbox with Notion and GitHub MCP servers... - -Sandbox created successfully! -MCP Gateway URL: https://50005-xxxxx.e2b.app/mcp - -Connecting Claude to MCP gateway... -Added HTTP MCP server e2b-mcp-gateway with URL: https://50005-xxxxx.e2b.app/mcp - -Connection successful! Cleaning up... -``` - -### Test with example workflow - -Now, test the setup by running a simple workflow that searches Notion and creates a GitHub issue. - -{{< tabs group="" >}} -{{< tab name="Typescript">}} - -> [!IMPORTANT] -> -> Replace `owner/repo` in the prompt with your actual GitHub username and repository -> name (for example, `yourname/test-repo`). - -Update `index.ts` with the following example: - -```typescript -import "dotenv/config"; -import { Sandbox } from "e2b"; - -async function exampleWorkflow(): Promise { - console.log("Creating sandbox...\n"); - - const sbx: Sandbox = await Sandbox.create({ - envs: { - ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY as string, - }, - mcp: { - notion: { - internalIntegrationToken: process.env - .NOTION_INTEGRATION_TOKEN as string, - }, - githubOfficial: { - githubPersonalAccessToken: process.env.GITHUB_TOKEN as string, - }, - }, - }); - - const mcpUrl = sbx.getMcpUrl(); - const mcpToken = await sbx.getMcpToken(); - - console.log("Sandbox created successfully\n"); - - // Wait for MCP servers to initialize - await new Promise((resolve) => setTimeout(resolve, 3000)); - - console.log("Connecting Claude to MCP gateway...\n"); - 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("\nRunning example: Search Notion and create GitHub issue...\n"); - - const prompt: string = `Using Notion and GitHub MCP tools: -1. Search my Notion workspace for databases -2. Create a test issue in owner/repo titled "MCP Toolkit Test" with description "Testing E2B + Docker MCP integration" -3. Confirm both operations completed successfully`; - - await sbx.commands.run( - `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, - { - timeoutMs: 0, - onStdout: console.log, - onStderr: console.log, - }, - ); - - await sbx.kill(); -} - -exampleWorkflow().catch(console.error); -``` - -Run the script: - -```console -$ npx tsx index.ts -``` - -{{< /tab >}} -{{< tab name="Python">}} - -Update `index.py` with this example: - -> [!IMPORTANT] -> -> Replace `owner/repo` in the prompt with your actual GitHub username and repository -> name (for example, `yourname/test-repo`). - -```python -import os -import asyncio -import shlex -from dotenv import load_dotenv -from e2b import Sandbox - -load_dotenv() - -async def example_workflow(): - print("Creating sandbox...\n") - - sbx = await Sandbox.beta_create( - envs={ - "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), - }, - mcp={ - "notion": { - "internalIntegrationToken": os.getenv("NOTION_INTEGRATION_TOKEN"), - }, - "githubOfficial": { - "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), - }, - }, - ) - - mcp_url = sbx.beta_get_mcp_url() - mcp_token = await sbx.beta_get_mcp_token() - - print("Sandbox created successfully\n") - - # Wait for MCP servers to initialize - await asyncio.sleep(3) - - print("Connecting Claude to MCP gateway...\n") - - def on_stdout(output): - print(output, end='') - - def on_stderr(output): - print(output, end='') - - await sbx.commands.run( - f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', - timeout_ms=0, - on_stdout=on_stdout, - on_stderr=on_stderr - ) - - print("\nRunning example: Search Notion and create GitHub issue...\n") - - prompt = """Using Notion and GitHub MCP tools: -1. Search my Notion workspace for databases -2. Create a test issue in owner/repo titled "MCP Toolkit Test" with description "Testing E2B + Docker MCP integration" -3. Confirm both operations completed successfully""" - - # Escape single quotes for shell - escaped_prompt = prompt.replace("'", "'\\''") - - await sbx.commands.run( - f"echo '{escaped_prompt}' | claude -p --dangerously-skip-permissions", - timeout_ms=0, - on_stdout=on_stdout, - on_stderr=on_stderr - ) - - await sbx.kill() - -if __name__ == "__main__": - try: - asyncio.run(example_workflow()) - except Exception as e: - print(f"Error: {e}") -``` - -Run the script: - -```console -$ python workflow.py -``` - -{{< /tab >}} -{{}} - -You should see: - -```console -Creating sandbox... - -Running example: Search Notion and create GitHub issue... - -## Task Completed Successfully - -I've completed both operations using the Notion and GitHub MCP tools: - -### 1. Notion Workspace Search - -Found 3 databases in your Notion workspace: -- **Customer Feedback** - Database with 12 entries tracking feature requests -- **Product Roadmap** - Planning database with 8 active projects -- **Meeting Notes** - Shared workspace with 45 pages - -### 2. GitHub Issue Creation - -Successfully created test issue: -- **Repository**: your-org/your-repo -- **Issue Number**: #47 -- **Title**: "MCP Test" -- **Description**: "Testing E2B + Docker MCP integration" -- **Status**: Open -- **URL**: https://github.com/your-org/your-repo/issues/47 - -Both operations completed successfully. The MCP servers are properly configured and working. -``` - -The sandbox connected multiple MCP servers and orchestrated a workflow across Notion and GitHub. You can extend this pattern to combine any of the 200+ MCP servers in the Docker MCP Catalog. - -## Related pages - -- [How to build an AI-powered code quality workflow with SonarQube and E2B](/guides/github-sonarqube-sandbox.md) -- [Docker + E2B: Building the Future of Trusted AI](https://www.docker.com/blog/docker-e2b-building-the-future-of-trusted-ai/) -- [Docker Sandboxes](/manuals/ai/sandboxes/_index.md) -- [Docker MCP Toolkit and Catalog](/manuals/desktop/features/mcp-catalog-and-toolkit/_index.md) -- [Docker MCP Gateway](/manuals/desktop/features/mcp-catalog-and-toolkit/mcp-gateway.md) -- [E2B MCP documentation](https://e2b.dev/docs/mcp) diff --git a/content/manuals/desktop/features/mcp-catalog-and-toolkit/mcp-gateway.md b/content/manuals/desktop/features/mcp-catalog-and-toolkit/mcp-gateway.md index 78540a05a9..fac08e4535 100644 --- a/content/manuals/desktop/features/mcp-catalog-and-toolkit/mcp-gateway.md +++ b/content/manuals/desktop/features/mcp-catalog-and-toolkit/mcp-gateway.md @@ -26,11 +26,6 @@ If you use Docker Desktop with MCP Toolkit enabled, the Gateway runs automatically in the background. You don't need to start or configure it manually. This documentation is for users who want to understand how the Gateway works or run it directly for advanced use cases. -> [!TIP] -> E2B sandboxes now include direct access to the Docker MCP Catalog, giving developers -> access to over 200 tools and services to seamlessly build and run AI agents. For -> more information, see [E2B Sandboxes](e2b-sandboxes.md). - ## How it works MCP Gateway runs MCP servers in isolated Docker containers with restricted diff --git a/data/redirects.yml b/data/redirects.yml index 7361209035..71443d6a87 100644 --- a/data/redirects.yml +++ b/data/redirects.yml @@ -404,6 +404,21 @@ # Desktop DMR "/ai/model-runner/": - /go/model-runner/ + - /guides/genai-pdf-bot/ + - /guides/use-case/genai-pdf-bot/ + - /guides/genai-pdf-bot/containerize/ + - /guides/genai-pdf-bot/develop/ + - /guides/rag-ollama/ + - /guides/use-case/rag-ollama/ + - /guides/rag-ollama/containerize/ + - /guides/rag-ollama/develop/ +"/ai/sandboxes/": + - /guides/github-sonarqube-sandbox/ + - /guides/github-sonarqube-sandbox/customize/ + - /guides/github-sonarqube-sandbox/troubleshoot/ + - /guides/github-sonarqube-sandbox/workflow/ + - /ai/mcp-catalog-and-toolkit/e2b-sandboxes/ + - /ai/mcp-catalog-and-toolkit/sandboxes/ # Docker Desktop - volumes cloud backup "/desktop/use-desktop/volumes/#export-a-volume":