Skip to main content

tessen

CI

Security scanner for AI agent configurations.

Tessen scans MCP (Model Context Protocol) configurations — the files that tell Claude Desktop, Claude Code, Cursor, and Windsurf which tools to load — for vulnerabilities that current tooling misses: plaintext credentials, dangerous invocation patterns, invisible Unicode instruction carriers, and filesystem-wide access grants.

Point it at your config, get findings in seconds, wire it into CI to keep them from coming back.

tessen scan

Named after the Japanese 鉄扇 (tessen), an iron war fan carried into places swords were forbidden. It looked like an ordinary fan. It was a weapon. So are the MCP servers hiding in your claude_desktop_config.json.


Status

Alpha (v0.1.0). The scanner works, the tests pass, but it hasn't been published to PyPI yet and the ruleset is small. Expect breaking changes to the rule format and CLI flags before v0.2. If you use it, run it locally, don't wire it into production CI yet.

Current coverage: 3 built-in rules, 3 client formats, 26 tests.


What it catches

Every finding includes a severity, location in the config, message explaining the issue, and a remediation suggestion.

tessen-secrets-in-env — plaintext credentials

Detects API keys and tokens stored directly in MCP server environment variables. Recognizes patterns from GitHub (ghp_, github_pat_, gho_), Anthropic, OpenAI, AWS access keys, Google API keys, Slack tokens, Linear, and SendGrid. Distinguishes real secrets from environment variable references (${VAR}), placeholders (<YOUR_KEY>), and common redaction markers (REDACTED, PLACEHOLDER).

Severity: critical

tessen-dangerous-command — shell wrappers and curl-pipe-shell

Flags MCP servers invoked through a shell interpreter (bash, sh, zsh, pwsh, cmd.exe), which grants the server arbitrary command execution and makes any prompt injection catastrophic. Also detects curl | sh-style patterns that fetch and execute unverified remote code — a supply-chain risk that has produced multiple real-world incidents in 2025-2026.

Severity: high (shell wrappers), critical (curl-pipe-shell)

tessen-suspicious-args — Unicode injection and overbroad scopes

Two related detections. First, invisible Unicode characters (categories Cf and Cc — zero-width spaces, bidi overrides, format characters) in server arguments, which are the primary carrier for hidden prompt injection instructions. Second, filesystem-wide access grants like /, /home, or ~ passed to filesystem MCP servers.

Severity: critical (invisible Unicode), high (filesystem grants)


Install

pipx install tessen-cli

Or with pip:

pip install tessen-cli

Requires Python 3.11+.

Usage

Auto-detect and scan everything

tessen scan

Walks the project tree upward from the current directory looking for .mcp.json (Claude Code) and .cursor/mcp.json (Cursor), then checks the OS-specific location for Claude Desktop (~/.config/Claude/claude_desktop_config.json on Linux, ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows). Scans every config it finds.

Scan a specific file

tessen scan path/to/claude_desktop_config.json
tessen scan .cursor/mcp.json --client cursor

The --client flag forces a specific parser when the filename doesn't match convention. Valid values: claude_desktop, claude_code, cursor.

Version

tessen version

CI integration

Tessen exits non-zero when it finds issues at or above a severity threshold. This is the mechanism to keep vulnerable configs out of your main branch.

tessen scan --fail-on high

Exit codes: 0 for clean, 1 when the threshold is met, 2 for invalid flag values.

Valid --fail-on values: critical, high, medium, low, never.

GitHub Actions example (roughly — this pipeline hasn't been dogfooded yet):

name: MCP Config Security

on:
  pull_request:
    paths:
      - ".mcp.json"
      - ".cursor/mcp.json"
      - ".claude/settings.json"

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.11"
      - name: Install tessen
        run: |
          pip install git+https://github.com/SlimBenTanfous1/tessen-cli.git
      - name: Scan MCP configs
        run: tessen scan --fail-on high

Example output

Scanning a config with three planted issues (real GitHub PAT, bash wrapper, filesystem-wide grant):

tessen scanning /path/to/claude_desktop_with_secrets.json

Findings for Claude Desktop  1 critical · 2 high

╭─ ● [CRITICAL] GitHub Personal Access Token exposed in plaintext ─────╮
│ The env var 'GITHUB_PERSONAL_ACCESS_TOKEN' in server                 │
│ 'github_real_leak' contains a value matching a GitHub Personal       │
│ Access Token pattern.                                                │
│                                                                      │
│ Location:    mcpServers.github_real_leak.env.GITHUB_PERSONAL_...     │
│ Rule:        tessen-secrets-in-env                                   │
│ Remediation: Move the secret to a secure store (1Password, Vault,    │
│              or OS keychain) and reference it via ${VAR_NAME}.       │
╰──────────────────────────────────────────────────────────────────────╯

Scan summary
  Total findings: 3
  Critical  1
  High      2

Supported clients

Client Config path Parser
Claude Desktop ~/.config/Claude/claude_desktop_config.json (Linux)
~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
%APPDATA%\Claude\claude_desktop_config.json (Windows)
claude_desktop
Claude Code <project>/.mcp.json claude_code
Cursor <project>/.cursor/mcp.json cursor
Windsurf Planned for v0.2 —

Development

git clone https://github.com/SlimBenTanfous1/tessen-cli.git
cd tessen-cli
uv sync                       # install runtime + dev deps
source .venv/bin/activate     # or use `uv run <command>`

# Run the test suite
pytest tests/ -v

# Run tessen against test fixtures
tessen scan tests/fixtures/claude_desktop_with_secrets.json
tessen scan tests/fixtures/claude_desktop_clean.json

Project layout

tessen-cli/
├── src/tessen/
│   ├── cli.py                # Typer app: scan + version + --fail-on
│   ├── models.py             # MCPConfig, ParseResult, ClientType
│   ├── output/human.py       # Rich severity panels
│   ├── parsers/
│   │   ├── common.py         # Shared JSON+Pydantic parse logic
│   │   ├── claude_desktop.py
│   │   ├── claude_code.py    # + walker for .mcp.json
│   │   ├── cursor.py         # + walker for .cursor/mcp.json
│   │   └── auto.py           # Multi-client auto-detection
│   └── rules/
│       ├── base.py           # Rule ABC
│       ├── engine.py         # RuleEngine with buggy-rule isolation
│       ├── models.py         # Finding, Severity, RuleMetadata
│       └── builtin/          # Built-in detection rules
└── tests/                    # 26 tests, ~0.3s runtime

Adding a rule

  1. Create src/tessen/rules/builtin/your_rule.py
  2. Subclass Rule, define METADATA, implement check(result) -> list[Finding]
  3. Register it in src/tessen/rules/builtin/__init__.py (BUILTIN_RULES list)
  4. Add tests in tests/test_rules.py

Rules should be pure functions: no I/O, no state, no side effects. Same input → same output.


Roadmap

v0.1 (current) — foundation: parsers for 3 clients, auto-detection, 3 detection rules, Rich terminal output, --fail-on for CI.

v0.2 — coverage:

  • 4 more rules: known-vulnerable MCP servers (CVE feed), HTTP transport without auth, dangerous auto-approve flags, hook injection in .claude/settings.json
  • JSON output format
  • SARIF output for GitHub Advanced Security compatibility
  • Windsurf parser
  • PyPI distribution (pipx install tessen)

v0.3 — trust:

  • GitHub Action for one-line CI integration
  • CVE feed as a subscribable JSON endpoint
  • First round of dogfooded findings on awesome-mcp-servers

Later:

  • Custom rules via YAML
  • Baseline files (.tessen-baseline) to suppress known findings
  • Runtime hooks that observe tool calls at execution time

Anything you want prioritized? Open an issue.


Why "Tessen"

A tessen (鉄扇) is an iron folding fan carried by samurai as a concealed weapon into places where swords were forbidden — tea houses, temples, palaces. Ordinary-looking. Genuinely dangerous.

Same shape as a poisoned MCP server: a small config file that looks like plumbing, and behaves like an attack.


License

MIT. See LICENSE.


About

Built by Slim Ben Tanfous — cybersecurity engineer at BrightByDesign (Zero Trust infrastructure, CI/CD security), joining the ESGI Paris Master's in Cybersecurity in September 2026. Prior: CCNA, CyberOps Associate, AWS Cloud Security Foundations, OCI Foundations.

Reach out: github.com/SlimBenTanfous1

Metadata

Release files for tessen-cli 0.1.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tessen-cli 0.1.2
File Size Uploaded Provenance
tessen_cli-0.1.2.tar.gz 20.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for tessen-cli 0.1.2
File Interpreter ABI Platform
tessen_cli-0.1.2-py3-none-any.whl Python 3 none any Details

Total release size: 51.9 kB

Release files / tessen_cli-0.1.2.tar.gz

Download URL tessen_cli-0.1.2.tar.gz
Size 20.6 kB
Tags Source
SHA-256 checksum
How to use checksums
34c94907d591ed99cbdcd7816840239778440ec64402058f4c57b496d713f67f
BLAKE2b-256 checksum
How to use checksums
70767f00ec693137e329d8d80176316143443a0e35bf9b6d480a54435df0dd48
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.

Transparency log

Release files / tessen_cli-0.1.2-py3-none-any.whl

Download URL tessen_cli-0.1.2-py3-none-any.whl
Size 31.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ca3b748af8d7a6b0faab1f583a8d5c17da8d263c6ef84a48ee650d52400de6f4
BLAKE2b-256 checksum
How to use checksums
48981ae6b9ad4faaa4522487b38bd166277dbf107703f3711d230dde65987ec8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.

Transparency log

Security for tessen-cli 0.1.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for tessen_cli-0.1.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page