npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

spanner-readonly-mcp

v0.0.11

Published

Read-only MCP server for Google Cloud Spanner

Downloads

1,104

Readme

spanner-readonly-mcp

English | 日本語

A read-only MCP server for Google Cloud Spanner. Lets LLMs safely inspect schemas and run SELECT queries against a Spanner database.

Prerequisites

gcloud auth application-default login

Environment Variables

The server reads these at runtime (the Claude Code plugin prompts for them on install and persists to settings.json; for other clients, set them in the MCP config's env block).

| Variable | Required | Description | |----------|----------|-------------| | SPANNER_PROJECT | Yes | GCP project ID | | SPANNER_INSTANCE | Yes | Spanner instance ID | | SPANNER_DATABASE | Yes | Spanner database ID | | SPANNER_EMULATOR_HOST | No | host:port (e.g. 127.0.0.1:9010) to point at a local Spanner emulator. Leave unset for production. |

Usage

Claude Code Plugin

This repo doubles as a Claude Code plugin marketplace. Register it and install the plugin:

/plugin marketplace add nu0ma/spanner-readonly-mcp
/plugin install spanner-readonly-mcp@spanner-readonly-mcp

The plugin launches the server via npx -y spanner-readonly-mcp@latest. Claude Code will prompt for SPANNER_PROJECT, SPANNER_INSTANCE, and SPANNER_DATABASE on install and persist them to settings.json.

Claude Code (without the plugin)

Create .mcp.json at your project root:

{
  "mcpServers": {
    "spanner-readonly-mcp": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "spanner-readonly-mcp@latest"],
      "env": {
        "SPANNER_PROJECT": "my-project",
        "SPANNER_INSTANCE": "my-instance",
        "SPANNER_DATABASE": "my-database"
      }
    }
  }
}

Claude Code picks this up on session start and prompts to approve the server on first launch.

Tools

| Tool | Description | |------|-------------| | list_tables | List all user tables in the database | | describe_table | Get column definitions (type, nullability, ordinal position) for a table | | list_indexes | List indexes, optionally filtered by table | | execute_query | Execute an arbitrary SELECT query |

Read-Only Guarantee

Writes are blocked even if your IAM credentials have write permissions. Two layers enforce this:

  1. Application layer (UX, best-effort): A regex rejects common mutation keywords (INSERT, UPDATE, UPSERT, DELETE, MERGE, and DDL/DCL) before they reach Spanner. Its purpose is to give the agent a clear, fast error when it accidentally writes a mutation — not to act as a security boundary. Payloads can trivially be rewritten (comments, CTE wrapping, parenthesization) to slip past it.
  2. Transaction layer (authoritative): Every query runs inside a Spanner read-only snapshot transaction (database.getSnapshot()). The Snapshot class does not expose DML methods, and the Spanner backend rejects any mutation attempt within a read-only transaction. This is the real guarantee.

All snapshot queries (including the metadata tools) run with a 30-second gaxOptions.timeout to bound worst-case latency.

Error messages returned to clients are sanitized (first line only, prefixed REGEX_BLOCKED: or SPANNER_ERROR:) to avoid leaking stack traces or internal paths.

IAM least privilege

execute_query accepts arbitrary SELECT statements, including queries against information_schema and spanner_sys. To bound the read surface, grant only roles/spanner.databaseReader to the service account — do not rely on application-level filtering alone.

Development

pnpm install
pnpm build           # compile to dist/
pnpm dev             # run from source without building
pnpm test            # vitest, starts the Spanner emulator via docker compose

Direct execution against a real Spanner instance (build first, then run):

pnpm build
SPANNER_PROJECT=my-project \
SPANNER_INSTANCE=my-instance \
SPANNER_DATABASE=my-database \
pnpm start

License

MIT