🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@suparse/mcp

Package Overview
Dependencies
Maintainers
1
Versions
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@suparse/mcp - npm Package Compare versions

Comparing version
1.1.0
to
1.2.0
+1
-1
dist/index.d.mts.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;iBAqKgB,sBAAA,CAAA,GAA0B,SAAA;AAAA,iBAsNpB,YAAA,CAAA,GAAgB,OAAA"}
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;iBAkPgB,sBAAA,CAAA,GAA0B,SAAA;AAAA,iBAkUpB,YAAA,CAAA,GAAgB,OAAA"}
#!/usr/bin/env node
import { existsSync, readFileSync, realpathSync } from "node:fs";
import { readdir, stat } from "node:fs/promises";
import os from "node:os";

@@ -9,3 +10,3 @@ import path from "node:path";

import { z } from "zod";
import { SuparseAPIError, SuparseAuthError, SuparseError, SuparseNodeClient, VERSION } from "@suparse/sdk/node";
import { ALLOWED_EXTENSIONS, SuparseAPIError, SuparseAuthError, SuparseError, SuparseNodeClient, VERSION } from "@suparse/sdk/node";

@@ -19,6 +20,8 @@ //#region src/index.ts

split: z.boolean().optional().describe("Enable auto-splitting of multi-page documents with mixed document types."),
cleanup: z.boolean().optional().describe("Delete processed documents from Suparse after results are fetched.")
cleanup: z.boolean().optional().describe("Only valid with result_mode return_json. Deletes processed Suparse documents after JSON results are returned, so later exports cannot be fetched from those document IDs."),
result_mode: z.enum(["defer", "return_json"]).optional().default("defer").describe("Controls whether extraction results in json format are returned directly. Use return_json only when you need the full JSON extraction in the MCP response. In all other cases you can retrieve the results in format of choice using download_results")
};
const clientOptionsSchema = { api_url: z.string().url().optional().describe("Optional API base URL. Defaults to SUPARSE_API_URL or Suparse production API.") };
const exportOptionsSchema = {
const exportOptionsSchema = { export_type: z.enum(["original", "unified"]).optional().describe("Export mode for JSON results. Defaults to unified.") };
const downloadOptionsSchema = {
format: z.enum([

@@ -29,5 +32,15 @@ "json",

"google_sheets"
]).optional().describe("Export format. Defaults to json."),
]).describe("Export format to write to local disk. Use this tool for csv and xlsx."),
export_type: z.enum(["original", "unified"]).optional().describe("Export mode for csv, xlsx, and google_sheets. Defaults to unified.")
};
const templateSummarySchema = z.object({
id: z.string(),
name: z.string(),
description: z.string().nullable().optional(),
template_language: z.string(),
version: z.number(),
is_active: z.boolean(),
is_system_template: z.boolean(),
created_at: z.string()
});
function getConfigApiKey() {

@@ -51,2 +64,3 @@ const configPath = path.join(os.homedir(), ".config", "suparse", "config.json");

return {
result_mode: "return_json",
total: result.total,

@@ -57,2 +71,7 @@ succeeded: result.succeeded,

}
async function listSupportedFolderFiles(folderPath) {
const resolved = path.resolve(folderPath);
if (!(await stat(resolved)).isDirectory()) throw new Error(`Not a directory: ${resolved}`);
return (await readdir(resolved)).filter((entry) => ALLOWED_EXTENSIONS.has(path.extname(entry).toLowerCase())).sort().map((entry) => path.join(resolved, entry));
}
function toolResult(text, structuredContent) {

@@ -82,2 +101,3 @@ return {

return JSON.stringify({
result_mode: payload.result_mode,
total: payload.total,

@@ -88,2 +108,11 @@ succeeded: payload.succeeded.length,

}
function summarizeDeferredBatch(payload) {
return JSON.stringify({
result_mode: payload.result_mode,
total: payload.total,
succeeded: payload.succeeded.length,
failed: payload.failed.length,
document_ids: payload.succeeded.flatMap((item) => item.document_ids)
}, null, 2);
}
function createSuparseMcpServer() {

@@ -96,3 +125,3 @@ const server = new McpServer({

title: "Extract File",
description: "Process one local document through Suparse in a single flow: upload, poll, fetch JSON results, and optionally clean up server documents.",
description: "Process one local document through Suparse. Defaults to result_mode defer, which uploads and polls only, then returns compact task_id/document_ids for later download_results. Use result_mode return_json only when you need the full JSON extraction in the MCP response. cleanup is only valid with return_json.",
inputSchema: {

@@ -104,2 +133,3 @@ file_path: z.string().min(1).describe("Local path to a supported document file."),

outputSchema: {
result_mode: z.enum(["defer", "return_json"]),
total: z.number(),

@@ -109,5 +139,39 @@ succeeded: z.array(z.unknown()),

}
}, async ({ file_path, template_id, split, cleanup, api_url }) => {
}, async ({ file_path, template_id, split, cleanup, result_mode, api_url }) => {
const client = createClient(api_url);
try {
if ((result_mode ?? "defer") === "defer") {
if (cleanup) throw new Error("cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.");
let taskId = null;
try {
taskId = await client.uploadFile(file_path, {
template_id,
split
});
const { documentIds } = await client.pollTaskStatus(taskId);
const payload = {
result_mode: "defer",
total: 1,
succeeded: [{
file_path,
task_id: taskId,
document_ids: documentIds
}],
failed: []
};
return toolResult(summarizeDeferredBatch(payload), payload);
} catch (error) {
const payload = {
result_mode: "defer",
total: 1,
succeeded: [],
failed: [{
file_path,
task_id: taskId,
error: error instanceof Error ? error.message : String(error)
}]
};
return toolResult(summarizeDeferredBatch(payload), payload);
}
}
const payload = toBatchResultPayload(await client.extract(file_path, {

@@ -127,3 +191,3 @@ template_id,

title: "Extract Folder",
description: "Process all supported files in an immediate local folder through Suparse and return final JSON results.",
description: "Process all supported files in an immediate local folder through Suparse. Defaults to result_mode defer, which uploads and polls only, then returns compact task_id/document_ids for later download_results. Use result_mode return_json only when you need full JSON extractions in the MCP response. cleanup is only valid with return_json.",
inputSchema: {

@@ -135,2 +199,3 @@ folder_path: z.string().min(1).describe("Local folder containing supported document files."),

outputSchema: {
result_mode: z.enum(["defer", "return_json"]),
total: z.number(),

@@ -140,5 +205,28 @@ succeeded: z.array(z.unknown()),

}
}, async ({ folder_path, template_id, split, cleanup, api_url }) => {
}, async ({ folder_path, template_id, split, cleanup, result_mode, api_url }) => {
const client = createClient(api_url);
try {
if ((result_mode ?? "defer") === "defer") {
if (cleanup) throw new Error("cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.");
const files = await listSupportedFolderFiles(folder_path);
const result = await client.processBatch(files, {
template_id,
split
});
const payload = {
result_mode: "defer",
total: result.succeeded.length + result.failed.length,
succeeded: result.succeeded.map((item) => ({
file_path: item.filePath,
task_id: item.taskId,
document_ids: item.documentIds
})),
failed: result.failed.map((item) => ({
file_path: item.filePath,
task_id: item.taskId,
error: item.error.message
}))
};
return toolResult(summarizeDeferredBatch(payload), payload);
}
const payload = toBatchResultPayload(await client.extractFolder(folder_path, {

@@ -163,8 +251,17 @@ template_id,

},
outputSchema: { templates: z.array(z.unknown()) }
outputSchema: { templates: z.array(templateSummarySchema) }
}, async ({ include_system, api_url }) => {
const client = createClient(api_url);
try {
const payload = { templates: await client.listTemplates({ includeSystem: include_system }) };
return toolResult(JSON.stringify(payload, null, 2), payload);
const payload = { templates: (await client.listTemplates({ includeSystem: include_system })).map(({ id, name, description, template_language, version, is_active, is_system_template, created_at }) => ({
id,
name,
description,
template_language,
version,
is_active,
is_system_template,
created_at
})) };
return toolResult(`Found ${payload.templates.length} templates.`, payload);
} catch (error) {

@@ -176,5 +273,5 @@ return toolError(error);

});
server.registerTool("fetch_results", {
title: "Fetch Results",
description: "Fetch extraction exports for one or more Suparse document IDs. JSON is returned as structured results; CSV/XLSX file exports are returned as base64 bytes.",
server.registerTool("fetch_json_results", {
title: "Fetch JSON Results",
description: "Fetch JSON extraction results for one or more Suparse document IDs directly in the MCP response. This can be large; use only when you need the full JSON in context. For CSV, XLSX, Google Sheets, or saved JSON files, use download_results. If you need cleanup after fetching, call delete_documents after this tool succeeds.",
inputSchema: {

@@ -186,2 +283,35 @@ document_ids: z.array(z.string().min(1)).min(1).describe("Suparse document IDs to export."),

outputSchema: {
format: z.literal("json"),
export_type: z.enum(["original", "unified"]),
results: z.array(z.unknown())
}
}, async ({ document_ids, export_type, api_url }) => {
const client = createClient(api_url);
try {
const exportType = export_type ?? "unified";
const payload = {
format: "json",
export_type: exportType,
results: await client.fetchResults(document_ids, {
format: "json",
export_type: exportType
})
};
return toolResult(JSON.stringify(payload, null, 2), payload);
} catch (error) {
return toolError(error);
} finally {
await client.close();
}
});
server.registerTool("download_results", {
title: "Download Results",
description: "Fetch an export for one or more Suparse document IDs and write it directly to local disk. Use this for CSV, XLSX, Google Sheets, and saved JSON files. Do not call fetch_json_results unless you intentionally need full JSON in the MCP response. If output_path is a directory, the API-provided filename is used inside that directory. If cleanup is needed, call delete_documents after this tool succeeds.",
inputSchema: {
document_ids: z.array(z.string().min(1)).min(1).describe("Suparse document IDs to export."),
output_path: z.string().min(1).optional().describe("Optional local output file path or existing directory. When omitted, writes to the current working directory using the API-provided or generated filename."),
...downloadOptionsSchema,
...clientOptionsSchema
},
outputSchema: {
format: z.enum([

@@ -194,43 +324,18 @@ "json",

export_type: z.enum(["original", "unified"]),
results: z.array(z.unknown()).optional(),
google_sheets: z.record(z.unknown()).optional(),
file: z.object({
filename: z.string().optional(),
content_type: z.string(),
is_zip: z.boolean(),
data_base64: z.string()
}).optional()
output_path: z.string(),
document_ids: z.array(z.string())
}
}, async ({ document_ids, format, export_type, api_url }) => {
}, async ({ document_ids, output_path, format, export_type, api_url }) => {
const client = createClient(api_url);
try {
const exportFormat = format ?? "json";
const exportType = export_type ?? "unified";
const exportResult = await client.fetchResults(document_ids, {
format: exportFormat,
export_type: exportType
});
let payload;
if (exportFormat === "csv" || exportFormat === "xlsx") {
const file = exportResult;
payload = {
format: exportFormat,
export_type: exportType,
file: {
filename: file.filename,
content_type: file.contentType,
is_zip: file.isZip,
data_base64: Buffer.from(file.data).toString("base64")
}
};
} else if (exportFormat === "google_sheets") payload = {
format: exportFormat,
const payload = {
format,
export_type: exportType,
google_sheets: exportResult
output_path: await client.downloadResults(document_ids, output_path, {
format,
export_type: exportType
}),
document_ids
};
else payload = {
format: exportFormat,
export_type: exportType,
results: exportResult
};
return toolResult(JSON.stringify(payload, null, 2), payload);

@@ -237,0 +342,0 @@ } catch (error) {

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","names":["SDK_VERSION"],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\nimport {\n SuparseAPIError,\n SuparseAuthError,\n SuparseError,\n SuparseNodeClient,\n VERSION as SDK_VERSION,\n type BatchResult,\n type ExportFormat,\n type ExportType,\n type FailedResult,\n type FileExport,\n type GoogleSheetsExport,\n type SuparseNodeClientOptions,\n type TaskExport,\n} from \"@suparse/sdk/node\";\n\nconst SERVER_NAME = \"suparse-mcp\";\nconst SERVER_VERSION = SDK_VERSION;\nconst API_KEY_NOT_FOUND_MESSAGE =\n \"API key not found. Set SUPARSE_API_KEY or add apiKey to ~/.config/suparse/config.json.\";\n\nconst extractOptionsSchema = {\n template_id: z\n .string()\n .optional()\n .describe(\"Optional extraction template ID. Omit to let Suparse auto-detect.\"),\n split: z\n .boolean()\n .optional()\n .describe(\"Enable auto-splitting of multi-page documents with mixed document types.\"),\n cleanup: z\n .boolean()\n .optional()\n .describe(\"Delete processed documents from Suparse after results are fetched.\"),\n};\n\nconst clientOptionsSchema = {\n api_url: z\n .string()\n .url()\n .optional()\n .describe(\"Optional API base URL. Defaults to SUPARSE_API_URL or Suparse production API.\"),\n};\n\nconst exportOptionsSchema = {\n format: z\n .enum([\"json\", \"csv\", \"xlsx\", \"google_sheets\"])\n .optional()\n .describe(\"Export format. Defaults to json.\"),\n export_type: z\n .enum([\"original\", \"unified\"])\n .optional()\n .describe(\"Export mode for csv, xlsx, and google_sheets. Defaults to unified.\"),\n};\n\ninterface BatchResultPayload extends Record<string, unknown> {\n total: number;\n succeeded: TaskExport[];\n failed: FailedResult[];\n}\n\ninterface TemplatesPayload extends Record<string, unknown> {\n templates: Awaited<ReturnType<SuparseNodeClient[\"listTemplates\"]>>;\n}\n\ninterface FetchResultsPayload extends Record<string, unknown> {\n format: ExportFormat;\n export_type: ExportType;\n results?: TaskExport[];\n google_sheets?: GoogleSheetsExport;\n file?: {\n filename?: string;\n content_type: string;\n is_zip: boolean;\n data_base64: string;\n };\n}\n\ninterface DeleteDocumentsPayload extends Record<string, unknown> {\n deleted: boolean;\n document_ids: string[];\n}\n\nfunction getConfigApiKey(): string | undefined {\n const configPath = path.join(os.homedir(), \".config\", \"suparse\", \"config.json\");\n if (!existsSync(configPath)) return undefined;\n\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as { apiKey?: unknown };\n return typeof config.apiKey === \"string\" && config.apiKey ? config.apiKey : undefined;\n}\n\nfunction getApiKey(): string {\n const apiKey = process.env.SUPARSE_API_KEY ?? getConfigApiKey();\n if (!apiKey) throw new Error(API_KEY_NOT_FOUND_MESSAGE);\n return apiKey;\n}\n\nfunction createClient(apiUrl?: string): SuparseNodeClient {\n const options: SuparseNodeClientOptions = { apiKey: getApiKey() };\n if (apiUrl) options.baseUrl = apiUrl;\n return new SuparseNodeClient(options);\n}\n\nfunction toBatchResultPayload(result: BatchResult): BatchResultPayload {\n return {\n total: result.total,\n succeeded: result.succeeded,\n failed: result.failed,\n };\n}\n\nfunction toolResult<T extends Record<string, unknown>>(\n text: string,\n structuredContent: T,\n): {\n content: { type: \"text\"; text: string }[];\n structuredContent: T;\n} {\n return {\n content: [{ type: \"text\" as const, text }],\n structuredContent,\n };\n}\n\nfunction toolError(error: unknown): {\n isError: boolean;\n content: { type: \"text\"; text: string }[];\n} {\n let message = error instanceof Error ? error.message : String(error);\n\n if (error instanceof SuparseAuthError) {\n message = `Permission denied (${error.statusCode}): ${error.message}. Check SUPARSE_API_KEY.`;\n } else if (error instanceof SuparseAPIError) {\n message = `API error (${error.statusCode}): ${error.message}`;\n } else if (error instanceof SuparseError) {\n message = `Suparse error: ${error.message}`;\n }\n\n return {\n isError: true,\n content: [{ type: \"text\" as const, text: message }],\n };\n}\n\nfunction summarizeBatch(payload: BatchResultPayload): string {\n return JSON.stringify(\n {\n total: payload.total,\n succeeded: payload.succeeded.length,\n failed: payload.failed.length,\n },\n null,\n 2,\n );\n}\n\nexport function createSuparseMcpServer(): McpServer {\n const server = new McpServer({\n name: SERVER_NAME,\n version: SERVER_VERSION,\n });\n\n server.registerTool(\n \"extract_file\",\n {\n title: \"Extract File\",\n description:\n \"Process one local document through Suparse in a single flow: upload, poll, fetch JSON results, and optionally clean up server documents.\",\n inputSchema: {\n file_path: z.string().min(1).describe(\"Local path to a supported document file.\"),\n ...extractOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n total: z.number(),\n succeeded: z.array(z.unknown()),\n failed: z.array(z.unknown()),\n },\n },\n async ({ file_path, template_id, split, cleanup, api_url }) => {\n const client = createClient(api_url);\n try {\n const result = await client.extract(file_path, {\n template_id,\n split,\n cleanup,\n });\n const payload = toBatchResultPayload(result);\n return toolResult(summarizeBatch(payload), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"extract_folder\",\n {\n title: \"Extract Folder\",\n description:\n \"Process all supported files in an immediate local folder through Suparse and return final JSON results.\",\n inputSchema: {\n folder_path: z\n .string()\n .min(1)\n .describe(\"Local folder containing supported document files.\"),\n ...extractOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n total: z.number(),\n succeeded: z.array(z.unknown()),\n failed: z.array(z.unknown()),\n },\n },\n async ({ folder_path, template_id, split, cleanup, api_url }) => {\n const client = createClient(api_url);\n try {\n const result = await client.extractFolder(folder_path, {\n template_id,\n split,\n cleanup,\n });\n const payload = toBatchResultPayload(result);\n return toolResult(summarizeBatch(payload), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"list_templates\",\n {\n title: \"List Templates\",\n description: \"List extraction templates available to the authenticated Suparse account.\",\n inputSchema: {\n include_system: z\n .boolean()\n .optional()\n .describe(\"Include all system templates, not only assigned account templates.\"),\n ...clientOptionsSchema,\n },\n outputSchema: {\n templates: z.array(z.unknown()),\n },\n },\n async ({ include_system, api_url }) => {\n const client = createClient(api_url);\n try {\n const payload: TemplatesPayload = {\n templates: await client.listTemplates({ includeSystem: include_system }),\n };\n return toolResult(JSON.stringify(payload, null, 2), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"fetch_results\",\n {\n title: \"Fetch Results\",\n description:\n \"Fetch extraction exports for one or more Suparse document IDs. JSON is returned as structured results; CSV/XLSX file exports are returned as base64 bytes.\",\n inputSchema: {\n document_ids: z.array(z.string().min(1)).min(1).describe(\"Suparse document IDs to export.\"),\n ...exportOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n format: z.enum([\"json\", \"csv\", \"xlsx\", \"google_sheets\"]),\n export_type: z.enum([\"original\", \"unified\"]),\n results: z.array(z.unknown()).optional(),\n google_sheets: z.record(z.unknown()).optional(),\n file: z\n .object({\n filename: z.string().optional(),\n content_type: z.string(),\n is_zip: z.boolean(),\n data_base64: z.string(),\n })\n .optional(),\n },\n },\n async ({ document_ids, format, export_type, api_url }) => {\n const client = createClient(api_url);\n try {\n const exportFormat = format ?? \"json\";\n const exportType = export_type ?? \"unified\";\n const exportResult: unknown = await client.fetchResults(document_ids, {\n format: exportFormat,\n export_type: exportType,\n } as any);\n\n let payload: FetchResultsPayload;\n if (exportFormat === \"csv\" || exportFormat === \"xlsx\") {\n const file = exportResult as FileExport;\n payload = {\n format: exportFormat,\n export_type: exportType,\n file: {\n filename: file.filename,\n content_type: file.contentType,\n is_zip: file.isZip,\n data_base64: Buffer.from(file.data).toString(\"base64\"),\n },\n };\n } else if (exportFormat === \"google_sheets\") {\n payload = {\n format: exportFormat,\n export_type: exportType,\n google_sheets: exportResult as GoogleSheetsExport,\n };\n } else {\n payload = {\n format: exportFormat,\n export_type: exportType,\n results: exportResult as TaskExport[],\n };\n }\n return toolResult(JSON.stringify(payload, null, 2), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"delete_documents\",\n {\n title: \"Delete Documents\",\n description: \"Delete one or more documents from Suparse by document ID.\",\n inputSchema: {\n document_ids: z.array(z.string().min(1)).min(1).describe(\"Suparse document IDs to delete.\"),\n ...clientOptionsSchema,\n },\n outputSchema: {\n deleted: z.boolean(),\n document_ids: z.array(z.string()),\n },\n },\n async ({ document_ids, api_url }) => {\n const client = createClient(api_url);\n try {\n const payload: DeleteDocumentsPayload = {\n deleted: await client.deleteDocuments(document_ids),\n document_ids,\n };\n return toolResult(JSON.stringify(payload, null, 2), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n return server;\n}\n\nexport async function runMcpServer(): Promise<void> {\n const server = createSuparseMcpServer();\n await server.connect(new StdioServerTransport());\n}\n\nfunction isDirectRun(): boolean {\n if (!process.argv[1]) return false;\n return import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;\n}\n\nif (isDirectRun()) {\n runMcpServer().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
{"version":3,"file":"index.mjs","names":["SDK_VERSION"],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync, readFileSync, realpathSync } from \"node:fs\";\nimport { readdir, stat } from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { z } from \"zod\";\n\nimport {\n SuparseAPIError,\n SuparseAuthError,\n SuparseError,\n SuparseNodeClient,\n VERSION as SDK_VERSION,\n ALLOWED_EXTENSIONS,\n type BatchResult,\n type ExportFormat,\n type ExportType,\n type FailedResult,\n type SuparseNodeClientOptions,\n type TaskExport,\n} from \"@suparse/sdk/node\";\n\nconst SERVER_NAME = \"suparse-mcp\";\nconst SERVER_VERSION = SDK_VERSION;\nconst API_KEY_NOT_FOUND_MESSAGE =\n \"API key not found. Set SUPARSE_API_KEY or add apiKey to ~/.config/suparse/config.json.\";\n\nconst extractOptionsSchema = {\n template_id: z\n .string()\n .optional()\n .describe(\"Optional extraction template ID. Omit to let Suparse auto-detect.\"),\n split: z\n .boolean()\n .optional()\n .describe(\"Enable auto-splitting of multi-page documents with mixed document types.\"),\n cleanup: z\n .boolean()\n .optional()\n .describe(\n \"Only valid with result_mode return_json. Deletes processed Suparse documents after JSON results are returned, so later exports cannot be fetched from those document IDs.\",\n ),\n result_mode: z\n .enum([\"defer\", \"return_json\"])\n .optional()\n .default(\"defer\")\n .describe(\n \"Controls whether extraction results in json format are returned directly. Use return_json only when you need the full JSON extraction in the MCP response. In all other cases you can retrieve the results in format of choice using download_results\",\n ),\n};\n\nconst clientOptionsSchema = {\n api_url: z\n .string()\n .url()\n .optional()\n .describe(\"Optional API base URL. Defaults to SUPARSE_API_URL or Suparse production API.\"),\n};\n\nconst exportOptionsSchema = {\n export_type: z\n .enum([\"original\", \"unified\"])\n .optional()\n .describe(\"Export mode for JSON results. Defaults to unified.\"),\n};\n\nconst downloadOptionsSchema = {\n format: z\n .enum([\"json\", \"csv\", \"xlsx\", \"google_sheets\"])\n .describe(\"Export format to write to local disk. Use this tool for csv and xlsx.\"),\n export_type: z\n .enum([\"original\", \"unified\"])\n .optional()\n .describe(\"Export mode for csv, xlsx, and google_sheets. Defaults to unified.\"),\n};\n\nconst templateSummarySchema = z.object({\n id: z.string(),\n name: z.string(),\n description: z.string().nullable().optional(),\n template_language: z.string(),\n version: z.number(),\n is_active: z.boolean(),\n is_system_template: z.boolean(),\n created_at: z.string(),\n});\n\ntype TemplateSummary = z.infer<typeof templateSummarySchema>;\n\ninterface BatchResultPayload extends Record<string, unknown> {\n result_mode: \"return_json\";\n total: number;\n succeeded: TaskExport[];\n failed: FailedResult[];\n}\n\ninterface DeferredExtractionSuccess {\n file_path: string;\n task_id: string;\n document_ids: string[];\n}\n\ninterface DeferredExtractionFailure {\n file_path: string;\n task_id: string | null;\n error: string;\n}\n\ninterface DeferredBatchResultPayload extends Record<string, unknown> {\n result_mode: \"defer\";\n total: number;\n succeeded: DeferredExtractionSuccess[];\n failed: DeferredExtractionFailure[];\n}\n\ninterface TemplatesPayload extends Record<string, unknown> {\n templates: TemplateSummary[];\n}\n\ninterface FetchResultsPayload extends Record<string, unknown> {\n format: \"json\";\n export_type: ExportType;\n results: TaskExport[];\n}\n\ninterface DownloadResultsPayload extends Record<string, unknown> {\n format: ExportFormat;\n export_type: ExportType;\n output_path: string;\n document_ids: string[];\n}\n\ninterface DeleteDocumentsPayload extends Record<string, unknown> {\n deleted: boolean;\n document_ids: string[];\n}\n\nfunction getConfigApiKey(): string | undefined {\n const configPath = path.join(os.homedir(), \".config\", \"suparse\", \"config.json\");\n if (!existsSync(configPath)) return undefined;\n\n const config = JSON.parse(readFileSync(configPath, \"utf-8\")) as { apiKey?: unknown };\n return typeof config.apiKey === \"string\" && config.apiKey ? config.apiKey : undefined;\n}\n\nfunction getApiKey(): string {\n const apiKey = process.env.SUPARSE_API_KEY ?? getConfigApiKey();\n if (!apiKey) throw new Error(API_KEY_NOT_FOUND_MESSAGE);\n return apiKey;\n}\n\nfunction createClient(apiUrl?: string): SuparseNodeClient {\n const options: SuparseNodeClientOptions = { apiKey: getApiKey() };\n if (apiUrl) options.baseUrl = apiUrl;\n return new SuparseNodeClient(options);\n}\n\nfunction toBatchResultPayload(result: BatchResult): BatchResultPayload {\n return {\n result_mode: \"return_json\",\n total: result.total,\n succeeded: result.succeeded,\n failed: result.failed,\n };\n}\n\nasync function listSupportedFolderFiles(folderPath: string): Promise<string[]> {\n const resolved = path.resolve(folderPath);\n const folderStats = await stat(resolved);\n if (!folderStats.isDirectory()) {\n throw new Error(`Not a directory: ${resolved}`);\n }\n\n return (await readdir(resolved))\n .filter((entry) => ALLOWED_EXTENSIONS.has(path.extname(entry).toLowerCase()))\n .sort()\n .map((entry) => path.join(resolved, entry));\n}\n\nfunction toolResult<T extends Record<string, unknown>>(\n text: string,\n structuredContent: T,\n): {\n content: { type: \"text\"; text: string }[];\n structuredContent: T;\n} {\n return {\n content: [{ type: \"text\" as const, text }],\n structuredContent,\n };\n}\n\nfunction toolError(error: unknown): {\n isError: boolean;\n content: { type: \"text\"; text: string }[];\n} {\n let message = error instanceof Error ? error.message : String(error);\n\n if (error instanceof SuparseAuthError) {\n message = `Permission denied (${error.statusCode}): ${error.message}. Check SUPARSE_API_KEY.`;\n } else if (error instanceof SuparseAPIError) {\n message = `API error (${error.statusCode}): ${error.message}`;\n } else if (error instanceof SuparseError) {\n message = `Suparse error: ${error.message}`;\n }\n\n return {\n isError: true,\n content: [{ type: \"text\" as const, text: message }],\n };\n}\n\nfunction summarizeBatch(payload: BatchResultPayload): string {\n return JSON.stringify(\n {\n result_mode: payload.result_mode,\n total: payload.total,\n succeeded: payload.succeeded.length,\n failed: payload.failed.length,\n },\n null,\n 2,\n );\n}\n\nfunction summarizeDeferredBatch(payload: DeferredBatchResultPayload): string {\n return JSON.stringify(\n {\n result_mode: payload.result_mode,\n total: payload.total,\n succeeded: payload.succeeded.length,\n failed: payload.failed.length,\n document_ids: payload.succeeded.flatMap((item) => item.document_ids),\n },\n null,\n 2,\n );\n}\n\nexport function createSuparseMcpServer(): McpServer {\n const server = new McpServer({\n name: SERVER_NAME,\n version: SERVER_VERSION,\n });\n\n server.registerTool(\n \"extract_file\",\n {\n title: \"Extract File\",\n description:\n \"Process one local document through Suparse. Defaults to result_mode defer, which uploads and polls only, then returns compact task_id/document_ids for later download_results. Use result_mode return_json only when you need the full JSON extraction in the MCP response. cleanup is only valid with return_json.\",\n inputSchema: {\n file_path: z.string().min(1).describe(\"Local path to a supported document file.\"),\n ...extractOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n result_mode: z.enum([\"defer\", \"return_json\"]),\n total: z.number(),\n succeeded: z.array(z.unknown()),\n failed: z.array(z.unknown()),\n },\n },\n async ({ file_path, template_id, split, cleanup, result_mode, api_url }) => {\n const client = createClient(api_url);\n try {\n const mode = result_mode ?? \"defer\";\n if (mode === \"defer\") {\n if (cleanup) {\n throw new Error(\n \"cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.\",\n );\n }\n\n let taskId: string | null = null;\n try {\n taskId = await client.uploadFile(file_path, {\n template_id,\n split,\n });\n const { documentIds } = await client.pollTaskStatus(taskId);\n const payload: DeferredBatchResultPayload = {\n result_mode: \"defer\",\n total: 1,\n succeeded: [{ file_path, task_id: taskId, document_ids: documentIds }],\n failed: [],\n };\n return toolResult(summarizeDeferredBatch(payload), payload);\n } catch (error) {\n const payload: DeferredBatchResultPayload = {\n result_mode: \"defer\",\n total: 1,\n succeeded: [],\n failed: [\n {\n file_path,\n task_id: taskId,\n error: error instanceof Error ? error.message : String(error),\n },\n ],\n };\n return toolResult(summarizeDeferredBatch(payload), payload);\n }\n }\n\n const result = await client.extract(file_path, {\n template_id,\n split,\n cleanup,\n });\n const payload = toBatchResultPayload(result);\n return toolResult(summarizeBatch(payload), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"extract_folder\",\n {\n title: \"Extract Folder\",\n description:\n \"Process all supported files in an immediate local folder through Suparse. Defaults to result_mode defer, which uploads and polls only, then returns compact task_id/document_ids for later download_results. Use result_mode return_json only when you need full JSON extractions in the MCP response. cleanup is only valid with return_json.\",\n inputSchema: {\n folder_path: z\n .string()\n .min(1)\n .describe(\"Local folder containing supported document files.\"),\n ...extractOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n result_mode: z.enum([\"defer\", \"return_json\"]),\n total: z.number(),\n succeeded: z.array(z.unknown()),\n failed: z.array(z.unknown()),\n },\n },\n async ({ folder_path, template_id, split, cleanup, result_mode, api_url }) => {\n const client = createClient(api_url);\n try {\n const mode = result_mode ?? \"defer\";\n if (mode === \"defer\") {\n if (cleanup) {\n throw new Error(\n \"cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.\",\n );\n }\n\n const files = await listSupportedFolderFiles(folder_path);\n const result = await client.processBatch(files, {\n template_id,\n split,\n });\n const payload: DeferredBatchResultPayload = {\n result_mode: \"defer\",\n total: result.succeeded.length + result.failed.length,\n succeeded: result.succeeded.map((item) => ({\n file_path: item.filePath,\n task_id: item.taskId,\n document_ids: item.documentIds,\n })),\n failed: result.failed.map((item) => ({\n file_path: item.filePath,\n task_id: item.taskId,\n error: item.error.message,\n })),\n };\n return toolResult(summarizeDeferredBatch(payload), payload);\n }\n\n const result = await client.extractFolder(folder_path, {\n template_id,\n split,\n cleanup,\n });\n const payload = toBatchResultPayload(result);\n return toolResult(summarizeBatch(payload), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"list_templates\",\n {\n title: \"List Templates\",\n description: \"List extraction templates available to the authenticated Suparse account.\",\n inputSchema: {\n include_system: z\n .boolean()\n .optional()\n .describe(\"Include all system templates, not only assigned account templates.\"),\n ...clientOptionsSchema,\n },\n outputSchema: {\n templates: z.array(templateSummarySchema),\n },\n },\n async ({ include_system, api_url }) => {\n const client = createClient(api_url);\n try {\n const templates = await client.listTemplates({ includeSystem: include_system });\n const payload: TemplatesPayload = {\n templates: templates.map(\n ({\n id,\n name,\n description,\n template_language,\n version,\n is_active,\n is_system_template,\n created_at,\n }) => ({\n id,\n name,\n description,\n template_language,\n version,\n is_active,\n is_system_template,\n created_at,\n }),\n ),\n };\n return toolResult(`Found ${payload.templates.length} templates.`, payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"fetch_json_results\",\n {\n title: \"Fetch JSON Results\",\n description:\n \"Fetch JSON extraction results for one or more Suparse document IDs directly in the MCP response. This can be large; use only when you need the full JSON in context. For CSV, XLSX, Google Sheets, or saved JSON files, use download_results. If you need cleanup after fetching, call delete_documents after this tool succeeds.\",\n inputSchema: {\n document_ids: z.array(z.string().min(1)).min(1).describe(\"Suparse document IDs to export.\"),\n ...exportOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n format: z.literal(\"json\"),\n export_type: z.enum([\"original\", \"unified\"]),\n results: z.array(z.unknown()),\n },\n },\n async ({ document_ids, export_type, api_url }) => {\n const client = createClient(api_url);\n try {\n const exportType = export_type ?? \"unified\";\n const exportResult = await client.fetchResults(document_ids, {\n format: \"json\",\n export_type: exportType,\n });\n const payload: FetchResultsPayload = {\n format: \"json\",\n export_type: exportType,\n results: exportResult,\n };\n return toolResult(JSON.stringify(payload, null, 2), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"download_results\",\n {\n title: \"Download Results\",\n description:\n \"Fetch an export for one or more Suparse document IDs and write it directly to local disk. Use this for CSV, XLSX, Google Sheets, and saved JSON files. Do not call fetch_json_results unless you intentionally need full JSON in the MCP response. If output_path is a directory, the API-provided filename is used inside that directory. If cleanup is needed, call delete_documents after this tool succeeds.\",\n inputSchema: {\n document_ids: z.array(z.string().min(1)).min(1).describe(\"Suparse document IDs to export.\"),\n output_path: z\n .string()\n .min(1)\n .optional()\n .describe(\n \"Optional local output file path or existing directory. When omitted, writes to the current working directory using the API-provided or generated filename.\",\n ),\n ...downloadOptionsSchema,\n ...clientOptionsSchema,\n },\n outputSchema: {\n format: z.enum([\"json\", \"csv\", \"xlsx\", \"google_sheets\"]),\n export_type: z.enum([\"original\", \"unified\"]),\n output_path: z.string(),\n document_ids: z.array(z.string()),\n },\n },\n async ({ document_ids, output_path, format, export_type, api_url }) => {\n const client = createClient(api_url);\n try {\n const exportType = export_type ?? \"unified\";\n const outputPath = await client.downloadResults(document_ids, output_path, {\n format,\n export_type: exportType,\n });\n const payload: DownloadResultsPayload = {\n format,\n export_type: exportType,\n output_path: outputPath,\n document_ids,\n };\n return toolResult(JSON.stringify(payload, null, 2), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n server.registerTool(\n \"delete_documents\",\n {\n title: \"Delete Documents\",\n description: \"Delete one or more documents from Suparse by document ID.\",\n inputSchema: {\n document_ids: z.array(z.string().min(1)).min(1).describe(\"Suparse document IDs to delete.\"),\n ...clientOptionsSchema,\n },\n outputSchema: {\n deleted: z.boolean(),\n document_ids: z.array(z.string()),\n },\n },\n async ({ document_ids, api_url }) => {\n const client = createClient(api_url);\n try {\n const payload: DeleteDocumentsPayload = {\n deleted: await client.deleteDocuments(document_ids),\n document_ids,\n };\n return toolResult(JSON.stringify(payload, null, 2), payload);\n } catch (error) {\n return toolError(error);\n } finally {\n await client.close();\n }\n },\n );\n\n return server;\n}\n\nexport async function runMcpServer(): Promise<void> {\n const server = createSuparseMcpServer();\n await server.connect(new StdioServerTransport());\n}\n\nfunction isDirectRun(): boolean {\n if (!process.argv[1]) return false;\n return import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;\n}\n\nif (isDirectRun()) {\n runMcpServer().catch((error: unknown) => {\n console.error(error instanceof Error ? error.message : error);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
{
"name": "@suparse/mcp",
"version": "1.1.0",
"version": "1.2.0",
"description": "MCP server for the Suparse Document Processing API",

@@ -39,3 +39,3 @@ "author": "Suparse <support@suparse.com>",

"zod": "^3.25.0",
"@suparse/sdk": "1.1.0"
"@suparse/sdk": "1.2.0"
},

@@ -42,0 +42,0 @@ "devDependencies": {

+17
-11

@@ -24,3 +24,3 @@ # @suparse/mcp

```bash
claude mcp add --env SUPARSE_API_KEY=your_api_key suparse -- npx -y @suparse/mcp
claude mcp add suparse -e SUPARSE_API_KEY=your_api_key -- npx -y @suparse/mcp
```

@@ -62,6 +62,7 @@

- `extract_file`: Process one local document in a single call: upload, poll, fetch JSON results, and optionally clean up server documents.
- `extract_folder`: Process supported files in one local folder and return final JSON results.
- `list_templates`: List templates available to the authenticated account.
- `fetch_results`: Fetch exports by document ID. Defaults to JSON; supports `json`, `csv`, `xlsx`, and `google_sheets`.
- `extract_file`: Process one local document. Defaults to `result_mode: "defer"`, returning compact `task_id`/`document_ids` metadata for later `download_results`. Use `result_mode: "return_json"` only when the full JSON extraction is needed in the MCP response.
- `extract_folder`: Process supported files in one local folder. Defaults to `result_mode: "defer"`, returning compact `task_id`/`document_ids` metadata for later `download_results`. Use `result_mode: "return_json"` only when full JSON extractions are needed in the MCP response.
- `list_templates`: List summary metadata for templates available to the authenticated account.
- `fetch_json_results`: Fetch JSON extraction results by document ID directly in the MCP response. Use only when full JSON is needed in context.
- `download_results`: Fetch an export by document ID and write it directly to local disk. Use this for `json`, `csv`, `xlsx`, and `google_sheets`.
- `delete_documents`: Delete documents by ID.

@@ -71,11 +72,16 @@

`fetch_results` accepts:
`fetch_json_results` accepts:
| Input | Values | Default |
| ------------- | -------------------------------------- | --------- |
| `format` | `json`, `csv`, `xlsx`, `google_sheets` | `json` |
| `export_type` | `original`, `unified` | `unified` |
| Input | Values | Default |
| ------------- | --------------------- | --------- |
| `export_type` | `original`, `unified` | `unified` |
JSON exports are returned as structured `results`. Google Sheets exports are returned as `google_sheets` with spreadsheet or folder URLs. CSV and XLSX exports are returned as `file` with `filename`, `content_type`, `is_zip`, and `data_base64` fields.
JSON exports are returned as structured `results`.
`download_results` accepts `json`, `csv`, `xlsx`, and `google_sheets`, plus an optional `output_path` local file path or existing directory. It writes the export directly to disk and returns the saved `output_path`. MCP clients should use `download_results` for CSV, XLSX, Google Sheets, and saved JSON files; they should not fetch base64 data and decode it with shell or Python.
`result_mode` controls whether extraction results in JSON format are returned directly. Use `return_json` only when you need the full JSON extraction in the MCP response. In all other cases you can retrieve the results in the format of your choice using `download_results`.
Important: `cleanup` on `extract_file` and `extract_folder` is only valid with `result_mode: "return_json"`. It fetches JSON and then deletes the processed Suparse documents, so later exports cannot be fetched from those document IDs. For CSV/XLSX/Google Sheets or saved JSON files, run `extract_file` or `extract_folder` with `result_mode: "defer"`, call `download_results`, then call `delete_documents`.
## Development

@@ -82,0 +88,0 @@