{
  "scope": [
    "S:\\mcp-servers\\analytics-node",
    "S:\\mcp-servers\\cf-node",
    "S:\\mcp-servers\\docs-node",
    "S:\\mcp-servers\\git-node",
    "S:\\mcp-servers\\linter-node",
    "S:\\mcp-servers\\mantis-node",
    "S:\\mcp-servers\\memory-node",
    "S:\\mcp-servers\\office-node",
    "S:\\mcp-servers\\playwright-node",
    "S:\\mcp-servers\\projectfs-node",
    "S:\\mcp-servers\\sql-node",
    "S:\\mcp-servers\\skills\\*\\SKILL.md (cross-reference only)"
  ],
  "servers": [
    {
      "name": "git-node (git-mcp-server)",
      "path": "S:\\mcp-servers\\git-node\\index.js",
      "tools": [
        { "tool": "git_query", "purpose": "Read-only repo inspection (status, history, blame, ancestor check, repo_info, rebase_status).", "pattern": "action-based enum (8 actions); project_path or roots[root_index] fallback." },
        { "tool": "git_diff", "purpose": "Diffs: working, compare, check, show, range_diff, export_zip.", "pattern": "action-based enum (6 actions); save_path for export_zip; project_path/roots fallback." },
        { "tool": "git_conflict_manager", "purpose": "Conflict lifecycle and write ops: list/analyze/read/resolve/stage/rebase_step/restore/trim_rebase_todo.", "pattern": "action-based enum (9 actions); dry_run boolean supported; project_path/roots fallback." }
      ],
      "schema_issues": [
        { "issue": "roots/root_index fallback duplicated identically across all 3 tools instead of a shared/typed reference; root_index has no explicit range validation in schema (only description).", "path": "git-node/index.js", "lines": "1159-1165,1198-1203,1250-1256" },
        { "issue": "git_diff mixes 6 semantically different diff types (working/compare/check/show/range_diff/export_zip) behind one action enum with largely disjoint parameter sets (save_path only for export_zip, original_range/rewritten_range only for range_diff).", "path": "git-node/index.js", "lines": "1189-1236" },
        { "issue": "git_conflict_manager action enum mixes read (list, list_detailed, analyze, read) and destructive/write (resolve, stage, rebase_step, restore, trim_rebase_todo_before_commit) operations in one tool with one shared dry_run flag whose applicability ('dove supportato') is not stated per-action in the schema.", "path": "git-node/index.js", "lines": "1242-1266" }
      ],
      "output_verbosity_notes": [
        "git_diff structuredContent includes both stdout and output (duplicate of same content) plus stderr always, even on success/clean results (git-node/index.js:1124-1141)."
      ],
      "error_actionability_notes": [
        "makeErrorResult returns only a free-text message plus an arbitrary 'context' object; no machine-readable error code field (git-node/index.js:187-197).",
        "GitCheckError subclass carries no numeric/string error code either, only a message and context object (git-node/index.js:900-941)."
      ],
      "capabilities": {
        "dry_run": "partial",
        "idempotency_notes": "Explicit per-tool annotations declared: git_query/git_diff idempotentHint=true; git_conflict_manager idempotentHint=false. dry_run parameter exists only on git_conflict_manager and its per-action coverage is undocumented in the schema.",
        "discovery_metadata": "TOOL_METADATA provides title + readOnlyHint/destructiveHint/idempotentHint annotations per tool (git-node/index.js:26-54); no runtime capability-discovery tool/action exists."
      }
    },
    {
      "name": "mantis-node (mantis-mcp-server)",
      "path": "S:\\mcp-servers\\mantis-node\\index.js",
      "tools": [
        { "tool": "mantis_get_my_info", "purpose": "Verify Mantis connectivity and return current user info.", "pattern": "project_path required (used to locate .env with MANTIS_URL/MANTIS_TOKEN)." },
        { "tool": "mantis_issue_reader", "purpose": "Read one issue or search a filtered list.", "pattern": "action-based (get_one, search); project_path required." },
        { "tool": "mantis_add_note", "purpose": "Add a private note to an existing ticket.", "pattern": "project_path + issue_id + text; always forces view_state=private, not exposed as a parameter." },
        { "tool": "mantis_files", "purpose": "Upload (base64) or download ticket attachments.", "pattern": "action-based (upload, download); save_path optional for download." }
      ],
      "schema_issues": [
        { "issue": "Every tool requires project_path solely to locate a local .env file containing MANTIS_URL/MANTIS_TOKEN; there is no dedicated config/connection tool, so callers must know a project has a .env with Mantis credentials before any call succeeds.", "path": "mantis-node/index.js", "lines": "46-82,88-95" },
        { "issue": "mantis_add_note silently and unconditionally forces the note to 'private' with no parameter to control visibility; not documented as a hard constraint in the tool description beyond the word 'PRIVATA'.", "path": "mantis-node/index.js", "lines": "113-124,236-243" },
        { "issue": "mantis_files action enum (upload/download) has divergent required fields (filename+content for upload; file_id for download) enforced only at runtime, not via schema oneOf/anyOf.", "path": "mantis-node/index.js", "lines": "126-141,247-276" }
      ],
      "output_verbosity_notes": [
        "mantis_issue_reader get_one returns full formatted notes_history and attachments as concatenated strings inside JSON rather than structured arrays (formattedNotes/formattedFiles built as newline-joined text blocks).",
        "path: mantis-node/index.js:174-204"
      ],
      "error_actionability_notes": [
        "Errors are returned as free text with an emoji prefix ('Mantis Error: ...'); API errors are just `API Error (${status}): ${JSON.stringify(data)}` with no stable machine error code.",
        "path: mantis-node/index.js:281-290"
      ],
      "capabilities": {
        "dry_run": false,
        "idempotency_notes": "No idempotency claims or annotations anywhere in the tool definitions.",
        "discovery_metadata": "No annotations object (readOnlyHint/destructiveHint) on any of the 4 tools; no read-vs-write distinction declared beyond tool descriptions."
      }
    },
    {
      "name": "cf-node (coldfusion-bridge)",
      "path": "S:\\mcp-servers\\cf-node\\index.js",
      "tools": [
        { "tool": "cf_bridge", "purpose": "Unified bridge to a ColdFusion server: evaluate arbitrary CFML code, list logs, read logs.", "pattern": "action-based (evaluate, logs_list, logs_read); project_path required, used to resolve CF_BRIDGE_URL/CF_MCP_TOKEN from .env." }
      ],
      "schema_issues": [
        { "issue": "Single tool mixes arbitrary CFML code execution ('evaluate', effectively remote eval) with read-only log listing/reading in one action enum, with a single generic description ('esecuzione codice e gestione log') that does not flag evaluate as high-risk/non-read-only.", "path": "cf-node/index.js", "lines": "50-68,93-115" },
        { "issue": "No schema-level constraint or annotation distinguishes evaluate (write/execute, unbounded side effects) from logs_list/logs_read (read-only); nothing in the schema itself prevents evaluate from performing DB writes, HTTP calls, or file writes on the CF server.", "path": "cf-node/index.js", "lines": "46-72" }
      ],
      "output_verbosity_notes": [],
      "error_actionability_notes": [
        "Two disjoint free-text error shapes depending on failure origin: application error from CFM ('❌ Errore ColdFusion: Msg/Dettaglio') vs. transport error ('🔥 Errore Comunicazione Bridge'); neither carries a stable error code.",
        "path: cf-node/index.js:121-127,132-145"
      ],
      "capabilities": {
        "dry_run": false,
        "idempotency_notes": "None declared.",
        "discovery_metadata": "No annotations block at all (no readOnlyHint/destructiveHint) in this file."
      }
    },
    {
      "name": "sql-node (sql-mcp-server)",
      "path": "S:\\mcp-servers\\sql-node\\index.js",
      "tools": [
        { "tool": "sql_executor", "purpose": "Execute read-only SQL query, get an explain plan, or inspect a table schema.", "pattern": "action-based (query, explain, schema); project_path required for .env DB credentials." }
      ],
      "schema_issues": [
        { "issue": "action enum requires different fields per action (query for query/explain, tableName for schema) enforced only at runtime; schema itself has no conditional requirement.", "path": "sql-node/index.js", "lines": "183-199" }
      ],
      "output_verbosity_notes": [
        "Query/schema results are returned as full JSON.stringify(rows, null, 2) text blocks with no pagination or row-limit parameter in the schema; large result sets are not truncated at the contract level.",
        "path: sql-node/index.js:241,254"
      ],
      "error_actionability_notes": [
        "Security block returns a fixed free-text message ('🚫 BLOCKED: Solo SELECT/WITH consentite per sicurezza.') with isError:true but no structured error code distinguishing it from other failure types.",
        "path: sql-node/index.js:248-250",
        "Generic catch-all: `Errore SQL (${action}): ${err.message}` with no code field.",
        "path: sql-node/index.js:270-271"
      ],
      "capabilities": {
        "dry_run": false,
        "idempotency_notes": "Read-only enforcement is hard-coded at the implementation level (isQueryReadOnly regex/keyword gate blocking INSERT/UPDATE/DELETE/DDL/multi-statement), effectively guaranteeing idempotency for all 3 actions, but this is not declared via annotations.",
        "discovery_metadata": "No annotations object; no readOnlyHint despite the tool being read-only by design (enforced code-side, not schema-side)."
      }
    },
    {
      "name": "office-node (office-mcp-server)",
      "path": "S:\\mcp-servers\\office-node\\index.js",
      "tools": [
        { "tool": "word_document", "purpose": "Read/create/edit .docx; read-only for .doc.", "pattern": "action-based (read, list_paragraphs, create, edit_paragraph, insert_paragraph, delete_paragraph); paragraph_index is a bare 0-based number." },
        { "tool": "excel_document", "purpose": "Read/create/write Excel workbooks.", "pattern": "action-based (list_sheets, read_sheet, write_cells, create); writes are in-place, no versioning." },
        { "tool": "pdf_document", "purpose": "Read PDF metadata/pages/ranges, export text.", "pattern": "action-based (metadata, read_all, read_page, read_range, export_text); backend enum (pdfjs/liteparse/auto)." },
        { "tool": "document_convert", "purpose": "Pandoc-based format conversion, status/setup.", "pattern": "action-based (status, list_formats, convert, setup_pandoc); many optional fields shared across unrelated actions (toc, reference_doc_path, template_path, extract_media)." }
      ],
      "schema_issues": [
        { "issue": "word_document.edit/insert/delete_paragraph rely on a bare paragraph_index number with no schema-level way to discover valid indices in the same call; index correctness depends entirely on a prior list_paragraphs call (no compound/atomic operation).", "path": "office-node/index.js", "lines": "1641-1661" },
        { "issue": "document_convert action enum (status/list_formats/convert/setup_pandoc) shares one large flat properties bag (~20 fields: standalone, overwrite, toc, number_sections, metadata, reference_doc_path, template_path, extract_media, pdf_engine, setup_mode, ...) with required fields differing per action, enforced only downstream.", "path": "office-node/index.js", "lines": "1785-1862+" },
        { "issue": "pdf_document.backend enum ('pdfjs'|'liteparse'|'auto') changes extraction/OCR behavior but the schema gives no indication that the effective backend used, or whether OCR ran, is reported back (that information exists only in response fields not visible in the schema itself).", "path": "office-node/index.js", "lines": "1775-1779" }
      ],
      "output_verbosity_notes": [
        "No pagination/limit parameters on pdf_document read_all or word_document read; full document text/paragraph content is returned as-is, which the mcp-office-expert skill separately warns can be large (see skill_compensations)."
      ],
      "error_actionability_notes": [
        "isError:true is used broadly (10+ call sites) but consistently only carries free text; no shared error-code taxonomy visible in the tool definitions.",
        "path: office-node/index.js:1122,1143,1154,1162,1171,1186,1196,1207 (representative isError sites)"
      ],
      "capabilities": {
        "dry_run": false,
        "idempotency_notes": "None declared; word_document/excel_document writes are in-place and destructive with no built-in versioning (confirmed by mcp-office-expert skill instructing manual copy-before-write, see skill_compensations).",
        "discovery_metadata": "No annotations object anywhere in office-node/index.js (grep for readOnlyHint/annotations returned no matches); no read-vs-write distinction declared in the tool contract itself, only inferable from action names."
      }
    },
    {
      "name": "playwright-node (playwright-mcp-server)",
      "path": "S:\\mcp-servers\\playwright-node\\index.js",
      "tools": [
        { "tool": "browser_session", "purpose": "Navigate, inspect DOM/console/network, screenshot, JS eval, tabs/frames/CDP attach, performance tracing, downloads.", "pattern": "single action enum with 38 heterogeneous actions covering navigation, diagnostics, tab/frame management, CDP lifecycle, and performance tracing." },
        { "tool": "browser_interact", "purpose": "Click/fill/scroll/hover/press on page or frame elements.", "pattern": "action-based (click, fill, scroll, hover, press_key, click_uid, fill_uid, hover_uid, press_uid)." }
      ],
      "schema_issues": [
        { "issue": "browser_session action enum has 38 distinct actions (navigate, get_dom, snapshot, screenshot, evaluate_js, annotate, click_by_id, export_state, load_state, get_network_errors, get_console_logs, console_list/get/clear, get_capabilities, get_browser_status, get_current_page, debug_bundle, network_list/get/save_body/clear, performance_start/stop_trace, performance_summary, list_pages, select_page(+by_url/by_title), attach_cdp, detach_cdp, use_launch_browser, close_browser_target, switch_tab, list_frames, select_frame, read_downloaded_file) sharing one flat ~30-field properties bag; this is the largest single-tool action surface found in the audited servers.", "path": "playwright-node/index.js", "lines": "1591-1704" },
        { "issue": "get_console_logs is explicitly marked legacy in its own description ('legacy error-only ... per debug JS strutturato usare console_list/console_get') yet remains a first-class enum value alongside its replacement, creating two overlapping ways to do the same thing.", "path": "playwright-node/index.js", "lines": "1613-1616,1641" },
        { "issue": "close_chrome field description says the close-real-Chrome capability 'in questo progetto e' bloccata per sicurezza' — a security constraint documented only in a field description string, not enforced/declared via annotations.", "path": "playwright-node/index.js", "lines": "1677" }
      ],
      "output_verbosity_notes": [
        "debug_bundle action aggregates snapshot+screenshot+storage+network+console into one response with max_items/save_artifacts controls, implying the server itself anticipates verbosity problems for this tool family.",
        "path: playwright-node/index.js:1655-1661"
      ],
      "error_actionability_notes": [
        "No annotations or structured error code convention found via grep in this file (only one unrelated match for 'ANNOTATE' logging)."
      ],
      "capabilities": {
        "dry_run": false,
        "idempotency_notes": "None declared.",
        "discovery_metadata": "get_capabilities and get_browser_status exist as explicit sub-actions of browser_session (runtime capability discovery available), but there is no static readOnlyHint/destructiveHint annotation anywhere in the file distinguishing read actions (get_dom, screenshot) from write/interactive actions (click_by_id, evaluate_js, close_browser_target)."
      }
    },
    {
      "name": "docs-node (docs-mcp-server)",
      "path": "S:\\mcp-servers\\docs-node\\index.js",
      "tools": [
        { "tool": "docs_management", "purpose": "Indexing, shelves, tags, shelf/document relations, heuristic correlation.", "pattern": "single action enum with 20 heterogeneous actions; apply:false on 'correlate' acts as a documented dry-run." },
        { "tool": "docs_navigation", "purpose": "Search, read a document, list documents.", "pattern": "action-based (search, read_document, list_documents); tag_match enum (all/any)." }
      ],
      "schema_issues": [
        { "issue": "docs_management action enum has 20 heterogeneous actions (scan_file, scan_folder, list_shelves, create/update/remove_shelf, list/create/update/remove_tag, set/bulk_set_document_tags, resync_all, feature_status, remove_document, export/import_shelf, manage_shelf_links, manage_document_links, correlate) sharing one ~35-field flat properties bag; required fields differ per action and are enforced only at runtime.", "path": "docs-node/index.js", "lines": "2302-2427" },
        { "issue": "generic-named fields reused across unrelated actions: 'operation' (free string, not an enum, description lists valid values only in prose: 'set_parents, add_parent, remove_parent, list, add, remove, replace_for_document, prune_heuristic_links') and 'mode'/'scope' (free strings, valid values only in description text, not enum-constrained).", "path": "docs-node/index.js", "lines": "2378,2410-2411" },
        { "issue": "confirm_replace (boolean) gates a destructive replace-on-import path but is only meaningful in combination with on_conflict='replace'; this cross-field dependency is not expressible/enforced in the JSON Schema.", "path": "docs-node/index.js", "lines": "2346-2355" }
      ],
      "output_verbosity_notes": [],
      "error_actionability_notes": [
        "No annotations or structured error-code convention found via grep in this file."
      ],
      "capabilities": {
        "dry_run": "partial",
        "idempotency_notes": "apply (boolean) on correlate provides a documented dry-run/apply split ('Se false esegue solo un dry-run mostrando i candidati', line 2418), but no equivalent dry-run exists for other write actions in the same tool (create/remove_shelf, remove_document, import_shelf, manage_*_links).",
        "discovery_metadata": "feature_status action exists for capability discovery; no static readOnlyHint/destructiveHint annotations anywhere in the file (grep for annotations/readOnlyHint returned no matches)."
      }
    },
    {
      "name": "linter-node (linter-mcp-server)",
      "path": "S:\\mcp-servers\\linter-node\\src\\index.ts",
      "tools": [
        { "tool": "lint_code", "purpose": "Lint CFML/JS/TS/SQL/PHP files, optionally auto-fix.", "pattern": "file_path + extension dispatch (not action-based); fix boolean write flag." },
        { "tool": "get_lint_config", "purpose": "Return current lint config/runtime status for a language.", "pattern": "language enum (cfml, js, sql, php)." }
      ],
      "schema_issues": [
        { "issue": "lint_code has a fix:true write/mutate capability but no annotations distinguishing it from the read-only default (fix:false/omitted); the only signal is the field description.", "path": "linter-node/src/index.ts", "lines": "44-47" }
      ],
      "output_verbosity_notes": [
        "serializeResult() explicitly strips the 'output' field (full file content) before returning over MCP specifically to avoid large-file response bloat — a deliberate anti-verbosity design choice worth noting as a positive pattern.",
        "path: linter-node/src/index.ts:22-27"
      ],
      "error_actionability_notes": [
        "PHP runtime errors ARE machine-actionable: PhpRuntimeError responses include structured userAction/suggestedEnv/candidates/warnings fields.",
        "path: linter-node/src/index.ts:178-193,225-238",
        "By contrast, all other lint errors and the generic catch-all fall back to a single free-text message with no code field.",
        "path: linter-node/src/index.ts:196-205,343"
      ],
      "capabilities": {
        "dry_run": "partial",
        "idempotency_notes": "fix:false (default) is effectively read-only/idempotent; fix:true is a write with no dry-run preview of what would change.",
        "discovery_metadata": "get_lint_config provides partial capability/runtime discovery (java/cflint/php runtime availability) but no static Tool.annotations block on either tool."
      }
    },
    {
      "name": "projectfs-node (projectfs-mcp-server)",
      "path": "S:\\mcp-servers\\projectfs-node\\src\\index.ts",
      "tools": [
        { "tool": "read_file", "purpose": "Read a text file within allowedRoots with line slicing and byte caps.", "pattern": "path + optional startLine/endLine/maxBytes." },
        { "tool": "list_dir", "purpose": "List directory entries with depth/glob filters.", "pattern": "path + depth/include/exclude/includeHidden." },
        { "tool": "grep_files", "purpose": "Literal-text search under a directory.", "pattern": "root + pattern (literal only, not regex) + include/exclude/caseSensitive/maxResults." },
        { "tool": "stat", "purpose": "Metadata for a file/directory.", "pattern": "path only." },
        { "tool": "read_many", "purpose": "Batch-read multiple files.", "pattern": "paths[] + maxBytesPerFile." }
      ],
      "schema_issues": [],
      "output_verbosity_notes": [
        "grep_files.pattern is explicitly documented as literal-text-only ('Regex is not supported') directly in the schema description, which correctly pre-empts a common ambiguity seen elsewhere (positive pattern, not a defect)."
      ],
      "error_actionability_notes": [
        "makeToolError returns free-text message plus arbitrary context object with no stable error-code field, same pattern as git-node.",
        "path: projectfs-node/src/fs-tools.ts:83-94"
      ],
      "capabilities": {
        "dry_run": true,
        "idempotency_notes": "Server is read-only by construction (no write tool exists at all); all 5 tools declare a shared TOOL_ANNOTATIONS block with readOnlyHint:true, destructiveHint:false, idempotentHint:true, openWorldHint:false. Server-level instructions also explicitly state ProjectFS does not support writes.",
        "discovery_metadata": "No dedicated capability-discovery tool, but SERVER_INSTRUCTIONS (index.ts:20-27) documents scope, allowedRoots policy, and explicitly tells callers to fall back to shell only for unsupported operations."
      }
    },
    {
      "name": "memory-node (memory-mcp-server)",
      "path": "S:\\mcp-servers\\memory-node\\src\\index.ts",
      "tools": [
        { "tool": "memory_status", "purpose": "DB status and project-scoped counts.", "pattern": "discrete tool, project_path/project_id optional." },
        { "tool": "memory_add_entry", "purpose": "Add an operational memory entry, with idempotent dedupe option.", "pattern": "discrete tool; required project_path/scope/topic/content/source_type; source_type is a closed 11-value enum." },
        { "tool": "memory_search", "purpose": "Search memory entries (FTS with LIKE fallback).", "pattern": "discrete tool; query required; mode enum (project/cross_project)." },
        { "tool": "memory_read_entry", "purpose": "Read a full entry by id.", "pattern": "discrete tool; entry_id required." },
        { "tool": "memory_list_scopes", "purpose": "List scopes with counts.", "pattern": "discrete tool." },
        { "tool": "memory_list_topics", "purpose": "List topics with counts.", "pattern": "discrete tool." },
        { "tool": "memory_list_tags", "purpose": "List tags with counts.", "pattern": "discrete tool." },
        { "tool": "memory_invalidate_entry", "purpose": "Soft-invalidate an entry (idempotent).", "pattern": "discrete tool; entry_id required." }
      ],
      "schema_issues": [
        { "issue": "project_path vs project_id dual-identity resolution (either may be supplied, with warnings on mismatch) is implicit in several tools; not a defect per se but the resolution rule ('project mode requires project_path or project_id') is enforced in code, not visible in the JSON Schema (no anyOf/oneOf).", "path": "memory-node/src/index.ts", "lines": "483-497,908-916" }
      ],
      "output_verbosity_notes": [
        "memory_search truncates via clampSearchLimit and returns snippets rather than full content by default (include_content:false unless requested) — a deliberate anti-verbosity design, worth noting as a positive pattern.",
        "path: memory-node/src/index.ts:705-713,1159"
      ],
      "error_actionability_notes": [
        "Machine-actionable: MemoryNodeError carries a stable string 'code' (e.g. INVALID_PARAM, NOT_FOUND) surfaced via toMcpError(error.code, error.message); this is the strongest error-code pattern found among the audited servers.",
        "path: memory-node/src/errors.ts:1-24; memory-node/src/index.ts:1679-1685"
      ],
      "capabilities": {
        "dry_run": false,
        "idempotency_notes": "Explicit per-tool annotations (readOnlyAnnotations / writeAnnotations / destructiveWriteAnnotations) with idempotentHint set correctly per tool (e.g. memory_add_entry idempotentHint:false unless dedupe:true is used at the app level; memory_invalidate_entry idempotentHint:true).",
        "discovery_metadata": "additionalProperties:false on every tool's inputSchema (strict schema, rejects unknown fields) — the only server in this audit doing so consistently; full annotations block per tool (memory-node/src/index.ts:1455-1646)."
      }
    },
    {
      "name": "analytics-node (analytics-mcp-server)",
      "path": "S:\\mcp-servers\\analytics-node\\src\\index.ts",
      "tools": [
        { "tool": "analytics_status", "purpose": "Runtime status and redacted local config.", "pattern": "discrete tool; readOnlyHint:true." },
        { "tool": "analytics_scan", "purpose": "Scan local sources, update DB incrementally.", "pattern": "discrete tool; sources[] enum, dry_run boolean, force boolean." },
        { "tool": "analytics_summary", "purpose": "Aggregated session/event totals.", "pattern": "discrete tool; group_by enum array." },
        { "tool": "analytics_models", "purpose": "Paginated model metrics.", "pattern": "discrete tool; limit/offset with explicit bounds (1-1000)." },
        { "tool": "analytics_sessions", "purpose": "Paginated sessions.", "pattern": "discrete tool; limit/offset bounded." },
        { "tool": "analytics_events", "purpose": "Paginated runtime events.", "pattern": "discrete tool; event_type enum, limit/offset bounded." },
        { "tool": "analytics_delete_imported", "purpose": "Delete imported analytics rows via two-step dry-run/confirm.", "pattern": "discrete tool; dry_run + confirm_delete + confirm_plan_id required sequence." }
      ],
      "schema_issues": [],
      "output_verbosity_notes": [
        "All list-style tools (models/sessions/events) enforce limit max 1000 and offset-based pagination directly in the schema (minimum/maximum bounds) — a deliberate anti-verbosity/anti-overload design, worth noting as a positive pattern.",
        "path: analytics-node/src/index.ts:127-129,143-145,160-162"
      ],
      "error_actionability_notes": [
        "Machine-actionable: AnalyticsError carries a stable 'code' surfaced via toMcpError(error.code, ...), same pattern as memory-node.",
        "path: analytics-node/src/index.ts:283-290"
      ],
      "capabilities": {
        "dry_run": true,
        "idempotency_notes": "analytics_delete_imported implements an explicit two-step dry_run -> confirm_delete+confirm_plan_id protocol with idempotentHint:false/destructiveHint:true declared; analytics_scan also exposes its own dry_run with idempotentHint:true. This is the most complete dry-run/confirm contract found in the audit.",
        "discovery_metadata": "Full annotations block (readOnlyHint/destructiveHint/idempotentHint/openWorldHint) on every one of the 7 tools; additionalProperties:false on all inputSchemas."
      }
    }
  ],
  "skill_compensations": [
    {
      "server": "git-node",
      "tool_or_area": "git_diff action=export_zip (save_path)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-git-mantis-workflow\\SKILL.md",
      "lines": "58-63,75",
      "compensation": "Skill spells out a save_path contract the tool schema only documents loosely: 'Richiede sempre save_path assoluto con estensione .zip' and a troubleshooting rule to check absolute path/.zip suffix/writability -- compensating for the schema not enforcing an absolute-path or extension pattern on save_path."
    },
    {
      "server": "git-node",
      "tool_or_area": "git_diff action=range_diff (original_range/rewritten_range)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-git-mantis-workflow\\SKILL.md",
      "lines": "49-56",
      "compensation": "Skill adds a semantic-equivalence precondition ('stessa serie logica di commit') and a pre-validation sequence (git_query history/check_ancestor before range_diff) that the schema cannot express -- the tool will happily accept unrelated ranges and return a misleading-but-'correct' only_left/only_right result."
    },
    {
      "server": "git-node / mantis-node",
      "tool_or_area": "project_path resolution",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-git-mantis-workflow\\SKILL.md",
      "lines": "74",
      "compensation": "Troubleshooting note ('verifica project_path assoluto e che punti alla root giusta') compensates for the fact that git-node's roots/root_index fallback and mantis-node's .env-lookup-by-project_path both fail silently/opaquely without a stable error code identifying the cause."
    },
    {
      "server": "docs-node",
      "tool_or_area": "docs_management (20-action mega tool: shelves, tags, links, correlate)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-docs-navigator\\SKILL.md",
      "lines": "12,16-18,44-50,54-58",
      "compensation": "Skill imposes strict operation ordering not present in the schema: 'feature_status prima di...', 'bulk_set_document_tags solo per riallineamenti massivi', 'resync_all o remove_shelf solo quando serve davvero', and an explicit 'ordine obbligatorio tag-first' fallback sequence, plus a 5-step mind-map procedure (feature_status -> list_shelves -> shelf_links -> correlate apply:false -> apply:true only if candidates coherent). This entirely replaces the missing per-action sequencing/guardrail metadata in the tool contract."
    },
    {
      "server": "docs-node",
      "tool_or_area": "docs_management action=import_shelf (on_conflict/confirm_replace)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-docs-navigator\\SKILL.md",
      "lines": "18",
      "compensation": "Skill requires explicit on_conflict and instructs setting confirm_replace:true 'solo dopo aver verificato l'impatto' -- compensating for the schema's inability to express the on_conflict='replace' <-> confirm_replace cross-field dependency or require an impact preview before the flag is honored."
    },
    {
      "server": "docs-node",
      "tool_or_area": "docs_management action=correlate (apply dry-run) and noise/runtime risk",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-docs-navigator\\SKILL.md",
      "lines": "47-50,113",
      "compensation": "Skill adds risk guardrails the schema doesn't encode: 'non fare correlazione documentale massiva senza restringere per tag/sottodominio/versione/path' and applies document_links only if dry-run candidates are coherent AND candidate_links > 0 -- both thresholds/heuristics live only in the skill, not in the tool contract."
    },
    {
      "server": "cf-node",
      "tool_or_area": "cf_bridge action=evaluate",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-coldfusion-developer\\SKILL.md",
      "lines": "64",
      "compensation": "Skill imposes a behavioral policy absent from the tool schema entirely: 'cf_bridge.evaluate solo per ispezione pura; niente side effect: query, HTTP, scritture file o workflow applicativi.' The tool itself has no readOnlyHint/destructiveHint and no server-side restriction preventing evaluate from doing exactly those things -- this is the clearest case in the audit of a skill manually substituting for a missing read/write contract."
    },
    {
      "server": "linter-node",
      "tool_or_area": "lint_code fix parameter",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-coldfusion-developer\\SKILL.md",
      "lines": "62-63",
      "compensation": "'Audit default: esegui lint_code ... prima di fix, bridge o consegna' and 'fix: true solo se esplicito' compensate for the tool schema not marking fix as a distinct destructive/write action (no annotations, no default-safe declaration)."
    },
    {
      "server": "sql-node",
      "tool_or_area": "sql_executor project_path (.env credential lookup) and generic security error",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-database-expert\\SKILL.md",
      "lines": "14,33-34",
      "compensation": "Skill explains the undocumented-in-schema fact that 'sql-mcp-server legge le credenziali dal .env del progetto' and instructs treating any 'errore di sicurezza' response as an interpretive signal ('batch multipli, statement non read-only o sintassi non supportata') rather than a specific cause -- compensating for sql-node's single free-text BLOCKED message with no structured error code (sql-node/index.js:248-250)."
    },
    {
      "server": "office-node",
      "tool_or_area": "word_document paragraph_index",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-office-expert\\SKILL.md",
      "lines": "23",
      "compensation": "'Usa word_document action: list_paragraphs prima di modificare: non indovinare mai l'indice' compensates for paragraph_index being a bare integer with no in-schema mechanism to discover valid indices atomically with the edit call."
    },
    {
      "server": "office-node",
      "tool_or_area": "excel_document write_cells (in-place, non-reversible writes)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-office-expert\\SKILL.md",
      "lines": "31,87",
      "compensation": "'crea sempre prima una copia del file template' and 'Le modifiche a .docx e .xlsx sono in-place e non reversibili dal tool: se il file e prezioso, crea prima una copia' directly compensate for the total absence of dry_run/versioning/backup capability in office-node's write actions."
    },
    {
      "server": "office-node",
      "tool_or_area": "pdf_document backend selection (pdfjs/liteparse/auto, OCR)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-office-expert\\SKILL.md",
      "lines": "40,114",
      "compensation": "'Verifica sempre backend_used, ocr_used ed eventuali warnings nella risposta' and a fallback retry sequence (default pdfjs -> auto -> liteparse) compensate for the schema not indicating upfront which backend will actually run or whether OCR will be invoked."
    },
    {
      "server": "office-node",
      "tool_or_area": "document_convert (Pandoc availability, PDF engine selection)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-office-expert\\SKILL.md",
      "lines": "44,48,54",
      "compensation": "Skill mandates calling action=status before convert and manually checking status.pandoc.available, and documents the undocumented-in-schema PDF engine fallback order (xelatex > typst > lualatex > pdflatex > weasyprint > wkhtmltopdf) and the PANDOC_PDF_ENGINE_MISSING failure mode -- none of which is discoverable from the inputSchema alone."
    },
    {
      "server": "playwright-node",
      "tool_or_area": "browser_session (38-action mega tool): CDP attach lifecycle",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-browser-automation\\SKILL.md",
      "lines": "17-36",
      "compensation": "A 14-step mandatory sequence ('Workflow Chrome Gia Aperto') dictates exact action ordering (get_capabilities/get_browser_status -> attach_cdp -> select_page_by_url/title or list_pages+select_page -> debug_bundle -> network/console tools) that the flat 38-action enum does not encode as valid state transitions."
    },
    {
      "server": "playwright-node",
      "tool_or_area": "browser_session get_console_logs (deprecated but present) vs console_list/console_get",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-browser-automation\\SKILL.md",
      "lines": "15,64",
      "compensation": "Skill repeats twice that get_console_logs is legacy/fallback-only and to prefer console_list/console_get, compensating for the tool schema keeping both as equal-weight enum values with no deprecated flag."
    },
    {
      "server": "playwright-node",
      "tool_or_area": "browser_session iframe/frame handling (list_frames/select_frame)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-browser-automation\\SKILL.md",
      "lines": "72-91",
      "compensation": "Skill encodes an explicit state machine for iframe-heavy pages (navigate -> list_frames if DOM incomplete -> select_frame -> interact -> re-check frame after each action) that has no representation in the flat action enum/schema."
    },
    {
      "server": "playwright-node",
      "tool_or_area": "browser_session navigate policy differences (launch vs CDP)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-browser-automation\\SKILL.md",
      "lines": "34,67-70",
      "compensation": "Skill documents that ALLOWED_URLS/ALLOW_FILE_URLS/ALLOWED_FILE_ROOTS/BLOCK_MEDIA policies apply differently depending on whether the session is CDP-attached or launch-internal -- a behavioral distinction invisible in the shared browser_session schema."
    },
    {
      "server": "memory-node",
      "tool_or_area": "memory_add_entry content hygiene (secrets/noise) and project_path vs project_id",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-memory-operator\\SKILL.md",
      "lines": "24-27",
      "compensation": "'Non salvare segreti, token, payload sensibili, stack trace lunghi o output rumorosi' and 'Usare project_path esplicito nelle operazioni project-scoped' compensate for memory-node having byte/length limits but no semantic secret/noise detection, and for the schema allowing project_id-only calls that the skill discourages in practice."
    },
    {
      "server": "memory-node",
      "tool_or_area": "Tool surface scope lock (M2 contract)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-memory-operator\\SKILL.md",
      "lines": "39-52",
      "compensation": "Skill explicitly enumerates the 8 real tools and states 'Non promettere o usare tool futuri: update/delete/import/export/resources/prompts/link/vector restano fuori scope' -- a guardrail against hallucinated tool capabilities, needed because the server exposes no capability-discovery tool listing supported vs. unsupported operations."
    },
    {
      "server": "analytics-node",
      "tool_or_area": "analytics_delete_imported dry_run/confirm_plan_id sequencing",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-analytics-operator\\SKILL.md",
      "lines": "42,55,83-93,102",
      "compensation": "Skill restates the two-step protocol and adds an operational rule not in the schema: 'se il piano e' cambiato, interrompere e rifare dry-run' -- compensating for the schema not itself invalidating/expiring a stale confirm_plan_id when underlying data changes between dry-run and confirm."
    },
    {
      "server": "analytics-node",
      "tool_or_area": "source naming (canonical vs alias)",
      "skill_path": "S:\\mcp-servers\\skills\\mcp-analytics-operator\\SKILL.md",
      "lines": "75-81",
      "compensation": "'usa solo nomi canonici con underscore; non usare alias con trattino nei tool MCP' compensates for the sources enum accepting only exact canonical strings with no alias-normalization or hyphen-tolerant matching declared in the schema."
    }
  ],
  "uncertainties": [
    "cf-node/mcp_tool/*.cfm (the actual ColdFusion-side bridge implementation of action=evaluate) was not read; the audit scope was limited to the MCP-side tool contract in cf-node/index.js as instructed, so the true blast-radius of 'evaluate' on the CF server side is inferred from the skill's compensation text, not verified server-side.",
    "playwright-node/index.js also defines legacy tool names (executeLegacyTool referencing browser_navigate etc. around line 1706+) that are not exposed via ListToolsRequestSchema; these were not analyzed since they are not part of the current advertised tool contract.",
    "sql-node/drivers/*.js (per-DB-type driver implementations for mssql/mysql/postgres/oracle) were not opened; schema/query/explain behavior differences per driver are not verified beyond the shared sql_executor contract in index.js.",
    "docs-node/index.js is 3662 lines; only the ListToolsRequestSchema block (lines ~2299-2472) and grep hits were inspected, not the full handler implementation, so some runtime-only validation (e.g. conditional requireds per action) may exist beyond what's stated here.",
    "Skill README/reference files under skills/*/references/*.md were not opened except where directly linked from the main SKILL.md files reviewed; additional compensations likely exist in those reference docs (e.g. mcp-office-expert/references/template-driven-publishing.md, mcp-git-mantis-workflow/references/rebase-playbook.md) but were out of scope for this pass.",
    "mcp-mantis-ticket-writer and mcp-mantis-test-writer skills were not grepped in this pass; additional mantis-node compensations may exist there beyond what mcp-git-mantis-workflow covers."
  ],
  "recommended_followups": [
    "Deeper pass on skills/*/references/*.md for the servers with the largest action surfaces (playwright-node browser_session, docs-node docs_management, office-node document_convert) to capture compensation detail beyond the top-level SKILL.md files.",
    "Read cf-node/mcp_tool/mcp_agent.cfm and _ai_bridge.cfm to determine whether evaluate has any server-side read/write restriction, since the MCP-side schema has none.",
    "Confirm at the protocol level (not just grep) whether git-node, mantis-node, cf-node, sql-node, office-node, docs-node, and playwright-node ever emit a stable machine-readable error code field; only memory-node and analytics-node were confirmed to do so in this pass."
  ]
}
