moneo/markdown-for-agents

Laravel package for Cloudflare's Markdown conversion services.

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/moneo/markdown-for-agents

v1.0.0 2026-02-18 13:58 UTC

This package is auto-updated.

Last update: 2026-02-18 14:19:31 UTC


README

Tests codecov Latest Version on Packagist PHP Version Laravel Version PHPStan Level Code Style License

A Laravel package that unifies Cloudflare's three Markdown conversion services under one elegant API.

Services

Service Driver Key Description
Markdown for Agents agents Content negotiation via Accept: text/markdown on any Cloudflare-enabled URL. Free, no auth.
Workers AI toMarkdown workers_ai Converts files (PDF, DOCX, images, etc.) to Markdown via REST API. Auth required.
Browser Rendering browser Headless browser renders JS-heavy pages then converts to Markdown. Auth required.

Requirements

  • PHP 8.2+
  • Laravel 11.x or 12.x

Installation

composer require moneo/markdown-for-agents

Publish the config file:

php artisan vendor:publish --tag=markdown-for-agents-config

Configuration

Add your Cloudflare credentials to .env:

# Required for workers_ai and browser drivers
CF_ACCOUNT_ID=your-account-id
CF_API_TOKEN=your-api-token

# Optional overrides
MFA_DRIVER=agents
MFA_CACHE=true
MFA_CACHE_STORE=redis
MFA_CACHE_TTL=3600

The agents driver does not require credentials.

Usage

Convert a URL

use Moneo\MarkdownForAgents\Facades\MarkdownForAgents;

$result = MarkdownForAgents::url('https://example.com')->convert();

$result->markdown;       // string — the converted content
$result->tokens;         // int — estimated token count
$result->contentSignals; // ?array — ['ai-train' => 'yes', ...]
$result->driver;         // string — which driver was used
$result->fromCache;      // bool — served from cache?

Convert a file

// Single file
$result = MarkdownForAgents::file('/path/to/document.pdf')->convert();

// Laravel UploadedFile
$result = MarkdownForAgents::file($request->file('document'))->convert();

// Batch conversion
$results = MarkdownForAgents::files([$pdf, $image, $spreadsheet])->convert();

foreach ($results as $result) {
    echo "{$result->name}: {$result->tokens} tokens\n";
}

Convert raw HTML

$result = MarkdownForAgents::driver('browser')
    ->html('<div><h1>Hello</h1><p>World</p></div>')
    ->convert();

Choose a driver

// Use the browser driver for JS-heavy pages
$result = MarkdownForAgents::driver('browser')
    ->url('https://spa-app.com')
    ->waitUntil('networkidle0')
    ->convert();

Fallback

If the primary driver fails, automatically retry with another:

$result = MarkdownForAgents::url('https://example.com')
    ->withFallback('browser')
    ->convert();

Browser options

$result = MarkdownForAgents::driver('browser')
    ->url('https://example.com')
    ->waitUntil('networkidle0')
    ->userAgent('MyBot/1.0')
    ->rejectPatterns(['/^.*\.(css|font)/'])
    ->cookies([['name' => 'session', 'value' => 'abc', 'domain' => '.example.com']])
    ->authenticate('user', 'pass')
    ->convert();

Cache control

Caching is enabled by default for URL and HTML conversions:

// Skip cache for this request
$result = MarkdownForAgents::url($url)->noCache()->convert();

// Custom TTL (seconds)
$result = MarkdownForAgents::url($url)->cache(7200)->convert();

// Clear cache
MarkdownForAgents::clearCache('https://example.com');
MarkdownForAgents::flushCache();

Conditional fluent API

PendingConversion uses Laravel's Conditionable and Tappable traits:

$result = MarkdownForAgents::url($url)
    ->when($useBrowser, fn ($c) => $c->waitUntil('networkidle0'))
    ->unless($isAdmin, fn ($c) => $c->noCache())
    ->convert();

Supported formats

$formats = MarkdownForAgents::supportedFormats();
// Returns SupportedFormat[] — delegates to Workers AI

Driver Comparison

Scenario Recommended Driver
Cloudflare-enabled site agents (fastest, free)
PDF, DOCX, XLSX, images workers_ai (only option)
SPA / JS-heavy page browser (full render)
Non-Cloudflare site browser
Raw HTML string browser
Make your app agent-ready Middleware (local, no API)

Middleware

Add the middleware to routes to make your Laravel app respond with Markdown when AI agents request it:

Route::get('/blog/{slug}', [BlogController::class, 'show'])
    ->middleware('markdown-for-agents');

When a request includes Accept: text/markdown, the middleware converts HTML responses to Markdown locally using league/html-to-markdown. No Cloudflare API calls are made.

Response headers added:

  • Content-Type: text/markdown; charset=utf-8
  • Vary: accept
  • x-markdown-tokens: {count}
  • Content-Signal: ai-train=yes, search=yes, ai-input=yes

Artisan Commands

# Convert a URL
php artisan markdown:convert https://example.com
php artisan markdown:convert https://example.com --driver=browser
php artisan markdown:convert https://example.com --save=output.md

# Convert a file
php artisan markdown:convert /path/to/document.pdf

# List supported formats
php artisan markdown:formats

# Clear cache
php artisan markdown:cache:clear
php artisan markdown:cache:clear --url=https://example.com

Events

Two events are dispatched during conversions:

  • MarkdownConverted — on success (includes result, source, duration)
  • ConversionFailed — on failure (includes source, driver, exception)
use Moneo\MarkdownForAgents\Events\MarkdownConverted;

Event::listen(MarkdownConverted::class, function (MarkdownConverted $event) {
    Log::info("Converted {$event->source} in {$event->duration}s");
});

Custom Drivers

Register custom drivers that implement MarkdownConverterInterface:

use Moneo\MarkdownForAgents\Facades\MarkdownForAgents;

MarkdownForAgents::extend('custom', function ($app) {
    return new MyCustomDriver($app['config']);
});

$result = MarkdownForAgents::driver('custom')->url($url)->convert();

Testing

The package uses orchestra/testbench. All tests mock HTTP responses -- no real API calls.

composer test

When testing your own app's code that uses this package, mock the facade:

use Moneo\MarkdownForAgents\Facades\MarkdownForAgents;
use Moneo\MarkdownForAgents\DTOs\ConversionResult;

MarkdownForAgents::shouldReceive('url->convert')
    ->andReturn(new ConversionResult(...));

License

MIT