Skip to main content

mysql-mcp-plus

mysql-mcp-plus 是一个本地运行、仅使用 STDIO 传输的 MySQL MCP 服务。单个 MCP 进程可以声明多个数据源,每次工具调用都必须显式指定 datasource,适合把开发、测试、 生产只读库等连接放在同一个 MCP 客户端配置中。

当前版本为 0.1.0,正式支持 MySQL 5.7 和 8.x。MariaDB、Percona 仅保证基础连接与 SQL 尽力兼容。

项目只提供通用的数据源发现、连通性检查和 SQL 执行能力,不提供 HTTP/SSE 服务、OAuth、 SSH 隧道、连接池、自动重试、完整 mysql CLI,也不提供专用的表结构、索引或健康检查工具。

运行要求

  • Python 3.11 或更高版本
  • uv
  • MCP 客户端能够启动本地 STDIO 服务
  • 至少一个可访问的 MySQL 账号和数据库

Windows 可通过 WinGet 安装 uv:

winget install --id astral-sh.uv --exact
uv --version

macOS/Linux 可使用 uv 官方安装脚本:

curl -LsSf https://astral.sh/uv/install.sh | sh
uv --version

安装后如果当前终端还找不到 uv,请按安装程序提示把 uv 目录加入 PATH,或重新打开终端。

从源码安装依赖:

git clone <仓库地址>
cd mysql_mcp_plus
uv sync --all-groups

验证入口命令可用:

uv run mysql-mcp-plus
uv run mysql-mcp-multi
uv run python -m mysql_mcp_multi

mysql-mcp-plus 是主命令;mysql-mcp-multi 是兼容别名。这三个入口都先验证环境配置, 再启动 STDIO MCP。配置错误写入 stderr 并以退出码 2 结束;启动阶段不会连接数据库。

配置

服务只读取进程环境变量,不会自动加载 .envMYSQL_SOURCES 声明数据源名称,每个名称 再映射到一组 MYSQL_<数据源大写>_* 变量。

全局变量

环境变量 必填 默认值 规则
MYSQL_SOURCES 逗号分隔且至少一个;名称匹配 ^[a-z][a-z0-9_]*$,不可重复或留空
MYSQL_MAX_ROWS_PER_RESULT 1000 正整数;单个结果集最多返回的行数
MYSQL_MAX_TOTAL_ROWS 5000 正整数,且不得小于单结果集上限;单次调用所有结果集的返回总量
MYSQL_MAX_SQL_BYTES 5242880 正整数;按 UTF-8 字节数限制完整 SQL 脚本
MYSQL_MAX_STATEMENTS 5000 正整数;单次脚本中的语句数上限
MYSQL_MCP_LOG_LEVEL INFO DEBUGINFOWARNINGERRORCRITICAL,不区分大小写

每个数据源的变量

以下表格以名为 dev 的数据源为例,实际前缀为 MYSQL_DEV_

后缀 完整示例 必填 默认值与规则
HOST MYSQL_DEV_HOST localhost;空值也回退到默认值
PORT MYSQL_DEV_PORT 3306;整数,范围 1..65535
USER MYSQL_DEV_USER 去除首尾空白后不可为空
PASSWORD MYSQL_DEV_PASSWORD 空字符串;保留原始值,不做 .env 解析
DATABASE MYSQL_DEV_DATABASE 去除首尾空白后不可为空,作为连接默认库
ROLE MYSQL_DEV_ROLE readonly;可选 readonlywriteradmin
CHARSET MYSQL_DEV_CHARSET utf8mb4;不可为空
CONNECT_TIMEOUT MYSQL_DEV_CONNECT_TIMEOUT 10 秒;正整数
READ_TIMEOUT MYSQL_DEV_READ_TIMEOUT 300 秒;正整数
WRITE_TIMEOUT MYSQL_DEV_WRITE_TIMEOUT 300 秒;正整数
SSL_CA MYSQL_DEV_SSL_CA CA 证书文件路径
SSL_CERT MYSQL_DEV_SSL_CERT 客户端证书路径;必须与 SSL_KEY 同时配置
SSL_KEY MYSQL_DEV_SSL_KEY 客户端私钥路径;必须与 SSL_CERT 同时配置
SSL_VERIFY_CERT MYSQL_DEV_SSL_VERIFY_CERT false;接受 true/false1/0yes/noon/off

配置任一 TLS 变量都会启用该数据源的 TLS 参数。启用证书校验时应提供可信的 SSL_CA;双向 TLS 需要同时提供 SSL_CERTSSL_KEY。文件是否存在以及服务端是否接受 证书由连接时的 PyMySQL/MySQL 校验,静态配置阶段只检查证书与私钥必须成对出现。

多数据源示例

PowerShell:

$env:MYSQL_SOURCES = "dev,staging,prod"
$env:MYSQL_DEV_HOST = "127.0.0.1"
$env:MYSQL_DEV_USER = "app_writer"
$env:MYSQL_DEV_PASSWORD = "replace-me"
$env:MYSQL_DEV_DATABASE = "app_dev"
$env:MYSQL_DEV_ROLE = "writer"

$env:MYSQL_STAGING_HOST = "staging-db.example.com"
$env:MYSQL_STAGING_USER = "app_admin"
$env:MYSQL_STAGING_PASSWORD = "replace-me"
$env:MYSQL_STAGING_DATABASE = "app_staging"
$env:MYSQL_STAGING_ROLE = "admin"

$env:MYSQL_PROD_HOST = "prod-db.example.com"
$env:MYSQL_PROD_USER = "app_readonly"
$env:MYSQL_PROD_PASSWORD = "replace-me"
$env:MYSQL_PROD_DATABASE = "app_prod"
$env:MYSQL_PROD_ROLE = "readonly"
$env:MYSQL_PROD_SSL_CA = "C:\certs\company-ca.pem"
$env:MYSQL_PROD_SSL_VERIFY_CERT = "true"

uv run mysql-mcp-plus

macOS/Linux shell:

export MYSQL_SOURCES='dev,prod'
export MYSQL_DEV_HOST='127.0.0.1'
export MYSQL_DEV_USER='app_writer'
export MYSQL_DEV_PASSWORD='replace-me'
export MYSQL_DEV_DATABASE='app_dev'
export MYSQL_DEV_ROLE='writer'

export MYSQL_PROD_HOST='prod-db.example.com'
export MYSQL_PROD_USER='app_readonly'
export MYSQL_PROD_PASSWORD='replace-me'
export MYSQL_PROD_DATABASE='app_prod'
export MYSQL_PROD_ROLE='readonly'
export MYSQL_PROD_SSL_CA='/etc/company/mysql-ca.pem'
export MYSQL_PROD_SSL_VERIFY_CERT='true'

uv run mysql-mcp-plus

DATABASE 只是连接默认库,不是 MCP 级访问边界。admin 可以执行 USE 或使用跨库限定名; 最终能访问哪些对象始终由 MySQL 账号权限决定。

MCP 客户端配置

下面使用常见的 mcpServers JSON 形状。客户端字段名若不同,只需把同一 commandargsenv 映射到对应设置。所有路径和凭据都是占位值,需要替换。

Windows

{
  "mcpServers": {
    "mysql-plus": {
      "command": "uv",
      "args": [
        "--directory",
        "C:\\Users\\you\\src\\mysql_mcp_plus",
        "run",
        "mysql-mcp-plus"
      ],
      "env": {
        "MYSQL_SOURCES": "dev,prod",
        "MYSQL_DEV_HOST": "127.0.0.1",
        "MYSQL_DEV_USER": "app_writer",
        "MYSQL_DEV_PASSWORD": "replace-me",
        "MYSQL_DEV_DATABASE": "app_dev",
        "MYSQL_DEV_ROLE": "writer",
        "MYSQL_PROD_HOST": "prod-db.example.com",
        "MYSQL_PROD_USER": "app_readonly",
        "MYSQL_PROD_PASSWORD": "replace-me",
        "MYSQL_PROD_DATABASE": "app_prod",
        "MYSQL_PROD_ROLE": "readonly"
      }
    }
  }
}

macOS

{
  "mcpServers": {
    "mysql-plus": {
      "command": "/opt/homebrew/bin/uv",
      "args": [
        "--directory",
        "/Users/you/src/mysql_mcp_plus",
        "run",
        "mysql-mcp-plus"
      ],
      "env": {
        "MYSQL_SOURCES": "local",
        "MYSQL_LOCAL_HOST": "127.0.0.1",
        "MYSQL_LOCAL_USER": "app_readonly",
        "MYSQL_LOCAL_PASSWORD": "replace-me",
        "MYSQL_LOCAL_DATABASE": "app_dev",
        "MYSQL_LOCAL_ROLE": "readonly"
      }
    }
  }
}

Intel Mac 上的 uv 常见路径是 /usr/local/bin/uv,可用 which uv 确认。

Linux

{
  "mcpServers": {
    "mysql-plus": {
      "command": "/home/you/.local/bin/uv",
      "args": [
        "--directory",
        "/home/you/src/mysql_mcp_plus",
        "run",
        "mysql-mcp-plus"
      ],
      "env": {
        "MYSQL_SOURCES": "prod",
        "MYSQL_PROD_HOST": "prod-db.example.com",
        "MYSQL_PROD_USER": "app_readonly",
        "MYSQL_PROD_PASSWORD": "replace-me",
        "MYSQL_PROD_DATABASE": "app_prod",
        "MYSQL_PROD_ROLE": "readonly",
        "MYSQL_PROD_SSL_CA": "/etc/company/mysql-ca.pem",
        "MYSQL_PROD_SSL_VERIFY_CERT": "true"
      }
    }
  }
}

发布后的 uvx 形式

当前仓库仅完成发布准备;在 mysql-mcp-plus 首次成功上传 PyPI 前,下面的配置不可用。 发布成功后,客户端可省去源码路径:

{
  "mcpServers": {
    "mysql-plus": {
      "command": "uvx",
      "args": ["mysql-mcp-plus"],
      "env": {
        "MYSQL_SOURCES": "dev",
        "MYSQL_DEV_USER": "app_readonly",
        "MYSQL_DEV_PASSWORD": "replace-me",
        "MYSQL_DEV_DATABASE": "app_dev"
      }
    }
  }
}

发布维护步骤和凭据安全要求见 docs/releasing.md

MCP 工具

服务只暴露三个工具,均返回结构化对象;没有默认数据源或全局“当前数据源”。

list_datasources() -> dict[str, Any]

列出静态配置,不建立数据库连接。返回名称、主机、端口、默认库、用户、角色和是否配置 TLS,不返回密码、连接串、证书路径或私钥内容。

{
  "success": true,
  "datasources": [
    {
      "name": "prod",
      "host": "prod-db.example.com",
      "port": 3306,
      "database": "app_prod",
      "user": "app_readonly",
      "role": "readonly",
      "ssl": true
    }
  ]
}

test_connection(datasource: str) -> dict[str, Any]

为指定数据源建立一次独立连接,执行探测 SQL,读取版本、默认库、实际 MySQL 用户和 TLS cipher 状态,然后关闭连接。不会自动重试。

{
  "success": true,
  "datasource": "prod",
  "latency_ms": 18,
  "server_version": "8.0.43",
  "default_database": "app_prod",
  "current_user": "app_readonly@%",
  "ssl": true
}

execute_sql(datasource: str, sql: str, max_rows: int | None = None)

执行完整 SQL 脚本。脚本可包含多条语句;max_rows 只能调低全局单结果集返回上限,布尔值、 0 和负数无效。执行前会依次完成 UTF-8 字节限制、SQL 拆分、语句数限制、客户端命令检查、 分类和整批权限校验,任何一条不合法都不会建立连接或执行前序语句。

{
  "datasource": "dev",
  "sql": "SELECT id, name FROM users ORDER BY id LIMIT 10",
  "max_rows": 10
}

成功响应按语句返回结果。CALL 等产生的多个结果集全部放在 result_sets;达到返回行数 上限后仍会消费服务器上的剩余行和后续结果集。

{
  "success": true,
  "datasource": "dev",
  "transaction": false,
  "committed": false,
  "results": [
    {
      "index": 1,
      "statement_type": "SELECT",
      "success": true,
      "result_sets": [
        {
          "columns": ["id", "name"],
          "rows": [[1, "Alice"]],
          "row_count": 1,
          "truncated": false
        }
      ],
      "affected_rows": 0,
      "last_insert_id": null,
      "warnings": 0
    }
  ],
  "elapsed_ms": 4
}

常用 SQL 可直接通过 execute_sql 完成,无需专用工具:

SHOW TABLES;
DESCRIBE users;
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';
SELECT * FROM users ORDER BY id DESC LIMIT 20;

角色权限

MCP 角色是执行前的语句类别保护层,不替代 MySQL GRANT 权限。

角色 允许的语句
readonly 安全的 SELECT/只读 CTE、SHOW、DESC/DESCRIBE、EXPLAIN
writer readonly 的能力,加 INSERT、UPDATE、DELETE、REPLACE 和写入 CTE
admin 除显式事务控制与不支持的客户端命令外,不限制服务端 SQL 类型;允许 USE 和跨库限定名

readonly 明确拒绝 SELECT ... FOR UPDATELOCK IN SHARE MODEINTO OUTFILEINTO DUMPFILEreadonly/writer 遇到无法保守分类的语句会拒绝。所有角色都拒绝脚本中的 BEGINSTART TRANSACTIONCOMMITROLLBACKSAVEPOINTRELEASE SAVEPOINTSET AUTOCOMMIT,也不支持 DELIMITERSOURCE\. 等 mysql 客户端命令。

最小权限仍应在 MySQL 层实现。例如给 readonly 数据源配置仅有 SELECT 权限的 MySQL 账号,给 writer 账号只授予目标库所需 DML 权限,不要因为 MCP 角色存在而复用 root 账号。

事务、限制与序列化

  • 纯只读批次使用 autocommit 连接,不显式 BEGIN,响应为 transaction: falsecommitted: false
  • 包含写操作、CALL、DDL 或不确定 admin 语句的批次由 MCP 开启事务;全部成功后统一提交。
  • 第一条执行错误会停止后续语句并尽可能回滚;不会自动重试。
  • MySQL 的 CREATE、ALTER、DROP、TRUNCATE、RENAME、GRANT、REVOKE、LOCK、UNLOCK 等 可能隐式提交。成功响应包含 implicit_commit_warning: true;失败响应包含 partial_commit_possible: true,因此这类批次无法保证完全原子。
  • 连接中断时不重试写入;若无法确认提交状态,失败响应包含 commit_state: "unknown"
  • 单结果集和单次调用总返回行数分别受全局限制控制。truncated: true 只表示响应省略了行, 不表示服务器结果集未消费。

MySQL 值按以下规则转换为 JSON 安全值:

MySQL/Python 值 JSON 表示
NULL null
DECIMAL 保留精度的字符串
DATE、DATETIME、TIME ISO 格式字符串
timedelta MySQL TIME 风格字符串
bytes/BLOB Base64 字符串
MySQL JSON 对象或数组;解析失败时保留原字符串

错误响应

预期的配置、参数、SQL、权限和 MySQL 错误返回结构化信息;未预料的程序缺陷才由 MCP 报告 Tool Error。

error_type 含义 SQL 是否可能已执行
unknown_datasource 请求的数据源不存在
invalid_argument max_rows 等参数无效
invalid_sql SQL 为空或无法解析
sql_limit_exceeded SQL 字节数或语句数超限
unsupported_client_command 使用了 DELIMITERSOURCE\. 等客户端命令
permission_denied 角色不允许某条语句或脚本包含事务控制
authentication_failed MySQL 1045,账号认证失败
unknown_database MySQL 1049,默认库不存在
connection_failed 无法建立连接
connection_lost 执行期间连接中断 可能,提交状态可能未知
lock_wait_timeout MySQL 1205 可能,服务会尝试回滚
deadlock MySQL 1213 可能,服务会尝试回滚
sql_execution_failed 其他 MySQL 执行错误 可能,服务会尝试回滚

预执行失败示例:

{
  "success": false,
  "datasource": "prod",
  "error_type": "permission_denied",
  "message": "readonly 数据源不允许执行 UPDATE 语句",
  "failed_index": 2,
  "executed": false
}

执行期失败还会包含 transactioncommittedrolled_backpartial_commit_possiblefailed_indexresults、MySQL error.code/error.messageretryableretryable 只描述错误类别,不代表服务会自动重试。

日志与安全边界

  • stdout 专用于 MCP STDIO 协议;普通日志和配置错误只写 stderr。
  • 默认 INFO 只记录工具名、数据源、语句数、事务状态、耗时和结果状态。
  • DEBUG 只记录去除注释、替换字符串/数字字面量后最多 200 字符的 SQL 摘要。
  • 不记录密码、私钥、完整连接串、完整 SQL、查询结果、业务数据或证书内容。
  • list_datasources 会显示主机、库名和账号名;如果这些元数据也敏感,应限制 MCP 客户端 配置与进程日志的读取权限。
  • MCP role 不是数据库沙箱。应使用独立 MySQL 账号、最小对象权限、网络访问控制和 TLS。
  • 不要把生产凭据写入仓库、README 示例或可被其他用户读取的客户端配置。

测试与构建

默认测试不访问网络或 MySQL:

uv sync --all-groups
uv run ruff check .
uv run ruff format --check .
uv run pytest
uv build

可选 MySQL 5.7/8.0 集成测试

compose.test.yaml 只提供测试基础设施,不是 MCP 的运行依赖。它使用公开的非生产测试凭据、 独立数据库和本机端口:MySQL 5.7 为 3357,MySQL 8.0 为 3380

集成测试会在 MYSQL_TEST_DATABASE 中创建、删除临时表并执行 DML。只能把这些变量指向隔离、 可丢弃的测试库,不要指向生产库或包含需保留数据的数据库。

启动并等待目标服务在 docker compose ps 中显示 healthy:

docker compose -f compose.test.yaml up -d mysql57
docker compose -f compose.test.yaml ps

PowerShell 运行 MySQL 5.7:

$env:MYSQL_TEST_HOST = "127.0.0.1"
$env:MYSQL_TEST_PORT = "3357"
$env:MYSQL_TEST_USER = "mcp_test"
$env:MYSQL_TEST_PASSWORD = "mcp_test_password"
$env:MYSQL_TEST_DATABASE = "mysql_mcp_test"
$env:MYSQL_TEST_EXPECT_VERSION = "5.7"
uv run pytest -m mysql

PowerShell 运行 MySQL 8.0:

docker compose -f compose.test.yaml up -d mysql80
$env:MYSQL_TEST_PORT = "3380"
$env:MYSQL_TEST_EXPECT_VERSION = "8.0"
uv run pytest -m mysql

macOS/Linux 运行 MySQL 5.7:

MYSQL_TEST_HOST=127.0.0.1 \
MYSQL_TEST_PORT=3357 \
MYSQL_TEST_USER=mcp_test \
MYSQL_TEST_PASSWORD=mcp_test_password \
MYSQL_TEST_DATABASE=mysql_mcp_test \
MYSQL_TEST_EXPECT_VERSION=5.7 \
uv run pytest -m mysql

macOS/Linux 运行 MySQL 8.0:

docker compose -f compose.test.yaml up -d mysql80
MYSQL_TEST_HOST=127.0.0.1 \
MYSQL_TEST_PORT=3380 \
MYSQL_TEST_USER=mcp_test \
MYSQL_TEST_PASSWORD=mcp_test_password \
MYSQL_TEST_DATABASE=mysql_mcp_test \
MYSQL_TEST_EXPECT_VERSION=8.0 \
uv run pytest -m mysql

集成测试只有同时满足以下条件才会连接数据库:命令显式包含 -m mysql,并且五个必需的 MYSQL_TEST_HOSTPORTUSERPASSWORDDATABASE 均已配置。缺失时会清晰跳过。

可选测试变量:

环境变量 用途
MYSQL_TEST_EXPECT_VERSION 断言服务端版本字符串以指定前缀开头
MYSQL_TEST_EXPECT_TLS true/false;断言探测到的实际 TLS 状态
MYSQL_TEST_SSL_CA 测试连接的 CA 路径
MYSQL_TEST_SSL_CERT 测试连接的客户端证书路径,必须与 KEY 成对
MYSQL_TEST_SSL_KEY 测试连接的客户端私钥路径,必须与 CERT 成对
MYSQL_TEST_SSL_VERIFY_CERT 传给数据源配置的证书校验开关

测试结束后删除容器和测试数据卷:

docker compose -f compose.test.yaml down -v

故障排查

启动立即退出,退出码为 2

查看 stderr 中指出的具体环境变量。常见原因是缺少 MYSQL_SOURCES、某数据源没有 USERDATABASE、名称包含大写/连字符、整数超出范围、角色无效,或 TLS 证书与私钥没有成对 配置。服务不会读取当前目录中的 .env

数据库连接失败

先调用 list_datasources 核对脱敏后的主机、端口、默认库和用户,再调用 test_connection。检查 DNS/防火墙、端口、MySQL 监听地址、账号来源主机、密码、默认库和 TLS CA。启动成功只代表静态配置有效,不代表数据库可达。

SQL 被拒绝

查看 error_typefailed_indexstatement_typepermission_denied 表示 MCP role 不允许整批中的某条语句,整批尚未执行;MySQL 1044/1142 等则表示数据库账号对象权限不足。 需要扩大能力时,同时审查 MCP role 与 MySQL GRANT,优先保持最小权限。

执行中连接中断

服务不会自动重试。若响应包含 commit_state: "unknown",不要直接重放写入脚本;先通过 业务唯一键、审计记录或只读查询确认数据库中的实际状态。

DDL 批次出现风险提示

这是 MySQL 隐式提交语义,不是可忽略的普通 warning。把 DDL 与 DML 分开执行,避免假设 DDL 失败后前序修改一定能回滚,并在变更前准备数据库级回滚方案。

Ruff 报告 E902 stream did not contain valid UTF-8

部分公司加密目录会让 Ruff 无法直接读取本来合法的 UTF-8 Python 文件。不要因此批量改编码 或重写源码。把仓库或只读校验副本放到未加密目录后运行 Ruff,并分别执行 Python 编译、 pytest 和构建来验证代码。本项目当前的正常验证工作区位于未加密的 C: 盘。

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mysql_mcp_plus-0.1.0.tar.gz (21.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mysql_mcp_plus-0.1.0-py3-none-any.whl (25.3 kB view details)

Uploaded Python 3

File details

Details for the file mysql_mcp_plus-0.1.0.tar.gz.

File metadata

  • Download URL: mysql_mcp_plus-0.1.0.tar.gz
  • Upload date:
  • Size: 21.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mysql_mcp_plus-0.1.0.tar.gz
Algorithm Hash digest
SHA256 10b1a65e46c025e8f9d3cb7a13255bce1923ee9966323af3d18fd8d69f3038ef
MD5 da2c3463801f3755e6e492929e8f88b3
BLAKE2b-256 4d39cc1af1475c1b4228086197f55fa7367487c1abe99617f6ab981310848e19

See more details on using hashes here.

File details

Details for the file mysql_mcp_plus-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: mysql_mcp_plus-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 25.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mysql_mcp_plus-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 92f87000b81d3d61b385c3390a9b3c575dda518e2dafa177f1a7bf4ce0659a03
MD5 81f8a79478a2411db7b7312b5d79490a
BLAKE2b-256 e6e109f61e9cd48e343078d36b46d9750fa0b72f03cd83ef4f912495f90d08f8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page