Handwriting OCR MCP Server
Official
#!/usr/bin/env node
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import axios from 'axios';
import * as fs from 'fs';
import FormData from 'form-data';
const API_TOKEN = process.env.API_TOKEN;
const server = new Server(
{
name: 'handwriting-ocr',
version: '0.1.0',
},
{
capabilities: {
tools: {},
},
}
);
server.onerror = (error) => console.error('[MCP Error]', error);
process.on('SIGINT', async () => {
await server.close();
process.exit(0);
});
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'upload_document',
description: 'Upload a document to Handwriting OCR API for transcription',
inputSchema: {
type: 'object',
properties: {
file: {
type: 'string',
description: 'Path to the document (PDF, JPG, PNG, etc.)',
},
delete_after: {
type: 'integer',
description: 'Seconds until auto-deletion (optional)',
},
extractor_id: {
type: 'string',
description: 'Extractor ID (required if action is extractor, will be ignored)',
},
prompt_id: {
type: 'string',
description: 'Prompt ID (requires Enterprise subscription, will be ignored)',
},
},
required: ['file'],
},
},
{
name: 'check_status',
description: 'Check the status of a document',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Document ID',
},
},
required: ['id'],
},
},
{
name: 'get_text',
description: 'Retrieve the transcribed text from a document',
inputSchema: {
type: 'object',
properties: {
id: {
type: 'string',
description: 'Document ID',
},
},
required: ['id'],
},
},
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (!API_TOKEN) {
throw new Error('API_TOKEN environment variable is required');
}
switch (request.params.name) {
case 'upload_document': {
interface FileObject {
data: any;
name: string;
}
const file = request.params.arguments?.file as string | FileObject;
if (!file) {
throw new Error('File is required');
}
let fileData: Buffer;
let fileName: string;
if (typeof file === 'string') {
// File path provided
const filePath = file;
fileData = fs.readFileSync(filePath);
fileName = filePath.split('/').pop() || 'document';
} else {
// File object (attachment data) provided
fileData = Buffer.from(file.data);
fileName = file.name;
}
const formData = new FormData();
formData.append('file', fileData, fileName);
formData.append('action', 'transcribe');
const deleteAfter = request.params.arguments?.delete_after;
if (deleteAfter) {
formData.append('delete_after', String(deleteAfter));
}
try {
const response = await axios.post(
'https://www.handwritingocr.com/api/v3/documents',
formData,
{
headers: {
'Content-Type': `multipart/form-data; boundary=${formData.getBoundary()}`,
Authorization: `Bearer ${API_TOKEN}`,
Accept: 'application/json',
},
}
);
return {
content: [
{
type: 'text',
text: JSON.stringify({
id: response.data.id,
status: response.data.status,
}),
},
],
};
} catch (error: any) {
console.error('[API Error]', error);
throw new Error(`Handwriting OCR API error: ${error.message}`);
}
}
case 'check_status': {
const documentId = String(request.params.arguments?.id);
if (!documentId) {
throw new Error('Document ID is required');
}
try {
const response = await axios.get(
`https://www.handwritingocr.com/api/v3/documents/${documentId}`,
{
headers: {
Authorization: `Bearer ${API_TOKEN}`,
Accept: 'application/json',
},
}
);
return {
content: [
{
type: 'text',
text: JSON.stringify({
id: response.data.id,
file_name: response.data.file_name,
action: response.data.action,
page_count: response.data.page_count,
status: response.data.status,
created_at: response.data.created_at,
updated_at: response.data.updated_at,
}),
},
],
};
} catch (error: any) {
console.error('[API Error]', error);
throw new Error(`Handwriting OCR API error: ${error.message}`);
}
}
case 'get_text': {
const documentId = String(request.params.arguments?.id);
if (!documentId) {
throw new Error('Document ID is required');
}
try {
const response = await axios.get(
`https://www.handwritingocr.com/api/v3/documents/${documentId}.txt`,
{
headers: {
Authorization: `Bearer ${API_TOKEN}`,
Accept: 'text/plain',
},
responseType: 'text',
}
);
return {
content: [
{
type: 'text',
text: response.data,
},
],
};
} catch (error: any) {
console.error('[API Error]', error);
throw new Error(`Handwriting OCR API error: ${error.message}`);
}
}
default:
throw new Error('Unknown tool');
}
});
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('Handwriting OCR MCP server running on stdio');
}
main().catch(console.error);