Skip to main content
Glama
zipnative-mcp

zipnative-mcp

Modify archive (incremental, no recompression)

modify_zip
Idempotent

Edit existing ZIP archives without recompression to add, replace, remove, rename entries, or set comments; choose append for byte-preserving saves or compact to drop removed data.

Instructions

Edit an existing archive without recompressing anything: operations add / replace / remove / rename / setComment applied in order. mode 'append' (default) = save(): the original bytes stay verbatim and edits are appended — fast and byte-preserving, BUT removed or replaced payloads REMAIN in the file (data remanence: recoverable by anyone; a ZIP_DEAD_BYTES_RATIO diagnostic fires past 50 % dead bytes, and 7-Zip is known to read the stale payload). mode 'compact' = saveCompact(): a rewrite without the dead bytes, entries SORTED by raw name bytes (an insertion-ordered layout — EPUB mimetype first, JAR manifest first — is not kept; 'append' keeps it), still no recompression, removed data truly gone, SFX prefix dropped; add canonical: {…} for the canonical deterministic form (zipnative 1.1.0, the canonicalize_zip rules applied after the edits). No edits and an unchanged comment return the same bytes (summary.noOp). The input is opened eagerly and, in 'append' mode, every entry is cross-checked against its local header and declared sizes before its bytes are re-emitted (ZIP_ENTRY_OVERLAP / ZIP_CD_LFH_MISMATCH / ZIP_LIMIT_EXCEEDED). Entry names travel verbatim (an unsafe name stays unsafe — extract_zip sanitises on the way out). Archives with duplicate entry names are refused — prefer create_zip for a full rebuild or when the source has duplicate names. Output base64 or a sandbox file.

Tool call template

Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.

{
  "id": 1,
  "jsonrpc": "2.0",
  "method": "tools/call",
  "params": {
    "arguments": {
      "operations": "<operations>"
    },
    "name": "modify_zip"
  }
}

Input Schema

NameRequiredDescriptionDefault
modeNo'append' (default): save() — original bytes verbatim + appended entries + new central directory; removed/replaced payloads REMAIN in the file (data remanence). 'compact': saveCompact() — a rewrite without recompression, entries sorted by raw name bytes, removed data truly gone, SFX prefix dropped (add `canonical` for the canonical deterministic form).append
limitsNoPer-call override of zipnative's security bounds (CWE-400/409); absent keys keep the engine defaults (describe_engine.defaultLimits). A value above its operator ceiling is LIMIT_CEILING_EXCEEDED. Raise only for trusted input.
strictNoTurn the first engine diagnostic into a ZIP_STRICT_DIAGNOSTIC error.
zipPathNoArchive path relative to the ZIPNATIVE_MCP_OUTPUT_DIR sandbox (container extensions: .zip .jar .docx .epub …). Exclusive with zipBase64; saves re-sending bytes between calls.
canonicalNomode 'compact' only (zipnative 1.1.0 saveCompact({ canonical })): after the edits, rewrite every entry into the canonical deterministic form — sorted by raw name bytes, one timestamp, UTF-8 flags, Zip64-only extras, version-made-by 0x032D — still without recompression. `{}` takes the defaults. The same rules as canonicalize_zip.
zipBase64NoArchive bytes, base64 (≤ 128 MiB decoded). Exclusive with zipPath.
operationsYesEdits applied in order. add: new entry (ZIP_ENTRY_EXISTS if present); replace: existing entry (ZIP_ENTRY_NOT_FOUND otherwise); remove; rename (never overwrites: remove the target first); setComment.
outputModeNo'base64' (default): inline embedded resource (≤ 50 MiB; the bytes appear in both its data: uri and its blob). 'file': written into the sandbox (never overwrites; ≤ ZIPNATIVE_MCP_MAX_FILE_OUTPUT_BYTES, default 4 GiB) and returned as a resource_link — far fewer tokens.base64
outputPathNoRelative path inside the sandbox (only when outputMode='file'); must end in a container extension (.zip recommended).
compressionNoCompression settings. `method` 'deflate' (default) or 'store'; `level` 0–9 (default 6); `deterministic: true` pins the pure-TS encoder so the bytes are identical on every runtime (slower).
defaultDateNoTimestamp for entries that set no `date`: an ISO-8601 instant (1980-01-01 … 2107-12-31, 2-second DOS granularity) or 'now' (wall clock — emits the ZIP_TIMESTAMP_NOT_PINNED diagnostic and makes the bytes non-reproducible). Omitted: the DOS epoch 1980-01-01T00:00:00 (reproducible).
includeSha256NoAdd `summary.sha256` of the produced bytes.

Output Schema

NameRequiredDescriptionDefault
modeYes
summaryYesmodify_zip summary.
filePathNoSandboxed absolute path (file mode).
sizeBytesYes
diagnosticsYesNon-fatal engine concerns (de-duplicated by code + entry, ≤ 200; codes in describe_engine.diagnosticCodes). Empty for a clean archive.
diagnosticCountsNoOccurrences per code, duplicates included.
diagnosticsTruncatedNotrue when rows beyond 200 were dropped.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed21 schema fields changedv1.0.1
    • addedInput schema / properties / canonical
      Added value: +{
      +  "additionalProperties": false,
      +  "description": "mode 'compact' only (zipnative 1.1.0 saveCompact({ canonical })): after the edits, rewrite every entry into the canonical deterministic form — sorted by raw name bytes, one timestamp, UTF-8 flags, Zip64-only extras, version-made-by 0x032D — still without recompression. `{}` takes the defaults. The same rules as canonicalize_zip.",
      +  "properties": {
      +    "date": {
      +      "description": "The one timestamp written on every entry (ISO-8601, 1980-01-01 … 2107-12-31, read as UTC; 2-second DOS granularity). Default: the DOS epoch 1980-01-01T00:00:00 — the deterministic default. Pass the same value to inspect_zip `canonicalDate` to verify the result.",
      +      "pattern": "^\\d{4}-\\d{2}-\\d{2}",
      +      "type": "string"
      +    },
      +    "keepComments": {
      +      "default": false,
      +      "description": "Keep the archive comment and the entry comments (default false: the canonical form carries none).",
      +      "type": "boolean"
      +    },
      +    "keepExternalAttributes": {
      +      "default": true,
      +      "description": "Keep each entry's external attributes — Unix modes, DOS attributes (default true); false writes the canonical defaults (0o100644 files, 0o40755 directories).",
      +      "type": "boolean"
      +    }
      +  },
      +  "type": "object"
      +}
    • changedInput schema / properties / limits / description
      Previous value: -"Override zipnative's named security bounds for this call (CWE-400 / CWE-409 guards). Every key is optional; absent keys keep the engine defaults (maxEntries 100000, maxEntryUncompressedSize 1 GiB, maxTotalUncompressedSize 8 GiB, maxCompressionRatio 1024, maxNameBytes 4096, maxExtraFieldBytes 65535, maxCommentBytes 65535, maxCentralDirectoryBytes 256 MiB). Values above the operator ceilings (ZIPNATIVE_MCP_MAX_UNCOMPRESSED_BYTES, ZIPNATIVE_MCP_MAX_ENTRIES) are refused with LIMIT_CEILING_EXCEEDED. Raise a bound for trusted input only."New value: +"Per-call override of zipnative's security bounds (CWE-400/409); absent keys keep the engine defaults (describe_engine.defaultLimits). A value above its operator ceiling is LIMIT_CEILING_EXCEEDED. Raise only for trusted input."
    • changedInput schema / properties / limits / properties / maxCentralDirectoryBytes / description
      Previous value: -"Maximum central-directory size in bytes."New value: +"Bytes."
    • changedInput schema / properties / limits / properties / maxCommentBytes / description
      Previous value: -"Maximum comment length in bytes."New value: +"Bytes."
    • changedInput schema / properties / limits / properties / maxCompressionRatio / description
      Previous value: -"Maximum declared uncompressed/compressed ratio for entries >= 1 KiB compressed (zip-bomb guard)."New value: +"Declared ratio (entries ≥ 1 KiB compressed)."
    • changedInput schema / properties / limits / properties / maxEntries / description
      Previous value: -"Maximum central-directory entry count."New value: +"Entries."
    • addedInput schema / properties / limits / properties / maxEntryCompressedSize
      Added value: +{
      +  "description": "Bytes, one compressed payload fetched by the byte-range reader (access range).",
      +  "minimum": 1,
      +  "type": "integer"
      +}
    • changedInput schema / properties / limits / properties / maxEntryUncompressedSize / description
      Previous value: -"Maximum decompressed size of one entry, bytes."New value: +"Bytes, one entry decompressed."
    • changedInput schema / properties / limits / properties / maxExtraFieldBytes / description
      Previous value: -"Maximum extra-field block length in bytes."New value: +"Bytes."
    • changedInput schema / properties / limits / properties / maxNameBytes / description
      Previous value: -"Maximum entry-name length in bytes."New value: +"Bytes."
    • changedInput schema / properties / limits / properties / maxTotalUncompressedSize / description
      Previous value: -"Maximum total decompressed size across an extraction, bytes."New value: +"Bytes, all entries decompressed."
    • changedInput schema / properties / mode / description
      Previous value: -"'append' (default): save() — original bytes verbatim + appended entries + new central directory; removed/replaced payloads REMAIN in the file (data remanence). 'compact': saveCompact() — canonical rewrite without recompression, removed data truly gone, SFX prefix dropped."New value: +"'append' (default): save() — original bytes verbatim + appended entries + new central directory; removed/replaced payloads REMAIN in the file (data remanence). 'compact': saveCompact() — a rewrite without recompression, entries sorted by raw name bytes, removed data truly gone, SFX prefix dropped (add `canonical` for the canonical deterministic form)."
    • changedInput schema / properties / outputMode / description
      Previous value: -"'base64' (default) returns the bytes inline as an embedded resource (≤ 50 MiB). 'file' writes them inside ZIPNATIVE_MCP_OUTPUT_DIR (never overwrites; ≤ 4 GiB, streamed) and returns a resource_link."New value: +"'base64' (default): inline embedded resource (≤ 50 MiB; the bytes appear in both its data: uri and its blob). 'file': written into the sandbox (never overwrites; ≤ ZIPNATIVE_MCP_MAX_FILE_OUTPUT_BYTES, default 4 GiB) and returned as a resource_link — far fewer tokens."
    • changedInput schema / properties / strict / description
      Previous value: -"Escalate the first engine diagnostic (prepended data, duplicate name, name mismatch, …) to a ZIP_STRICT_DIAGNOSTIC error instead of reporting it."New value: +"Turn the first engine diagnostic into a ZIP_STRICT_DIAGNOSTIC error."
    • changedInput schema / properties / zipBase64 / description
      Previous value: -"The archive bytes as base64 (exactly once; a data: URI prefix is tolerated). Exclusive with zipPath. Decoded size ≤ 128 MiB."New value: +"Archive bytes, base64 (≤ 128 MiB decoded). Exclusive with zipPath."
    • changedInput schema / properties / zipPath / description
      Previous value: -"Relative path of an archive inside the ZIPNATIVE_MCP_OUTPUT_DIR sandbox (no absolute paths, no '..'; container extensions only: .zip .jar .docx .epub …). Exclusive with zipBase64. Lets a create/modify → verify/extract chain avoid re-sending the bytes."New value: +"Archive path relative to the ZIPNATIVE_MCP_OUTPUT_DIR sandbox (container extensions: .zip .jar .docx .epub …). Exclusive with zipBase64; saves re-sending bytes between calls."
    • changedOutput schema / description
      Previous value: -"Result of an archive-producing tool. base64 mode: the archive arrives as an embedded `resource` content block (not duplicated here); file mode: `filePath` + a resource_link."New value: +"Archive result: base64 mode = an embedded `resource` block (not repeated here); file mode = `filePath` + a resource_link."
    • changedOutput schema / properties / diagnosticCounts / description
      Previous value: -"Number of diagnostics received per code (counts every occurrence, including de-duplicated ones)."New value: +"Occurrences per code, duplicates included."
    • changedOutput schema / properties / diagnostics / description
      Previous value: -"Non-fatal conformance concerns the engine raised while parsing or writing (de-duplicated by code + entry, at most 200). Empty for a clean archive. Codes: ZIP_PREPENDED_DATA, ZIP_MULTIPLE_EOCD, ZIP_NAME_MISMATCH, ZIP_UNICODE_PATH_CONFLICT, ZIP_INVALID_UTF8_NAME, ZIP_DUPLICATE_NAME, ZIP_EXTRA_FIELD_MALFORMED, ZIP_ZIP64_EXTRA_IGNORED, ZIP_TIMESTAMP_NOT_PINNED, ZIP_NONDETERMINISTIC_CODEC, ZIP_DEAD_BYTES_RATIO."New value: +"Non-fatal engine concerns (de-duplicated by code + entry, ≤ 200; codes in describe_engine.diagnosticCodes). Empty for a clean archive."
    • changedOutput schema / properties / diagnosticsTruncated / description
      Previous value: -"Present (true) when more than 200 distinct diagnostics were dropped from `diagnostics`."New value: +"true when rows beyond 200 were dropped."
    • addedOutput schema / properties / summary / properties / canonical
      Added value: +{
      +  "description": "Present (true) when the compact rewrite produced the canonical deterministic form (input canonical).",
      +  "type": "boolean"
      +}
  2. First observedv1.0.0

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Far exceeds the annotations: it discloses data remanence of removed/replaced payloads in append mode, the ZIP_DEAD_BYTES_RATIO 50% diagnostic, that 7-Zip reads stale payload, that compact re-sorts entries and drops SFX prefixes (breaking EPUB/JAR layout expectations), the no-op byte-identity guarantee, and the eager cross-checks (ZIP_ENTRY_OVERLAP / ZIP_CD_LFH_MISMATCH / ZIP_LIMIT_EXCEEDED). destructiveHint=false is consistent, since edits produce new bytes and never overwrite the input.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the verb and operation list, and nearly every clause carries a hazard or constraint. It is a single dense paragraph with some duplication of the `mode` and `outputMode` schema text ('Output base64 or a sandbox file'), which costs a point.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 12-parameter mutation tool with nested objects, it covers the decision-critical facts: mode semantics and consequences, ordering, duplicate-name refusal, no-op behaviour, diagnostics, and where output lands. Return values are covered by the output schema and only referenced (summary.noOp, summary.sha256) where relevant.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description adds meaning the schema does not: that 'append' preserves insertion order while 'compact' sorts by raw name bytes, and that `canonical: {...}` applies canonicalize_zip rules after edits. Entry-name verbatim handling is also clarified beyond the `name` field description.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource ('Edit an existing archive without recompressing') and enumerates the exact operation set (add/replace/remove/rename/setComment). It also carves out its lane against siblings by naming create_zip for full rebuilds and deferring sanitisation to extract_zip.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit routing rules: use create_zip 'for a full rebuild or when the source has duplicate names' (duplicates are refused here), and pass the same date to inspect_zip `canonicalDate` to verify. Mode choice is framed as a trade-off (fast/byte-preserving vs. dead-bytes-free) rather than left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.