Skip to main content
Glama

VAmPI-MCP — the Vulnerable API, over MCP

A faithful port of erev0s/VAmPI from a REST / OpenAPI service to a Model Context Protocol (MCP) server. There is no HTTP API and no Swagger spec anymore — every former endpoint is now an MCP tool. The vulnerabilities live in the handler logic and the data model, so they port over unchanged and are reachable by any MCP client (Claude Desktop, opencode, an agent, a scanner, etc.).

⚠️ Vulnerable on purpose. Run it locally / in an isolated lab only. Do not expose it to untrusted networks. It stores plaintext passwords, is injectable, and hands out admin on request — by design.


What changed vs. upstream VAmPI

Upstream (REST)

This port (MCP tool)

GET /

api_info

GET /createdb

create_db

GET /users/v1

get_all_users

GET /users/v1/_debug

debug_users

POST /users/v1/register

register_user

POST /users/v1/login

login

GET /me

me

GET /users/v1/{username}

get_user_by_name

PUT /users/v1/{username}/email

update_email

PUT /users/v1/{username}/password

update_password

DELETE /users/v1/{username}

delete_user

GET /books/v1

get_all_books

POST /books/v1

add_book

GET /books/v1/{book_title}

get_book_by_title

Auth model. MCP has no request headers, so the old Authorization: Bearer <jwt> becomes an explicit token argument on every authenticated tool. Log in with login, then pass the returned auth_token into me, update_email, update_password, delete_user, add_book and get_book_by_title.

Vulnerabilities preserved (with vulnerable=1)

  • Excessive data exposuredebug_users leaks passwords + admin flags (leaks even when hardened).

  • Mass assignmentregister_user(..., admin=true) grants yourself admin.

  • SQL injectionget_user_by_name concatenates the username into raw SQL.

  • User / password enumerationlogin returns distinct error messages.

  • BOLA (object-level auth)update_password changes any user's password; get_book_by_title reads any user's secret.

  • Broken JWT auth — HS256 signed with the hard-coded key random.

  • ReDoSupdate_email uses a catastrophic-backtracking regex.

Set vulnerable=0 to serve the hardened variant (parameterised queries, ownership checks, generic login errors). debug_users stays leaky regardless — that's bad practice, not a toggle.

Dropped (transport-specific, no MCP equivalent)

HTTP verb tampering, CORS misconfig, rate limiting, and OpenAPI spec-conformance testing don't exist in an MCP transport, so they're gone.


Related MCP server: MCP Goat

Seeded data

create_db (and first launch) seeds three users:

username

password

admin

name1

pass1

no

name2

pass2

no

admin

pass1

yes

Each user also gets one book with secret content.


Requirements

  • Python 3.10+ (this repo was verified on 3.12).

  • The macOS system python3 may be 3.9 — too old. Use Homebrew Python: brew install python@3.12.


Local install & run

cd ~/Downloads/vampi-mcp

# use a 3.10+ interpreter
python3.12 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e .          # installs deps + the `vampi-mcp` command

Run it (stdio — the transport local clients use):

.venv/bin/vampi-mcp
# equivalent: .venv/bin/python -m vampi_mcp

It will create and seed vampi_mcp/database.db on first launch. A stdio server has no interactive output — it waits for an MCP client to speak to it over stdin/stdout. To point a client at it, use the absolute path ~/Downloads/vampi-mcp/.venv/bin/vampi-mcp (see the client sections below).

Configuration (env vars)

Variable

Default

Meaning

vulnerable

1

1 vulnerable, 0 hardened

tokentimetolive

60

JWT lifetime in seconds

MCP_TRANSPORT

stdio

stdio or streamable-http

MCP_HOST

0.0.0.0

bind host (HTTP transport)

MCP_PORT

5000

bind port (HTTP transport)

VAMPI_DB_PATH

in-package

path to the sqlite file

Smoke test

.venv/bin/python - <<'PY'
import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
async def main():
    p = StdioServerParameters(command=".venv/bin/vampi-mcp", args=[])
    async with stdio_client(p) as (r,w):
        async with ClientSession(r,w) as s:
            await s.initialize()
            print([t.name for t in (await s.list_tools()).tools])
asyncio.run(main())
PY

Docker (networked / streamable-HTTP transport)

The container runs the server over streamable-HTTP so it's reachable on the network. The MCP endpoint is http://<host>:5000/mcp/ (note the trailing slash).

cd ~/Downloads/vampi-mcp
docker compose up --build -d
docker compose logs -f          # watch it start
docker compose down             # stop

plain docker

cd ~/Downloads/vampi-mcp
docker build -t vampi-mcp:latest .
docker run -d --name vampi-mcp -p 5000:5000 \
  -e vulnerable=1 -e tokentimetolive=60 \
  -v vampi-data:/data vampi-mcp:latest

Verify it's up:

curl -s -X POST http://localhost:5000/mcp/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}'

You should get an SSE event: message line with a JSON-RPC result.


Add it to opencode

opencode reads ~/.config/opencode/opencode.json (global) or an opencode.json in your project root. Add an mcp entry.

Local (stdio) — no Docker needed

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "vampi": {
      "type": "local",
      "command": [
        "/Users/mcropsey/Downloads/vampi-mcp/.venv/bin/vampi-mcp"
      ],
      "enabled": true,
      "environment": {
        "vulnerable": "1",
        "tokentimetolive": "60"
      }
    }
  }
}

Remote (the Docker container over HTTP)

{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "vampi-http": {
      "type": "remote",
      "url": "http://localhost:5000/mcp/",
      "enabled": true
    }
  }
}

Restart opencode. Ask it to "list VAmPI tools" or "call api_info" to confirm.


Add it to Claude Desktop

Claude Desktop launches local MCP servers over stdio. Edit its config file:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json

  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Add (or merge) an mcpServers entry. Use the absolute path to the console script inside the venv:

{
  "mcpServers": {
    "vampi-mcp": {
      "command": "/Users/mcropsey/Downloads/vampi-mcp/.venv/bin/vampi-mcp",
      "args": [],
      "env": {
        "vulnerable": "1",
        "tokentimetolive": "60"
      }
    }
  }
}

Then fully quit and reopen Claude Desktop. The VAmPI tools appear under the tools (🔌 / hammer) menu. Try: "Use api_info, then log in as admin/pass1 and call debug_users."

Claude Desktop connects only over stdio, so it uses the local venv command, not the Docker HTTP endpoint. Run the Docker version for clients that support remote/streamable-HTTP MCP (like opencode's remote type).


Test prompts

See TEST_PROMPTS.md for ready-to-paste natural-language prompts (for Claude Desktop / any MCP client) that exercise every tool — a happy-path walkthrough, a per-endpoint reference mapped to the old REST calls, and the full set of vulnerability exercises.

Example attack walk-through (any client)

  1. api_info → confirms vulnerable: 1.

  2. debug_users → dumps all passwords (excessive data exposure).

  3. register_user(username="evil", password="pw", email="e@x.com", admin=true) → mass assignment.

  4. login(username="evil", password="pw") → grab auth_token.

  5. get_user_by_name(username="name1' OR '1'='1") → SQL injection.

  6. update_password(token=<evil token>, username="admin", password="pwned") → BOLA: hijack admin.

  7. get_book_by_title(token=<evil token>, book_title="<someone else's book>") → BOLA: read another user's secret.


Project layout

vampi-mcp/
├── vampi_mcp/
│   ├── __init__.py
│   ├── __main__.py      # python -m vampi_mcp
│   ├── config.py        # env-driven config (vulnerable, TTL, transport)
│   ├── models.py        # User/Book models + sqlite + SQLi/JWT logic
│   └── server.py        # FastMCP server: all 14 tools
├── requirements.txt
├── pyproject.toml       # installs the `vampi-mcp` command
├── Dockerfile
├── docker-compose.yaml
├── TEST_PROMPTS.md      # ready-to-paste prompts for every tool
└── README.md

Credit: original VAmPI by erev0s.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    An intentionally vulnerable case management system designed for security training that provides MCP tools for SOC analyst workflows like case handling and indicator search. It enables users to explore and demonstrate common security weaknesses such as prompt injection, SQL injection, and broken authorization in an MCP-integrated environment.
    22
    3
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    An intentionally vulnerable MCP server designed as a live demo target for the MCP Trust security scanner. It contains deliberate insecure patterns to demonstrate scanning capabilities.
    -
  • F
    license
    Not graded
    quality
    B
    maintenance
    A deliberately vulnerable MCP server demonstrating API key exposure through hardcoding, plaintext logging, and returning secrets to the model, part of the OWASP MCP Top 10 security lab.
    -

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mcropsey/vampi-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server