zipnative-mcp
MCP serverDev toolsZIP MCP server: create, inspect, verify, extract securely, modify without recompression. 13 tools
Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.
Connect ahel once, and every AI you use reads what you have installed.
From the project's README
As published by nizoka/zipnative-mcp in README.md.
MCP server for ZIP archives — deterministic creation, inspection and listing without extracting, random-access entry reads, secure-by-default extraction, one-call verification, forward scanning of truncated streams, incremental modification without recompression, CRC-32 and raw-DEFLATE inflation — 13 tools on the zipnative engine (zero-dependency, ISO/IEC 21320-1 conformance validated in CI), for Claude Desktop, Cursor, ChatGPT and any MCP client.
✨ Features
zipnative-mcp exposes 13 tools to any MCP host:
| Tool | What it does | Read-only |
|---|---|---|
inspect_zip | ONE-call archive report: sizes, entry / file / directory counts, Zip64, comment, compressed vs uncompressed totals, per-method counts, encrypted / symlink / data-descriptor / Zip64 / cp437 / duplicate / unsafe-name counts, date range, a determinism verdict (the structural one: epoch timestamps + canonical order + UTF-8 flags) and every engine diagnostic. Opens eagerly: every entry's real extent is checked up front — overlapping entries, entries reaching into the central directory or past EOF, Zip64 spoofing — and refused with their ZIP_* code (a method / CRC / size divergence between central and local headers is caught by verify_zip and on read, not here). check: […] + assert: {…} turn it into a CI gate. | ✓ |
list_zip_entries | Paged central-directory inventory, nothing decompressed — every entry as a full row (sizes, CRC-32, method, timestamp, flags, Unix mode, symlink, Zip64, offsets, extra fields, the sanitized path an extraction would use, raw name bytes). filter by names / prefix / glob, offset + limit (200 default, 2000 max). | ✓ |
read_zip_entry | ONE entry by name or index without extracting: decompressed content as base64 or UTF-8 text (CRC-verified), a byte range through the chunked stream, the raw compressed payload (mode: 'raw'), or a non-throwing integrity check (mode: 'verify'). | ✓ |
verify_zip | Deep verification in one call — the engine's verifyZip report verbatim: structure, every entry's CRC-32 / size / local-header agreement, encrypted entries honestly skipped — while an entry whose method has no codec here (anything but 0 store / 8 deflate) is reported failed, not skipped. Never isError for an archive problem: branch on ok and error.code. | ✓ |
extract_zip | Secure by default: zip-slip / device names, symlinks, duplicate paths, declared-size and ratio bombs, overlapping entries and central/local divergence refused with frozen ZIP_* codes; relaxations are explicit named inputs that skip, never emit. Inline files (includeData: false = dry run — it opens eagerly and refuses an overlapping, offset-into-CD or Zip64-spoofed archive before returning any plan) or streamed into the sandbox with resource links. | |
scan_zip_forward | Walk local headers in stream order with bounded memory — the only tool that works on a truncated download or a cut / unseekable stream that starts at a local header, and the only one whose result is NOT authoritative (trust: 'local-headers-only'). tolerateTruncation: true returns the partial inventory plus the error. It cannot skip an SFX / prepended prefix and refuses one (ZIP_SIGNATURE_MISMATCH): inspect_zip reports it (prependedData, ZIP_PREPENDED_DATA) and modify_zip mode: 'compact' drops it. | ✓ |
sanitize_entry_paths | The engine's single traversal gate over a list of names: the safe /-relative form each maps to, or null with the rule that fired (traversal, absolute, drive, UNC, NUL, ADS, device name). No archive needed. | ✓ |
create_zip | Write a ZIP from inline text / base64 / sandbox files (up to 100 000 entries — Zip64 auto-promotes past 65 535). Reproducible on one runtime by default (canonical order, DOS-epoch timestamps, UTF-8 names); compression.deterministic: true for identical bytes on every runtime — summary.deterministic is true ONLY then (a default call reports false with deflateTier: 'node-zlib'); store / deflate at archive or entry level; order: 'insertion' for EPUB / JAR; comments, Unix modes, extra fields; streamed sources; the worker pool (parallel, byte-identical); includeSha256 proofs in base64 and file mode. | |
modify_zip | add / replace / remove / rename / setComment without recompressing anything. mode: 'append' keeps the original bytes verbatim (removed content stays recoverable — data remanence, said loudly); mode: 'compact' rewrites canonically so it is truly gone. | |
compute_crc32 | The ZIP checksum (IEEE 802.3 CRC-32, the engine's slice-by-8) of text, base64 or a sandbox file streamed in 1 MiB chunks; seed chains chunks, expect compares. | ✓ |
inflate_raw | Raw DEFLATE (RFC 1951) through the engine's resumable inflater with a mandatory maxOutput bound: exact bytesConsumed, trailing bytes as leftover. Feed it read_zip_entry mode: 'raw'. | |
describe_engine | Offline preflight: versions, deflate tiers, runtime codecs and workers, the engine's default limits, the operator ceilings, every server cap, sandbox / cache state, the 39 error codes and 11 diagnostic codes, the deliberately unexposed engine exports. network is always 'none'. | ✓ |
draft_governance_issue | Draft a governance-compliant GitHub issue locally for a human to review and submit — never submits, no network, no GitHub write path. |
What every tool guarantees:
- 🔐 Secure by default — every engine guard is on; a relaxation is an explicit, named input (
rejectTraversal,rejectSymlinks,onDuplicate,limits) that skips, never emits an unsafe path or materialises a link. Overlaps, central/local divergence and Zip64 spoofing have no opt-out at all. - 🌐 No network, ever — the server has no network code path: no telemetry, no GitHub, no URL from any argument, no operator-configurable endpoint. The only filesystem boundary is
ZIPNATIVE_MCP_OUTPUT_DIR. - 🔁 Reproducible by default, deterministic on request —
create_zipemits canonical order, DOS-epoch timestamps and UTF-8 names unless you opt out, so the bytes are stable on one runtime;compression.deterministic: truepins the pure-TypeScript encoder for identical SHA-256 on every runtime and is the only setting under whichsummary.deterministicistrue;inspect_zip.determinismis the separate structural verdict (epoch timestamps required);parallelis byte-identical. - 🧭 Frozen
ZIP_*error codes, verbatim — the engine's 39-code vocabulary reaches you unchanged in_meta.error.code(with the entry name, the limit that fired, both CRCs, …), plus the wrapper's own 16 codes. Branch on the code, never on the message. - 🪙 Token-frugal projections — the read tools accept
verbosity: 'summary'andfields: […]; produced archives are delivered once as an embeddedresourceblock, never duplicated intostructuredContent. - 📏 ISO/IEC 21320-1 conformance gate — every archive the tools write is validated clause by clause by an engine-independent validator (veraZIP) on Linux and Windows in CI and again before publish.
All archive-producing tools support two output modes:
base64(default) — the archive is returned once as an embeddedresourcecontent block (adata:application/zip;base64,…URI);structuredContentcarries{ mode, sizeBytes, summary, diagnostics, diagnosticCounts }.file— the archive is streamed into a sandboxed directory configured viaZIPNATIVE_MCP_OUTPUT_DIR(≤ 4 GiB, never overwritten) and the result carries aresource_link. File I/O is disabled unless this variable is set; absolute paths, traversal, non-container extensions and NUL bytes are all rejected, and the real path of every file read and of every parent written must stay inside the sandbox (a planted symlink or junction isSECURITY_VIOLATIONon both sides). The same sandbox serveszipPath/sourcePathinputs, so acreate_zip → modify_zip → verify_zip → extract_zipchain never re-sends the bytes.
Token-frugal reads. The read tools (inspect_zip, list_zip_entries, read_zip_entry, verify_zip, scan_zip_forward, sanitize_entry_paths, describe_engine) and extract_zip accept two optional inputs:
verbosity: 'summary'— a compact scalar-only verdict (drops the rows and payloads). E.g.verify_zip→{ ok, error, entryCount, verifiedCount, failedCount, skippedCount, diagnosticCount };inspect_zipkeepsdeterministic,canonicalLayout,checksPassed.fields: ['a', 'b.c']— projects the structured result to named dot-paths; composes afterverbosity. Unmatched paths are omitted and reported in_meta.unmatchedFields(with_meta.availableFields).
Smallest "is this archive intact?" probe: { "zipBase64": "…", "verbosity": "summary", "fields": ["ok", "error", "failedCount"] } on verify_zip.
The server also ships seven MCP prompts — secure_extraction, reproducible_archive, incremental_update, forensic_scan, verify_before_trust, governance_contract, draft_issue_workflow — and exposes every file in the sandbox as a zipnative://output/{+path} resource.
Why zipnative?
zipnative-mcp inherits every guarantee of the underlying engine:
- Zero runtime dependencies in the engine — pure TypeScript, no native bindings, no
eval; one API across Node ≥ 22, browsers, Deno, Bun and Workers (this server adds only the MCP SDK and zod: three runtime dependencies in total). - Safe by default — path traversal, symlinks, duplicate names, decompression bombs, overlapping entries, parser-differential smuggling and Zip64 spoofing are refused, not guessed at; every parser loop runs under a named, CWE-tagged, caller-configurable bound.
- Random access and streaming — read one entry from a multi-gigabyte archive without touching the rest; iterate unseekable streams with bounded memory.
- Deterministic — a written determinism contract: reproducible layout always, byte-stable per environment by default, and the same SHA-256 on every runtime under
compression.deterministic: true. - Incremental modification — replace, remove, add or rename entries without recompressing the untouched 99 % (append-only), or compact for true deletion.
- A frozen 39-code error vocabulary and
verifyZip()— one call, a machine-readable report that never throws for archive problems. - The first open clause-by-clause ISO/IEC 21320-1:2015 validator, blocking in CI.
- What it will NOT do: no encryption (read or write, in 1.x — ZipCrypto is broken), no other archive formats, no multi-disk archives, no archive repair, no filesystem I/O in the engine, no network access, ever.
🚀 Installation
# Run directly with npx (recommended for MCP clients)
npx -y zipnative-mcp
# Or install globally
npm install -g zipnative-mcp
zipnative-mcp
Requirements: Node.js ≥ 22.
⚙️ Configuration
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"zipnative": {
"command": "npx",
"args": ["-y", "zipnative-mcp"],
"env": {
"ZIPNATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-archives"
}
}
}
}
Cursor / Continue / Zed / Windsurf / Cline / Roo Code
Any MCP-compatible client that supports stdio servers will work. Use the same command + args + env triple. Example for Cursor (~/.cursor/mcp.json):
{
"mcpServers": {
"zipnative": {
"command": "npx",
"args": ["-y", "zipnative-mcp"],
"env": { "ZIPNATIVE_MCP_OUTPUT_DIR": "/Users/you/Documents/mcp-archives" }
}
}
}
Windsurf / Cline / Roo Code use the same shape inside their respective MCP config files.
VS Code
.vscode/mcp.json:
{
"servers": {
"zipnative": {
"type": "stdio",
"command": "npx",
"args": ["-y", "zipnative-mcp"],
"env": { "ZIPNATIVE_MCP_OUTPUT_DIR": "${workspaceFolder}/.mcp-archives" }
}
}
}
🌐 Supported AI Ecosystem & Clients
zipnative-mcp is designed for MCP-native environments and works with clients that support MCP over stdio or Streamable HTTP: Claude Desktop, Claude Code, Cursor, Continue, Zed, Windsurf, Cline, Roo Code, VS Code, ChatGPT (Streamable HTTP), the MCP Inspector, and any client built on the MCP TypeScript / Python SDKs. Because the server never opens a socket of its own, it is also safe to run inside air-gapped or egress-restricted hosts.
🔌 MCP protocol compliance
The server is built on the MCP TypeScript SDK v2 (@modelcontextprotocol/server) and speaks MCP 2026-07-28:
- Stateless serving —
server/discoverreplaces the session handshake; every result carriesresultTypeand the_metaserverInfoenvelope. Over HTTP, 2026-07-28 clients sendMcp-Method/Mcp-Nameheaders with eachPOST /mcp. - Cache hints —
tools/listandprompts/listarepublicwith a 24 httlMs,server/discoverispublicfor 1 h, andresources/list/resources/templates/list/resources/readareprivatewithttlMs: 0(sandbox files are per-host user data). - Protocol errors —
tools/callwith an unknown tool name is a JSON-RPC error (-32602,[UNKNOWN_TOOL] Unknown tool: …) rather than anisErrorresult, and an unknown resource URI is-32602(Invalid params), as the specification requires;isError: trueis reserved for execution failures. - Automatic legacy fallback — a client that opens with
initializeis served through the SDK's legacy path on both stdio and HTTP for every revision the SDK negotiates: 2024-10-07, 2024-11-05, 2025-03-26, 2025-06-18 and 2025-11-25 (legacy) next to 2026-07-28 (modern). Nothing changes for existing hosts.tests/protocol-revisions.test.tsproves each legacy revision negotiates exactly that revision and serves the 13 tools;tests/stdio-modern.test.tsandtests/http-modern.test.tsprove the 2026-07-28 path on stdio and HTTP. - HTTP —
POST /mcponly;GET/DELETE /mcpanswer 405 (no SSE resumability; the server is stateless). Bound to127.0.0.1with aHost/Originguard (foreign values → 403; theOriginport must equal the server port);ZIPNATIVE_MCP_HTTP_TOKENadds an opt-in bearer-token gate (401+WWW-Authenticatewithout it); bodies above 256 MiB → 413. - stdio — the SDK's 10 MiB default frame cap is raised to 256 MiB so multi-MiB base64 archives fit in one
tools/call; as in every SDK release, a request sent beforeinitializeis dropped without a reply on the legacy path. - Output schemas — every
structuredContentvalidates against the tool'soutputSchema(a 2026-07-28 MUST), includingverbosity: 'summary'andfieldsprojections: the read tools declare projectable schemas (all properties optional,additionalProperties: falsekept). Input schemas carry no$schemakeyword and no$refby policy (some hosts forwardinputSchemato function-calling APIs that reject unknown keywords).serverInfocarrieswebsiteUrl; the resource template iszipnative://output/{+path}.
The tools/call payload (content, structuredContent, isError) is identical between the 2026-07-28 path and the legacy path; tests/http-modern.test.ts asserts it, and tests/schema-conformance.test.ts validates structuredContent with the SDK's JSON Schema 2020-12 validator.
| Client | Transport | Protocol negotiated |
|---|---|---|
| Claude Desktop, Cursor, Continue, Zed, Windsurf, Cline | stdio | legacy initialize (2024-xx / 2025-xx) — unchanged |
| ChatGPT and other Streamable HTTP hosts | HTTP POST /mcp | legacy stateless streamable HTTP — unchanged |
MCP 2026-07-28 clients (SDK v2 Client, current MCP Inspector) | stdio / HTTP | server/discover, cache hints, _meta envelope |
Compatibility matrix — every negotiated revision, on both transports, with the test that proves it:
| Revision | Era | stdio | HTTP POST /mcp | Proven by |
|---|---|---|---|---|
| 2024-10-07 | legacy initialize | ✓ | ✓ | tests/protocol-revisions.test.ts |
| 2024-11-05 | legacy initialize | ✓ | ✓ | tests/protocol-revisions.test.ts |
| 2025-03-26 | legacy initialize (JSON-RPC batches accepted over HTTP, refused on stdio) | ✓ | ✓ | tests/protocol-revisions.test.ts, tests/cli-stdio.test.ts, tests/http-modern.test.ts |
| 2025-06-18 | legacy initialize | ✓ | ✓ | tests/protocol-revisions.test.ts, tests/cli-stdio.test.ts, tests/http-modern.test.ts, tests/http-transport.test.ts |
| 2025-11-25 | legacy initialize | ✓ | ✓ | tests/protocol-revisions.test.ts, tests/cli-stdio.test.ts, tests/http-modern.test.ts, tests/autonomy.test.ts |
| 2026-07-28 | modern (server/discover, _meta envelope, cache hints) | ✓ | ✓ (+ bearer token) | tests/stdio-modern.test.ts, tests/http-modern.test.ts, tests/autonomy.test.ts |
Autonomy proof. tests/autonomy.test.ts replays one scripted agent scenario (S0 handshake → S1 tools/list → S2 describe_engine → S3 create_zip file / base64 / parallel → S4 verify_zip → S5 inspect_zip checks → S6 list_zip_entries → S7 read_zip_entry / inflate_raw / compute_crc32 → S8 modify_zip append then compact → S9 extract_zip dry run then files → S10 resources/* → S11 scan_zip_forward → S12 hostile refusals → S13 ceilings → S14 protocol errors → S15 draft_governance_issue → S16 prompts/get → S17 clean shutdown) against the built dist/cli.js on four transports — stdio legacy, stdio modern, HTTP legacy, HTTP modern with ZIPNATIVE_MCP_HTTP_TOKEN — under a sandbox, a cache directory, ZIPNATIVE_MCP_MAX_ENTRIES=5000 and ZIPNATIVE_MCP_WORKERS=2, asserting no interaction, no unexpected isError, a code and a remedy on every refusal, a cache hit over the wire, a JSON-only stdout and a clean exit. To replay it by hand from Claude Code, Claude Desktop or the MCP Inspector, see docs/guides/LOCAL_TESTING.md § "Drive it from Claude Code / Claude Desktop / MCP Inspector" (the repository ships a ready .mcp.json).
Environment variables
| Variable | Default | Meaning |
|---|---|---|
ZIPNATIVE_MCP_OUTPUT_DIR | (unset) | Absolute path of the one sandbox directory: zipPath / sourcePath inputs are read from it, outputMode: 'file' / outputDir outputs are written under it (exclusive create — never overwritten). The real path of every file read and of every parent written must stay inside it: a symlink or junction planted in the sandbox is SECURITY_VIOLATION on zipPath / sourcePath / resources/read as much as on writes. Unset ⇒ every path input and file output is refused and the tools work on base64 only. |
ZIPNATIVE_MCP_CACHE_DIR | (unset) | Opt-in persistent SHA-256-keyed result cache (1 h TTL, 256 MiB LRU, plaintext at rest; key namespaced by tool API + package + engine version). Never caches zipPath / sourcePath inputs, file output, defaultDate: 'now', parallel, describe_engine or draft_governance_issue; a hit carries _meta.cached: true. |
ZIPNATIVE_MCP_PORT | (unset → stdio) | When set to a valid port (1–65535), serves Streamable HTTP on http://127.0.0.1:<port>/mcp instead of stdio. Loopback only, DNS-rebinding protection (foreign Host / Origin → 403), GET / DELETE → 405. No authentication unless ZIPNATIVE_MCP_HTTP_TOKEN is set — other local processes can reach the endpoint. |
ZIPNATIVE_MCP_HTTP_TOKEN | (unset) | (secret) Opt-in bearer token for the HTTP transport (≥ 16 characters, no whitespace — a weaker value aborts startup). When set, every /mcp request must carry Authorization: Bearer <token>; otherwise 401 + WWW-Authenticate: Bearer realm="zipnative-mcp" (with error="invalid_token" only when credentials were sent — RFC 6750 §3.1). Compared constant-time, never logged. |
ZIPNATIVE_MCP_MAX_UNCOMPRESSED_BYTES | 8589934592 (8 GiB) | Operator ceiling for limits.maxEntryUncompressedSize and limits.maxTotalUncompressedSize. Integer ≥ 1024, read once at startup — an invalid value refuses to start. A per-call value above it is LIMIT_CEILING_EXCEEDED; a ceiling below the engine default tightens the default silently. |
ZIPNATIVE_MCP_MAX_ENTRIES | 100000 | Operator ceiling for limits.maxEntries and scan_zip_forward maxEntries (whose default of 10 000 is clamped to the ceiling; only an explicit value above it is LIMIT_CEILING_EXCEEDED). Integer ≥ 1, read once at startup. |
ZIPNATIVE_MCP_WORKERS | 8 | Operator ceiling for create_zip parallel.workers; with no explicit workers the engine default max(1, min(cores − 1, 8)) applies, bounded by this ceiling; 0 disables worker threads (compression stays on the calling thread). Integer ≥ 0, read once at startup. |
🛠 Tool reference
Every archive-consuming tool takes exactly one of zipBase64 (the raw archive as base64 exactly once; a data:…;base64, prefix is tolerated; decoded ≤ 128 MiB) or zipPath (a relative path inside the sandbox; container extensions only — .zip .jar .war .ear .docx .xlsx .pptx .odt .ods .odp .epub .vsix .nupkg .whl .apk .ipa .xpi .crx .kmz; ≤ 1 GiB buffered). Every engine-touching tool takes limits (the eight named ZipLimits bounds — maxEntries, maxEntryUncompressedSize, maxTotalUncompressedSize, maxCompressionRatio, maxNameBytes, maxExtraFieldBytes, maxCommentBytes, maxCentralDirectoryBytes; values above the operator ceilings → LIMIT_CEILING_EXCEEDED) and strict (escalate the first diagnostic to ZIP_STRICT_DIAGNOSTIC). The read tools take verbosity ('full' | 'summary') and fields (≤ 16 dot paths). Every result carries diagnostics[] + diagnosticCounts (+ diagnosticsTruncated past 200). Error results: content[0].text = "<tool> failed [CODE]: message", isError: true, _meta.error = { code, message, data? }. Full input/output schemas are advertised by tools/list; the complete error table is in AGENTS.md.
Shortened here. Read the whole README on GitHub.
Signals
- Last commit
- Sep 2026
- Weekly downloads
- 51
Advanced
- Delivery
- zipnative-mcp MCP server → your ahel gateway (mcp.ahel.ai) → every connected AI client.
- Catalog kind
- mcp-server
- Gateway key
io-github-nizoka-zipnative-mcp- Source
- github.com/nizoka/zipnative-mcp