# Directory Structure
```
├── .gitignore
├── Dockerfile
├── package-lock.json
├── package.json
├── README.md
├── smithery.yaml
├── src
│ └── index.ts
└── tsconfig.json
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
node_modules/
build/
*.log
.env*
```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
```markdown
# Handwriting OCR MCP Server
[](https://smithery.ai/server/@Handwriting-OCR/handwriting-ocr-mcp-server)
A Model Context Protocol (MCP) Server for [Handwriting OCR](https://www.handwritingocr.com) API.
## Overview
The Handwriting OCR MCP Server enables integration between MCP clients and the Handwriting OCR service. This document outlines the setup process and provides a basic example of using the client.
This server allows you to upload images and PDF documents, check their status, and retrieve the OCR result as Markdown.
## Tools
### Transcription
* Upload Document
* Check Status
* Get Text
## Prerequisites
Before you begin, ensure you have the following:
* Node.js installed on your system (recommended version 18.x or higher).
* An active account on the [Handwriting OCR Platform](https://www.handwritingocr.com) and an active [API token](https://www.handwritingocr.com/settings/api).
## Installation
### Installing via Smithery
To install handwriting-ocr-mcp-server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@Handwriting-OCR/handwriting-ocr-mcp-server):
```bash
npx -y @smithery/cli install @Handwriting-OCR/handwriting-ocr-mcp-server --client claude
```
### Installing manually for Claude Desktop
To use the Handwriting OCR MCP Server in Claude Desktop application, use:
```json
{
"mcpServers": {
"handwriting-ocr": {
"command": "node",
"args": [
"/Users/mateo/Local/Code/MCP/handwriting-ocr/build/index.js"
],
"env": {
"API_TOKEN": "your-api-token",
},
"disabled": false,
"autoApprove": []
}
}
}
```
## Configuration
The Handwriting OCR MCP Server supports environment variables to be set for authentication and configuration:
* `API_TOKEN`: Your API token.
You can find these values in the API settings dashboard on the [Handwriting OCR Platform](https://www.handwritingocr.com).
## Support
Please refer to the [Handwriting OCR API Documentation](https://www.handwritingocr.com/api/docs).
For support with the Handwriting OCR MCP Server, please submit a [GitHub Issue](https://github.com/modelcontextprotocol/servers/issues).
## About
Model Context Protocol (MCP) Server for Handwriting OCR Platform
```
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
```
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
```dockerfile
# Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
FROM node:lts-alpine
WORKDIR /app
# Copy package files
COPY package.json package-lock.json ./
# Install dependencies (ignore scripts to avoid any unwanted behavior)
RUN npm install --ignore-scripts
# Copy the rest of the source code
COPY . .
# Build the project
RUN npm run build
# Expose port if needed (not specified, but MCP runs on stdio)
# Run the MCP server
CMD [ "node", "build/index.js" ]
```
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
```yaml
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
startCommand:
type: stdio
configSchema:
# JSON Schema defining the configuration options for the MCP.
type: object
required:
- apiToken
properties:
apiToken:
type: string
description: Your Handwriting OCR API token
commandFunction:
# A JS function that produces the CLI command based on the given config to start the MCP on stdio.
|-
(config) => ({
command: 'node',
args: ['build/index.js'],
env: { API_TOKEN: config.apiToken }
})
exampleConfig:
apiToken: your-api-token
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
{
"name": "handwriting-ocr",
"version": "0.1.0",
"description": "A Model Context Protocol server to enable integration between MCP clients and the Handwriting OCR service.",
"private": true,
"type": "module",
"bin": {
"handwriting-ocr": "./build/index.js"
},
"files": [
"build"
],
"scripts": {
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
"prepare": "npm run build",
"watch": "tsc --watch",
"inspector": "npx @modelcontextprotocol/inspector build/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "0.6.0",
"axios": "^1.8.2"
},
"devDependencies": {
"@types/node": "^20.11.24",
"typescript": "^5.3.3"
}
}
```
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
```typescript
#!/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);
```