Skip to main content
Drop MCP Logo

Drop MCP

Drop a skills/ and prompts/ folder, get a FastMCP server — with a browseable catalog — in one line.

import dropmcp

dropmcp.run(skills="skills", prompts="prompts")

dropmcp is the reusable, repo-agnostic engine behind several internal skills/prompts MCP servers, extracted as a standalone library.

Features

  • Browseable skill catalog — a built-in web UI to search, filter, and preview every skill and prompt, with copy-paste install snippets for each MCP client.
  • Zero-boilerplate server — point it at a skills/ and prompts/ folder and get a tested FastMCP server over streamable-HTTP.
  • Filesystem as source of truth — skills and prompts are just markdown with YAML frontmatter; no registry to drift out of sync.
  • Observability built in — per-invocation structured logs plus optional OpenTelemetry metrics and traces.
  • Agent feedback loop — a record_feedback tool and triage UI for when agents get corrected.
  • Repository feedback backlog — an optional record_repo_feedback tool and triage UI for flaky tests, CI-only checks, setup gaps, and other repo friction.
  • Benchmarks page — an optional models × skills matrix of the latest E2E eval results, shown only to identified users.
  • Per-user subscriptions — optional opt-in so each user's agent sees only a curated subset of the catalog.
Drop MCP browseable skill catalog

Why dropmcp?

Skills and prompts are just markdown — anyone can write them, and there are plenty of ways to get them in front of an agent: copy them into .cursor/skills, ship an IDE plugin, sync a folder, paste them into context. The trouble is that every one of those is tied to a single tool, has no central updates, no way to scope who sees what, and no signal about what actually gets used. Spread across many people, repos, and editors, that fragments fast — the same skill forked five ways with no canonical copy, and skills that work in one agent but not the next.

Serving skills over MCP solves the delivery problem generically: one server is reachable from any MCP client (Cursor, Claude Code, CI, whatever comes next), the filesystem stays the single source of truth so there's no registry to drift, and because every skill call is one request, usage is observable for free. The catch is that standing up that server is the same boilerplate every time — frontmatter parsing, tool/prompt/resource registration, a browseable catalog, telemetry, hosting.

dropmcp is that boilerplate, extracted and built around Fast MCP. Point it at a skills/ and prompts/ folder and you get a tested, observable MCP server, so you run one focused server per audience instead of rebuilding the engine each time. Authors maintain content, not infrastructure.

Install

pip install dropmcp

Optional OpenTelemetry export:

pip install "dropmcp[otel]"

Quick start

Lay out your content:

skills/
  my-skill/
    SKILL.md         # YAML frontmatter: name, category, description
    reference.md     # optional supporting files -> resource links
prompts/
  my-prompt/
    PROMPT.md        # YAML frontmatter: name, description, arguments
    assets/          # optional assets -> prompt://my-prompt/assets/<file>

Then serve it over streamable-HTTP:

import dropmcp

dropmcp.run(skills="skills", prompts="prompts")              # binds 127.0.0.1:8000
dropmcp.run(skills="skills", prompts="prompts", host="0.0.0.0", port=8000)

dropmcp is a hosted server — it exists to share skills and prompts with remote MCP clients, so it serves over streamable-HTTP only (no local stdio). The catalog UI is at http://<host>:<port>/ and the MCP endpoint at /mcp.

Need to customise the server before it runs? Use the factory:

mcp = dropmcp.create_server(skills="skills", prompts="prompts")
# add your own routes / middleware ...
mcp.run(transport="streamable-http", host="0.0.0.0", port=8000)

A runnable example lives in examples/.

Scaffold a new server (copier)

Generate a ready-to-run project from the bundled template:

pip install copier
copier copy gh:agoda-com/dropmcp//template my-skills-mcp
cd my-skills-mcp
pip install -r requirements.txt
python server.py

The template asks for a project name and whether to include Promptfoo eval scaffolding under tests/.

Configuration

Every option can be passed as a keyword argument, set via a DROPMCP_* environment variable, or left to its default (kwargs win, then env, then default).

kwarg env default purpose
skills DROPMCP_SKILLS skills skills directory
prompts DROPMCP_PROMPTS prompts prompts directory
name DROPMCP_NAME dropmcp server name shown to clients and OTEL service name
website_url DROPMCP_WEBSITE_URL – server homepage URL
icon DROPMCP_ICON – path to an icon (svg/png)
instructions DROPMCP_INSTRUCTIONS auto INSTRUCTIONS.md template
host DROPMCP_HOST 127.0.0.1 bind host
port DROPMCP_PORT 8000 bind port
ui_enabled DROPMCP_UI true serve the catalog HTTP routes
feedback_enabled DROPMCP_FEEDBACK true enable the record_feedback tool, feedback HTTP routes, and always-on instructions
repo_feedback_enabled DROPMCP_REPO_FEEDBACK false enable the optional record_repo_feedback tool, repo feedback HTTP routes, and always-on instructions
user_subscriptions_enabled DROPMCP_USER_SUBSCRIPTIONS false per-user skill/prompt opt-in over MCP and subscription HTTP API
user_header DROPMCP_USER_HEADER X-User-Email HTTP header carrying the trusted caller identity
reload DROPMCP_RELOAD false re-scan skills/prompts on every request
database_url DROPMCP_DATABASE_URL sqlite:///<cwd>/dropmcp.db feedback database tables (SQLite file or Postgres URL)
eval_results_project DROPMCP_EVAL_RESULTS_PROJECT – project path for E2E eval results (enables /api/telemetry when a store is available)
eval_results_commit_sha DROPMCP_EVAL_RESULTS_COMMIT_SHA COMMIT_SHA file deployed commit to filter eval results
– DROPMCP_EVAL_RESULTS_SKILL_QUERY – SQL for one skill; required for the built-in MySQL store
– DROPMCP_EVAL_RESULTS_ALL_QUERY – SQL for every skill; required for the built-in MySQL store
benchmarks_enabled DROPMCP_BENCHMARKS false serve the /benchmarks page and /api/benchmarks (needs eval_results_project and benchmark_results_store)
benchmark_results_store – – object implementing get_latest_benchmark_results(project, lookback_days); required when benchmarks are enabled
catalog_defaults DROPMCP_CATALOG_DEFAULTS bundled SVGs category thumbnail fallbacks for the catalog grid

If an INSTRUCTIONS.md sits next to your content folders it is picked up automatically; otherwise a generic default ships with the package. The {{INSTRUCTION_SUMMARIES}} and {{PROMPT_SUMMARIES}} placeholders are filled from each item's instruction_summary frontmatter.

Hosting guide

dropmcp serves over streamable-HTTP for multi-client hosted deployments. Run your server.py:

DROPMCP_HOST=0.0.0.0 DROPMCP_PORT=8000 python server.py

The catalog UI is available at http://localhost:8000/, the health check endpoint at http://localhost:8000/health, and the MCP endpoint at http://localhost:8000/mcp. Point your remote MCP clients there.

Docker

A minimal Dockerfile for a hosted deployment:

FROM python:3.12-slim

WORKDIR /app

RUN pip install dropmcp

COPY skills/ skills/
COPY prompts/ prompts/
COPY INSTRUCTIONS.md .          # optional

ENV DROPMCP_HOST=0.0.0.0
ENV DROPMCP_PORT=8000
ENV DROPMCP_NAME="My Skills MCP"

EXPOSE 8000
CMD ["python", "-m", "dropmcp"]

Build and run:

docker build -t my-skills-mcp .
docker run -p 8000:8000 my-skills-mcp

Connect a remote MCP client to http://<host>:8000/mcp.

Environment-only deployment

All settings can be passed via environment variables and started with python -m dropmcp — no server.py needed:

export DROPMCP_SKILLS=/data/skills
export DROPMCP_PROMPTS=/data/prompts
export DROPMCP_HOST=0.0.0.0
export DROPMCP_PORT=8000
export DROPMCP_NAME="Acme Skills"
export DROPMCP_WEBSITE_URL="https://skills.example.com"

python -m dropmcp

OpenTelemetry

Install the OTEL extra and point at your collector:

pip install "dropmcp[otel]"
export OTEL_EXPORTER_OTLP_ENDPOINT="http://otel-collector:4318"
python -m dropmcp

Metrics and structured logs are emitted per skill invocation, prompt render, resource read, and MCP protocol event (initialize, tools/list). Metric names use dotted OTel style (for example skill.invocations, skill.invocation.duration) with delta temporality for histograms. The OTEL service name defaults to DROPMCP_NAME; override with OTEL_SERVICE_NAME if needed.

Telemetry preserves richer self-reported MCP metadata in structured logs, including initialize.params.clientInfo, protocol version, capability summary, selected request _meta keys (agent, ide, team, repo, environment, launcher, launcher_version, trace_id), session/transport details, HTTP fallback headers, operation names, and sanitized error context. These values are for observability only and are not security signals.

Metric attributes are deliberately bounded: client, client_version, client_source, transport, team, environment, operation_kind, outcome, and error.type, plus the relevant local operation name such as skill, prompt, or resource. Unknown values roll up to other, missing values roll up to unknown, and request _meta values are allowlisted, truncated, and scrubbed before logging or metric attribution. Set DROPMCP_TELEMETRY_TEAM_BUCKETS=supply,platform,... to control which declared team names are allowed as metric buckets.

When OTEL_EXPORTER_OTLP_ENDPOINT is unset, OpenTelemetry export is a no-op — no extra imports, no export overhead — but structured per-invocation logs still go to the console.

Agent feedback

dropmcp includes a built-in feedback loop for when agents get corrected and for reusable work agents discover after invoking skills:

  • record_feedback MCP tool — agents write structured feedback (no external Slack/GitLab wiring).
  • SQLite by default — a dropmcp.db file is created next to your content folders on first run.
  • Postgres override — set DROPMCP_DATABASE_URL=postgresql://user:pass@host/db for durable hosted storage.
  • Feedback UI — browse, search, filter, and triage at /feedback in the catalog SPA (GET/PATCH /api/feedback).

Feedback rows include feedback_type (correction by default, or agent_work), skill_name for the skill that was in use when feedback is about a specific skill, and optional structured details. agent_work entries can include reusable scripts or procedural artifacts under details.artifacts; script content is stored as JSON text and shown in an expandable UI panel.

SQLite auto-creates the feedback table and lightly adds missing skill_name, feedback_type, and details columns for existing local databases. Postgres deployments must ship the equivalent SyncDB migration for any missing columns:

ALTER TABLE feedback
  ADD COLUMN IF NOT EXISTS skill_name text,
  ADD COLUMN IF NOT EXISTS feedback_type text NOT NULL DEFAULT 'correction',
  ADD COLUMN IF NOT EXISTS details text;

COMMENT ON COLUMN feedback.skill_name IS 'Skill that was invoked or active when the feedback was produced; null when feedback is not about a specific skill.';
COMMENT ON COLUMN feedback.feedback_type IS 'Feedback category: correction for user corrections or agent_work for reusable work created after invoking a skill.';
COMMENT ON COLUMN feedback.details IS 'Optional JSON-encoded supporting material for agent_work feedback, such as reusable artifacts.';

Privacy guardrails: no verbatim user prompts, code, secrets, or PII. When feedback is enabled, dropmcp injects always-on guidance into the server instructions describing when and how agents should call record_feedback — no separate skill to install. Disable the whole feature (tool, HTTP routes, and instructions) with DROPMCP_FEEDBACK=false.

In containers, mount a volume over the SQLite file (or use Postgres) or feedback is lost when the pod restarts.

Repository feedback

Set DROPMCP_REPO_FEEDBACK=true (or repo_feedback_enabled=True) to enable a second feedback channel for repository friction. It is disabled by default.

  • record_repo_feedback MCP tool — agents report repo-caused friction such as CI-only tests, flaky tests, warning noise, slow feedback loops, missing setup docs, dependency issues, and missing scripts.
  • Separate storage — rows are written to repo_feedback in the same SQLite or Postgres database configured by DROPMCP_DATABASE_URL.
  • Deduplication — open rows with the same normalized repo, category, and summary are collapsed by fingerprint and occurrence_count is incremented. Closed rows (actioned, wontfix) do not absorb new reports.
  • Repo feedback UI/API — browse and triage at /repo-feedback, backed by GET /api/repo-feedback, GET /api/repo-feedback/{id}, and PATCH /api/repo-feedback/{id}.

Categories are fixed: tests_require_ci, flaky_test, build_warnings, lint_noise, slow_feedback, local_setup, docs_gap, dependency_issue, tooling_gap, and other. Status values are new, triaged, actioned, and wontfix.

SQLite auto-creates the repo_feedback table and lightly backfills missing columns for existing local databases. Hosted Postgres deployments must ship the equivalent SyncDB migration. The core table shape is:

CREATE TABLE repo_feedback (
  id text PRIMARY KEY,
  created_at timestamptz NOT NULL,
  last_seen_at timestamptz NOT NULL,
  category text NOT NULL,
  repo text NOT NULL,
  summary text NOT NULL,
  impact text NOT NULL,
  suggested_fix text,
  model text NOT NULL,
  client text,
  details text,
  fingerprint text NOT NULL,
  occurrence_count integer NOT NULL DEFAULT 1,
  status text NOT NULL DEFAULT 'new',
  resolution_url text
);

CREATE INDEX repo_feedback_fingerprint_idx ON repo_feedback (fingerprint);
CREATE INDEX repo_feedback_repo_status_idx ON repo_feedback (repo, status);

The same privacy rule applies: no secrets, PII, customer data, proprietary code snippets, or verbatim prompts. Repo feedback instructions are injected only when the feature flag is enabled.

Trusted user identity

When dropmcp is deployed behind an authentication proxy, set the trusted caller identity header with DROPMCP_USER_HEADER or the user_header kwarg. The default header is X-User-Email.

The catalog HTTP API exposes that identity at GET /api/me:

{
  "email": "user@example.com",
  "authenticated": true
}

The catalog footer shows the signed-in identity when the header is present and stays unchanged for anonymous requests.

Per-user subscriptions

When DROPMCP_USER_SUBSCRIPTIONS=true, users can opt in to individual skills and prompts so their agent only sees a curated subset over MCP. The catalog UI still lists the full catalog; subscription checkboxes appear when the request includes the configured identity header (default X-User-Email, set upstream by your auth mesh).

  • Flag off — unchanged behaviour; everything is published to every caller.
  • Flag on, no identity header — MCP exposes everything; UI controls are disabled.
  • Flag on, identity present, first sighting — user is logged in user_seen, automatically subscribed to every catalog group, and directly subscribed to any ungrouped catalog items (MCP and HTTP). They can still opt out of individual items or whole groups afterwards.
  • HTTP API — GET/POST /api/subscriptions, DELETE /api/subscriptions/{type}/{name}, group routes POST/DELETE /api/subscriptions/group/{group}, and POST /api/subscriptions/groups to re-subscribe every catalog group.
  • group frontmatter — optional string on SKILL.md / PROMPT.md; surfaced in /catalog JSON and the catalog Group filter row. Group opt-ins are stored in user_group_subscription so new skills added to a followed group are included automatically; users can still opt out of individual items within a group via user_subscription_exclusion.
  • SQLite auto-creates user_subscription, user_group_subscription, user_subscription_exclusion, user_seen, and user_subscription_onboarding locally; Postgres consumers must ship a SyncDB migration (same caveat as feedback).

MCP clients cache tools/list / prompts/list — subscription changes take effect after the client re-lists (typically on reconnect).

E2E eval results (telemetry panel)

The catalog detail page includes an E2E Test Results panel (ported from skills-mcp) showing per-skill Promptfoo eval scores from your CI pipeline.

Eval results are pluggable — dropmcp ships the UI and HTTP routes, but the data source is optional so the library stays deployment-agnostic:

  • Pass an eval_results_store to create_server() (any object implementing get_results_for_skill / get_all_latest_results). Use this when the deployment owns the query, or

  • Install the MySQL extra and inject both queries. The skill query is bound as (project, skill_name, commit_sha, datadate); the all-results query is bound as (project, commit_sha, datadate). Rows must match EvalResult column order. Host, port, database, and credentials come from MYSQL_HOST, MYSQL_PORT, MYSQL_DATABASE, MYSQL_USER, and MYSQL_PASSWORD (no defaults):

    pip install "dropmcp[mysql]"
    export DROPMCP_EVAL_RESULTS_PROJECT="group/project"
    export DROPMCP_EVAL_RESULTS_COMMIT_SHA="$(cat COMMIT_SHA)"
    export DROPMCP_EVAL_RESULTS_SKILL_QUERY="SELECT ... WHERE project = %s AND testname LIKE CONCAT(%s, '/%') AND commitsha = %s AND datadate >= %s"
    export DROPMCP_EVAL_RESULTS_ALL_QUERY="SELECT ... WHERE project = %s AND commitsha = %s AND datadate >= %s"
    export MYSQL_HOST="mysql.example.com"
    export MYSQL_PORT="3306"
    export MYSQL_DATABASE="your_database"
    

When no store is configured the panel renders an empty state; routes are not registered. Setting only DROPMCP_EVAL_RESULTS_PROJECT does not open a connection.

Benchmarks page

Set DROPMCP_BENCHMARKS=true to add a Benchmarks page: a models × skills matrix of the latest E2E result per test and model on main over the last 60 days, with a per-test breakdown for each skill.

  • An Overall row gives each model's average score across every test it ran, its pass count, and how many tests it covered. An All models column gives each skill's average across models.

  • Latest shows the newest result. 60-day average shows the mean of every run in the window and colours a cell by whether that average meets the threshold. In the latest view, ▲/▼ mark how far the newest result sits from the window average, and expanded tests show a trend line of recent scores.

  • Columns are ranked by the selected average. Compact drops the detail lines, and a model picker (with an "All" and "Top 5" preset) hides columns for wide comparisons. The view lives in the URL (metric=history, sort=name, compact=1, models=a,b) so it can be shared. Overall figures always cover every model, whichever columns are shown.

  • It reports the project in DROPMCP_EVAL_RESULTS_PROJECT only; the API takes no project parameter.

  • The data source is always supplied by the deployment: pass an object with get_latest_benchmark_results(project, lookback_days) as benchmark_results_store. Enabling the page without a project or a valid store fails at startup. No query, host or credential defaults ship in the library.

  • With the mysql extra, MySQLBenchmarkResultsStore runs your SQL. The query is bound as (project, datadate) and must return columns in BenchmarkResult order: test_name, worker_model, passed, score, threshold, triggered_at, pipeline_id, commit_sha. Host, port, database and credentials come from the constructor or MYSQL_*. The datadate bound parameter is formatted with datadate_format (default %Y-%m-%d), so match it to your partition key. Query failures surface as an "unavailable" banner rather than an empty page.

    from dropmcp.eval_results_mysql import MySQLBenchmarkResultsStore
    
    dropmcp.create_server(
        eval_results_project="group/project",
        benchmarks_enabled=True,
        benchmark_results_store=MySQLBenchmarkResultsStore(
            query="SELECT ... WHERE project = %s AND datadate >= %s",
            datadate_format="%Y%m%d",
        ),
    )
    
  • /api/benchmarks answers 401 without the identity header (DROPMCP_USER_HEADER), and the header link only appears for identified users. dropmcp trusts that header as sent, so it is a usability gate rather than access control: put an authenticating proxy in front of the server and deny /benchmarks and /api/benchmarks to everyone else.

  • Responses carry Cache-Control: no-store, X-Content-Type-Options: nosniff and Referrer-Policy: no-referrer, and never include reasoning or error text.

Skill and prompt format

SKILL.md

---
name: my-skill
category: my-category
group: my-group
description: One-line description shown to the LLM as the tool description.
instruction_summary: Short phrase for the server-level INSTRUCTIONS.md bullet.
---

Full skill body here — this is what the LLM receives when it calls the tool.

PROMPT.md

---
name: my-prompt
description: Short description shown in the catalog.
instruction_summary: Short phrase for INSTRUCTIONS.md.
arguments:
  - name: who
    description: The person to greet.
    required: true
  - name: tone
    description: Greeting tone (optional).
    required: false
---

Write a {{tone}} greeting addressed to {{who}}.

Validate your content before starting the server with the bundled checker:

python -c "import sys; from dropmcp.validate import run_validation; sys.exit(run_validation('skills', 'prompts'))"

Releasing

Releases are cut by pushing a v* git tag. The CI workflow does the rest: on a tag it builds the catalog UI, builds the wheel + sdist, publishes to PyPI via trusted publishing (no API token needed), and creates a GitHub Release with auto-generated notes.

To ship a new version:

  1. Bump the version in both pyproject.toml (version) and src/dropmcp/__init__.py (__version__) — they must match, and the tag must match too. Use semver.

  2. Land the bump on main via a merged PR (CI runs tests + the UI build on the PR).

  3. Tag the merge commit and push the tag:

    git checkout main && git pull
    git tag v0.2.0
    git push origin v0.2.0
    
  4. Watch the publish-pypi job in Actions. When it's green, the new version is live on PyPI and a GitHub Release exists for the tag.

The tag must start with v (e.g. v0.2.0) — that prefix is what gates the publish job. Pushing to main without a tag only runs tests and builds the wheel artifact; it never publishes.

License

Apache-2.0.

Metadata

Release files for dropmcp 0.14.0

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

Source distribution (sdist)

Source distribution for dropmcp 0.14.0
File Size Uploaded
dropmcp-0.14.0.tar.gz 2.3 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for dropmcp 0.14.0
File Interpreter ABI Platform
dropmcp-0.14.0-py3-none-any.whl Python 3 none any Details

Total release size: 2.6 MB

Release files / dropmcp-0.14.0.tar.gz

Download URL dropmcp-0.14.0.tar.gz
Size 2.3 MB
Tags Source
SHA-256 checksum
How to use checksums
53f4a85baa84d883b4f83e535020bc458f4aa8f15a74ed4e12864b6a259e2898
BLAKE2b-256 checksum
How to use checksums
ea5f45b3e1b27c6f5402dbb94975ff96ebd4c7f5d5e77d605feec9a85f0e3cf9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.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 Oct 1, 2026.

Transparency log

Release files / dropmcp-0.14.0-py3-none-any.whl

Download URL dropmcp-0.14.0-py3-none-any.whl
Size 282.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5c8fdfca6b57fb1c12272471c37b07d4dd37799491512a6e4853c1ec8c967bf9
BLAKE2b-256 checksum
How to use checksums
9a118a97347a731098f6361fae13a8919a44c058d6dec8b393039eb14d5e94ae
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.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 Oct 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.14.0 This release

2 release files

0.13.0

2 release files

0.10.2

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

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