Skip to main content

Convert documents (txt, markdown, pdf, etc.) to HTML

Project description

Toa is a library for converting documents to HTML.

[!tip] By default, it returns a string-type HTML fragment. You can decide how to render it yourself, such as beautifying the page display.

The built-in HTML templates can return fully structured HTML content, supporting Mermaid stylesheets, formulas, and syntax highlighting, which can be enabled selectively.

Toa supports the following conversions:

  • Ebooks: EPUB
  • Emails: EML, MSG(outlook)
  • Documents: DOCX, PDF, TXT, MD(Markdown)
  • Spreadsheets: XLSX, XLS, CSV
  • Presentations: PPTX
  • Data Exchange: JSON, JSONL
  • Markup Languages: XML, HTML
  • Multimedia: audio files, video files, supports audio-to-text conversion and video subtitles extraction.
  • Resources from the Internet
  • ...

Toa provides convenience for online previewing of documents, emails, data reports, e-books, etc.

Of course, Toa can also convert HTML to Markdown.

Install

pip install toa

Can be used after installation

toa -h
toa-mcp -help

It may only support following content can be converted to HTML.

  • such as json, jsonl, txt, md, etc.
  • file streams
  • external links (such as web crawlers, FTP, and WebSockets)

Extras: [mcp], [csv], [docx], [epub], [excel], [outlook], [pdf], [pptx], [rss], [audio], [video]

uv add 'toa[mcp, csv, excel]'
  • [lite] supports capabilities other than [video].
  • [all] will install all dependencies. If you don't want to make a choice, this might be the best option, and an additional OCR engine needs to be installed.

with OCR If OCR image recognition is required, you can choose an OCR engine. Such as paddleocr, easyocr, pytesseract, for example:

uv add paddlepaddle paddleocr

Usage

Use in command line

toa md.md

toa md.md -o md.html --wrapper default --mermaid --highlight --formula

[!note] If the file does not exist, it may be treated as a string and ultimately returned as an HTML fragment.

  • --wrapper: HTML fragment wrapper
  • -o: output filename
  • --mermaid: mermaid rendering is required.
  • --highlight: highlight rendering is required.
  • --formula: Requires support for rendering mathematical formulas.

standard input

cat pdf.pdf | toa

cat pdf.pdf | toa --wrapper default -o pdf.html --source-url https://example.com/pdf.pdf --engine default
  • --source-url: An accessible file address ensures the page displays correctly.
  • --engine: rendering engine name

[!important] Some content can only be displayed correctly with server support.

fetch from websocket endpoint

toa ws://examples.com/ws
toa wss://examples.com/wss

fetch from ftp server

toa ftp://ftp.example.com/readme.txt

Use it in Python coding

from toa import Toa

toa = Toa()
result = toa.convert('md.md')
print(result.content)  # <p>md.md</p>

toa-mcp

Toa has built-in MCP capabilities. The corresponding separate command is toa-mcp. toa-mcp is a server based on the MCP (Model Context Protocol) protocol, providing a standardized API interface for the toa command-line tool.

Multi-mode Operation:

  • HTTP: Provides a RESTful API interface, suitable for web integration.
  • SSE: Server sends events, supports real-time push notifications.
  • WebSocket: Bidirectional persistent connection for real-time interaction.
  • Stdio: Standard input/output communication, suitable for integration with AI assistants.
endpoint method description
/sse Get/POST Message data is transmitted in a streaming manner (Server-to-Client).
/messages GET Standard message retrieval endpoint (Polling/REST).
/mcp GET Streamable HTTP (Model Context Protocol / Long-polling transport).
/ws GET (Upgrade) Full-duplex bidirectional streaming (WebSocket connection for real-time interaction).
/ping GET Check service availability (Heartbeat).

MCP protocol supports

{
  "jsonrpc": "2.0",
  "method": "tools/list",
  "params": {},
  "id": 1
}

Integrations

There are multiple ways to integrate.

Microservice architecture

prepare docker-compose.yml

# docker-compose.yml
services:
  toa-cli:
    image: sunshmo/toa:latest
    entrypoint: ["toa"]
    command: ["--help"]
    stdin_open: true
    tty: true
  toa-mcp:
    image: sunshmo/toa:latest
    entrypoint: ["toa-mcp", "--transport", "http"]
    ports:
      - "56156:56156"

  # other services

Build and start service

docker-compose up toa-cli
docker-compose up -d toa-mcp

AI assistant integration Such as, Cursor, openclaw, claude, etc.

{
  "mcpServers": {
    "toa": {
      "command": "toa-mcp",
      "args": ["--transport", "stdio"]
    }
  }
}

You can also use the docker command.

{
  "mcpServers": {
    "toa": {
      "command": "docker",
      "args": [
        "run",
        "--rm",
        "-i",
        "-v",
        "./path/data:/data", // resource directory mapping
        "sunshmo/toa:latest",
        "toa-mcp",
        "--transport",
        "stdio"
      ]
    }
  }
}

Integration with coding If toa-mcp is already running, then you can call it in the program. No programming language restrictions. For example, below uses HTTP transport.

async def with_http():
    import httpx

    async with httpx.AsyncClient(follow_redirects=True) as client:
        response = await client.post(
            "http://localhost:56156/mcp",
            headers={"Content-Type": "application/json", "Accept": "application/json"},
            json={"jsonrpc": "2.0", "method": "tools/list", "id": 1},
        )
        print("tools:", response.json())
Below is the capability of Java to call toa-mcp
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class McpHttpClient {
    private static final ObjectMapper mapper = new ObjectMapper();
    
    public static void withHttp() throws Exception {
        HttpClient client = HttpClient.newBuilder()
            .followRedirects(HttpClient.Redirect.NORMAL)
            .connectTimeout(Duration.ofSeconds(10))
            .build();
        
        // build JSON-RPC request
        ObjectNode request = mapper.createObjectNode();
        request.put("jsonrpc", "2.0");
        request.put("method", "tools/list");
        request.put("id", 1);
        
        HttpRequest httpRequest = HttpRequest.newBuilder()
            .uri(URI.create("http://localhost:56156/mcp"))
            .header("Content-Type", "application/json")
            .header("Accept", "application/json")
            .timeout(Duration.ofSeconds(30))
            .POST(HttpRequest.BodyPublishers.ofString(mapper.writeValueAsString(request)))
            .build();
        
        HttpResponse<String> response = client.send(httpRequest, HttpResponse.BodyHandlers.ofString());
        
        if (response.statusCode() == 200) {
            JsonNode result = mapper.readTree(response.body());
            System.out.println("tools: " + result.toPrettyString());
        } else {
            System.err.println("HTTP Error: " + response.statusCode());
        }
    }
    
    public static void main(String[] args) throws Exception {
        withHttp();
    }
}

with web MCP Inspector debug

npx @modelcontextprotocol/inspector

The accessible UI address after startup is http://localhost:6274. You can switch the Transport Type to STDIO, SSE, or Streamable HTTP.

STDIO: Enter toa-mcp in the command field and click Connect.

SSE:

  • Start service: toa-mcp --transport http, or toa-mcp --transport sse, or toa-mcp --transport ws can all start the web service.
  • Enter URL: http://localhost:56156/sse
  • click Connect

Streamable HTTP:

  • Enter URL: http://localhost:56156/mcp
  • click Connect

Docker

Running MCP Web Server Using Docker to build eliminates the hassle of OCR installation. Running toa-mcp service in Docker.

docker run -d \
  -p 56156:56156 \
  --name toa \
  -v /the/host/dir:/data \
  sunshmo/toa:latest \
  toa-mcp --transport http

Verify if the service is usable, visit http://localhost:56156/ping, Seeing "pong" indicates a normal startup.

[!tip] -v option maps a host directory to a container directory, allowing you to use a directory path within the container as the target file path when calling the function.

STDIO

docker run --rm -i sunshmo/toa:latest toa < your.eml

Plugin

In reality, there may be files that Toa currently does not support for conversion, or you may want to override Toa's default capabilities. In such cases, you can achieve this by using an extension plugin.

You can do like this:

  1. Implement a class that inherits from BaseConverter
  2. then register the converter with Toa

Below is one full example scenario.

plugin example
from typing import BinaryIO

from toa import Toa, BaseConverter, InputInfo, OutputInfo, ConversionResult


class MyToaConverter(BaseConverter):
    def __init__(self) -> None:
        super().__init__()

    @property
    def check_dependencies(self) -> bool:
        """
        Returns a boolean value. Returns True if there are no external dependencies;
        otherwise, the conversion will throw an error.
        """
        return True

    @property
    def suffixes(self) -> list[str]:
        """Define extension (Format: ['.toa'])"""
        return [".toa"]

    @property
    def mime_types(self) -> list[str]:
        return []

    def convert(
        self,
        input_stream: BinaryIO,
        input_info: InputInfo,
        output_info: OutputInfo | None = None,
        **kwargs,
    ) -> ConversionResult:
        return ConversionResult(content="Result from plugin conversion")


toa = Toa()

# register
toa.add_converter(MyToaConverter())

result = toa.convert("abc.toa")
print(result.content)

[!note] Either suffixes or mime_types must be a value that is not of type NoneType.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

toa-0.1.1.tar.gz (61.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

toa-0.1.1-py3-none-any.whl (77.6 kB view details)

Uploaded Python 3

File details

Details for the file toa-0.1.1.tar.gz.

File metadata

  • Download URL: toa-0.1.1.tar.gz
  • Upload date:
  • Size: 61.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.8

File hashes

Hashes for toa-0.1.1.tar.gz
Algorithm Hash digest
SHA256 487d8effeeb0d279be61bfa03a1aa2508bfca26a6f5c84307b06fd44d14f7b25
MD5 9c62a69aec747e50ca58d766c1084f43
BLAKE2b-256 cf314e8faa57fccf0a775cf57318428ac665e0daf1e6dfe2a8b5d2e188b7d083

See more details on using hashes here.

File details

Details for the file toa-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: toa-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 77.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.8

File hashes

Hashes for toa-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 69f34b05eb76cc62f822f2a7f7dab88049321bf98952573ef3fc7e212bd23f90
MD5 d1b4352637c799e71f52afea5674cc16
BLAKE2b-256 a881a8442aae4456198b1ac078f84d26399ea03d5672c7333adcc1c18deae7d6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page