pdf-to-markdown-mcp 1.0.8 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "chat.instructionsFilesLocations": {
3
3
  ".github/instructions": true,
4
- "C:\\Users\\YXZHK\\source\\explore\\awesome-copilot\\instructions\\markdown.instructions.md": true,
5
- "C:\\Users\\YXZHK\\source\\explore\\awesome-copilot\\instructions\\security-and-owasp.instructions.md": true,
6
- "C:\\Users\\YXZHK\\source\\explore\\awesome-copilot\\instructions\\self-explanatory-code-commenting.instructions.md": true,
7
- "C:\\Users\\YXZHK\\source\\explore\\awesome-copilot\\instructions\\typescript-mcp-server.instructions.md": true,
8
- "C:\\Users\\YXZHK\\source\\explore\\awesome-copilot\\instructions\\typescript-5-es2022.instructions.md": true,
9
- },
4
+ "~/source/explore/awesome-copilot/instructions/markdown.instructions.md": true,
5
+ "~/source/explore/awesome-copilot/instructions/security-and-owasp.instructions.md": true,
6
+ "~/source/explore/awesome-copilot/instructions/self-explanatory-code-commenting.instructions.md": true,
7
+ "~/source/explore/awesome-copilot/instructions/typescript-mcp-server.instructions.md": true,
8
+ "~/source/explore/awesome-copilot/instructions/typescript-5-es2022.instructions.md": true,
9
+ "~/source/explore/awesome-copilot/instructions/update-docs-on-code-change.instructions.md": true,
10
+ }
10
11
  }
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
4
  [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)
5
5
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
6
- [![MCP](https://img.shields.io/badge/MCP-1.25.2-purple.svg)](https://modelcontextprotocol.io/)
6
+ [![MCP](https://img.shields.io/badge/MCP-1.27.1-purple.svg)](https://modelcontextprotocol.io/)
7
7
 
8
8
  A Model Context Protocol (MCP) server that converts PDF pages to markdown format using the Qwen VL vision model.
9
9
 
@@ -28,7 +28,11 @@ npm run build
28
28
  The server needs the following environment variables:
29
29
  - `QWEN_API_URL`: The endpoint URL (e.g., `https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions`)
30
30
  - `QWEN_API_KEY`: Your authentication key.
31
- - `QWEN_MODEL`: The specific model name (defaults to `qwen-vl-max`).
31
+ - `QWEN_MODEL`: The specific model name (defaults to `Qwen3-VL-235B-A22B-Instruct`).
32
+ - `VLLM_REASONING_PARSER` (optional): When you serve a reasoning model through vLLM and need thinking disabled, set this to `kimi_k2` for Kimi K2.x or `qwen3` for Qwen3. The server will send the matching `chat_template_kwargs` expected by vLLM.
33
+ - `WORKSPACE` (optional): Comma-separated list of absolute directory paths. When set, the server will only process PDF files located within these directories, preventing access to files outside the allowed workspace.
34
+
35
+ The runtime and test scripts automatically load a project-local `.env` file when one is present, so you can usually keep these values in `.env` instead of exporting them in your shell.
32
36
 
33
37
  ### 3. Setup with Claude Desktop
34
38
  Add this to your Claude Desktop configuration file:
@@ -45,7 +49,36 @@ Add this to your Claude Desktop configuration file:
45
49
  "env": {
46
50
  "QWEN_API_URL": "https://your-qwen-api-endpoint.com/v1/chat/completions",
47
51
  "QWEN_API_KEY": "your-api-key-here",
48
- "QWEN_MODEL": "qwen-vl-max"
52
+ "QWEN_MODEL": "Qwen3-VL-235B-A22B-Instruct"
53
+ }
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### vLLM reasoning models
60
+
61
+ If your OpenAI-compatible endpoint is powered by vLLM and the served model defaults to thinking mode, configure the parser so the server can disable it correctly:
62
+
63
+ - `VLLM_REASONING_PARSER=kimi_k2` sends `chat_template_kwargs: { "thinking": false }`
64
+ - `VLLM_REASONING_PARSER=qwen3` sends `chat_template_kwargs: { "enable_thinking": false }`
65
+
66
+ Example MCP configuration for Kimi K2.x through vLLM:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "pdf-to-markdown": {
72
+ "command": "npx",
73
+ "args": [
74
+ "-y",
75
+ "pdf-to-markdown-mcp"
76
+ ],
77
+ "env": {
78
+ "QWEN_API_URL": "http://your-vllm-host:8000/v1/chat/completions",
79
+ "QWEN_API_KEY": "EMPTY",
80
+ "QWEN_MODEL": "moonshotai/Kimi-K2.6-Vision-Instruct",
81
+ "VLLM_REASONING_PARSER": "kimi_k2"
49
82
  }
50
83
  }
51
84
  }
@@ -76,7 +109,7 @@ Depending on your OS, you may need additional libraries for PDF rendering:
76
109
  ## Troubleshooting
77
110
  - **"PDF file not found"**: Ensure the path is absolute and the file is accessible.
78
111
  - **"Invalid page number"**: Check that the page number exists in the document.
79
- - **API Errors**: Verify your `QWEN_API_URL` and `QWEN_API_KEY`.
112
+ - **API Errors**: Verify your `QWEN_API_URL` and `QWEN_API_KEY` in `.env` or your shell environment.
80
113
  - **Render Failures**: If conversion fails on Linux/macOS, ensure the **System Dependencies** above are installed.
81
114
 
82
115
  ## License
package/TESTING.md CHANGED
@@ -17,7 +17,7 @@ This will test:
17
17
 
18
18
  ### 2. Test Full PDF to Markdown Conversion (Requires API Keys)
19
19
 
20
- First, set up your Qwen API credentials:
20
+ The test runner automatically loads a project-local `.env` file when one is present. If you prefer not to use `.env`, set up your Qwen API credentials manually:
21
21
 
22
22
  **Windows (PowerShell):**
23
23
  ```powershell
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- export {};
2
+ import "dotenv/config";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC"}
package/dist/src/index.js CHANGED
@@ -1,34 +1,19 @@
1
1
  #!/usr/bin/env node
2
+ import "dotenv/config";
2
3
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
5
  import * as fs from "node:fs/promises";
6
+ import * as path from "node:path";
5
7
  import { z } from "zod";
8
+ import { buildOpenAICompatibleRequest } from "./openaiCompatibleRequest.js";
6
9
  import { convertPdfPageToImage } from "./pdfConverter.js";
7
10
  /**
8
11
  * Call Qwen VL API to convert image to markdown
9
12
  */
10
- async function convertImageToMarkdown(imageBuffer, apiUrl, apiKey, modelName) {
11
- const base64Image = imageBuffer.toString("base64");
12
- const requestBody = {
13
- model: modelName,
14
- messages: [
15
- {
16
- role: "user",
17
- content: [
18
- {
19
- type: "image_url",
20
- image_url: {
21
- url: `data:image/png;base64,${base64Image}`,
22
- },
23
- },
24
- {
25
- type: "text",
26
- text: "Please convert this image to markdown format. Extract all text, tables, and structure accurately.",
27
- },
28
- ],
29
- },
30
- ],
31
- };
13
+ async function convertImageToMarkdown(imageBuffer, apiUrl, apiKey, modelName, vllmReasoningParser) {
14
+ const requestBody = buildOpenAICompatibleRequest(imageBuffer, modelName, {
15
+ vllmReasoningParser,
16
+ });
32
17
  const response = await fetch(apiUrl, {
33
18
  method: "POST",
34
19
  headers: {
@@ -58,10 +43,11 @@ async function main() {
58
43
  const apiUrl = process.env.QWEN_API_URL;
59
44
  const apiKey = process.env.QWEN_API_KEY;
60
45
  const modelName = process.env.QWEN_MODEL || "Qwen3-VL-235B-A22B-Instruct";
46
+ const vllmReasoningParser = process.env.VLLM_REASONING_PARSER;
61
47
  if (!apiUrl || !apiKey) {
62
48
  throw new Error("Missing required environment variables: QWEN_API_URL and QWEN_API_KEY must be set");
63
49
  }
64
- const se = "1.0.8";
50
+ const se = "1.0.9";
65
51
  const server = new McpServer({
66
52
  name: "pdf-to-markdown-mcp",
67
53
  version: se,
@@ -85,6 +71,18 @@ async function main() {
85
71
  !Number.isInteger(page_number)) {
86
72
  throw new Error("page_number must be a positive integer");
87
73
  }
74
+ // Validate pdf_path is within an allowed workspace directory
75
+ const workspaceEnv = process.env.WORKSPACE;
76
+ if (workspaceEnv) {
77
+ const allowedDirs = workspaceEnv
78
+ .split(",")
79
+ .map((d) => path.resolve(d.trim()));
80
+ const resolvedPdfPath = path.resolve(pdf_path);
81
+ const isAllowed = allowedDirs.some((dir) => resolvedPdfPath.startsWith(dir + path.sep) || resolvedPdfPath === dir);
82
+ if (!isAllowed) {
83
+ throw new Error(`Access denied: pdf_path must be located within one of the allowed workspace directories`);
84
+ }
85
+ }
88
86
  // Check if file exists
89
87
  try {
90
88
  await fs.access(pdf_path);
@@ -95,7 +93,7 @@ async function main() {
95
93
  // Convert PDF page to image
96
94
  const imageBuffer = await convertPdfPageToImage(pdf_path, page_number);
97
95
  // Convert image to markdown using Qwen VL
98
- const markdown = await convertImageToMarkdown(imageBuffer, apiUrl, apiKey, modelName);
96
+ const markdown = await convertImageToMarkdown(imageBuffer, apiUrl, apiKey, modelName, vllmReasoningParser);
99
97
  return {
100
98
  content: [
101
99
  {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAc1D;;GAEG;AACH,KAAK,UAAU,sBAAsB,CACnC,WAAmB,EACnB,MAAc,EACd,MAAc,EACd,SAAiB,EACjB,mBAA4B;IAE5B,MAAM,WAAW,GAAG,4BAA4B,CAAC,WAAW,EAAE,SAAS,EAAE;QACvE,mBAAmB;KACpB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;QACnC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,uBAAuB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE,CAC/E,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;IAEzD,6CAA6C;IAC7C,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAC1C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;IAC3C,CAAC;IAED,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,MAAM,IAAI,KAAK,CACb,mCAAmC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACxC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,6BAA6B,CAAC;IAC1E,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;IAE9D,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,mFAAmF,CACpF,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,GAAG,OAAO,CAAC;IACnB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;QAC3B,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,EAAE;KACZ,CAAC,CAAC;IAEH,gBAAgB;IAChB,MAAM,CAAC,YAAY,CACjB,8BAA8B,EAC9B;QACE,KAAK,EAAE,8BAA8B;QACrC,WAAW,EACT,4MAA4M;QAC9M,WAAW,EAAE;YACX,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC3B,gFAAgF,CACjF;YACD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAC9B,iGAAiG,CAClG;SACF;KACF,EACD,KAAK,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,kBAAkB;YAClB,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC9C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACzD,CAAC;YAED,IACE,OAAO,WAAW,KAAK,QAAQ;gBAC/B,WAAW,GAAG,CAAC;gBACf,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,EAC9B,CAAC;gBACD,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;YAC5D,CAAC;YAED,6DAA6D;YAC7D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;YAC3C,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,WAAW,GAAG,YAAY;qBAC7B,KAAK,CAAC,GAAG,CAAC;qBACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;gBACtC,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBAC/C,MAAM,SAAS,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACzC,eAAe,CAAC,UAAU,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,eAAe,KAAK,GAAG,CACtE,CAAC;gBACF,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,MAAM,IAAI,KAAK,CACb,yFAAyF,CAC1F,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,uBAAuB;YACvB,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,EAAE,CAAC,CAAC;YACrD,CAAC;YAED,4BAA4B;YAC5B,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;YAEvE,0CAA0C;YAC1C,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAC3C,WAAW,EACX,MAAM,EACN,MAAM,EACN,SAAS,EACT,mBAAmB,CACpB,CAAC;YAEF,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,QAAQ;qBACf;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACzD,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,0CAA0C,YAAY,EAAE;qBAC/D;iBACF;gBACD,OAAO,EAAE,IAAI;aACd,CAAC;QACJ,CAAC;IACH,CAAC,CACF,CAAC;IAEF,mBAAmB;IACnB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,oBAAoB,CAAC,CAAC;AACxE,CAAC;AAED,IAAI,CAAC;IACH,MAAM,IAAI,EAAE,CAAC;AACf,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,25 @@
1
+ export declare const supportedVllmReasoningParsers: readonly ["kimi_k2", "qwen3"];
2
+ export type VllmReasoningParser = (typeof supportedVllmReasoningParsers)[number];
3
+ type VisionContentPart = {
4
+ type: "image_url";
5
+ image_url: {
6
+ url: string;
7
+ };
8
+ } | {
9
+ type: "text";
10
+ text: string;
11
+ };
12
+ export interface OpenAICompatibleChatCompletionRequest {
13
+ model: string;
14
+ messages: Array<{
15
+ role: "user";
16
+ content: VisionContentPart[];
17
+ }>;
18
+ chat_template_kwargs?: Record<string, boolean>;
19
+ }
20
+ interface BuildOpenAICompatibleRequestOptions {
21
+ vllmReasoningParser?: string;
22
+ }
23
+ export declare function buildOpenAICompatibleRequest(imageBuffer: Buffer, modelName: string, options?: BuildOpenAICompatibleRequestOptions): OpenAICompatibleChatCompletionRequest;
24
+ export {};
25
+ //# sourceMappingURL=openaiCompatibleRequest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openaiCompatibleRequest.d.ts","sourceRoot":"","sources":["../../src/openaiCompatibleRequest.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,6BAA6B,+BAAgC,CAAC;AAE3E,MAAM,MAAM,mBAAmB,GAC7B,CAAC,OAAO,6BAA6B,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD,KAAK,iBAAiB,GAClB;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE;QACT,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH,GACD;IACE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEN,MAAM,WAAW,qCAAqC;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,iBAAiB,EAAE,CAAC;KAC9B,CAAC,CAAC;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChD;AAED,UAAU,mCAAmC;IAC3C,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAqCD,wBAAgB,4BAA4B,CAC1C,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,mCAAwC,GAChD,qCAAqC,CAmCvC"}
@@ -0,0 +1,53 @@
1
+ export const supportedVllmReasoningParsers = ["kimi_k2", "qwen3"];
2
+ function parseVllmReasoningParser(vllmReasoningParser) {
3
+ if (!vllmReasoningParser) {
4
+ return undefined;
5
+ }
6
+ const normalizedParser = vllmReasoningParser
7
+ .trim()
8
+ .toLowerCase()
9
+ .replace(/[\s-]+/g, "_");
10
+ if (normalizedParser === "kimi_k2" || normalizedParser === "qwen3") {
11
+ return normalizedParser;
12
+ }
13
+ throw new Error(`Unsupported VLLM_REASONING_PARSER \"${vllmReasoningParser}\". Supported values: ${supportedVllmReasoningParsers.join(", ")}.`);
14
+ }
15
+ function getThinkingDisabledChatTemplateKwargs(vllmReasoningParser) {
16
+ if (!vllmReasoningParser) {
17
+ return undefined;
18
+ }
19
+ if (vllmReasoningParser === "kimi_k2") {
20
+ return { thinking: false };
21
+ }
22
+ return { enable_thinking: false };
23
+ }
24
+ export function buildOpenAICompatibleRequest(imageBuffer, modelName, options = {}) {
25
+ const base64Image = imageBuffer.toString("base64");
26
+ const vllmReasoningParser = parseVllmReasoningParser(options.vllmReasoningParser);
27
+ const requestBody = {
28
+ model: modelName,
29
+ messages: [
30
+ {
31
+ role: "user",
32
+ content: [
33
+ {
34
+ type: "image_url",
35
+ image_url: {
36
+ url: `data:image/png;base64,${base64Image}`,
37
+ },
38
+ },
39
+ {
40
+ type: "text",
41
+ text: "Please convert this image to markdown format. Extract all text, tables, and structure accurately.",
42
+ },
43
+ ],
44
+ },
45
+ ],
46
+ };
47
+ const chatTemplateKwargs = getThinkingDisabledChatTemplateKwargs(vllmReasoningParser);
48
+ if (chatTemplateKwargs) {
49
+ requestBody.chat_template_kwargs = chatTemplateKwargs;
50
+ }
51
+ return requestBody;
52
+ }
53
+ //# sourceMappingURL=openaiCompatibleRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openaiCompatibleRequest.js","sourceRoot":"","sources":["../../src/openaiCompatibleRequest.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,SAAS,EAAE,OAAO,CAAU,CAAC;AA8B3E,SAAS,wBAAwB,CAC/B,mBAA4B;IAE5B,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,gBAAgB,GAAG,mBAAmB;SACzC,IAAI,EAAE;SACN,WAAW,EAAE;SACb,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;IAE3B,IAAI,gBAAgB,KAAK,SAAS,IAAI,gBAAgB,KAAK,OAAO,EAAE,CAAC;QACnE,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IAED,MAAM,IAAI,KAAK,CACb,uCAAuC,mBAAmB,yBAAyB,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAC/H,CAAC;AACJ,CAAC;AAED,SAAS,qCAAqC,CAC5C,mBAAyC;IAEzC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,WAAmB,EACnB,SAAiB,EACjB,UAA+C,EAAE;IAEjD,MAAM,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnD,MAAM,mBAAmB,GAAG,wBAAwB,CAClD,OAAO,CAAC,mBAAmB,CAC5B,CAAC;IACF,MAAM,WAAW,GAA0C;QACzD,KAAK,EAAE,SAAS;QAChB,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,WAAW;wBACjB,SAAS,EAAE;4BACT,GAAG,EAAE,yBAAyB,WAAW,EAAE;yBAC5C;qBACF;oBACD;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,mGAAmG;qBAC1G;iBACF;aACF;SACF;KACF,CAAC;IAEF,MAAM,kBAAkB,GAAG,qCAAqC,CAC9D,mBAAmB,CACpB,CAAC;IAEF,IAAI,kBAAkB,EAAE,CAAC;QACvB,WAAW,CAAC,oBAAoB,GAAG,kBAAkB,CAAC;IACxD,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -1,2 +1,2 @@
1
- export {};
1
+ import "dotenv/config";
2
2
  //# sourceMappingURL=demo.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../test/demo.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../test/demo.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
package/dist/test/demo.js CHANGED
@@ -1,3 +1,4 @@
1
+ import "dotenv/config";
1
2
  import { convertPdfPageToImage } from "../src/pdfConverter.js";
2
3
  import * as fs from "node:fs/promises";
3
4
  import * as path from "node:path";
@@ -55,9 +56,7 @@ async function main() {
55
56
  console.error("\nPlease set environment variables:");
56
57
  console.error(" QWEN_API_URL - Qwen API endpoint URL");
57
58
  console.error(" QWEN_API_KEY - Your Qwen API key\n");
58
- console.error("Windows (PowerShell):");
59
- console.error(' $env:QWEN_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"');
60
- console.error(' $env:QWEN_API_KEY = "your-api-key-here"\n');
59
+ console.error("The project automatically loads a local .env file when present, so you can store the values there instead of exporting them every time.\n");
61
60
  process.exit(1);
62
61
  }
63
62
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../test/demo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"demo.js","sourceRoot":"","sources":["../../test/demo.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAgBlC,KAAK,UAAU,sBAAsB,CACnC,WAAmB,EACnB,MAAc,EACd,MAAc;IAEd,MAAM,WAAW,GAAG,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAEnD,MAAM,WAAW,GAAG;QAClB,KAAK,EAAE,aAAa;QACpB,KAAK,EAAE;YACL,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP;4BACE,KAAK,EAAE,yBAAyB,WAAW,EAAE;yBAC9C;wBACD;4BACE,IAAI,EAAE,mGAAmG;yBAC1G;qBACF;iBACF;aACF;SACF;KACF,CAAC;IAEF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,MAAM,EAAE;QACnC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,aAAa,EAAE,UAAU,MAAM,EAAE;SAClC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC;KAClC,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CACb,4BAA4B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,SAAS,EAAE,CACpF,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAmB,CAAC;IAEzD,IAAI,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACxB,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED,MAAM,IAAI,KAAK,CACb,mCAAmC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAC5D,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,+BAA+B;IAC/B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,EAAE,EAAE,CAAC,CAAC;IAEhD,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;IACpC,OAAO,CAAC,GAAG,CAAC,gBAAgB,UAAU,IAAI,CAAC,CAAC;IAE5C,wBAAwB;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IACxC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAExC,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACxD,OAAO,CAAC,KAAK,CAAC,sCAAsC,CAAC,CAAC;QACtD,OAAO,CAAC,KAAK,CACX,2IAA2I,CAC5I,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,IAAI,CAAC;QACH,oCAAoC;QACpC,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,WAAW,GAAG,MAAM,qBAAqB,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QACrE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACzC,OAAO,CAAC,GAAG,CAAC,sBAAsB,SAAS,OAAO,WAAW,CAAC,MAAM,WAAW,CAAC,CAAC;QAEjF,0BAA0B;QAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CACzB,OAAO,CAAC,GAAG,EAAE,EACb,MAAM,EACN,YAAY,UAAU,MAAM,CAC7B,CAAC;QACF,MAAM,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,mBAAmB,SAAS,IAAI,CAAC,CAAC;QAE9C,oCAAoC;QACpC,OAAO,CAAC,GAAG,CAAC,uDAAuD,CAAC,CAAC;QACrE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAChC,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QAC3E,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,yBAAyB,OAAO,OAAO,QAAQ,CAAC,MAAM,gBAAgB,CAAC,CAAC;QAEpF,gBAAgB;QAChB,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAC5B,OAAO,CAAC,GAAG,EAAE,EACb,MAAM,EACN,YAAY,UAAU,KAAK,CAC5B,CAAC;QACF,MAAM,EAAE,CAAC,SAAS,CAAC,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,sBAAsB,YAAY,IAAI,CAAC,CAAC;QAEpD,kBAAkB;QAClB,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;QACxC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QACxC,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;QACrE,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QAEvC,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC;QACpD,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,IAAI,CAAC,CAAC;IACzD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;QACnC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=openaiCompatibleRequest.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openaiCompatibleRequest.test.d.ts","sourceRoot":"","sources":["../../test/openaiCompatibleRequest.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,21 @@
1
+ import assert from "node:assert/strict";
2
+ import { buildOpenAICompatibleRequest, supportedVllmReasoningParsers, } from "../src/openaiCompatibleRequest.js";
3
+ function run() {
4
+ const sampleImage = Buffer.from("sample-image");
5
+ const genericRequest = buildOpenAICompatibleRequest(sampleImage, "moonshotai/Kimi-K2.6-Vision-Instruct");
6
+ assert.equal(genericRequest.chat_template_kwargs, undefined);
7
+ const kimiRequest = buildOpenAICompatibleRequest(sampleImage, "moonshotai/Kimi-K2.6-Vision-Instruct", { vllmReasoningParser: "kimi_k2" });
8
+ assert.deepEqual(kimiRequest.chat_template_kwargs, { thinking: false });
9
+ const qwenRequest = buildOpenAICompatibleRequest(sampleImage, "Qwen/Qwen3-VL-235B-A22B-Instruct", { vllmReasoningParser: "qwen3" });
10
+ assert.deepEqual(qwenRequest.chat_template_kwargs, {
11
+ enable_thinking: false,
12
+ });
13
+ assert.throws(() => buildOpenAICompatibleRequest(sampleImage, "moonshotai/Kimi-K2.6", {
14
+ vllmReasoningParser: "unknown-parser",
15
+ }), {
16
+ message: new RegExp(`Supported values: ${supportedVllmReasoningParsers.join(", ")}`),
17
+ });
18
+ console.log("✓ OpenAI-compatible request builder tests passed");
19
+ }
20
+ run();
21
+ //# sourceMappingURL=openaiCompatibleRequest.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openaiCompatibleRequest.test.js","sourceRoot":"","sources":["../../test/openaiCompatibleRequest.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EACL,4BAA4B,EAC5B,6BAA6B,GAC9B,MAAM,mCAAmC,CAAC;AAE3C,SAAS,GAAG;IACV,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAEhD,MAAM,cAAc,GAAG,4BAA4B,CACjD,WAAW,EACX,sCAAsC,CACvC,CAAC;IACF,MAAM,CAAC,KAAK,CAAC,cAAc,CAAC,oBAAoB,EAAE,SAAS,CAAC,CAAC;IAE7D,MAAM,WAAW,GAAG,4BAA4B,CAC9C,WAAW,EACX,sCAAsC,EACtC,EAAE,mBAAmB,EAAE,SAAS,EAAE,CACnC,CAAC;IACF,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,oBAAoB,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAExE,MAAM,WAAW,GAAG,4BAA4B,CAC9C,WAAW,EACX,kCAAkC,EAClC,EAAE,mBAAmB,EAAE,OAAO,EAAE,CACjC,CAAC;IACF,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,oBAAoB,EAAE;QACjD,eAAe,EAAE,KAAK;KACvB,CAAC,CAAC;IAEH,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CACH,4BAA4B,CAAC,WAAW,EAAE,sBAAsB,EAAE;QAChE,mBAAmB,EAAE,gBAAgB;KACtC,CAAC,EACJ;QACE,OAAO,EAAE,IAAI,MAAM,CACjB,qBAAqB,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAChE;KACF,CACF,CAAC;IAEF,OAAO,CAAC,GAAG,CAAC,kDAAkD,CAAC,CAAC;AAClE,CAAC;AAED,GAAG,EAAE,CAAC"}
@@ -1,2 +1,2 @@
1
- export {};
1
+ import "dotenv/config";
2
2
  //# sourceMappingURL=pdfToMarkdown.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pdfToMarkdown.test.d.ts","sourceRoot":"","sources":["../../test/pdfToMarkdown.test.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"pdfToMarkdown.test.d.ts","sourceRoot":"","sources":["../../test/pdfToMarkdown.test.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
@@ -1,31 +1,15 @@
1
+ import "dotenv/config";
1
2
  import { convertPdfPageToImage } from "../src/pdfConverter.js";
3
+ import { buildOpenAICompatibleRequest } from "../src/openaiCompatibleRequest.js";
2
4
  import * as fs from "node:fs/promises";
3
5
  import * as path from "node:path";
4
6
  /**
5
7
  * Convert image to markdown using Qwen VL API
6
8
  */
7
- async function convertImageToMarkdown(imageBuffer, apiUrl, apiKey) {
8
- const base64Image = imageBuffer.toString("base64");
9
- const requestBody = {
10
- model: "Qwen3-VL-235B-A22B-Instruct",
11
- messages: [
12
- {
13
- role: "user",
14
- content: [
15
- {
16
- type: "image_url",
17
- image_url: {
18
- url: `data:image/png;base64,${base64Image}`,
19
- },
20
- },
21
- {
22
- type: "text",
23
- text: "Please convert this image to markdown format. Extract all text, tables, and structure accurately.",
24
- },
25
- ],
26
- },
27
- ],
28
- };
9
+ async function convertImageToMarkdown(imageBuffer, apiUrl, apiKey, modelName, vllmReasoningParser) {
10
+ const requestBody = buildOpenAICompatibleRequest(imageBuffer, modelName, {
11
+ vllmReasoningParser,
12
+ });
29
13
  const response = await fetch(apiUrl, {
30
14
  method: "POST",
31
15
  headers: {
@@ -56,6 +40,8 @@ async function testPdfToMarkdown() {
56
40
  const testPdfPath = path.join(process.cwd(), "test", "test.pdf");
57
41
  const apiUrl = process.env.QWEN_API_URL;
58
42
  const apiKey = process.env.QWEN_API_KEY;
43
+ const modelName = process.env.QWEN_MODEL || "Qwen3-VL-235B-A22B-Instruct";
44
+ const vllmReasoningParser = process.env.VLLM_REASONING_PARSER;
59
45
  // Test 1: PDF to Image conversion
60
46
  console.log("Test 1: Convert PDF page to image");
61
47
  let imageBuffer;
@@ -87,22 +73,16 @@ async function testPdfToMarkdown() {
87
73
  // Test 2: Check API credentials
88
74
  console.log("\nTest 2: Verify API credentials");
89
75
  if (!apiUrl || !apiKey) {
90
- console.log("⚠ Test 2 skipped: QWEN_API_URL and QWEN_API_KEY environment variables not set");
91
- console.log("\nTo test full PDF to markdown conversion, set environment variables:");
92
- console.log(" Windows (PowerShell):");
93
- console.log(' $env:QWEN_API_URL = "http://osl4420:13000/v1/chat/completions"');
94
- console.log(' $env:QWEN_API_KEY = "sk-*****"');
95
- console.log("\n Linux/Mac:");
96
- console.log(' export QWEN_API_URL="https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"');
97
- console.log(' export QWEN_API_KEY="your-api-key-here"');
76
+ console.log("⚠ Test 2 skipped: QWEN_API_URL and QWEN_API_KEY were not found in process.env or the project .env file");
77
+ console.log("\nAdd the credentials to the project .env file or export them in your shell, then rerun npm run test:full.");
98
78
  return;
99
79
  }
100
80
  console.log("✓ Test 2 passed: API credentials found");
101
81
  // Test 3: Full conversion - Image to Markdown
102
82
  console.log("\nTest 3: Convert image to markdown using Qwen VL API");
103
83
  try {
104
- console.log(" Sending request to Qwen VL API...");
105
- const markdown = await convertImageToMarkdown(imageBuffer, apiUrl, apiKey);
84
+ console.log(" Sending request to the OpenAI-compatible vision API...");
85
+ const markdown = await convertImageToMarkdown(imageBuffer, apiUrl, apiKey, modelName, vllmReasoningParser);
106
86
  if (!markdown || typeof markdown !== "string") {
107
87
  throw new Error("Expected markdown string but got: " + typeof markdown);
108
88
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pdfToMarkdown.test.js","sourceRoot":"","sources":["../../test/pdfToMarkdown.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pdfToMarkdown.test.js","sourceRoot":"","sources":["../../test/pdfToMarkdown.test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pdf-to-markdown-mcp",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "MCP server that converts PDF pages to markdown using Qwen VL model",
5
5
  "type": "module",
6
6
  "main": "dist/src/index.js",
@@ -12,7 +12,7 @@
12
12
  "build": "tsc",
13
13
  "start": "node dist/src/index.js",
14
14
  "dev": "tsc --watch",
15
- "test": "tsc && node dist/test/convertPdfPageToImage.test.js",
15
+ "test": "tsc && node dist/test/convertPdfPageToImage.test.js && node dist/test/openaiCompatibleRequest.test.js",
16
16
  "test:full": "tsc && node dist/test/pdfToMarkdown.test.js",
17
17
  "test:all": "npm test && npm run test:full",
18
18
  "demo": "tsc && node dist/test/demo.js"
@@ -26,12 +26,19 @@
26
26
  "author": "",
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@modelcontextprotocol/sdk": "^1.25.2",
29
+ "@modelcontextprotocol/sdk": "^1.27.1",
30
30
  "canvas": "3.2.0",
31
- "pdfjs-dist": "4.8.69"
31
+ "dotenv": "^16.6.1",
32
+ "pdfjs-dist": "4.8.69",
33
+ "zod": "^3.25.76"
32
34
  },
33
35
  "overrides": {
34
- "hono":"4.11.7"
36
+ "hono":"4.12.18",
37
+ "ajv":"8.18.0",
38
+ "fast-uri":"3.1.2",
39
+ "ip-address":"10.2.0",
40
+ "@hono/node-server":"1.19.14",
41
+ "path-to-regexp":"8.4.2"
35
42
  },
36
43
  "devDependencies": {
37
44
  "@types/node": "^22.10.2",
package/src/index.ts CHANGED
@@ -1,9 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import "dotenv/config";
3
4
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
6
  import * as fs from "node:fs/promises";
7
+ import * as path from "node:path";
6
8
  import { z } from "zod";
9
+ import { buildOpenAICompatibleRequest } from "./openaiCompatibleRequest.js";
7
10
  import { convertPdfPageToImage } from "./pdfConverter.js";
8
11
 
9
12
  interface QwenVLResponse {
@@ -25,30 +28,12 @@ async function convertImageToMarkdown(
25
28
  imageBuffer: Buffer,
26
29
  apiUrl: string,
27
30
  apiKey: string,
28
- modelName: string
31
+ modelName: string,
32
+ vllmReasoningParser?: string
29
33
  ): Promise<string> {
30
- const base64Image = imageBuffer.toString("base64");
31
-
32
- const requestBody = {
33
- model: modelName,
34
- messages: [
35
- {
36
- role: "user",
37
- content: [
38
- {
39
- type: "image_url",
40
- image_url: {
41
- url: `data:image/png;base64,${base64Image}`,
42
- },
43
- },
44
- {
45
- type: "text",
46
- text: "Please convert this image to markdown format. Extract all text, tables, and structure accurately.",
47
- },
48
- ],
49
- },
50
- ],
51
- };
34
+ const requestBody = buildOpenAICompatibleRequest(imageBuffer, modelName, {
35
+ vllmReasoningParser,
36
+ });
52
37
 
53
38
  const response = await fetch(apiUrl, {
54
39
  method: "POST",
@@ -89,6 +74,7 @@ async function main() {
89
74
  const apiUrl = process.env.QWEN_API_URL;
90
75
  const apiKey = process.env.QWEN_API_KEY;
91
76
  const modelName = process.env.QWEN_MODEL || "Qwen3-VL-235B-A22B-Instruct";
77
+ const vllmReasoningParser = process.env.VLLM_REASONING_PARSER;
92
78
 
93
79
  if (!apiUrl || !apiKey) {
94
80
  throw new Error(
@@ -96,7 +82,7 @@ async function main() {
96
82
  );
97
83
  }
98
84
 
99
- const se = "1.0.8";
85
+ const se = "1.0.9";
100
86
  const server = new McpServer({
101
87
  name: "pdf-to-markdown-mcp",
102
88
  version: se,
@@ -133,6 +119,23 @@ async function main() {
133
119
  throw new Error("page_number must be a positive integer");
134
120
  }
135
121
 
122
+ // Validate pdf_path is within an allowed workspace directory
123
+ const workspaceEnv = process.env.WORKSPACE;
124
+ if (workspaceEnv) {
125
+ const allowedDirs = workspaceEnv
126
+ .split(",")
127
+ .map((d) => path.resolve(d.trim()));
128
+ const resolvedPdfPath = path.resolve(pdf_path);
129
+ const isAllowed = allowedDirs.some((dir) =>
130
+ resolvedPdfPath.startsWith(dir + path.sep) || resolvedPdfPath === dir
131
+ );
132
+ if (!isAllowed) {
133
+ throw new Error(
134
+ `Access denied: pdf_path must be located within one of the allowed workspace directories`
135
+ );
136
+ }
137
+ }
138
+
136
139
  // Check if file exists
137
140
  try {
138
141
  await fs.access(pdf_path);
@@ -148,7 +151,8 @@ async function main() {
148
151
  imageBuffer,
149
152
  apiUrl,
150
153
  apiKey,
151
- modelName
154
+ modelName,
155
+ vllmReasoningParser
152
156
  );
153
157
 
154
158
  return {
@@ -0,0 +1,105 @@
1
+ export const supportedVllmReasoningParsers = ["kimi_k2", "qwen3"] as const;
2
+
3
+ export type VllmReasoningParser =
4
+ (typeof supportedVllmReasoningParsers)[number];
5
+
6
+ type VisionContentPart =
7
+ | {
8
+ type: "image_url";
9
+ image_url: {
10
+ url: string;
11
+ };
12
+ }
13
+ | {
14
+ type: "text";
15
+ text: string;
16
+ };
17
+
18
+ export interface OpenAICompatibleChatCompletionRequest {
19
+ model: string;
20
+ messages: Array<{
21
+ role: "user";
22
+ content: VisionContentPart[];
23
+ }>;
24
+ chat_template_kwargs?: Record<string, boolean>;
25
+ }
26
+
27
+ interface BuildOpenAICompatibleRequestOptions {
28
+ vllmReasoningParser?: string;
29
+ }
30
+
31
+ function parseVllmReasoningParser(
32
+ vllmReasoningParser?: string
33
+ ): VllmReasoningParser | undefined {
34
+ if (!vllmReasoningParser) {
35
+ return undefined;
36
+ }
37
+
38
+ const normalizedParser = vllmReasoningParser
39
+ .trim()
40
+ .toLowerCase()
41
+ .replace(/[\s-]+/g, "_");
42
+
43
+ if (normalizedParser === "kimi_k2" || normalizedParser === "qwen3") {
44
+ return normalizedParser;
45
+ }
46
+
47
+ throw new Error(
48
+ `Unsupported VLLM_REASONING_PARSER \"${vllmReasoningParser}\". Supported values: ${supportedVllmReasoningParsers.join(", ")}.`
49
+ );
50
+ }
51
+
52
+ function getThinkingDisabledChatTemplateKwargs(
53
+ vllmReasoningParser?: VllmReasoningParser
54
+ ): Record<string, boolean> | undefined {
55
+ if (!vllmReasoningParser) {
56
+ return undefined;
57
+ }
58
+
59
+ if (vllmReasoningParser === "kimi_k2") {
60
+ return { thinking: false };
61
+ }
62
+
63
+ return { enable_thinking: false };
64
+ }
65
+
66
+ export function buildOpenAICompatibleRequest(
67
+ imageBuffer: Buffer,
68
+ modelName: string,
69
+ options: BuildOpenAICompatibleRequestOptions = {}
70
+ ): OpenAICompatibleChatCompletionRequest {
71
+ const base64Image = imageBuffer.toString("base64");
72
+ const vllmReasoningParser = parseVllmReasoningParser(
73
+ options.vllmReasoningParser
74
+ );
75
+ const requestBody: OpenAICompatibleChatCompletionRequest = {
76
+ model: modelName,
77
+ messages: [
78
+ {
79
+ role: "user",
80
+ content: [
81
+ {
82
+ type: "image_url",
83
+ image_url: {
84
+ url: `data:image/png;base64,${base64Image}`,
85
+ },
86
+ },
87
+ {
88
+ type: "text",
89
+ text: "Please convert this image to markdown format. Extract all text, tables, and structure accurately.",
90
+ },
91
+ ],
92
+ },
93
+ ],
94
+ };
95
+
96
+ const chatTemplateKwargs = getThinkingDisabledChatTemplateKwargs(
97
+ vllmReasoningParser
98
+ );
99
+
100
+ if (chatTemplateKwargs) {
101
+ requestBody.chat_template_kwargs = chatTemplateKwargs;
102
+ }
103
+
104
+ return requestBody;
105
+ }
package/test/demo.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import "dotenv/config";
1
2
  import { convertPdfPageToImage } from "../src/pdfConverter.js";
2
3
  import * as fs from "node:fs/promises";
3
4
  import * as path from "node:path";
@@ -88,9 +89,9 @@ async function main() {
88
89
  console.error("\nPlease set environment variables:");
89
90
  console.error(" QWEN_API_URL - Qwen API endpoint URL");
90
91
  console.error(" QWEN_API_KEY - Your Qwen API key\n");
91
- console.error("Windows (PowerShell):");
92
- console.error(' $env:QWEN_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"');
93
- console.error(' $env:QWEN_API_KEY = "your-api-key-here"\n');
92
+ console.error(
93
+ "The project automatically loads a local .env file when present, so you can store the values there instead of exporting them every time.\n"
94
+ );
94
95
  process.exit(1);
95
96
  }
96
97
 
@@ -0,0 +1,47 @@
1
+ import assert from "node:assert/strict";
2
+ import {
3
+ buildOpenAICompatibleRequest,
4
+ supportedVllmReasoningParsers,
5
+ } from "../src/openaiCompatibleRequest.js";
6
+
7
+ function run() {
8
+ const sampleImage = Buffer.from("sample-image");
9
+
10
+ const genericRequest = buildOpenAICompatibleRequest(
11
+ sampleImage,
12
+ "moonshotai/Kimi-K2.6-Vision-Instruct"
13
+ );
14
+ assert.equal(genericRequest.chat_template_kwargs, undefined);
15
+
16
+ const kimiRequest = buildOpenAICompatibleRequest(
17
+ sampleImage,
18
+ "moonshotai/Kimi-K2.6-Vision-Instruct",
19
+ { vllmReasoningParser: "kimi_k2" }
20
+ );
21
+ assert.deepEqual(kimiRequest.chat_template_kwargs, { thinking: false });
22
+
23
+ const qwenRequest = buildOpenAICompatibleRequest(
24
+ sampleImage,
25
+ "Qwen/Qwen3-VL-235B-A22B-Instruct",
26
+ { vllmReasoningParser: "qwen3" }
27
+ );
28
+ assert.deepEqual(qwenRequest.chat_template_kwargs, {
29
+ enable_thinking: false,
30
+ });
31
+
32
+ assert.throws(
33
+ () =>
34
+ buildOpenAICompatibleRequest(sampleImage, "moonshotai/Kimi-K2.6", {
35
+ vllmReasoningParser: "unknown-parser",
36
+ }),
37
+ {
38
+ message: new RegExp(
39
+ `Supported values: ${supportedVllmReasoningParsers.join(", ")}`
40
+ ),
41
+ }
42
+ );
43
+
44
+ console.log("✓ OpenAI-compatible request builder tests passed");
45
+ }
46
+
47
+ run();
@@ -1,4 +1,6 @@
1
+ import "dotenv/config";
1
2
  import { convertPdfPageToImage } from "../src/pdfConverter.js";
3
+ import { buildOpenAICompatibleRequest } from "../src/openaiCompatibleRequest.js";
2
4
  import * as fs from "node:fs/promises";
3
5
  import * as path from "node:path";
4
6
 
@@ -20,30 +22,13 @@ interface QwenVLResponse {
20
22
  async function convertImageToMarkdown(
21
23
  imageBuffer: Buffer,
22
24
  apiUrl: string,
23
- apiKey: string
25
+ apiKey: string,
26
+ modelName: string,
27
+ vllmReasoningParser?: string
24
28
  ): Promise<string> {
25
- const base64Image = imageBuffer.toString("base64");
26
-
27
- const requestBody = {
28
- model: "Qwen3-VL-235B-A22B-Instruct",
29
- messages: [
30
- {
31
- role: "user",
32
- content: [
33
- {
34
- type: "image_url",
35
- image_url: {
36
- url: `data:image/png;base64,${base64Image}`,
37
- },
38
- },
39
- {
40
- type: "text",
41
- text: "Please convert this image to markdown format. Extract all text, tables, and structure accurately.",
42
- },
43
- ],
44
- },
45
- ],
46
- };
29
+ const requestBody = buildOpenAICompatibleRequest(imageBuffer, modelName, {
30
+ vllmReasoningParser,
31
+ });
47
32
 
48
33
  const response = await fetch(apiUrl, {
49
34
  method: "POST",
@@ -86,6 +71,8 @@ async function testPdfToMarkdown() {
86
71
  const testPdfPath = path.join(process.cwd(), "test", "test.pdf");
87
72
  const apiUrl = process.env.QWEN_API_URL;
88
73
  const apiKey = process.env.QWEN_API_KEY;
74
+ const modelName = process.env.QWEN_MODEL || "Qwen3-VL-235B-A22B-Instruct";
75
+ const vllmReasoningParser = process.env.VLLM_REASONING_PARSER;
89
76
 
90
77
  // Test 1: PDF to Image conversion
91
78
  console.log("Test 1: Convert PDF page to image");
@@ -124,14 +111,12 @@ async function testPdfToMarkdown() {
124
111
  // Test 2: Check API credentials
125
112
  console.log("\nTest 2: Verify API credentials");
126
113
  if (!apiUrl || !apiKey) {
127
- console.log("⚠ Test 2 skipped: QWEN_API_URL and QWEN_API_KEY environment variables not set");
128
- console.log("\nTo test full PDF to markdown conversion, set environment variables:");
129
- console.log(" Windows (PowerShell):");
130
- console.log(' $env:QWEN_API_URL = "http://osl4420:13000/v1/chat/completions"');
131
- console.log(' $env:QWEN_API_KEY = "sk-*****"');
132
- console.log("\n Linux/Mac:");
133
- console.log(' export QWEN_API_URL="https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation"');
134
- console.log(' export QWEN_API_KEY="your-api-key-here"');
114
+ console.log(
115
+ " Test 2 skipped: QWEN_API_URL and QWEN_API_KEY were not found in process.env or the project .env file"
116
+ );
117
+ console.log(
118
+ "\nAdd the credentials to the project .env file or export them in your shell, then rerun npm run test:full."
119
+ );
135
120
  return;
136
121
  }
137
122
 
@@ -140,8 +125,14 @@ async function testPdfToMarkdown() {
140
125
  // Test 3: Full conversion - Image to Markdown
141
126
  console.log("\nTest 3: Convert image to markdown using Qwen VL API");
142
127
  try {
143
- console.log(" Sending request to Qwen VL API...");
144
- const markdown = await convertImageToMarkdown(imageBuffer, apiUrl, apiKey);
128
+ console.log(" Sending request to the OpenAI-compatible vision API...");
129
+ const markdown = await convertImageToMarkdown(
130
+ imageBuffer,
131
+ apiUrl,
132
+ apiKey,
133
+ modelName,
134
+ vllmReasoningParser
135
+ );
145
136
 
146
137
  if (!markdown || typeof markdown !== "string") {
147
138
  throw new Error("Expected markdown string but got: " + typeof markdown);