E2B로 AI 기반 코드 품질 워크플로 만들기

E2B로 AI 기반 코드 품질 워크플로 만들기 (SonarQube + GitHub)

이 가이드는 Docker의 MCP 카탈로그를 활용한 E2B 샌드박스로 GitHub와 SonarQube 통합을 자동화해 AI 기반 코드 품질 워크플로를 만드는 방법을 알려줘요.

출처: 문서

본문

이 가이드는 Docker의 MCP 카탈로그와 함께 E2B 샌드박스를 사용해 AI 기반 코드 품질 워크플로를 만드는 방법을 보여줘요. GitHub 저장소의 코드 품질 문제를 SonarQube로 자동 분석한 다음, 수정 사항이 담긴 풀 리퀘스트를 생성하는 시스템을 만들게 돼요.

무엇을 만들게 될까요?

E2B 샌드박스를 띄우고, GitHub와 SonarQube MCP 서버를 연결하며, Claude Code를 사용해 코드 품질을 분석하고 개선안을 제안하는 Node.js 스크립트를 만들게 돼요. MCP 서버는 컨테이너화되어 E2B 샌드박스의 일부로 실행돼요.

무엇을 배우게 될까요?

이 가이드에서 다음을 배우게 돼요:

  • 여러 MCP 서버가 있는 E2B 샌드박스 만드는 방법
  • AI 워크플로를 위한 GitHub와 SonarQube MCP 서버 구성 방법
  • 샌드박스 안에서 Claude Code를 사용해 외부 도구와 상호작용하는 방법
  • 품질 게이트(quality gate)가 있는 풀 리퀘스트를 만드는 자동 코드 리뷰 워크플로 구축 방법

왜 E2B 샌드박스를 쓸까요?

이 워크플로를 E2B 샌드박스에서 실행하면 로컬 실행보다 여러 이점이 있어요:

  • 보안(Security): AI 생성 코드가 격리된 컨테이너에서 실행되어 로컬 환경과 자격 증명을 보호해요
  • 제로 설정(Zero setup): SonarQube, GitHub CLI를 설치하거나 의존성을 로컬에서 관리할 필요가 없어요
  • 확장성(Scalability): 코드 스캔 같은 리소스 집약적 작업이 로컬 리소스를 소모하지 않고 클라우드에서 실행돼요

더 알아보기 (Learn more)

Docker의 블로그 글을 읽어보세요: Docker + E2B: Building the Future of Trusted AI.

코드 품질 검사 워크플로 만들기

이 섹션에서는 완전한 코드 품질 자동화 워크플로를 단계별로 만들 거예요. GitHub와 SonarQube MCP 서버가 있는 E2B 샌드박스를 만드는 것부터 시작해, 기능을 점진적으로 추가해서 코드 품질을 분석하고 풀 리퀘스트를 만드는 프로덕션 준비가 된 워크플로까지 만들게 돼요.

각 단계를 순서대로 진행하면서 MCP 서버가 어떻게 동작하는지, Claude로 그것들과 어떻게 상호작용하는지, 그리고 강력한 자동화 워크플로를 만들기 위해 연산을 어떻게 연결하는지 배우게 돼요.

준비 사항 (Prerequisites)

시작하기 전에 다음을 갖추고 있는지 확인해요:

이 예제는 E2B 샌드박스에 미리 설치되어 오는 Claude CLI를 사용해요. 하지만 다른 AI 어시스턴트를 사용하도록 예제를 조정할 수도 있어요. 대안 연결 방법은 E2B의 MCP 문서를 참고해요.

Note

이 가이드는 E2B 샌드박스에서 자동 명령 실행을 가능하게 하려고 Claude의 --dangerously-skip-permissions 플래그를 사용해요. 이 플래그는 권한 프롬프트를 우회하는데, 샌드박스가 일회용이고 로컬 머신과 분리되어 있는 E2B 같은 격리된 컨테이너 환경에 적합해요.

하지만 Claude가 샌드박스 내에서 어떤 명령이든 실행할 수 있다는 점에 유의하세요. 그 환경에서 사용 가능한 파일과 자격 증명에 접근하는 것을 포함해서요. 신뢰할 수 있는 코드와 워크플로에서만 이 접근 방식을 사용하세요. 자세한 내용은 Anthropic의 컨테이너 보안 지침을 참고해요.

프로젝트 설정하기

TypeScript:

  1. 워크플로용 새 디렉토리를 만들고 Node.js를 초기화해요:
$ mkdir github-sonarqube-workflow && cd github-sonarqube-workflow
$ npm init
$ npm install e2b @e2b/mcp dotenv tsx
  1. package.json을 열어 ES 모듈용으로 구성해요:
{
  "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"
}
  1. 필수 의존성을 설치해요.
  2. 프로젝트 루트에 .env 파일을 만들어요.
  3. 플레이스홀더를 실제 자격 증명으로 바꿔 API 키와 구성을 추가해요.
  4. .gitignore에 .env를 추가해 자격 증명을 보호해요:
echo ".env" >> .gitignore
echo "node_modules/" >> .gitignore

Python:

  1. 워크플로용 새 디렉토리를 만들어요:
$ mkdir github-sonarqube-workflow && cd github-sonarqube-workflow
  1. 가상 환경을 만들고 활성화해요:
$ python3 -m venv venv
$ source venv/bin/activate
  1. 필수 의존성을 설치해요:
$ pip install e2b python-dotenv
  1. 프로젝트 루트에 .env 파일을 만들어요.
  2. 플레이스홀더를 실제 자격 증명으로 바꿔 API 키와 구성을 추가해요.
  3. .gitignore에 .env를 추가해 자격 증명을 보호해요:
echo ".env" >> .gitignore
echo "venv/" >> .gitignore
echo "__pycache__/" >> .gitignore

1단계: 첫 번째 샌드박스 만들기

샌드박스를 만들고 MCP 서버가 올바르게 구성됐는지 확인하는 것부터 시작해요.

프로젝트 루트에 01-test-connection.ts라는 파일을 만들어요:

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("\nConnection successful! Cleaning up...");
  await sbx.kill();
}

testConnection().catch(console.error);

이 스크립트를 실행해 구성을 확인해요:

$ npx tsx 01-test-connection.ts

프로젝트 루트에 01_test_connection.py라는 파일을 만들어요:

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())

이 스크립트를 실행해 구성을 확인해요:

$ python 01_test_connection.py

출력은 다음 예시와 비슷해야 해요:

Creating E2B sandbox with GitHub and SonarQube MCP servers...

 Sandbox created successfully!
MCP Gateway URL: https://mcp-gateway....e2b.dev

Connecting Claude CLI to MCP gateway...
✅ MCP gateway connected

 Connection successful! Cleaning up...

방금 여러 MCP 서버가 구성된 E2B 샌드박스를 만드는 방법을 배웠어요. betaCreate 메서드는 Claude CLI와 지정한 MCP 서버가 있는 클라우드 환경을 초기화해요.

2단계: 사용 가능한 MCP 도구 살펴보기

MCP 서버는 Claude가 호출할 수 있는 도구를 노출해요. GitHub MCP 서버는 저장소 관리 도구를 제공하고, SonarQube는 코드 분석 도구를 제공해요. 도구를 나열하면 어떤 연산이 가능한지 알 수 있어요.

MCP 도구를 나열해보려면:

02-list-tools.ts를 만들어요:

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);

스크립트를 실행해요:

$ npx tsx 02-list-tools.ts

02_list_tools.py를 만들어요:

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())

스크립트를 실행해요:

$ python 02_list_tools.py

콘솔에서 MCP 도구 목록을 볼 수 있어요:

Tools available:
- GitHub: get_repository, list_issues, create_issue, search_code, ...
- SonarQube: get_quality_gate, list_projects, get_issues, ...

3단계: GitHub MCP 도구 테스트하기

MCP 도구를 사용해 GitHub를 테스트해보죠. 저장소 이슈를 나열하는 것부터 간단히 시작해요.

03-test-github.ts를 만들어요:

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);

스크립트를 실행해요:

$ npx tsx 03-test-github.ts

03_test_github.py를 만들어요:

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())

스크립트를 실행해요:

$ python 03_test_github.py

Claude가 GitHub MCP 도구를 사용해 저장소의 이슈를 나열하는 것을 볼 수 있어요:

Issues in <your-repo>:
- #1 "Fix authentication bug" by @user1
- #2 "Add unit tests" by @user2

이제 프롬프트를 보내고 자연어를 통해 GitHub와 상호작용할 수 있어요. Claude는 프롬프트에 따라 어떤 도구를 호출할지 결정해요.

4단계: SonarQube MCP 도구 테스트하기

SonarQube MCP 도구를 사용해 코드 품질을 분석해보죠.

04-test-sonarqube.ts를 만들어요:

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);

스크립트를 실행해요:

$ npx tsx 04-test-sonarqube.ts

04_test_sonarqube.py를 만들어요:

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())

스크립트를 실행해요:

$ python 04_test_sonarqube.py

Note

이 스크립트는 실행하는 데 몇 분 걸릴 수 있어요.

Claude가 SonarQube 분석 결과를 출력하는 것을 볼 수 있어요:

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.

이제 SonarQube MCP 도구를 사용해 자연어를 통해 코드 품질을 분석할 수 있어요. 품질 메트릭을 검색하고, 문제를 식별하며, 어떤 코드를 수정해야 하는지 이해할 수 있어요.

5단계: 브랜치 만들고 코드 변경하기

이제 SonarQube가 발견한 품질 문제를 바탕으로 코드를 수정하도록 Claude에게 가르쳐볼 거예요.

05-fix-code-issue.ts를 만들어요:

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(`\nCheck your repository for branch: ${branchName}`);

  await sbx.kill();
}

fixCodeIssue().catch(console.error);

스크립트를 실행해요:

$ npx tsx 05-fix-code-issue.ts

05_fix_code_issue.py를 만들어요:

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())

스크립트를 실행해요:

$ python 05_fix_code_issue.py

Note

이 스크립트는 실행하는 데 몇 분 걸릴 수 있어요.

Claude가 저장소를 분석하고 코드 품질 문제를 수정할 거예요:

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 `<your-repo>` 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/<github-org>/<your-repo>/tree/quality-fix-xxxxx

The fix is ready for review and can be merged to improve the code quality of the repository!

이제 같은 워크플로에서 GitHub와 SonarQube MCP 도구를 사용해 파일을 읽고, 코드를 변경하며, 커밋할 수 있어요.

6단계: 품질 게이트가 있는 풀 리퀘스트 만들기

마지막으로 완전한 워크플로를 만들어보죠: 품질을 분석하고, 문제를 수정하며, 개선이 있을 때만 PR을 만드는 워크플로요.

06-quality-gated-pr.ts를 만들어요:

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);

스크립트를 실행해요:

$ npx tsx 06-quality-gated-pr.ts

06_quality_gated_pr.py를 만들어요:

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())

스크립트를 실행해요:

$ python 06_quality_gated_pr.py

Note

이 스크립트는 실행하는 데 몇 분 걸릴 수 있어요.

Claude가 전체 워크플로를 실행해 품질 개선을 만들고 GitHub에 PR을 열 거예요:

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/<github-org>/<your-repo>/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/<github-org>/<your-repo>/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!

이제 조건부 로직이 있는 완전한 다단계 워크플로를 만들었어요. Claude가 SonarQube로 품질을 분석하고, GitHub 도구로 수정을 하며, 개선을 검증하고, 품질이 실제로 개선될 때만 PR을 만들어요.

7단계: 오류 처리 추가하기

프로덕션 워크플로에는 오류 처리가 필요해요. 워크플로를 더 견고하게 만들어보죠.

07-robust-workflow.ts를 만들어요:

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);

스크립트를 실행해요:

$ npx tsx 07-robust-workflow.ts

07_robust_workflow.py를 만들어요:

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())

스크립트를 실행해요:

$ python 07_robust_workflow.py

Claude가 전체 워크플로를 실행하고, 오류가 발생하면 견고한 오류 메시지로 응답할 거예요.

다음 단계

다음 섹션에서는 필요에 맞게 워크플로를 커스터마이즈할 거예요.

코드 품질 검사 워크플로 커스터마이즈하기

이제 E2B 샌드박스에서 GitHub와 SonarQube로 코드 품질 워크플로를 자동화하는 기본을 이해했으니, 필요에 맞게 워크플로를 커스터마이즈할 수 있어요.

특정 품질 문제에 집중하기

프롬프트를 수정해 특정 이슈 유형을 우선시하세요:

CI/CD와 통합하기

이 워크플로를 GitHub Actions에 추가해 풀 리퀘스트마다 자동으로 실행되게 해요:

TypeScript:

name: Automated quality checks
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - 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

Python:

name: Automated quality checks
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - 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

파일 패턴으로 필터링하기

코드베이스의 특정 부분을 대상으로 하세요:

품질 임계값 설정하기

PR이 언제 생성돼야 하는지 정의하세요:

다음 단계

흔한 문제를 해결하는 방법을 배워보세요.

코드 품질 워크플로 문제 해결하기

이 페이지는 E2B 샌드박스와 MCP 서버로 코드 품질 워크플로를 만들 때 마주칠 수 있는 흔한 문제와 해결책을 다뤄요.

여기서 다루지 않는 문제를 겪고 있다면 E2B 문서를 확인해요.

MCP 도구를 사용할 수 없음

문제: Claude가 I don't have any MCP tools available이라고 보고해요.

해결책:

  1. 인증 헤더를 사용하고 있는지 확인해요:
  2. MCP 초기화를 기다리고 있는지 확인해요.
  3. 자격 증명이 envs와 mcp 구성 둘 다에 있는지 확인해요:
// 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
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",
        },
    },
)
  1. API 토큰이 유효하고 적절한 스코프를 갖고 있는지 확인해요.

GitHub 도구는 동작하는데 SonarQube는 안 됨

문제: GitHub MCP 도구는 로드되는데 SonarQube 도구가 나타나지 않아요.

해결책: SonarQube MCP 서버는 GitHub가 동시에 구성되어 있어야 해요. 하나만 테스트하더라도 샌드박스 구성에 항상 두 서버를 모두 포함하세요.

// 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",
    },
  },
});
# 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",
        },
    },
)

Claude가 사설 저장소에 접근할 수 없음

문제: "I don't have access to that repository".

해결책:

  1. GitHub 토큰에 repo 스코프가 있는지 확인해요 (public_repo만이 아니라).
  2. 먼저 공개 저장소로 테스트해요.
  3. .env에서 저장소 소유자와 이름이 올바른지 확인해요.

워크플로가 타임아웃되거나 너무 오래 실행됨

문제: 워크플로가 완료되지 않거나 Claude 크레딧이 다 떨어져요.

해결책:

  1. 복잡한 워크플로에는 timeoutMs: 0 (TypeScript) 또는 timeout_ms=0 (Python)을 사용해 무제한 시간을 허용해요:
await sbx.commands.run(
  `echo '${prompt}' | claude -p --dangerously-skip-permissions`,
  {
    timeoutMs: 0, // No timeout
    onStdout: console.log,
    onStderr: console.log,
  },
);
  1. 복잡한 워크플로를 더 작고 집중된 작업으로 나눠요.
  2. Anthropic API 크레딧 사용량을 모니터링해요.
  3. 프롬프트에 체크포인트를 추가해요: "After each step, show progress before continuing".

샌드박스 정리 오류

문제: 샌드박스가 제대로 정리되지 않아 리소스 고갈로 이어져요.

해결책: 항상 finally 블록에서 정리하는 적절한 오류 처리를 사용해요:

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();
    }
  }
}
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()

환경 변수가 로드되지 않음

문제: 환경 변수가 "undefined" 또는 "None"으로 스크립트가 실패해요.

해결책:

  1. 파일 맨 위에 dotenv가 로드되는지 확인해요:

  2. .env 파일이 스크립트와 같은 디렉토리에 있는지 확인해요.

  3. 변수 이름이 정확히 일치하는지 확인해요 (대소문자 구분).

  4. 파일 맨 위에 dotenv가 로드되는지 확인해요:

from dotenv import load_dotenv
load_dotenv()
  1. .env 파일이 스크립트와 같은 디렉토리에 있는지 확인해요.
  2. 변수 이름이 정확히 일치하는지 확인해요 (대소문자 구분).

SonarQube가 빈 결과를 반환함

문제: SonarQube 분석이 프로젝트나 이슈를 반환하지 않아요.

해결책:

  1. SonarCloud 조직 키가 올바른지 확인해요.
  2. SonarCloud에 최소한 하나의 프로젝트가 구성되어 있는지 확인해요.
  3. SonarQube 토큰이 필요한 권한을 갖고 있는지 확인해요.
  4. 프로젝트가 SonarCloud에서 최소한 한 번 분석되었는지 확인해요.

더 알아보기 (Learn more)