From fdc94e08cf289cf0ac2d6d60f5bfbf31799d2400 Mon Sep 17 00:00:00 2001 From: vitya Date: Tue, 9 Jun 2026 20:48:18 +0300 Subject: [PATCH] =?UTF-8?q?refactor(using-markitdown):=20rewrite=20MCP?= =?UTF-8?q?=E2=86=92CLI,=20drop=20Docker=20section=20v1.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace all mcp__markitdown__convert_to_markdown invocations with the native `markitdown ` CLI (v0.1.6, on PATH). Outputs to stdout or `-o `; sees the full host filesystem, so the Docker bind-mount caveat (host→container file:// translation, [Errno 2] /c:/Users/...) is gone and that whole section is removed. Updated the ingest pattern (-o straight into .wiki/raw/), gotchas table (command-not-found → check `markitdown --version`), and contrast table header (CLI, not MCP). Description triggers unchanged. PATCH bump 1.0.0→1.0.1; dist artifact rebuilt. Decommissioned the Docker MCP containers: no container is named `markitdown-mcp` (the server spawns anonymous ones from markitdown-mcp:latest, 3 had piled up); removed all by image ancestor. Left mcpServers.markitdown in ~/.claude.json untouched (out of scope) — flagged as a follow-up in the concept page. Wiki: concepts/using-markitdown-cli-migration.md + index + log. Co-Authored-By: Claude Opus 4.8 --- .../using-markitdown-cli-migration.md | 58 ++++++++++++++++++ .wiki/index.md | 1 + .wiki/log.md | 1 + dist/using-markitdown.skill | Bin 2984 -> 2884 bytes skills/using-markitdown/SKILL.md | 45 +++++++------- 5 files changed, 81 insertions(+), 24 deletions(-) create mode 100644 .wiki/concepts/using-markitdown-cli-migration.md diff --git a/.wiki/concepts/using-markitdown-cli-migration.md b/.wiki/concepts/using-markitdown-cli-migration.md new file mode 100644 index 0000000..8a33a41 --- /dev/null +++ b/.wiki/concepts/using-markitdown-cli-migration.md @@ -0,0 +1,58 @@ +--- +title: using-markitdown — MCP → CLI migration +type: concept +updated: 2026-06-09 +--- + +# using-markitdown — MCP → CLI migration + +`using-markitdown` v1.0.0 → v1.0.1 (PATCH). Rewrote the skill from the Docker-based +`mcp__markitdown__convert_to_markdown` MCP tool to the native `markitdown` CLI (v0.1.6, +on `PATH`). + +## Why + +The MCP path ran markitdown inside a Docker container with a single host directory +bind-mounted (`-v C:\Users\vitya:/workdir`). That forced a brittle host→container path +translation for every local file (`file:///workdir/...`), and the failure mode +(`[Errno 2] No such file or directory: '/c:/Users/...'`) was a recurring foot-gun. The +container also could not see files outside its one mount. + +The CLI is a normal local process: it sees the full host filesystem, takes a plain path +or URL as its positional arg, and writes markdown to stdout (or to a file with `-o`). No +mount, no path rewriting, no `file://` URIs. The whole "Docker-mount caveat (READ FIRST)" +section of the skill became dead weight and was removed. + +## CLI contract + +``` +markitdown # → markdown to stdout +markitdown -o out.md # → write to a file +cat file.pdf | markitdown -x pdf # → stdin + format hint +``` + +Verified on this machine: `markitdown 0.1.6`; URL fetch (`markitdown https://example.com`) +and stdout conversion both work. + +## Container cleanup gotcha + +The task asked to run `docker stop markitdown-mcp && docker rm markitdown-mcp`. There was +**no container named `markitdown-mcp`** — the MCP server spawns a fresh anonymously-named +container from the `markitdown-mcp:latest` image per session, and three had piled up +(`sharp_jones`, `boring_goldberg`, `admiring_kowalevski`, ages 47s–28h). The correct +decommission is by image ancestor, not by name: + +``` +docker rm -f $(docker ps -aq --filter "ancestor=markitdown-mcp:latest") +``` + +(Stopping them races with the server's own `--rm` cleanup, briefly leaving "Dead" +containers that finish removing themselves — re-checking the filter confirms none remain.) + +## Out of scope / follow-up + +The `markitdown` **MCP server registration** in `~/.claude.json` was left untouched (the +task scoped only the running container, and editing user-global config is cross-cutting). +While that entry remains, a new container will respawn on the next session that loads the +MCP. A full decommission would deregister `mcpServers.markitdown` from `~/.claude.json` — +recommended as a separate, explicitly-confirmed step. diff --git a/.wiki/index.md b/.wiki/index.md index dc2a720..270c324 100644 --- a/.wiki/index.md +++ b/.wiki/index.md @@ -47,6 +47,7 @@ Catalog of all wiki pages. One line per page, organized by type. Updated on ever - [using-system-snapshot-design.md](concepts/using-system-snapshot-design.md) — `using-system-snapshot` v0.1.0 — thin read-only skill wrapping the single `mcp__projects-meta__meta_system_snapshot` call (poller + local docker + cached task summary); replaces scattered `tasklist`/`docker ps`/manual `meta_status`; core rule = no liveness claim without calling the tool this turn; three-line output; defers deep docker to [[using-vds-ops]] and precise tasks to [[using-projects-meta]] - [using-tasks-status-archival.md](concepts/using-tasks-status-archival.md) — `using-tasks` v1.3.0 done-task archival rule (≥10 🟢 → `.tasks/archive/YYYY-MM.md`) fixes STATUS.md bloat; documents why `tasks_get_status` (single-task, by slug) / `tasks_aggregate` (cross-project cache) can't replace the orientation board-read, so the literal task instruction was not followed - [delegate-task-negative-trigger-fp.md](concepts/delegate-task-negative-trigger-fp.md) — `delegate-task` v0.2.1 FP fix: «создать задачу себе» stem-matched the «создать задачу на агента» positive trigger; abstract "does NOT apply when doing the work yourself" carve-out loses to literal stem-match under the 1%-rule → made the negative literal + routed (→ using-tasks). Verified pos 5/5, neg 4/5 (was 0/5) +- [using-markitdown-cli-migration.md](concepts/using-markitdown-cli-migration.md) — `using-markitdown` v1.0.0→v1.0.1 (PATCH): rewrote from the Docker-based `mcp__markitdown__convert_to_markdown` MCP tool to the native `markitdown` CLI (0.1.6, on PATH); dropped the host→container `file://` mount caveat; container decommission is by image ancestor (`--filter ancestor=markitdown-mcp:latest`), not by the non-existent name `markitdown-mcp` ## Packages diff --git a/.wiki/log.md b/.wiki/log.md index 330582c..9549246 100644 --- a/.wiki/log.md +++ b/.wiki/log.md @@ -74,3 +74,4 @@ Parseable: `grep "^## \[" .wiki/log.md | tail -20`. ## [2026-06-09] decision | using-tasks-session-break — `using-tasks` 1.1.0→1.2.0 (MINOR): added the `session_break` marker. Task author sets `session_break: true | ""` in task frontmatter (mirrored as `**Session break:**` on the local board); after the task closes 🟢, before `tasks_claim_next`, an autonomous agent prints the verbatim line `🔚 SESSION BOUNDARY — [slug] закрыта. Рекомендую завершить текущую сессию. Следующий трек: [value | "см. STATUS.md"]` and stops instead of chaining the next task. Absent → behaviour unchanged. Enforced in Task completion step 6 + Rules bullet + format docs. Marker not heuristic: the stop-point is an authoring choice, not a runner guess. ## [2026-06-09] review | using-tasks-status-archival v1.3.0 — VERDICT PASS 3/3 (using-tasks-status-read-perf-review). Criterion «ориентация через `tasks_get_status`, не Read» is satisfied by a **validated deviation**, not a literal swap: re-verified against the live tool schema that `tasks_get_status(target_project, slug)→{status, found}` takes a required slug and returns ONE task — it cannot enumerate the board, so it cannot drive orientation; the implementer correctly rejected the impossible instruction and fixed the real problem (bloat→archival). No regression: orientation still reads local STATUS.md (Session start §2) and the «what's next» flow still reads the board — change is purely additive. Archival rule clear & complete (≥10 threshold, two trigger points, monthly append-only archive, verbatim blocks, dedicated commit, cross-referenced). One informational non-blocking note: this repo's own STATUS.md (>10 🟢) would itself trip the rule — dogfooding tracked separately as tasks-board-cleanup-2026-05. No follow-up tasks. Verdict appended to concepts/using-tasks-status-archival.md. ## [2026-06-09] decision | delegate-task-review-weight — `delegate-task` 0.2.2→0.2.3 (PATCH): Step 5 (paired `-review` task) now sets an explicit `weight`, inherited from the impl-task with a `needs-claude` floor (impl `needs-human`→review `needs-human`; `needs-claude`→`needs-claude`; `cheap-ok`→`needs-claude`). Root cause of commit `c0af151` ("add Weight: needs-claude to 4 review tasks — reconciler was skipping them"): the authoring skill omitted `weight` on review tasks, making them invisible to fleet routing. Floor (not pure inheritance) chosen to stay internally consistent with the skill's own "What NOT to do" bullet that forbids `cheap-ok` for review tasks — a `cheap-ok` impl would otherwise propagate a forbidden `cheap-ok` review. Added a What-NOT-to-do bullet against weightless review tasks. Concept page concepts/delegate-task-review-weight.md + index. TDD N/A (markdown policy artifact). +## [2026-06-09] decision | using-markitdown-cli-migration — `using-markitdown` 1.0.0→1.0.1 (PATCH): rewrote the skill from the Docker-based `mcp__markitdown__convert_to_markdown` MCP tool to the native `markitdown` CLI (v0.1.6, on PATH). Tool block now `markitdown ` → stdout (or `-o file`); removed the whole "Docker-mount caveat (READ FIRST)" section (host→container `file://` translation + `[Errno 2] /c:/Users/...` symptom are gone — CLI sees the full host FS). Updated the ingest pattern (use `-o` straight into `.wiki/raw/`), the gotchas table (`command not found` → check `markitdown --version`, install `pip install markitdown[all]`; dropped the MCP "tool not available / ToolSearch" row), and the contrast-table header (CLI, not MCP). Description frontmatter (the WHEN-to-use triggers) left unchanged. Container decommission: the task's literal `docker stop/rm markitdown-mcp` had no target — no container is named that; the MCP spawns anonymously-named containers from `markitdown-mcp:latest` per session (3 had piled up). Removed all by image ancestor (`docker rm -f $(docker ps -aq --filter "ancestor=markitdown-mcp:latest")`), verified none remain. Left the `mcpServers.markitdown` entry in `~/.claude.json` untouched (out of scope; a container will respawn next session until it's deregistered — flagged as a follow-up). Concept page concepts/using-markitdown-cli-migration.md + index. TDD N/A (markdown skill). diff --git a/dist/using-markitdown.skill b/dist/using-markitdown.skill index 05f271cf25b2152f1e9a4544f175d2aa4ced4e2a..542a41c7b6bf37f45256771b250e5182307f53ef 100644 GIT binary patch delta 2827 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 delta 2928 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 Convert almost any URI to plain markdown using Microsoft's `markitdown` MCP server. Returns **raw textual content**, not an LLM summary. +> Convert almost any path or URL to plain markdown using Microsoft's `markitdown` CLI (v0.1.6, on `PATH`). Returns **raw textual content**, not an LLM summary. ## Tool ``` -mcp__markitdown__convert_to_markdown(uri: string) → markdown string +markitdown # → markdown to stdout +markitdown -o out.md # → write markdown to a file +cat file.pdf | markitdown # → read from stdin (use -x/-m to hint the format) ``` -`uri` accepts: `http://`, `https://`, `file://`, `data:`. +The positional argument accepts a **local file path** (host path, normal slashes) or an `http://` / `https://` URL. The CLI runs natively, so it sees your full host filesystem — no Docker mount, no `file://` URI translation, no path rewriting. -## Local files — Docker-mount caveat (READ FIRST) +Useful flags: `-o ` (write to a file instead of stdout), `-x ` / `-m ` (format hint when reading from stdin). -The markitdown MCP usually runs in a **Docker container** with a single host directory bind-mounted. The container does **not** see your full host filesystem. `file://` URIs must point to the **in-container path**, not the host path. +## Local files -1. Open `~/.claude.json` and find `mcpServers.markitdown.args`. Look for the `-v` flag — e.g. `-v C:\Users\vitya:/workdir` means host `C:\Users\vitya` is mounted at `/workdir` inside the container. -2. Translate the host path to the container path before forming the URI. -3. Forward slashes only inside the container path. - -**Example.** Host file at `C:\Users\vitya\modular\heart-and-mask\.wiki\raw\foo.html` with mount `C:\Users\vitya:/workdir`: +Pass the host path directly — relative or absolute, with native separators: ``` -file:///workdir/modular/heart-and-mask/.wiki/raw/foo.html +markitdown C:\Users\vitya\modular\heart-and-mask\.wiki\raw\foo.html -o foo.md ``` -**Symptom of getting this wrong:** `[Errno 2] No such file or directory: '/c:/Users/...'` — the container literally tried to open the host-shaped path. The fix is path translation, not URL encoding. +No mount caveats: the CLI is a normal local process. The old Docker `-v` mount translation and `/c:/Users/...` `[Errno 2]` symptom no longer apply. -**If the file falls outside the mount:** either copy it into the mounted tree, or extend the mount in `~/.claude.json` (a Claude restart is required for MCP changes to take effect — MCP servers are spawned at session start). - -**Filenames.** Non-ASCII filenames (Cyrillic, etc.) inside `file://` URIs are flaky across the URL-encode → urllib → Docker → host-FS chain. Rename to Latin kebab-case **before** calling markitdown. +**Filenames.** Non-ASCII filenames (Cyrillic, etc.) still travel better as Latin kebab-case through downstream wiki/ingest steps. Rename to Latin kebab-case before saving the output, per `.wiki/CLAUDE.md` naming rules. ## When to use @@ -47,18 +43,19 @@ file:///workdir/modular/heart-and-mask/.wiki/raw/foo.html - You only need a *summary* or an *answer about* a page → use **WebFetch** (cheaper, runs through a small model, returns prose). - The URI is GitHub/PR/issue/release content → use `gh` CLI (richer metadata, structured output). - The URI is private/authenticated (GDocs, Confluence, Jira, Slack, Notion, `share.google/*` sign-in walls) → markitdown receives the **public-facing fallback page** (sign-in screen, cookie banner) and returns *that* as markdown. Verify the result is real content before saving. -- **The URI is a browser-rendered web page the user is already viewing** → ask the user to capture it via **Obsidian Web Clipper** (browser extension, runs Readability extraction client-side) and drop the resulting `.md` into `raw/`. Web Clipper output is dramatically cleaner than markitdown's HTML pass — no nav chrome, no sidebar history, no cookie banners — plus it carries YAML frontmatter (title / source URL / date) out of the box. Reserves markitdown for things browsers can't easily save (PDF, DOCX, PPTX, XLSX, EPUB, file:// resources). Note: rename the resulting file to Latin kebab-case before ingest (Web Clipper preserves the page `` verbatim, often non-ASCII). +- **The URI is a browser-rendered web page the user is already viewing** → ask the user to capture it via **Obsidian Web Clipper** (browser extension, runs Readability extraction client-side) and drop the resulting `.md` into `raw/`. Web Clipper output is dramatically cleaner than markitdown's HTML pass — no nav chrome, no sidebar history, no cookie banners — plus it carries YAML frontmatter (title / source URL / date) out of the box. Reserves markitdown for things browsers can't easily save (PDF, DOCX, PPTX, XLSX, EPUB, local files). Note: rename the resulting file to Latin kebab-case before ingest (Web Clipper preserves the page `<title>` verbatim, often non-ASCII). ## Pattern: ingest a remote source into a wiki ``` -1. mcp__markitdown__convert_to_markdown(uri="https://example.com/foo.pdf") -2. Inspect the head of the result. If it looks like a sign-in/cookie/consent page, abort — ask the user for an alternative (manual save, paste, authenticated MCP). -3. Write the result to .wiki/raw/<slug>.md (kebab-case, Latin only). -4. Register the new file in .wiki/raw/README.md. -5. Hand off to the wiki ingest workflow (creates sources/<slug>.md summary + entity/concept updates). +1. markitdown "https://example.com/foo.pdf" -o .wiki/raw/<slug>.md (kebab-case, Latin only) +2. Inspect the head of the result. If it looks like a sign-in/cookie/consent page, abort — ask the user for an alternative (manual save, paste, authenticated source). +3. Register the new file in .wiki/raw/README.md. +4. Hand off to the wiki ingest workflow (creates sources/<slug>.md summary + entity/concept updates). ``` +For a huge (book-length) document, write straight to a file with `-o` and summarize *from the saved file* — do not pipe the whole markdown through working context. + ## Common gotchas | Symptom | Cause | Fix | @@ -66,8 +63,8 @@ file:///workdir/modular/heart-and-mask/.wiki/raw/foo.html | Output is a Google/Microsoft sign-in page in some random language | URI behind auth wall | Ask user to export the content manually (Save as PDF, copy-paste) and put it in `raw/` | | Output is mostly nav/cookie banner text | Site is JS-rendered or anti-bot | Try the cached or print URL; or ask user for HTML export | | Output lacks images / diagrams | Markdown is text-only by design | Save the original asset separately under `raw/assets/`; reference it from the `sources/` summary | -| Tool not available in session | MCP server not loaded | Confirm `mcp__markitdown__convert_to_markdown` appears via ToolSearch; load with `select:mcp__markitdown__convert_to_markdown` | -| Huge output (book-length) | Whole document converted in one call | Save raw, then summarize *from the saved file* — do not hold the entire markdown in working context | +| `markitdown: command not found` | CLI not on `PATH` | Confirm with `markitdown --version` (expect `markitdown 0.1.6`); install with `pip install markitdown[all]` if missing | +| Huge output (book-length) | Whole document converted in one call | Use `-o <file>` to save raw, then summarize *from the saved file* — do not hold the entire markdown in working context | ## Quick contrast with WebFetch and Web Clipper