Skip to content

attack surface mcp server AGENTS.md changelog

io.github.cyanheads/attack-surface-mcp-server

4 changes recorded. MCP Harbor re-reads this listing from the official registry every six hours and asks the live server what it exposes, and keeps what changed between one look and the next.

  • AGENTS.md +7 −7

    https://raw.githubusercontent.com/cyanheads/attack-surface-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.5.0− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.13.18`− **Engines:** Bun ≥1.4.0, Node ≥24.0.0− This is a defensive external-attack-surface-mapping (EASM) server. It is **passive and non-intrusive by mandate**: it reads public records (Certificate Transparency, DNS, RDAP/WHOIS) and each target's *own* published surface (one TLS handshake, one HTTP GET per host). It does **not** port-scan, exploit, brute-force, fuzz, or probe for vulnerabilities — that capability is excluded from the surface by design, never gated behind a flag. Output is descriptive (what exists, what the posture is), neve− - **The SSRF guard is the security spine.** Every outbound connection to a user-supplied target (DNS, TLS, HTTP) must pass `assertSafeDomain` / `assertSafeUrl` / `assertSafeResolverIp` (`src/utils/ssrf-guard.ts`) before connecting. It rejects private, loopback, link-local, cloud-metadata, and reserved ranges. Never route a new outbound path around it.− | `bun run lint:packaging` | Packaging surface checks — `server.json`/`manifest.json` env-var parity (run by devcheck) |− `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored local files — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. Root `skills/` ships on purpose. The pack step is followed by+ **Version:** 0.6.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.22`+ **Engines:** Bun ≥1.4.4, Node ≥24.0.0+ This is a defensive external-attack-surface-mapping (EASM) server. It is **passive and non-intrusive by mandate**: it reads public records (Certificate Transparency, DNS, RDAP/WHOIS) and each target's *own* published surface (TLS handshakes and HTTP GETs). It does **not** port-scan, exploit, brute-force, fuzz, or probe for vulnerabilities — that capability is excluded from the surface by design, never gated behind a flag. Output is descriptive (what exists, what the posture is), never an exploit+ - **The SSRF guard is the security spine.** TLS, HTTP and registration connections pass `assertSafeDomain` / `assertSafeUrl` / `resolveSafeHost` (`src/utils/ssrf-guard.ts`) before connecting. Guarded hostnames require public A/AAAA evidence from the first configured resolver and validated system answers; resolved addresses never appear in rejection text. DNS-only evidence uses explicit configured resolvers validated by `assertSafeResolverIp`, without connecting to the queried host. Private DNS a+ | `bun run lint:packaging` | Check environment variable parity, `.mcpbignore` coverage, plugin manifests, and published skills (run by devcheck) |+ `bun run bundle` produces a stdio `.mcpb` bundle for Claude Desktop. Put every repository-only path in `.mcpbignore` with a root-anchored pattern such as `/docs/`. Root `skills/` ships with the server. After packing, `scripts/clean-mcpb.ts` prunes development dependencies, removes agent docs at each installed dependency's root and dependency `SKILL.md` files at any depth, and retains runtime modules such as the SDK's `dist/ext/skills/`. It also removes platform-specific bindings, node-gyp build 

  • AGENTS.md +12 −5

    https://raw.githubusercontent.com/cyanheads/attack-surface-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.4.0− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`− **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1− Skills are modular instructions in `framework-skills/` at the project root. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. The directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand these development skills to every agent that installs it. Keep `skills/` free for skills meant f− `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. The pack step is followed by `scripts/clean-mcpb.ts`, which prunes dev dependencies (`mcpb clean`) and strips two classes of `node_modules/**` content that root-anchored `.mcpbignore` patterns cannot reach: dependency-shipped agent docs (`framework-skills/`, `skills/`, `.claude/`, `.agents/`, `SKILL.md`) and platform-specific native bindings, which would otherwise lock the bundle to the platform it was + **Version:** 0.5.0+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `0.13.18`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.4.0+ Two skill trees live at the project root, written for different readers:+ | Tree | For | Written by |+ |:-----|:----|:-----------|+ | `framework-skills/` | The agent developing this server — framework skills (`metadata.audience: external`) plus any development-time skills of the server's own | `init` and the `maintenance` sync (framework skills); you (your own) |+ | `skills/` | The agents that install the server and call its tools — tool chains, identifier sources, gates | You, deliberately, with the `add-skill` skill. Absent by default; usually none is needed |+ Development skills are modular instructions. Read them directly when a task matches — e.g., `framework-skills/add-tool/SKILL.md` when adding a tool. `bun run list-skills` prints the full registry. Their directory is deliberately not `skills/`: Claude Code and Codex auto-load a plugin's root `skills/`, so a server that ships `.claude-plugin/` or `.codex-plugin/` would hand them to every agent that installs it.+ A published skill in root `skills/` reaches plugin installs and the `.mcpb` bundle as-is, ships in the npm package and the Docker image, and reaches MCP clients when the server opts in with `createApp({ skills: 'skills' })`. `lint:packaging` validates the tree.+ | `add-skill` | Author a skill the server publishes in root `skills/` for the agents that use it — when one earns its place, structure, validation, delivery |+ `npm run bundle` produces a `.mcpb` extension bundle for one-click install in Claude Desktop. `mcpb pack` reads `.mcpbignore`, not `.gitignore`, so every root path the bundle must not ship — source, tests, docs, caches, logs, agent docs, plugin manifests, gitignored local files — needs a root-anchored line there (`/docs/`, never `docs/`, which also strips `node_modules/*/docs/`); `lint:packaging` names each missing or unanchored line. Root `skills/` ships on purpose. The pack step is followed by

  • AGENTS.md +13 −9

    https://raw.githubusercontent.com/cyanheads/attack-surface-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.3.2− **Zod:** ^4.6.5−     hosts: z.array(z.string().describe('Hostname or IP to inspect.')).min(1).max(50)−       .describe('Hosts to inspect (1–50).'),−   output: z.object({ results: z.array(/* TlsResultSchema */ z.unknown()).describe('Per-host results.') }),−     const results = await getTlsService().inspectHosts(input.hosts, input.port);−     return { results };−   format: (result) => [{ type: 'text', text: renderTlsResults(result.results) }],−   maxSubdomains: z.coerce.number().int().min(1).max(5000).default(200).describe('Max subdomains resolved per map_domain run.'),+ **Version:** 0.4.0+ **Zod:** ^4.6.7+     hosts: z.array(z.string().describe('Hostname or IP to inspect.')).min(1).max(1000)+       .describe('Hosts to inspect (1–1000); process the first 50 and resubmit remainingHosts.'),+   output: z.object({+     results: z.array(/* TlsResultSchema */ z.unknown()).describe('Per-host results.'),+     processedCount: z.number().describe('Number of hosts processed, at most 50.'),+     remainingHosts: z.array(z.string()).describe('Exact unprocessed suffix; resubmit to continue.'),+   }),+     const results = await getTlsService().inspectHosts(input.hosts.slice(0, 50), input.port);+     return { results, processedCount: Math.min(input.hosts.length, 50), remainingHosts: input.hosts.slice(50) };+   format: (result) => [{ type: 'text', text: `Processed ${result.processedCount}; remainingHosts=${JSON.stringify(result.remainingHosts)}\n${renderTlsResults(result.results)}` }],+   maxSubdomains: z.coerce.number().int().min(1).max(5000).default(200).describe('Map/snapshot resolution cap; enumeration resolves min(this value, 50, requested limit) candidates per page.'),

  • AGENTS.md +6 −5

    https://raw.githubusercontent.com/cyanheads/attack-surface-mcp-server/HEAD/AGENTS.md

    − **Version:** 0.2.3− **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.10`− **MCP SDK:** `@modelcontextprotocol/server` ^2.1.0− **`ATTACKSURFACE_ALLOW_PRIVATE_TARGETS` is deliberately *not* in this schema** — the SSRF guard (`src/utils/ssrf-guard.ts`) reads it directly from `process.env`, since the guard is a shared util consumed by the dns/tls/http services rather than a service with config access. Setting it `true` disables all SSRF checks (local/trusted internal-network assessment only).− **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the changelog entry plus a gates section); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the branch, and publishes. A caller's bri+ **Version:** 0.3.2+ **Framework:** [@cyanheads/mcp-ts-core](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) `^0.13.15`+ **MCP SDK:** `@modelcontextprotocol/server` ^2.3.1+ - **Registration authority and literal evidence.** CIDRs require one decimal prefix in the family range; zone identifiers are rejected. RDAP IP paths preserve literal delimiters. WHOIS parses only recognizable authoritative referral responses, with typed `no_match` for explicit negatives and `source_unavailable` for unavailable/unsupported responses. Normalize allocation ranges to exact CIDRs while retaining invalid text in `rawWhois`. Registration/HTTP/TLS/DNS/Shodan formatters render upstream + **`ATTACKSURFACE_ALLOW_PRIVATE_TARGETS` is deliberately *not* in this schema** — the SSRF guard (`src/utils/ssrf-guard.ts`) reads it directly from `process.env`, since the guard is a shared util consumed by the dns/tls/http services rather than a service with config access. Setting it `true` permits non-public target ranges for trusted internal-network assessment. URL parsing and HTTP(S) scheme checks remain enforced; IPv6 range checks use parsed numeric addresses, including recognized IPv4 embe+ **Every release goes through a release PR, straight-through** — `git-wrapup`'s "Release PR mode", mode `straight-through`. One run: `git-wrapup` lands the commit stack on `release/<version>`, pushes it, and opens the PR (title = the release commit subject, body = the release digest: theme line, `## Changes`, `## Gates`, changelog link last); `release-and-publish` then fast-forwards `main` locally with `git merge --ff-only`, creates the tag on `main`'s tip, pushes `main` and the tag, deletes the