{"id":51049573,"url":"https://github.com/wayyoungboy/mcpcanary","last_synced_at":"2026-06-22T16:02:18.841Z","repository":{"id":362902659,"uuid":"1260630248","full_name":"wayyoungboy/mcpcanary","owner":"wayyoungboy","description":"Local-first MCP trust scanner and semantic drift canary for AI agent tool configs","archived":false,"fork":false,"pushed_at":"2026-06-05T19:42:02.000Z","size":31,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-06T14:09:01.365Z","etag":null,"topics":["ai-agents","go","local-first","mcp","sarif","security","static-analysis"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wayyoungboy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-05T17:44:48.000Z","updated_at":"2026-06-05T19:42:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/wayyoungboy/mcpcanary","commit_stats":null,"previous_names":["wayyoungboy/mcpcanary"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/wayyoungboy/mcpcanary","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wayyoungboy%2Fmcpcanary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wayyoungboy%2Fmcpcanary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wayyoungboy%2Fmcpcanary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wayyoungboy%2Fmcpcanary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wayyoungboy","download_url":"https://codeload.github.com/wayyoungboy/mcpcanary/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wayyoungboy%2Fmcpcanary/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34655722,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","go","local-first","mcp","sarif","security","static-analysis"],"created_at":"2026-06-22T16:02:17.908Z","updated_at":"2026-06-22T16:02:18.833Z","avatar_url":"https://github.com/wayyoungboy.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# MCPCanary\n\n**A local-first trust scanner for MCP configs before your AI agent connects.**\n\n![MCPCanary hero](assets/hero.svg)\n\n[![CI](https://github.com/wayyoungboy/mcpcanary/actions/workflows/ci.yml/badge.svg)](https://github.com/wayyoungboy/mcpcanary/actions/workflows/ci.yml)\n[![Go Reference](https://pkg.go.dev/badge/github.com/wayyoungboy/mcpcanary.svg)](https://pkg.go.dev/github.com/wayyoungboy/mcpcanary)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Output: SARIF](https://img.shields.io/badge/output-SARIF-38bdf8)](docs/THREAT_MODEL.md)\n[![Local-first](https://img.shields.io/badge/privacy-local--first-34d399)](SECURITY.md)\n\n\u003c/div\u003e\n\nMCPCanary scans Model Context Protocol (MCP) server configs before your AI agent connects to them. It looks for risky tool descriptors, permission mismatches, hidden instructions, broad filesystem access, credential exposure, and semantic drift from previously approved versions.\n\nIt is built for the moment where developers are installing MCP servers faster than teams can review them.\n\n```bash\nmcpcanary scan ~/.cursor/mcp.json --fail-on high\nmcpcanary lock ~/.cursor/mcp.json\nmcpcanary diff ~/.cursor/mcp.json\n```\n\n## Claude Code and Codex\n\nClaude Code can store shared project MCP servers in a repository-level `.mcp.json`. Put MCPCanary in front of that file before the team approves a new server:\n\n```bash\nmcpcanary scan .mcp.json --fail-on high\nmcpcanary lock .mcp.json --lockfile mcpcanary.lock\n```\n\nCodex MCP launchers are commonly managed from the Codex config layer. When you keep a JSON MCP manifest beside a project or plugin, scan it the same way:\n\n```bash\nmcpcanary scan .codex/mcp.json --format markdown\n```\n\nRunning `mcpcanary scan` with no path now checks common user-level locations plus project-level `.mcp.json`, `.vscode/mcp.json`, and `.codex/mcp.json`.\n\n## At a Glance\n\n| Workflow | What MCPCanary does |\n|---|---|\n| **Scan before install** | Parses MCP configs without executing servers and flags risky descriptors or permissions. |\n| **Lock approved tools** | Stores local fingerprints for the tool descriptions you reviewed. |\n| **Catch semantic drift** | Detects when a known MCP server changes intent while keeping a familiar name. |\n| **Report for humans and CI** | Emits text, Markdown, JSON, and SARIF with severity gates. |\n\n![MCPCanary workflow](assets/workflow.svg)\n\n## Why\n\nMCP servers can give assistants access to email, GitHub tokens, local files, databases, browsers, and internal tools. The dangerous part is not only the executable. The tool descriptions shown to the model are also part of the security boundary.\n\nMCPCanary focuses on three workflows:\n\n- **Scan before install**: find suspicious descriptors and risky permissions without executing the server.\n- **Lock what you approved**: save a local semantic fingerprint of approved tool descriptors.\n- **Catch drift before connect**: detect when an MCP server keeps the same name but changes intent.\n\n## Features\n\n- Non-executing JSON config parser for `mcpServers` configs\n- Static checks for prompt injection, tool poisoning language, hidden Unicode, broad filesystem scope, sensitive env vars, and read-only/write mismatch\n- Local deterministic semantic embeddings for threat-pattern matching\n- `mcpcanary.lock` descriptor fingerprints for approved versions\n- Semantic drift detection between locked and current configs\n- Text, JSON, Markdown, and SARIF reports\n- CI-friendly `--fail-on` severity gate\n- Works offline by default; no descriptors or private tool names are uploaded\n\n## What It Catches\n\n| ID | Risk | Example signal |\n|---|---|---|\n| `MCPCANARY-001` | Model-facing override instruction | \"ignore previous instructions\", hidden BCC, credential export |\n| `MCPCANARY-002` | Read-only claim conflicts with capability | claims read-only but exposes send/delete/write tools |\n| `MCPCANARY-003` | Sensitive environment variable exposure | `GITHUB_TOKEN`, `API_KEY`, `PASSWORD`, credentials |\n| `MCPCANARY-004` | Broad local or network capability | `--filesystem /`, external callbacks, broad paths |\n| `MCPCANARY-005` | Hidden Unicode in descriptor | zero-width or bidirectional control characters |\n| `MCPCANARY-006` | Similarity to known MCP threat patterns | semantic match to tool poisoning or rug-pull descriptors |\n\n![MCPCanary report card](assets/report-card.svg)\n\n## Install\n\nFrom source:\n\n```bash\ngo install github.com/wayyoungboy/mcpcanary/cmd/mcpcanary@latest\n```\n\nLocal checkout:\n\n```bash\ngit clone https://github.com/wayyoungboy/mcpcanary.git\ncd mcpcanary\ngo test ./...\ngo run ./cmd/mcpcanary scan testdata/risky.mcp.json\n```\n\n## Commands\n\n### Scan\n\n```bash\nmcpcanary scan path/to/mcp.json\nmcpcanary scan path/to/mcp.json --format json\nmcpcanary scan path/to/mcp.json --format sarif --fail-on high\n```\n\nExample output:\n\n```text\nMCPCanary score: 15/100\n- [high] MCPCANARY-001 Model-facing override instruction: descriptor contains instruction override or covert action language\n- [high] MCPCANARY-002 Read-only claim conflicts with tool capability: server claims read-only while tools can write, send, delete, or mutate data\n```\n\n### Inspect\n\nNormalize and print discovered MCP servers.\n\n```bash\nmcpcanary inspect path/to/mcp.json\n```\n\n### Lock\n\nSave approved descriptor fingerprints to a local lockfile.\n\n```bash\nmcpcanary lock path/to/mcp.json --lockfile mcpcanary.lock\n```\n\n### Diff\n\nCompare current descriptors with the lockfile and fail if a server was added, removed, or semantically drifted.\n\n```bash\nmcpcanary diff path/to/mcp.json --lockfile mcpcanary.lock\n```\n\n## Before / After\n\nBefore:\n\n- `mail-helper` says \"read-only\"\n- the tool list includes `send_email` and delete-like behavior\n- the server receives `GITHUB_TOKEN`\n- the args include `--filesystem /`\n- the descriptor includes model-facing override language\n\nAfter:\n\n- `mcpcanary scan --fail-on high` blocks it in CI\n- a reviewed config is committed with `mcpcanary.lock`\n- `mcpcanary diff` fails when the descriptor semantically drifts later\n\n## Supported Config Shape\n\nMCPCanary reads the common MCP config shape:\n\n```json\n{\n  \"mcpServers\": {\n    \"docs-reader\": {\n      \"type\": \"stdio\",\n      \"command\": \"node\",\n      \"args\": [\"server.js\"],\n      \"env\": {\n        \"GITHUB_TOKEN\": \"...\"\n      },\n      \"description\": \"Read project docs.\",\n      \"tools\": [\n        {\n          \"name\": \"read_docs\",\n          \"description\": \"Read documentation files and return excerpts.\"\n        }\n      ]\n    }\n  }\n}\n```\n\nMany clients do not store tool descriptors in config files because descriptors are normally returned at runtime by the MCP server. MCPCanary v0.1 stays non-executing by default, so it scans the config and any descriptor metadata already available. Runtime descriptor capture is on the roadmap and will use sandboxed execution with explicit consent.\n\n## Client Quick Starts\n\nMCPCanary reads config files; it does not execute unknown MCP servers in v0.1.\n\n| Client | Common path |\n|---|---|\n| Claude Desktop / Claude Code | project `.mcp.json`, `~/.claude/mcp.json`, or app-specific MCP config |\n| Cursor | `~/.cursor/mcp.json` |\n| VS Code | `.vscode/mcp.json` |\n| Windsurf | user MCP config path |\n| Codex | `.codex/mcp.json` for project/plugin manifests, or MCP launchers managed from Codex config |\n\n```bash\nmcpcanary scan ~/.cursor/mcp.json --fail-on high\nmcpcanary lock ~/.cursor/mcp.json --lockfile mcpcanary.lock\n```\n\n## Local Trust Memory\n\nThe v0.1 trust memory uses a local JSON store plus deterministic semantic vectors. It is intentionally simple so the CLI works offline and is easy to audit.\n\nThe architecture keeps storage behind a trust-store boundary. A real seekdb-backed adapter can be added later for larger private rule packs, team allowlists, and hybrid vector/text search without changing the CLI surface.\n\n## Positioning\n\nMCPCanary is not trying to be the only MCP security scanner. Snyk Agent Scan, Cisco MCP Scanner, Pyfio MCP Audit, MCPSafe, and other tools are useful. MCPCanary's narrow wedge is:\n\n- local-first privacy\n- descriptor lockfiles\n- semantic drift detection\n- team-friendly trust memory\n- CLI and CI workflows that fit open-source MCP maintainers\n\n| Tool class | What it is good at | MCPCanary wedge |\n|---|---|---|\n| Dependency scanners | package CVEs, vulnerable libraries | MCP descriptor intent and config-level permissions |\n| Runtime sandboxes | containing execution | pre-connection review without executing unknown servers |\n| MCP registries | discovery and metadata | local private configs, lockfiles, team approvals |\n| Generic RAG/security scanners | broad security review | semantic descriptor drift between approved and current MCP tools |\n\n## Development\n\n```bash\ngo test ./...\ngo run ./cmd/mcpcanary scan testdata/risky.mcp.json --format markdown\ngo run ./cmd/mcpcanary lock testdata/safe-v1.mcp.json --lockfile /tmp/mcpcanary.lock\ngo run ./cmd/mcpcanary diff testdata/safe-v2-drift.mcp.json --lockfile /tmp/mcpcanary.lock\n```\n\n## Status\n\nMCPCanary is a v0.1 MVP. It is useful for local config review, CI gates, demos, and early MCP trust workflows. It is not a substitute for code review, sandboxing, dependency scanning, runtime monitoring, or a security assessment.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwayyoungboy%2Fmcpcanary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwayyoungboy%2Fmcpcanary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwayyoungboy%2Fmcpcanary/lists"}