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
Install code dependencies
Install via code, suitable for developers
uv add toa
# or
pip install toa
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]
# for convenience, `uv` as the primary tool here
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
Install with docker
Using Docker to build eliminates the hassle of OCR installation.
# from source
# docker build -t toa:latest .
# from the built image
docker pull toa:latest
Using toa CLI capabilities
docker run --rm toa:latest toa md.md
Using toa-mcp server capabilities
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
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>
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:
- Implement a class that inherits from BaseConverter
- then register the converter with Toa
Below is one full example scenario.
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
suffixesormime_typesmust be a value that is not of typeNoneType.
[!tip] If you don't want to use the built-in templates, you can implement one or more methods that return HTML strings, and then set
wrapperas the method name.
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.
MCP protocol supports
{
"jsonrpc": "2.0",
"method": "tools/list",
"params": {},
"id": 1
}
toa-mcp Integrations
There are multiple ways to integrate.
As a Docker Service
Build the toa-mcp service using Docker command.
docker run -d \
-p 56156:56156 \
--name toa \
-v /the/host/dir:/data \
toa:latest \
toa-mcp --transport http
Verify if the service is usable, visit http://localhost:56156/ping, Seeing "pong" indicates a normal startup.
[!tip]
-voption 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.
Microservice architecture
prepare docker-compose.yml
# docker-compose.yml
services:
toa-cli:
image: toa:latest
command: ["toa", "--help"]
stdin_open: true
tty: true
toa-mcp:
image: toa:latest
command: ["toa-mcp"]
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
"toa:latest",
"toa-mcp",
"--transport",
"stdio"
]
}
}
}
Web Service
| 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). |
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 mode: Enter toa-mcp in the command field and click Connect.
SSE mode:
- Activate virtual environment:
source .venv/bin/activate - Start service:
toa-mcp --transport http, ortoa-mcp --transport sse, ortoa-mcp --transport wscan all start the web service. - Enter
URL:http://localhost:56156/sse - click
Connect
Streamable HTTP mode:
- The virtual environment is already activated and does not need to be activated again; the web service is already started.
- Enter
URL:http://localhost:56156/mcp - click
Connect
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file toa-0.1.0.tar.gz.
File metadata
- Download URL: toa-0.1.0.tar.gz
- Upload date:
- Size: 61.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
767fb56e1dc583f6ec072eee726c09b9dc6d158fa144ac5cf4d305b08fb02bab
|
|
| MD5 |
f66ce31e5514ae82f90cac69af83ec88
|
|
| BLAKE2b-256 |
af1b90b46755e5ea89d6e8dbc051ceb0bd7627094d776e446c8212dbb479d5a5
|
File details
Details for the file toa-0.1.0-py3-none-any.whl.
File metadata
- Download URL: toa-0.1.0-py3-none-any.whl
- Upload date:
- Size: 77.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
652da9a5971916dd4f54f3a703412e9dbde9bebb485831b6463e0cfd7489ba0a
|
|
| MD5 |
fe846f8d955fb489bf3f6749a1670c0b
|
|
| BLAKE2b-256 |
90127c3b09f844070195fcf118ceda142d27430e185b3b3a911131b548d3d7bf
|