Skip to content
anymd
Changelog

What shipped, and when.

Pulled live from GitHub. Stable releases ship to anymd.cc; beta builds run on staging first.

  1. betaba33535

    fix(mcp-oauth): verify OAuth linking, scopes and revocation for ChatGPT and MCP hosts (#62)

    Delete needs the elevated library:delete scope (never in the default grant; migration 0014 keeps existing keys deleting). Adds a read-only consent option, per-tool securitySchemes, the _meta step-up challenge and CIMD for ChatGPT. Suspended or deleted accounts cannot refresh, account deletion revokes every grant, authorize errors show a return link instead of an open redirect, and the auth failure budget is per OAuth connection. Documents the client compatibility matrix and its evidence.

    Closes #55

    Claude-Session: https://claude.ai/code/session_01FL32ryVcuJvQMH8ADXJcgJ

  2. betae8902fd

    feat(claude-directory): AnyMD Research plugin, directory-ready MCP surface and paged results (#60)

    Customer OAuth connections see only the public research and library tools, server instructions name only the tool groups the caller can use, and long Markdown in read_url, convert_url and get_document comes back in token-sized windows. Adds the anymd-research plugin with a marketplace manifest, the /docs/claude page, the maintainer playbook for directory submission, and the RapidAPI social and video providers in the privacy policy.

    Refs #52 Closes #57 Closes #58

    Claude-Session: https://claude.ai/code/session_01FL32rvfhHwjyHJ9vS1rnfh

  3. beta664dad6

    fix(security): harden untrusted web content, redirects and tool outputs (#59)

    • fix(security): re-check redirect hops, bound fetches and mark fetched content untrusted

    Conversion fetches follow redirects by hand and re-validate every hop, read bodies with hard caps under one deadline, and refuse trailing-dot, own-subdomain and special-purpose IP spellings. MCP results that carry page text are labelled untrusted, usage rows redact credential query values, and adversarial tests cover injection, SSRF and cross-user reads.

    Refs #56

    Claude-Session: https://claude.ai/code/session_01FL32rx7RPqUp6L9ZFcsGa5

    • fix(security): keep fetch errors honest and close remaining redirect gaps
    • Narrow web fetch error mapping so AI/D1 failures stay 500 internal
    • Treat apex and www as one site for robots/rate-limit on redirects
    • Route image enrichment through fetchPublic and pin YouTube stream redirects
    • Broaden credential redaction and apply it to trace spans and meta
    • Normalize the start URL in fetchPublic; cancel bodies over the cap
    • Mark search tools as returning untrusted content; share private suffixes

    Refs #56

    Claude-Session: https://claude.ai/code/session_01FL32rx7RPqUp6L9ZFcsGa5


  4. stablecec2276

    feat(convert): MP4 video links for YouTube and social video posts (#50)

    • feat(convert): MP4 video links for YouTube and social video posts

    YouTube reads link the lowest-quality MP4 with sound, resolved through VidCap then RapidAPI ytstream, best-effort and at no extra cost. Instagram, Threads, LinkedIn and Facebook posts link the MP4 URLs their providers already return, carousels included, and keep them when images are removed.

    Closes #44

    • fix(convert): keep the signed YouTube link out of saved documents

    The direct MP4 link now travels as direct_video and is appended to the response Markdown only, so library saves, embeddings and update events stay stable across reads. The lookup shares one 5-second budget across providers, links the parsed URL and drops unexpected quality labels. Instagram reads with images off no longer fail when the media endpoint only adds video links.

    • fix(convert): link only VidCap streams that outlast the read cache

    ytstream URLs are not proven to play outside the provider's IP, so the direct YouTube link now comes from VidCap alone, must be a googlevideo host and must stay valid for 90 minutes. Instagram keeps reading without media only when the provider has none; budget limits still stop the read. Docs note that saved social copies keep the link from their read.

    • fix(convert): require a stream lifetime and correct video link docs

    A direct YouTube link now needs an expire parameter, so the 90-minute floor always holds. Docs mention fresh=1 for a new link and the silent stream fallback; comments match the VidCap-only and Instagram behaviour. Tests cover Threads carousel videos and the silent stream label.


  5. stablea0ec2a4

    docs(agents): re-check an issue for parallel claims before opening a PR (#49)

    Two sessions picked up the same issue within a minute and both saw no in-progress label. Agents of one user share the label and assignee, so claims now also use a comment naming the session, and the issue is re-read after claiming and before opening a PR.

    Closes #48

    Claude-Session: https://claude.ai/code/session_01HEFMRKTRPFR7bcYfCRaGPD

  6. stable2f3ee2b

    fix(social): report provider quota exhaustion as provider_quota (#46)

    • fix(social): report provider quota exhaustion as provider_quota

    RapidAPI answers 429 when a plan's request quota is used up. rapidJson mapped that to a generic 502 upstream_error, so users and traces could not tell an exhausted quota from a provider outage. It now throws 503 provider_quota, tags the social.fetch span with the provider host, and the dashboard shows the provider-unavailable hint for it. Documents the code on the URL, REST and social search pages and lists 503 on POST /convert in the OpenAPI spec.

    Refs #45

    Claude-Session: https://claude.ai/code/session_01Lg6g1D9mGG228QDb6FW7To

    • fix(social): skip the popular-comments retry on an exhausted provider

    A 429 can be a burst rate limit as well as a used-up quota, so the message and docs now say both. Instagram no longer retries comments with the popular sort on a host that just answered provider_quota, which only spent another request.

    Refs #45

    Claude-Session: https://claude.ai/code/session_01Lg6g1D9mGG228QDb6FW7To


  7. stable3295098

    feat(mcp-events): owner-scoped library, video and feedback event subscriptions (#38)

    Opt-in MCP Events (events/list, events/subscribe, events/unsubscribe) for MCP 2026-07-28 clients, with a same-batch outbox, signed webhooks, SSRF and DNS-rebinding guards, retries with dead letters, per-subscription backlog caps and credential re-checks before every delivery. Off unless EVENTS_SECRET_KEY is set.

    Claude-Session: https://claude.ai/code/session_01NeTgjwv4ToXPP17jTB2BK6

  8. stable7c3d4b3

    feat(admin/mcp): access discovery, user KPIs, support and operations tools (#37)

    • feat(admin/mcp): add access discovery, user KPIs, support and operations tools
    • get_admin_access_status: scope-free self-diagnosis that explains why admin tools are hidden and how to reconnect (consent preset, step-up scope, key preset)
    • OAuth consent screen: optional admin access presets for owner/admin (admin-read-only, support), still capped by the current role on every call
    • get_user_stats / get_user_activity: registered, signups, DAU/WAU/MAU, activation, paying accounts and per-user activity with explicit definitions
    • get_customer_billing_summary, list_billing_events, get_billing_event: support views with masked email; orders and invoices reported as unsupported
    • get_operations_health, list_failed_jobs, get_job: incident diagnostics, metadata only, configuration as booleans
    • matching REST routes and OpenAPI operations under /api/v1/admin
    • additive indexes for the new queries

    Claude-Session: https://claude.ai/code/session_01UyeagmM7QxmhaciLU3doxd

    • fix(admin): tighten consent presets, redact webhook errors, bound health queries
    • consent presets never add owner-only scopes; those stay API-key only
    • billing webhook error text redacts URLs and email addresses
    • operations health selects video jobs by status so the index is used
    • activation counts probe documents per user instead of reading the whole index
    • rename registered_users.active_accounts to non_suspended; report ended cancellations
    • document the new admin tools, KPI definitions, consent options and REST routes
    • fix the split "Pages and posts" table in the MCP docs (closes #36)

    Claude-Session: https://claude.ai/code/session_01UyeagmM7QxmhaciLU3doxd


  9. stable57516ac

    feat(social-search): date filters and exact dedupe across pages (#33)

    Add period/since/until time filters, exact post-identity dedupe across pages and single-use leased paging sessions to social search on REST, MCP, CLI and the dashboard. In "all", a platform that fails mid-search is retried while another platform is still paging.

    Closes #31. Closes #30. Closes #32.

    Claude-Session: https://claude.ai/code/session_01NoabQyvyW3zcdvs8sRitZa

  10. stable6a5030f

    feat(feedback): publish product feedback as public GitHub issues (#29)

    One shared service behind dashboard, REST, MCP and CLI: secret scrubbing, explicit public consent, idempotency, atomic rate limits, duplicate detection and a reconciling publish state machine with a cron retry.

    Closes #28

    Claude-Session: https://claude.ai/code/session_014QHrKLWpca3c2MFFkUdDJp

  11. stable0b64516

    feat(youtube): per-request downloadVideo and analyzeVideo options

    REST, URL API, MCP and CLI accept downloadVideo and analyzeVideo on each conversion, following request > saved default > off. An explicit analyzeVideo turns the download on; an explicit downloadVideo=false turns a saved analysis off. Both need an account.

  12. stableb47ff27

    feat(youtube): optional Gemini video analysis after download

    Adds the analyzeVideo saved setting (needs downloadVideo). Once a video is stored on the CDN, the queue consumer sends its URL to google/gemini-3.8-flash through OpenRouter and charges 10 + 20 credits per started minute when the analysis is ready. Videos over 60 minutes are not analyzed.

  13. stable5168f47

    feat(youtube): download lowest-quality videos to the CDN in the background

    Adds the downloadVideo account setting (off by default). When on, a YouTube read enqueues a job that stores the smallest MP4 with sound in R2, charges 20 credits only when ready, and returns the job id with polling instructions. Jobs are readable over REST (GET /api/v1/videos/:id), MCP (get_video_download) and the CLI (anymd video <id>).

  14. stable16f1b85

    fix(search): use threads-scraper-api2 for Threads social search

    The previous Threads search provider returned results for only one in six calls. threads-scraper-api2 returned posts for most queries, with the same post shape inside a flat data list. Threads post and comment reading keeps its existing provider.

  15. betad050fb4

    feat(search): search all social platforms at once and keep search history

    Social search accepts platform "all": every platform is searched in parallel, posts are merged newest first, and a per-platform status (count, credits, error) comes back with every search. One failing platform no longer fails the search; only all of them failing does. The next_cursor of an "all" search continues only the platforms that have another page.

    LinkedIn pages now cost 100 credits, since its provider costs about ten times more per search; other platforms stay at 10. "all" costs the sum for the platforms that returned results.

    Every successful search page is saved (newest 200 per user) so the dashboard lists recent searches and reopens their results for free. Adds migration 0008 (social_searches). CLI bumped to 0.1.4.

  16. beta2b35d76

    test(admin): wait for the first claim before sending the duplicate

    The concurrent-duplicate idempotency test started both requests back to back, but each hashes its payload with crypto.subtle.digest, whose completion order is not guaranteed. When the duplicate's digest finished first it took the claim and blocked on the gate forever, the test timed out at 5s, and the un-awaited first request rejected with an unhandled 409 idempotency_in_progress.

    Signal from run() once the first request holds the claim and send the duplicate only after that, racing against the first request so an early failure surfaces instead of hanging. The assertions are unchanged.

Source: GitHub commits on main (stable) and dev (beta) · github.com/digitopvn/anymd

# Markdown
Open .md