
mdisbetter-mcp
io.github.dresseurmonster2-oss/mdisbetter-mcp
Convert a local file to another format through mdisbetter.com: documents, images, audio, video.
Documentation
mdisbetter MCP server
An MCP server that lets an agent convert files. It exposes the mdisbetter.com converter over stdio: 3649 proven conversions across 162 input formats. Images, documents, ebooks, audio, video, office files, fonts, archives, data formats, subtitles and bibliographies. Eleven families, in and out, not only to PDF.
Two tools, and that is the whole surface:
| Tool | What it does |
|---|---|
convert_file | Takes a path and an output format, writes the converted file, returns its path. |
list_conversions | With an extension, the output formats proven for it. Without, every input extension accepted. |
What it does not do
This list is here because the opposite claim is easy to make by accident. The server has no batch mode, no webhooks, no job polling, no queue, no streaming, no URL input, and no CLI beyond the stdio entry point. It converts one local file per call, and waits for the answer.
It used to say "no audio or video conversion" here. That stopped being true when the conversion table was resynchronised: audio and video are served like every other family. Video that has to be re-encoded is capped at 120 seconds of source, because a re-encode costs real CPU; a container change that keeps the codecs is a stream copy and has no such limit.
Requirements
- Node.js 20 or later.
- An mdisbetter.com account and an API key. Signup is required; keys start with
mdb_sk_.
Create one from your dashboard, under API keys. The plaintext key is shown once and never again: only its hash is stored, so nobody can recover it for you, us included. Store it before closing the panel.
Conversions cost credits from the same balance the website spends. There is no separate quota for programmatic use.
Install
git clone https://github.com/dresseurmonster2-oss/mdisbetter-mcp.git
cd mdisbetter-mcp
npm install
Or run it without cloning:
npx -y github:dresseurmonster2-oss/mdisbetter-mcp
As an MCP bundle
Clients that open .mcpb bundles can install the server from the file attached to each
release: download
mdisbetter-mcp.mcpb, open it, and enter your API key when asked. The bundle is the same code
as this repository with its dependencies included; build it yourself with npm run bundle.
The server is listed in the official MCP Registry as
io.github.dresseurmonster2-oss/mdisbetter-mcp.
Configure
Two environment variables, one of them optional.
| Variable | Required | Meaning |
|---|---|---|
MDISBETTER_API_KEY | yes | Your key, starting with mdb_sk_. |
MDISBETTER_BASE_URL | no | Defaults to https://mdisbetter.com. Point it at a preview deployment to test against one. |
The key is read from the server environment and is never a tool argument. The server does not write it to an output file. Keep your client configuration private, and revoke the key from your dashboard if it is exposed.
Wire it into a client
Most MCP clients read a JSON block like this one. The path is wherever you cloned the repository.
{
"mcpServers": {
"mdisbetter": {
"command": "node",
"args": ["/absolute/path/to/mcp-server/src/index.js"],
"env": {
"MDISBETTER_API_KEY": "mdb_sk_your_key_here"
}
}
}
}
Restart the client. list_conversions and convert_file appear in its
tool list.
The tools
list_conversions
// every input format the converter accepts
{ "name": "list_conversions", "arguments": {} }
// what a .docx can become
{ "name": "list_conversions", "arguments": { "extension": "docx" } }
The extension may be written with or without its dot, in any case.
.DOCX, docx and .docx are the same question.
An extension the converter cannot read is refused, rather than answered with an empty list.
convert_file
{
"name": "convert_file",
"arguments": {
"path": "/home/me/report.docx",
"target": "pdf"
}
}
| Argument | Required | Meaning |
|---|---|---|
path | yes | The file to convert, on the machine running this server. |
target | yes | Output format as a bare extension: pdf, png, md. |
output_path | no | Where to write the result. Overwrites if it exists. |
Without output_path, the result lands beside the input with the new
extension: report.docx becomes report.pdf in the same directory. If
that name is taken the server writes report-1.pdf instead. It never
overwrites a file it was not pointed at, and it never touches the input.
The server rejects local inputs above 150 MB. The effective limit is lower when your account plan or the conversion family requires it: Free accounts have a 10 MB file limit, and document conversions have a 25 MB family limit. The server ceiling is not a 150 MB allowance for every format or account.
Under 4 MB the file travels in the request itself, which is one round trip. Above it, the server uploads to storage first and sends only the address, because the platform hosting the API caps a request body AND a response body at 4.5 MB. Measured on 31/08/2026 in 100 KB steps: 4.2 MB passes, 4.3 MB returns a 413 raised before the function even runs. This README claimed 25 MB until that day, a figure taken from the engine and never reachable through the transport.
A pair that is not in the table is refused before anything is sent, and the refusal lists the targets that would work:
.png to .docx is not a proven conversion. Proven targets for .png (28):
avif, bmp, dds, eps, gif, hdr, ico, jng, jp2, jpeg, jpg, miff, pbm, pcx,
pdf, pgm, pnm, ppm, ps, psd, ras, sgi, tga, tif, tiff, wbmp, webp, xpm.
Walkthrough: local HTML to Markdown
- Sign up or sign in at your dashboard, create an API key and save it when it is shown. Configure your MCP client as above and restart it. Keep the key out of prompts and source control.
- Save this owned example as
field-notes.htmlon the machine running the server:
<html><body><h1>Harbor field notes</h1><p>Inspection marker: MCPCA0420261013. Tide reading: 42 centimetres.</p><h2>Actions</h2><ul><li>Check the north gate.</li><li>Record the reading.</li></ul></body></html>
- Ask your client to call these tools in order. Replace the input path with the actual absolute path, using a drive-qualified path on Windows:
{"name":"list_conversions","arguments":{"extension":"html"}}
{"name":"convert_file","arguments":{"path":"/absolute/path/field-notes.html","target":"md"}}
Check that the first answer lists md before converting. The second answer
reports where it wrote the file. Open that Markdown file and verify the heading,
reading and both action items. These are MCP tool arguments, not a separate
public HTTP interface or a standalone conversion command.
The verified output is:
# Harbor field notes
Inspection marker: MCPCA0420261013. Tide reading: 42 centimetres.
## Actions
- Check the north gate.
- Record the reading.
Verified against source 84b2bed and production on October 13, 2026
(Singapore time): a fresh ordinary Free account started with 50 credits.
Listing HTML targets cost zero credits; converting this file over real stdio
cost 0.5 credit and wrote 151 bytes. The identical conversion authenticated
with an ordinary website session also cost 0.5 credit and returned identical
bytes. The heading, marker, 42-centimetre reading and both actions survived.
An unsupported HTML-to-PNG request wrote no file and cost zero credits.
The test key was revoked and the synthetic account disabled afterward.
This small HTML example does not certify every catalogue pair, large uploads,
paid-plan limits, your client configuration or complex document fidelity.
No account allowance was increased for the test. Inspect each result before
using it; an explicit output_path replaces an existing file at that path.
When the file does not come back
The server translates each answer rather than passing along a bare status code.
| Situation | What the agent is told |
|---|---|
| Out of credits | How many credits the conversion costs, how many remain, how many are missing, and the top-up page. This is not a fault: the account is simply out of credits, and the conversion did not happen. |
| Key rejected | The key is missing, invalid, or revoked. Set MDISBETTER_API_KEY. |
| File refused | The pair is proven, so the file itself is the problem: contents that do not match the extension, or something the engine could not read. |
| File too large | The local server ceiling, account plan limit or conversion family limit was exceeded. |
| Converter at capacity | The service sheds load on purpose rather than degrading. Nothing broke, nothing was charged, retry in a few seconds. |
| Service unavailable or timed out | Said plainly, and no file is written. |
Nothing is ever written to disk unless a converted file actually came back with bytes in it.
Where the conversion table comes from
src/formats.js is a copy of the table the website itself uses. It is
generated, not written by hand, and it contains only pairs that a probe
converted for real and then read back, checking the output was the format
it claims to be. Formats an engine merely advertises do not make it in.
Do not edit it. Regenerate it in the main repository:
python scripts/generer-formats.py
That rewrites site/content/formats.js from the probe results, and the
file is then copied here unchanged, keeping its original header.
The table maps each pair to one of four endpoints, chosen by what serves that pair best rather than by the file's family:
| Endpoint | Pairs | Typically |
|---|---|---|
/api/convert-image | 1189 | image to image |
/api/convert-media | 1117 | audio and video |
/api/convert-document | 895 | markup and document formats |
/api/convert-ebook | 298 | ebook and reader formats |
/api/convert-office | 41 | office documents, spreadsheets and slides |
/api/convert-archive | 30 | archives: ZIP, TAR and its compressions, 7z |
/api/convert-subtitle | 25 | subtitles: SRT, VTT, ASS, SSA, LRC and TTML |
/api/convert-data | 24 | data formats: JSON, YAML, TOML, CSV, TSV, NDJSON |
/api/convert-font | 18 | fonts: TrueType, OpenType, WOFF and WOFF2 |
/api/convert-bibliography | 12 | bibliographies: BibTeX, BibLaTeX, CSL JSON, RIS and EndNote XML |
These counts are checked against the table by test/readme.test.mjs. The
table itself is generated from probes against the real engine, so a stale
number here means the README was not updated, never that the table is
wrong.
Several extensions could be served by more than one of these. A .docx
to PDF goes through the office endpoint because that keeps the layout,
while .docx to Markdown goes through the document endpoint because that
keeps the text faithful. The caller never has to know: it names a file and
a target format.
Tests
npm test
The suite runs offline. It spawns the server as a real process and speaks MCP to it over stdio, checks the routing of every one of the 3649 pairs, checks that an unproven pair is refused before any request is made, and checks the wording of each answer, including that running out of credits is never described as a failure.
The tests do not perform a real conversion. That needs a key and a credit balance, neither of which belongs in a test suite.
License
MIT. See LICENSE.
The converter itself is a hosted service; this repository is the connector that reaches it. Conversions cost credits from your mdisbetter.com balance.
https://github.com/dresseurmonster2-oss/mdisbetter-mcp/releases/download/v0.1.0/mdisbetter-mcp.mcpb# mcpb: https://github.com/dresseurmonster2-oss/mdisbetter-mcp/releases/download/v0.1.0/mdisbetter-mcp.mcpb