dangerous-skills-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@dangerous-skills-mcplist all skills"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
skills-over-mcp-server
A TypeScript MCP server that delivers a vendored "dangerous skills" corpus over MCP for security-research testing, implementing the delivery model from SEP-2640 (the Skills extension). It also ships a net-new set of crafted, SEP-violating adversarial fixtures (the research contribution) under a separate profile, so the faithful corpus stays clean.
Part of the skills-over-mcp research topic in research-hub. The faithful corpus is forked from gricha/dangerous-skills (MIT © 2026 Greg Pstrucha). Every payload here — corpus and adversarial — is benign: it writes a marker file or prints a canary string; nothing performs real harm. The HTTP transport is localhost-only by default but configurable for remote hosting — it's deployed as a public HF Space (see Status).
Status — live, and consumable by a real host
The server is deployed as a public HF Docker Space: https://olaservo-dangerous-skills-mcp.hf.space/mcp (repo olaservo/dangerous-skills-mcp; in the adversarial profile it serves all 32 skills — 12 faithful + 20 adversarial fixtures). It's a free CPU Space, so the first request after idle is a slow cold start — retry once. To run or redeploy it, see hf-space/README.md. (Redeploy the Space to pick up new fixtures / the archive-only index shape.)
A real host consumes this over MCP today: fast-agent implements the registry/install half of SEP-2640 (src/fast_agent/skills/mcp_registry.py). It reads skill://index.json, and /skills add downloads + SHA-256-verifies + unpacks a skill (with archive-safety hardening — rejects ../absolute/symlink/decompression-bomb) into its managed skills dir, where it then runs as a normal local skill. So the over-MCP loop is demonstrable end-to-end — install from this server → execute — and the adversarial fixtures can be scored against fast-agent's installer (blocked / installed-with-gap), not just asserted. fast-agent does not yet expose MCP skill resources live to the model, so the install-path MUSTs (archive safety, integrity scope, name-collision) are exercisable while the live model-facing ones (cross-origin reads, live divergence) are not.
Point fast-agent at it:
fast-agent go --shell
/mcp connect --name dsk https://olaservo-dangerous-skills-mcp.hf.space/mcp
/skills registry dsk
/skills add check-licenses # downloads + verifies + installs over MCP, then run itTwo validations remain useful regardless: the smoke client (src/smoke-client.ts) exercises the server end-to-end (resources/list / resources/read / digest-verify / resources/directory/read / archive read), and its --adversarial mode prints the documented oracle — what a SEP-conformant host MUST do per fixture (reject / gate / re-prompt), tagged with the SEP-2640 clause and reviewer (Den Delimarsky) item.
Related MCP server: MCP Server TypeScript Template
Delivery model (SEP-2640)
Skills are addressed under a custom URI scheme skill://.
skill://<skill-path>/<file-path>— the final<skill-path>segment equalsfrontmatter.name.SKILL.mdis alwaysskill://<name>/SKILL.md; supporting files are siblings, e.g.skill://<name>/scripts/foo.sh.skill://<name>.tar.gzandskill://<name>.zip— per-skill archives (blob resources).skill://index.json— the catalog.
The server speaks three things on top of standard MCP resources:
resources/list— enumerates the index, eachSKILL.md, each supporting file, and each archive. Per SEP Resource Metadata, eachSKILL.mdentry carries frontmatter-derivedname/descriptionand the full frontmatter under aio.modelcontextprotocol.skills/frontmatter_metakey (theio.modelcontextprotocol.skills/prefix is SEP-reserved; thefrontmatterkey itself is server-defined); static resources also reportsize(the base-MCPResource.size).resources/read— text files come back ascontents:[{uri,text,mimeType}]; binaries and archives come back as base64contents:[{uri,blob,mimeType}]. A skill is readable from its URI alone whether or not it appears in the index (SEP: a skill's URI is directly readable viaresources/readwhether or not it appears in any index — which is what lets a host satisfy the MUST to load a skill given only its URI).resources/directory/read— a custom JSON-RPC method (registered via a low-level handler with a Zod schema). Given a directory URI (mimeType: inode/directory, no trailing slash) it returns that directory's direct children only (non-recursive):{ resources: [{uri,name,mimeType}, ...] }, with subdirectories markedinode/directory. Unknown / non-directory URIs return JSON-RPC error -32602. AnextCursorpagination field is supported in the shape (a no-op today: all direct children fit in one page).
skill://index.json
{
"skills": [
{
"url": "skill://<name>/SKILL.md",
"digest": "sha256:<hex of raw SKILL.md bytes>",
"frontmatter": { "name": "...", "description": "...", "...": "all yaml fields" },
"archives": [
{ "url": "skill://<name>.tar.gz", "mimeType": "application/gzip", "digest": "sha256:<hex of archive bytes>" }
]
}
]
}A skill-entry
digestis the sha256 of the rawSKILL.mdbytes.An archive
digestis the sha256 of the archive bytes.frontmatteralways includesname+description(plus every other YAML field present).Per SEP, every entry MUST include
url, a non-emptyarchives, or both — anddigestis present iffurlis. The server serves all three configurations: faithful skills as both; therefundsname-collision pair as archive-only (nourl/digest— its files are not individually addressable, so the host must unpack the archive to address them); andcross-server-readas url-only (noarchives).
Capability declaration
The initialize response advertises the Skills extension capability:
"capabilities": {
"resources": { "listChanged": false },
"extensions": { "io.modelcontextprotocol/skills": { "directoryRead": true } }
}This uses the installed SDK's ServerCapabilities.extensions record (constructor capabilities option). EXPLANATION.md files in the corpus are not served — they are corpus meta.
Archives
At startup each skill directory is packed into .tar.gz (via the tar package) and .zip (via archiver's ZipArchive) into an OS temp cache. SKILL.md sits at the archive root; entries are relative. Tar packing is deterministic (portable mode + fixed mtime) so the digest in index.json always matches the served bytes.
Adversarial fixtures (the research contribution)
These live in src/adversarial/ and are served only when the server runs with --adversarial (a.k.a. --profile adversarial). They use a separate namespace — names are prefixed adv- (the name-collision pair use refunds under distinct path prefixes) — so the faithful corpus is never contaminated. Every fixture is benign (marker/canary payloads) and localhost-only.
The fixture → SEP-clause → Den-item → required-host-action mapping lives once in src/adversarial/catalog.ts — the single source the smoke client prints — and, PR-facing, in findings.md. The table below is a quick index: what each fixture serves, and whether the clause is a current SEP MUST or a Den review proposal not yet in the spec text (a host that installs a Den-proposed case is evidence the WG should adopt the rule, not a SEP violation). (18 distinct cases; 20 served fixtures, since name-collision and cumulative-budget are each a pair.)
Fixture | What it serves | SEP status | Den item |
|
| current MUST | C1 |
| symlink | current MUST | C1 |
| hard-link | current MUST | C1 |
| tiny | current MUST | C1 |
|
| Den-proposed | C1 |
|
| Den-proposed | C1 |
| two ~30 MiB archives from one server (≈60 MiB aggregate, over a 50 MiB budget) | Den-proposed | C1 |
|
| current MUST | C1 |
|
| Den-proposed | C1 |
| ZIP with | current MUST | C1 |
| ZIP symlink | current MUST | C1 |
| index.json | current MUST¹ | B2 |
| url-only skill; supporting script fetched undigested (only | Den-proposed | B1 |
| both delivery; archive copy digest-verified, live | Den-proposed | B2 |
|
| Den-proposed | D5 |
| same URI returns different | current MUST + D7 | D7 |
| two archive-only skills both | Den-proposed (A2) | A2 |
| url-only skill whose | Den-proposed | D4 |
¹ The index-vs-SKILL.md divergence violates a current MUST ("the frontmatter object MUST be identical in content to the frontmatter of the SKILL.md it describes"); Den B2(1)'s field-by-field host re-verification is the proposed escalation.
Running it
Requires Node 20+ and pnpm. Run via tsx (no build step).
pnpm install
pnpm typecheck # tsc --noEmit
# Serve (stdio — the validated default transport)
pnpm serve:stdio # faithful corpus only
pnpm serve:stdio -- --adversarial # + adversarial fixtures
# Serve (HTTP — 127.0.0.1:3940/mcp by default; set HOST/PORT/ALLOWED_HOSTS for remote)
pnpm serve:http
pnpm serve:http -- --adversarial
# Smoke client (spawns the stdio server itself — self-contained)
pnpm smoke # core conformance checks, PASS/FAIL per check
pnpm smoke -- --adversarial # also prints the per-fixture documented oracle
# Smoke client over HTTP (point at a running `pnpm serve:http`)
pnpm smoke:httpThe smoke client prints PASS/FAIL per check and exits non-zero if any check fails. In --adversarial mode it additionally fetches each fixture and prints the SEP clause + Den item + the required host action, and demonstrates the content-rotation TOCTOU live (read #1 vs read #2 bytes differ).
Configuration
SKILLS_ROOT— override the corpus root (defaults to../third_party/dangerous-skills/skills; the HF image sets/app/skills).HOST/PORT— HTTP bind address (default127.0.0.1:3940; the HF Space uses0.0.0.0:7860).ALLOWED_HOSTS— comma-separated extraHostheader values to accept (e.g. an HF Space host).MCP_DISABLE_DNS_REBINDING_PROTECTION=1— relax the localhost host check for a remote deploy behind a proxy (defaults ON / localhost posture; a remote server's real access control is the Space's public/private setting).SERVE_PROFILE— used by the HF image'sCMD(--adversarialto serve fixtures, empty for faithful only).
File layout
src/
corpus.ts # load skills from disk, parse frontmatter, compute skill:// URIs + digests
index-json.ts # build the skill://index.json document (+ adversarial frontmatter override)
archives.ts # pack .tar.gz / .zip; low-level raw-tar builders for malformed fixtures
resources.ts # the resource registry: list / read / directory tree / read counters
server.ts # low-level MCP Server: capabilities + resources/list, read, directory/read
stdio.ts # stdio entry point
http.ts # streamable-HTTP entry point (localhost by default; remote-capable, stateless, shared registry)
adversarial/
index.ts # all adversarial fixture builders, each tagged with SEP clause + Den item
smoke-client.ts # MCP client: core checks + documented adversarial oracle
hf-space/ # HF Docker Space deploy bundle (Dockerfile, Space README, assemble.ps1)Safety discipline
Benign only. Corpus payloads (and every adversarial payload) write marker files or print canary strings. No fixture performs real harm. Do not add real-harm payloads.
Localhost-only by default. The HTTP transport binds
127.0.0.1+ DNS-rebinding protection by default; remote hosting (the HF Space) opts out explicitly viaHOST/ALLOWED_HOSTS/MCP_DISABLE_DNS_REBINDING_PROTECTION. A public deploy's real access control is the Space's public/private setting; payloads stay benign either way.Faithful corpus is read-only.
frontmatter.nameandSKILL.mdbytes are served verbatim;EXPLANATION.md(corpus meta) is excluded. Adversarial cases never touch the faithful set — they are gated behind--adversarialand namespacedadv-/ path-prefixed.
SDK
Built on the npm-published @modelcontextprotocol/sdk v1.29.0. Imports used: @modelcontextprotocol/sdk/server/index.js (low-level Server), .../server/stdio.js, .../server/streamableHttp.js, .../types.js (request schemas, McpError, ErrorCode), and the client equivalents under .../client/.
Attribution & license
Faithful corpus: MIT © 2026 Greg Pstrucha — gricha/dangerous-skills. This server and the adversarial fixtures are released under MIT as well (see LICENSE). The corpus keeps its own MIT notice under third_party/dangerous-skills/LICENSE.
This server cannot be installed
Maintenance
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/olaservo/dangerous-skills-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server