{
  "markdown": "# Custom MCP Database\n\n<!-- MCP Registry ownership proof — required to publish to registry.modelcontextprotocol.io -->\nmcp-name: io.github.renanlido/custom-mcp-database\n\nAn [MCP](https://modelcontextprotocol.io) server that lets AI agents run **alias-based**\nqueries against **PostgreSQL, MySQL, MongoDB and Oracle** — without ever exposing\ncredentials to the model. Connections are configured once and stored locally; the\nagent only ever references them by alias.\n\nWorks with Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Gemini CLI, and\nany other MCP client (all use the same stdio launch command).\n\n---\n\n## Quickstart\n\nThere are **two roles**, on purpose. Keeping them separate is what stops your DB\npassword from ever reaching the model.\n\n### You (once, in your terminal) — install the credentials\n\nThe **agent never installs credentials.** You do, with the CLI. The secret stays on\nyour machine and is never sent to the model.\n\nEasiest way — the **guided wizard** (asks type, host, user, and how to supply the\nsecret; optionally tests the connection):\n\n```bash\nuvx custom-mcp-database setup\n```\n\nOr do it in one line (you'll be prompted for the password — hidden input):\n\n```bash\nuvx custom-mcp-database add-db --alias prod_ro --type postgres \\\n  --host db.internal --port 5432 --user reporting --dbname app\n\nuvx custom-mcp-database list-aliases   # confirm it's there\n```\n\n### The agent (always) — uses it by alias\n\nPoint your MCP client at the server (see [Install](#install)), then just ask:\n\n> \"Using **prod_ro**, run `SELECT count(*) FROM orders`.\"\n\nThe agent calls `db_execute_query` with the **alias** `prod_ro` — never a host, user,\nor password. It physically cannot see the credentials; they live in your local config,\nresolved only inside the server process at query time.\n\n**Why the agent can't add the DB:** an MCP tool's arguments are produced and read by the\nLLM. If the agent typed your password into an `add` tool, that password would land in the\nmodel's context, the provider, and the logs. So credential setup is a human/CLI step by\ndesign. (Need an agent to wire connections in an automated pipeline? See\n`MCP_DB_ALLOW_ADMIN_TOOLS` in [SECURITY.md](SECURITY.md) — even then it only accepts a\n*reference* to a secret, e.g. an env-var name, never the secret itself.)\n\nWrites are **off by default** (read-only). To allow them for a task:\n`export MCP_DB_READONLY=0 MCP_DB_ALLOW_WRITES=1`.\n\n---\n\n## Install\n\nThe server runs over **stdio**. The universal launch command is `uvx custom-mcp-database run`\n(requires [uv](https://docs.astral.sh/uv/); the package is fetched from PyPI on first run).\n\n### Claude Code\n\n```bash\n# Direct (published package)\nclaude mcp add custom-mcp-database -- uvx custom-mcp-database run\n\n# Or install the full plugin from this repo's marketplace\n/plugin marketplace add renanlido/custom-mcp-database\n/plugin install custom-mcp-database@renanlido-mcp\n```\n\n### Claude Desktop\n\nTwo options:\n\n1. **One-click bundle** — build the `.mcpb` (`mcpb pack`) and open it in Claude Desktop. See [Distribution](#distribution).\n2. **Manual config** — add the snippet from [`examples/mcp-clients/claude-desktop.json`](examples/mcp-clients/claude-desktop.json) to `claude_desktop_config.json`.\n\n### Other clients\n\nCopy the matching snippet — all use the same `command`/`args`, only the file and key differ:\n\n| Client | Config file | Key | Snippet |\n| --- | --- | --- | --- |\n| Cursor | `~/.cursor/mcp.json` | `mcpServers` | [cursor.json](examples/mcp-clients/cursor.json) |\n| VS Code | `.vscode/mcp.json` | `servers` | [vscode.json](examples/mcp-clients/vscode.json) |\n| Windsurf | `~/.codeium/windsurf/mcp_config.json` | `mcpServers` | [windsurf.json](examples/mcp-clients/windsurf.json) |\n| Gemini CLI | `~/.gemini/settings.json` | `mcpServers` | [gemini-cli.json](examples/mcp-clients/gemini-cli.json) |\n\nFull client matrix and a local-checkout variant: [`examples/mcp-clients/README.md`](examples/mcp-clients/README.md).\n\n---\n\n## Configure connections\n\n**Configure connections from your terminal with the CLI — never through the agent.**\nA connection's password is a real secret; if it were passed as an MCP tool argument it\nwould enter the model's context (and the provider, transcripts, and logs). So the\ncredential-management tools are **off the MCP surface by default**; provisioning is a\nhuman/CLI task. The agent only lists and uses aliases.\n\nOmit `--password`/`--uri` to be prompted securely (hidden input, not stored in shell\nhistory). Even better, keep the secret out of the config file entirely with\n`--password-env` / `--password-file` (resolved at connection time):\n\n```bash\n# PostgreSQL — prompted for the password (recommended)\nuvx custom-mcp-database add-db --alias pg --type postgres \\\n  --host localhost --port 5432 --user me --dbname app\n\n# MySQL — password taken from an env var at connect time (nothing secret on disk)\nMYSQL_PW=... uvx custom-mcp-database add-db --alias my --type mysql \\\n  --host localhost --port 3306 --user root --dbname app --password-env MYSQL_PW\n\n# Oracle — password read from a file (e.g. a mounted secret)\nuvx custom-mcp-database add-db --alias ora --type oracle \\\n  --host db.example.com --port 1521 --user system --dbname ORCLPDB1 \\\n  --password-file /run/secrets/ora_pw\n\n# MongoDB — full URI from a file (the URI embeds credentials)\nuvx custom-mcp-database add-db --alias mongo --type mongo \\\n  --dbname app --uri-file /run/secrets/mongo_uri\n\nuvx custom-mcp-database list-aliases\nuvx custom-mcp-database remove-db --alias pg\n```\n\nConfig location (override with `MCP_DB_CONFIG`):\n`$XDG_CONFIG_HOME/custom-mcp-database/mcp_config.sqlite3`\n(default `~/.config/custom-mcp-database/mcp_config.sqlite3`, `0600`).\n\n> If you pass a literal `--password`/`--uri`, it is stored as **plaintext JSON** in that\n> SQLite file. Prefer `--password-env`/`--password-file` (or `--uri-env`/`--uri-file`) so\n> only a reference is stored. Either way, keep the file secret (it is `0600`, gitignored,\n> not encrypted).\n\n---\n\n## MCP tools\n\n| Tool | Purpose |\n| --- | --- |\n| `db_list_aliases` | List configured aliases and types |\n| `db_execute_query` | Run SQL or a MongoDB JSON filter |\n| `db_list_collections` | List MongoDB collections |\n| `db_security_status` | Report the active security policy |\n\n`db_add_database` / `db_remove_database` are **not exposed over MCP by default** — manage\nconnections with the CLI. To opt into exposing them (the add tool only accepts secrets by\nreference, never a literal password), set `MCP_DB_ALLOW_ADMIN_TOOLS=1`.\n\n`db_execute_query` notes: SQL runs as given with parameterized binds (add your own\n`LIMIT`); MongoDB takes a JSON filter + `collection`, caps results at 10 (`--limit`),\nrejects empty filters, and coerces 24-char hex strings to `ObjectId`.\n\n---\n\n## Security\n\nThis server handles **real credentials** and **production data**, so it ships\n**deny-by-default**:\n\n- **Read-only by default.** Only SELECT-class SQL runs. Writes/DDL require explicit opt-in.\n- **No stacked statements** (`;`-injection blocked), **single statement per call**.\n- **MongoDB server-side JavaScript blocked** (`$where`, `$function`, `$accumulator`, mapReduce, …).\n- **Identifiers validated** (`oracle_schema` can't be used for injection).\n- **Results capped** at `MCP_DB_MAX_ROWS` (default 1000); **secrets redacted** from errors.\n- **Credential store** is `0600` plaintext SQLite — keep the host disk encrypted.\n\nCheck the live posture: `custom-mcp-database security-status` (or the `db_security_status` tool).\n\nEnable writes for a specific task (then turn it back off):\n\n```bash\nexport MCP_DB_READONLY=0\nexport MCP_DB_ALLOW_WRITES=1     # INSERT/UPDATE/DELETE\n# export MCP_DB_ALLOW_DDL=1      # only if you really need CREATE/DROP/ALTER/...\n```\n\n**Read the full protocol — least-privilege DB roles, TLS, prompt-injection handling,\nvulnerability reporting — in [SECURITY.md](SECURITY.md).** The app-layer guards are\ndefense-in-depth; the authoritative control is a least-privilege database account.\n\n## Develop\n\n```bash\nuv sync                 # create .venv and install deps\nmake run                # run the server (stdio)\nmake lint               # ruff\nmake build              # sdist + wheel into dist/\n```\n\nInspect tools interactively:\n\n```bash\nuv run mcp dev src/custom_mcp_database/server.py\n```\n\n---\n\n## Distribution\n\nThis repo ships ready-to-publish metadata for every major channel. All of it is\npublished automatically on push to `main` (see below):\n\n| Channel | File | Published by |\n| --- | --- | --- |\n| PyPI | `pyproject.toml` | `release.yml` (push to main) |\n| MCP Registry | `server.json` | `release.yml` (push to main) |\n| Claude Code plugin | `.claude-plugin/plugin.json`, `.mcp.json` | available on GitHub push |\n| Claude Code marketplace | `.claude-plugin/marketplace.json` | available on GitHub push |\n| Claude Desktop bundle | `manifest.json` | `release.yml` attaches `.mcpb` to the Release |\n\n### Automated release — just push to `main`\n\nReleases are fully automated. On every push to `main`,\n[`.github/workflows/release.yml`](.github/workflows/release.yml):\n\n1. Picks the next **semantic version** from your commits since the last tag\n   (`feat:` → minor, `BREAKING CHANGE`/`type!:` → major, anything else → patch;\n   add `[skip release]` to a commit message to skip).\n2. Writes that version into `pyproject.toml` and **syncs it into every artifact**\n   (`server.json`, `manifest.json`, plugin + marketplace) via `scripts/sync_version.py` —\n   version lives in **one place**, no hand-bumping.\n3. Builds, commits `chore(release): vX [skip ci]`, tags `vX`, pushes.\n4. Publishes to **PyPI** (Trusted Publishing/OIDC), then the **MCP Registry** (GitHub OIDC).\n5. Packs the **`.mcpb`** and cuts a **GitHub Release** with the wheel + bundle attached.\n\nThe release commit carries `[skip ci]`, so it does not re-trigger the workflow.\n\n**One-time setup** (can't be automated — needs your accounts):\n\n- Create a [PyPI Trusted Publisher](https://docs.pypi.org/trusted-publishers/) for\n  `renanlido/custom-mcp-database`, workflow `release.yml`.\n- Allow GitHub Actions to push to `main` (repo → Settings → Actions → *Read and write\n  permissions*; if `main` is a protected branch, allow the actions bot to bypass or use a PAT).\n\nThe MCP Registry namespace is `io.github.renanlido/custom-mcp-database` (GitHub-validated).\n\nLocal manual escape hatch: `make build` (syncs version + builds) then `uv publish`.\n\n---\n\n## License\n\nMIT\n",
  "bytes": 10435,
  "sha": "a49257d9fb5d2ce5d3c81d6808c8cbaac3a9d47e5bd8cb1997237ab72c17f6e1",
  "repo_slug": "renanlido/custom-mcp-database",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_renanlido_custom_mcp_database_625c8fbd/readme"
}