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

goosy-mcp

v0.1.3

Published

Goosy MCP server — exposes Goosy's security-scan engine to AI coding agents over the Model Context Protocol.

Downloads

465

Readme

goosy-mcp

Goosy's security-scan engine, exposed to AI coding agents over the Model Context Protocol.

It is a thin client. There is no scanning logic, no rules, and no LLM calls in this repository — every tool call becomes an authenticated HTTPS request to the Goosy FastAPI resource server (a separate Python service). This package's entire job is: speak MCP on one side, speak Goosy's REST API on the other, and shape the results so they fit inside an agent's context window.

Implements docs/GOOSY_MCP_SERVER.md.


Install

// Claude Code / Claude Desktop / Cursor — MCP server config
{
  "mcpServers": {
    "goosy": {
      "command": "npx",
      "args": ["-y", "goosy-mcp"],
      "env": { "GOOSY_API_BASE": "https://api.goosy.ai" }
    }
  }
}

Then authorize once:

npx goosy-mcp login     # opens a device-code flow in your browser
npx goosy-mcp doctor    # verifies config, auth, and backend reachability

You can skip login — the first tool call returns an AUTH_REQUIRED error carrying the URL and code for the agent to relay to you. See Authentication.


The six tools

| Tool | What it does | |---|---| | goosy_scan_local | Bundles the working tree (including uncommitted files) and starts a scan. Returns a scan_id immediately. | | goosy_scan_status | Polls state and progress. Carries retry_after_seconds so agents pace themselves. | | goosy_list_findings | Paginated, summary-shaped findings with severity and path filters. | | goosy_get_finding | Full detail for one finding: snippet, CWE, call path, remediation. | | goosy_generate_fix | Returns a unified diff as text. The agent reviews and applies it. | | goosy_explain_finding | Prose explanation with references, for justifying a change to a human. |

The design rule that matters most

Goosy returns data; the agent acts.

goosy_generate_fix returns a patch as text. This server never writes to your filesystem, never commits, and never opens a pull request. The agent already has file-editing tools and your trust to use them — supplying a diff keeps your existing review surface (the agent's own diff preview) intact.

v1 therefore deliberately exposes no write tools. There is no create_pr, no apply_patch, and no dismiss_finding: an agent in a loop must not be able to mutate a repository or silently suppress a security finding unattended. This is asserted mechanically in tests/tool-definitions.test.ts.


Layout

One module per responsibility. Each layer is testable without the one beneath it.

src/
├── index.ts                  CLI entry: serve (default), login, logout, whoami, doctor, tools
├── server.ts                 MCP wiring — stdio transport ↔ tool registry
├── config.ts                 Environment resolution, caps, timeouts
├── logger.ts                 stderr-only logging + token redaction + stdout guard
├── errors.ts                 Typed error envelope {code, message, retryable}
├── types.ts                  Domain and backend wire types
├── auth/
│   ├── device-flow.ts        OAuth device grant against /api/platform/auth/*
│   ├── token-store.ts        0600 config.json, atomic writes, cross-process lock
│   └── auth-manager.ts       AUTH_REQUIRED contract, refresh, grant coalescing
├── api/
│   ├── client.ts             Bearer injection, retry/backoff, HTTP → error codes
│   └── endpoints.ts          One method per backend route
├── bundler/
│   ├── path-guard.ts         Workspace-root confinement (rejects /, ~, .., symlink escapes)
│   ├── deny-list.ts          Secret deny-list: path patterns + content signatures
│   ├── git.ts                HEAD sha and changed-file detection for scope:"diff"
│   ├── walker.ts             Tree walk: gitignore, deny-list, caps, exclusion reporting
│   └── bundler.ts            Streaming tar + zstd/gzip to a temp file
├── shaper/
│   ├── cursor.ts             Opaque pagination cursors
│   └── result-shaper.ts      Summary-first, bounded snippets, truncation disclosure
└── tools/
    ├── definitions.ts        JSON Schemas (advertised) + zod validators (enforced)
    ├── registry.ts           Validation, dispatch, error shaping
    ├── context.ts            Handler dependency bundle
    └── {scan-local, scan-status, list-findings,
           get-finding, generate-fix, explain-finding}.ts

Two invariants worth knowing before you edit anything

stdout is the protocol. Any byte written to stdout that isn't a JSON-RPC message corrupts the session — this is the single most common way MCP servers break. All logging goes to stderr, and guardStdout() reroutes console.* so a transitive dependency's deprecation notice can't kill a session.

A failed scan is never reported as clean. goosy_scan_status surfaces failed as failed with findings_available: false, and goosy_list_findings refuses to return an empty array for a failed scan. The difference between "your code is clean" and "we didn't look" is the whole value of a security tool.


Authentication

MCP tool calls are request/response over stdio, but device-flow auth needs a human to open a browser — which can take a minute. A tool call cannot block that long, and this server has no UI channel of its own. Its only channel is the agent's transcript.

So the first call with no valid token returns a structured error, written for the model to relay:

{
  "error": {
    "code": "AUTH_REQUIRED",
    "retryable": true,
    "message": "Goosy needs authorization. Ask the user to open https://app.goosy.ai/device and enter code WXYZ-1234, then call this tool again.",
    "verification_uri": "https://app.goosy.ai/device",
    "user_code": "WXYZ-1234",
    "expires_in": 900
  }
}

Polling continues in the background; the token is persisted on approval; the agent's retry just works. Concurrent tool calls join the same pending grant — issuing a second device code would show you a second number and invalidate the first.

Tokens are stored at ${XDG_CONFIG_HOME:-~/.config}/goosy/config.json (%APPDATA%\goosy\config.json on Windows), mode 0600, keyed by API base so production and a local stack can coexist. Refresh is transparent on 401 and serialized by a file lock so two processes can't race a rotation. Tokens are redacted from all log output.


Security model

| Risk | Control | |---|---| | Secrets uploaded from the working tree | Deny-list by path and by content signature, applied after .gitignore and overriding it — a git-tracked .env is still refused. The backend re-checks on ingest; this client is untrusted. | | Agent steered to scan outside the project | Every path resolved through realpath and proven to sit under the workspace root. /, ~, .., and symlink escapes are rejected. | | Unattended repo mutation | No write tool exists in v1. | | Over-large bundles | Hard caps on file count, total bytes, and per-file size. Over-cap is an error with the actual numbers, never a silent truncation — a partial scan reported as complete is a false clean bill of health. | | Runaway agent cost | retry_after_seconds on every pending response; server-side ceilings. |

Symlinks are never followed into a bundle. Every exclusion is counted and reported in the tool response's warnings.

The two gaps from the spec are now closed

Both lived in the backend, and both are fixed by platform/ in this repo:

  1. ~~MCP token scopes are not enforced server-side.~~ Every route in platform/ declares the scope it requires, and platform/src/lib/auth.ts refuses a token that lacks it. A token minted with ["scan:read"] is now refused at patch:write and scan:write with a FORBIDDEN naming both required and granted scopes. v1's safety property is no longer "the MCP server exposes no write tools" but the stronger "the token cannot perform writes" (backlog SEC-1).
  2. ~~The goosy_mcp_* token authenticates against nothing.~~ platform/ stores every credential as a SHA-256 hash and actually reads it on each request. The format from §2.3 is preserved and is now verifiable, with the scopes and expiresAt the old scaffold could not express (backlog AUTH-5).

Still open: session auth on /device/approve is a stub that fails closed — it must be pointed at a real session provider before deployment. See platform/README.md.


Development

npm install
npm run typecheck
npm test            # 151 tests
npm run build
npm run dev         # run from source via tsx
npm run dev:env     # same, with .env loaded via node --env-file

The test suite covers the spec's release gates directly: no write tool in tools/list, no secret in any bundle, path confinement, failed never reported as clean, truncation always disclosed, and the AUTH_REQUIRED coalescing contract.

Deployment

This package is not deployed as a service — it's a local stdio process an agent host spawns, published to npm and run via npx. The Dockerfile at the repo root exists only for CI/headless scanning (GOOSY_TOKEN + goosy-mcp doctor/scan), not hosting; see docs/DEPLOYMENT.md for why and how, and for deploying platform/ (the piece that actually runs as a server) to Vercel or Docker.

Environment

Copy .env.example to .env for the full list with defaults and commentary. Note the server does not read .env itself — there is no dotenv dependency, and loadConfig() reads process.env directly. In normal use these go in the env block of your MCP host config, because the host spawns the process rather than your shell.

| Variable | Default | Purpose | |---|---|---| | GOOSY_API_BASE | https://api.goosy.ai | Backend base URL | | GOOSY_TOKEN | — | Pre-issued token for headless/CI; bypasses the device flow | | GOOSY_WORKSPACE_ROOT | cwd | Directory scans are confined to | | GOOSY_CONFIG | see above | Token store path override | | GOOSY_LOG | text | json for structured logs (always on stderr) | | GOOSY_LOG_LEVEL | info | debug | info | warn | error | silent | | GOOSY_MAX_FILES | 20000 | Bundle file-count cap | | GOOSY_MAX_TOTAL_BYTES | 104857600 | Bundle size cap |

Repository layout

| Directory | What it is | |---|---| | src/, tests/ | goosy-mcp — the npm-published MCP server. Runs locally, holds only a scoped token. | | platform/ | goosy-platform — Next.js auth/API edge on Neon via Prisma. Issues and verifies tokens, enforces scopes, proxies scans. | | (external) | The Python FastAPI scan engine. Not in this repo. |

Run the whole stack locally:

cd platform && npm install && npm run db:push && npm run db:seed && npm run dev
cd .. && GOOSY_API_BASE=http://localhost:3000 node dist/index.js login

Backend contract

This package expects these routes on GOOSY_API_BASE — served by platform/, see src/api/endpoints.ts:

POST /api/platform/auth/device/start | /device/poll | /refresh | /revoke
POST /api/v1/scans/local?mode=fast|deep      (multipart bundle upload)
GET  /api/v1/scans/{id}
GET  /api/v1/scans/{id}/findings
GET  /api/v1/findings/{id}
POST /api/v1/findings/{id}/patch
POST /api/v1/findings/{id}/explain

Response parsing is deliberately tolerant of key aliases (scan_id/id, file/file_path, patch/diff) so a backend rename doesn't break the agent loop silently.

#login system for powershell

$env:GOOSY_API_BASE = "http://localhost:3001"

node dist/index.js login