Skill + CLI over MCP: Replacing Sentry MCP with a multi-profile CLI wrapper

2026-02-22 03:38 (7 months ago)
Whence Trick
Play a song themed on this article

Background

I want to use Sentry's CLI tool sentry-cli with multiple organizations, but it has no native multi-profile feature like the AWS CLI's --profile.

Installing sentry-cli

brew install getsentry/tools/sentry-cli

Configuration Priority

According to the official documentation (Configuration and Authentication), settings are loaded in the following order of priority.

  1. Command-line arguments (-o, -p, --auth-token)
  2. Environment variables (SENTRY_AUTH_TOKEN, SENTRY_ORG, SENTRY_PROJECT)
  3. A .sentryclirc file, searched for upward from the current directory
  4. ~/.sentryclirc (global default)

The .sentryclirc file name is fixed. A file renamed to something else (such as .sentryclirc-staging) is not loaded (reference).

What About Sentry MCP Server?

Sentry also provides an MCP server, which lets an AI agent operate Sentry directly. But it has several problems.

  • The old version (stdio transport + Auth Token) has in effect been replaced by its successor. The sentry-mcp-stdio repository remains "for educational purposes", but the remote MCP server is now the recommended one
  • The new version (OAuth + Streamable HTTP) needs re-authentication every time the session expires. I have to go through the OAuth flow in the browser again, and having my work interrupted is stressful
  • It consumes a lot of context. Sentry event and issue data has a complex structure, and the MCP tool definitions alone use quite a lot of tokens
  • It does not support multiple accounts. Creating multiple MCP server instances works around this, but the context consumed grows in proportion to the number of instances, which is painful

In the end, a CLI wrapper plus an AI agent skill is lighter and more flexible to work with.

Solution: zsh Wrapper Function

A zsh function always takes priority over a binary of the same name on PATH, so just defining a function named sentry-cli wraps the real sentry-cli installed with brew.

Profile Storage

~/.config/sentry-cli/profiles/<name>.env

The content is a simple env file.

SENTRY_AUTH_TOKEN=sntrys_xxx...
SENTRY_ORG=my-org
SENTRY_URL=https://my-org.sentry.io/

It is better not to fix SENTRY_PROJECT in the profile and to pass it with -p when running a command, because one org usually has several projects.

The Wrapper Function

Add the following function to ~/.zshrc, an alias file, or similar.

# sentry-cli wrapper with --profile support
# Profiles: ~/.config/sentry-cli/profiles/<name>.env
function sentry-cli () {
  local _sentry_cli
  # whence -p: ignores functions and returns only the binary path
  _sentry_cli=$(whence -p sentry-cli) || { echo "sentry-cli not found in PATH" >&2; return 1; }
  local profile_dir="${HOME}/.config/sentry-cli/profiles"
  local profile=""
  local setup=0
  local args=()

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --profile=*) profile="${1#--profile=}"; shift ;;
      --profile)   profile="$2"; shift 2 ;;
      --profiles)
        if [[ -d "$profile_dir" ]]; then
          for f in "$profile_dir"/*.env(N); do
            echo "${f:t:r}"
          done
        else
          echo "No profiles directory: $profile_dir" >&2
        fi
        return 0
        ;;
      --setup) setup=1; shift ;;
      *) args+=("$1"); shift ;;
    esac
  done

  # --setup: create or edit a profile
  if [[ $setup -eq 1 ]]; then
    if [[ -z "$profile" ]]; then
      echo "Usage: sentry-cli --profile=<name> --setup" >&2
      return 1
    fi
    mkdir -p "$profile_dir"
    local pfile="${profile_dir}/${profile}.env"
    if [[ -f "$pfile" ]]; then
      echo "Editing existing profile: $profile"
    else
      echo "Creating new profile: $profile"
      cat > "$pfile" <<TMPL
SENTRY_AUTH_TOKEN=
SENTRY_ORG=${profile}
SENTRY_URL=https://${profile}.sentry.io/
TMPL
    fi
    ${EDITOR:-vim} "$pfile"
    return 0
  fi

  # No profile: passthrough
  if [[ -z "$profile" ]]; then
    "$_sentry_cli" "${args[@]}"
    return $?
  fi

  # Load profile and run
  local pfile="${profile_dir}/${profile}.env"
  if [[ ! -f "$pfile" ]]; then
    echo "Profile not found: $pfile" >&2
    echo "Run: sentry-cli --profile=${profile} --setup" >&2
    return 1
  fi

  # Source in a subshell so the current environment variables are not polluted
  (
    set -a
    source "$pfile"
    set +a
    "$_sentry_cli" "${args[@]}"
  )
}

Gotcha: The command -v Trap

My first implementation got the binary path with command -v sentry-cli, but zsh's command -v also returns the function of the same name itself, so it went into infinite recursion and failed with the error maximum nested function level reached; increase FUNCNEST?.

With whence -p, you can ignore functions and get only the binary path. This is a zsh-specific builtin command.

Usage

# Create a profile (opens an editor)
sentry-cli --profile=myorg --setup

# Check the connection
sentry-cli --profile=myorg info

# List projects
sentry-cli --profile=myorg projects list

# List releases (with a project specified)
sentry-cli --profile=myorg releases list -p my-project

# List profiles
sentry-cli --profiles

# Without a profile (works as the normal sentry-cli)
sentry-cli info

Registering as a Claude Code Skill

I also registered this wrapper as a Claude Code skill. With a SKILL.md written, the AI agent can naturally use sentry-cli --profile=xxx to operate Sentry.

The whole skill definition is below.

---
name: sentry-cli
description: Multi-profile wrapper for Sentry CLI. Release management, sourcemap uploads, event sending, etc.
allowed-tools: Bash(sentry-cli:*)
---

# Sentry CLI (multi-profile wrapper)

A zsh wrapper for using Sentry CLI with multiple profiles.

## Important: Check the help before operating

If a sentry-cli subcommand or argument is unclear, read the help first.

  sentry-cli --help
  sentry-cli <subcommand> --help

## Profile management

### Create or edit a profile

  sentry-cli --profile=<name> --setup

### List profiles

  sentry-cli --profiles

### Run with a profile

  sentry-cli --profile=<name> <subcommand> [options]

### Run without a profile (works as the normal sentry-cli)

  sentry-cli <subcommand> [options]

## Common subcommands

- info: Check connection info and auth status
- releases: List, create, and finalize releases
- sourcemaps: Upload sourcemaps
- send-event: Send a test event
- deploys: Record deploys
- issues: List and resolve issues
- projects: List projects

The points are as follows.

  • allowed-tools: Bash(sentry-cli:*) allows the agent to run the sentry-cli command
  • Including an instruction to read --help before operating prevents wrong subcommand arguments
  • Profile management commands and common subcommands are listed as a reference so the agent can use them right away
Please rate this article (No signup or login required)
Currently unrated
The author runs the application development company Cyberneura.
We look forward to discussing your development needs.

Categories

Archive