#
tokens: 2650/50000 7/7 files
lines: off (toggle) GitHub
raw markdown copy
# 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
[![smithery badge](https://smithery.ai/badge/@Handwriting-OCR/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);

```