🎩 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.2.0
to
1.3.0
+38
CHANGELOG.md
# @suparse/mcp
## 1.3.0
### Minor Changes
- Split `list_templates` output into `team_templates` and discovery-only `system_templates`, with guidance for choosing extraction templates.
- Tighten extraction and JSON result output schemas while preserving support for deferred and returned JSON modes.
- Preserve all JSON exports returned by SDK extraction while reporting totals as processed inputs.
### Patch Changes
- Updated dependencies
- @suparse/sdk@1.3.0
## 1.2.0
### Minor Changes
- Change MCP extraction to defer by default, add `download_results`, rename direct JSON fetching to `fetch_json_results`, and return compact template summaries from the SDK.
### Patch Changes
- Updated dependencies
- @suparse/sdk@1.2.0
## 1.1.0
### Minor Changes
- Add MCP server support and expanded export formats.
The SDK now supports JSON, CSV, XLSX, and Google Sheets exports with export type options, plus Node helpers for writing file exports to disk. The CLI adds MCP server startup, config-file API key lookup, and export format flags. A new @suparse/mcp package exposes Suparse document processing as MCP stdio tools.
### Patch Changes
- Updated dependencies
- @suparse/sdk@1.1.0
import { existsSync, readFileSync, realpathSync } from "node:fs";
import { readdir, stat } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import {
SuparseAPIError,
SuparseAuthError,
SuparseError,
SuparseNodeClient,
VERSION as SDK_VERSION,
ALLOWED_EXTENSIONS,
type BatchResult,
type ExportFormat,
type ExportType,
type FailedResult,
type SuparseNodeClientOptions,
type TaskExport,
} from "@suparse/sdk/node";
const SERVER_NAME = "suparse-mcp";
const SERVER_VERSION = SDK_VERSION;
const API_KEY_NOT_FOUND_MESSAGE =
"API key not found. Set SUPARSE_API_KEY or add apiKey to ~/.config/suparse/config.json.";
const extractOptionsSchema = {
template_id: z
.string()
.optional()
.describe(
"Optional extraction template ID. Use only a non-system team template ID from list_templates. Do not pass system template IDs directly; ask the user to add the matching system template to their templates first. Omit to let Suparse auto-detect.",
),
split: z
.boolean()
.optional()
.describe("Enable auto-splitting of multi-page documents with mixed document types."),
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 = {
export_type: z
.enum(["original", "unified"])
.optional()
.describe("Export mode for JSON results. Defaults to unified."),
};
const downloadOptionsSchema = {
format: z
.enum(["json", "csv", "xlsx", "google_sheets"])
.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(),
});
type TemplateSummary = z.infer<typeof templateSummarySchema>;
const taskExportSchema = z
.object({
task_id: z.string(),
original_file: z.string(),
total_documents_extracted: z.number(),
documents: z.array(z.unknown()),
})
.passthrough();
const failedResultSchema = z.object({
file: z.string(),
error: z.string(),
});
const deferredExtractionSuccessSchema = z.object({
file_path: z.string(),
task_id: z.string(),
document_ids: z.array(z.string()),
});
const deferredExtractionFailureSchema = z.object({
file_path: z.string(),
task_id: z.string().nullable(),
error: z.string(),
});
const TEMPLATE_AGENT_GUIDANCE =
"Use team_templates for extraction. If no matching team template exists, check system_templates. When a matching system template exists, ask the user to add that system template to their templates in the Suparse UI before processing. If neither team_templates nor system_templates contains a matching template for the document type, ask the user to create a custom extraction schema for that document type in the Suparse UI.";
interface BatchResultPayload extends Record<string, unknown> {
result_mode: "return_json";
total: number;
succeeded: TaskExport[];
failed: FailedResult[];
}
interface DeferredExtractionSuccess {
file_path: string;
task_id: string;
document_ids: string[];
}
interface DeferredExtractionFailure {
file_path: string;
task_id: string | null;
error: string;
}
interface DeferredBatchResultPayload extends Record<string, unknown> {
result_mode: "defer";
total: number;
succeeded: DeferredExtractionSuccess[];
failed: DeferredExtractionFailure[];
}
interface TemplatesPayload extends Record<string, unknown> {
templates: TemplateSummary[];
team_templates: TemplateSummary[];
system_templates: TemplateSummary[];
agent_guidance: string;
}
interface FetchResultsPayload extends Record<string, unknown> {
format: "json";
export_type: ExportType;
results: TaskExport[];
}
interface DownloadResultsPayload extends Record<string, unknown> {
format: ExportFormat;
export_type: ExportType;
output_path: string;
document_ids: string[];
}
interface DeleteDocumentsPayload extends Record<string, unknown> {
deleted: boolean;
document_ids: string[];
}
function getConfigApiKey(): string | undefined {
const configPath = path.join(os.homedir(), ".config", "suparse", "config.json");
if (!existsSync(configPath)) return undefined;
const config = JSON.parse(readFileSync(configPath, "utf-8")) as { apiKey?: unknown };
return typeof config.apiKey === "string" && config.apiKey ? config.apiKey : undefined;
}
function getApiKey(): string {
const apiKey = process.env.SUPARSE_API_KEY ?? getConfigApiKey();
if (!apiKey) throw new Error(API_KEY_NOT_FOUND_MESSAGE);
return apiKey;
}
function createClient(apiUrl?: string): SuparseNodeClient {
const options: SuparseNodeClientOptions = { apiKey: getApiKey() };
const baseUrl = apiUrl ?? process.env.SUPARSE_API_URL;
if (baseUrl) options.baseUrl = baseUrl;
return new SuparseNodeClient(options);
}
function toBatchResultPayload(result: BatchResult): BatchResultPayload {
return {
result_mode: "return_json",
total: result.total,
succeeded: result.succeeded,
failed: result.failed,
};
}
async function listSupportedFolderFiles(folderPath: string): Promise<string[]> {
const resolved = path.resolve(folderPath);
const folderStats = await stat(resolved);
if (!folderStats.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<T extends Record<string, unknown>>(
text: string,
structuredContent: T,
): {
content: { type: "text"; text: string }[];
structuredContent: T;
} {
return {
content: [{ type: "text" as const, text }],
structuredContent,
};
}
function toolError(error: unknown): {
isError: boolean;
content: { type: "text"; text: string }[];
} {
let message = error instanceof Error ? error.message : String(error);
if (error instanceof SuparseAuthError) {
message = `Permission denied (${error.statusCode}): ${error.message}. Check SUPARSE_API_KEY.`;
} else if (error instanceof SuparseAPIError) {
message = `API error (${error.statusCode}): ${error.message}`;
} else if (error instanceof SuparseError) {
message = `Suparse error: ${error.message}`;
}
return {
isError: true,
content: [{ type: "text" as const, text: message }],
};
}
function summarizeBatch(payload: BatchResultPayload): string {
return JSON.stringify(
{
result_mode: payload.result_mode,
total: payload.total,
succeeded: payload.succeeded.length,
failed: payload.failed.length,
},
null,
2,
);
}
function summarizeDeferredBatch(payload: DeferredBatchResultPayload): string {
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,
);
}
export function createSuparseMcpServer(): McpServer {
const server = new McpServer({
name: SERVER_NAME,
version: SERVER_VERSION,
});
server.registerTool(
"extract_file",
{
title: "Extract File",
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: {
file_path: z.string().min(1).describe("Local path to a supported document file."),
...extractOptionsSchema,
...clientOptionsSchema,
},
outputSchema: {
result_mode: z.enum(["defer", "return_json"]),
total: z.number(),
succeeded: z.array(z.union([deferredExtractionSuccessSchema, taskExportSchema])),
failed: z.array(z.union([deferredExtractionFailureSchema, failedResultSchema])),
},
},
async ({ file_path, template_id, split, cleanup, result_mode, api_url }) => {
let client: SuparseNodeClient | undefined;
try {
client = createClient(api_url);
const mode = result_mode ?? "defer";
if (mode === "defer") {
if (cleanup) {
throw new Error(
"cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.",
);
}
let taskId: string | null = null;
try {
taskId = await client.uploadFile(file_path, {
template_id,
split,
});
const { documentIds } = await client.pollTaskStatus(taskId);
const payload: DeferredBatchResultPayload = {
result_mode: "defer",
total: 1,
succeeded: [{ file_path, task_id: taskId, document_ids: documentIds }],
failed: [],
};
return toolResult(summarizeDeferredBatch(payload), payload);
} catch (error) {
const payload: DeferredBatchResultPayload = {
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 result = await client.extract(file_path, {
template_id,
split,
cleanup,
});
const payload = toBatchResultPayload(result);
return toolResult(summarizeBatch(payload), payload);
} catch (error) {
return toolError(error);
} finally {
await client?.close();
}
},
);
server.registerTool(
"extract_folder",
{
title: "Extract Folder",
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: {
folder_path: z
.string()
.min(1)
.describe("Local folder containing supported document files."),
...extractOptionsSchema,
...clientOptionsSchema,
},
outputSchema: {
result_mode: z.enum(["defer", "return_json"]),
total: z.number(),
succeeded: z.array(z.union([deferredExtractionSuccessSchema, taskExportSchema])),
failed: z.array(z.union([deferredExtractionFailureSchema, failedResultSchema])),
},
},
async ({ folder_path, template_id, split, cleanup, result_mode, api_url }) => {
let client: SuparseNodeClient | undefined;
try {
client = createClient(api_url);
const mode = result_mode ?? "defer";
if (mode === "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: DeferredBatchResultPayload = {
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 result = await client.extractFolder(folder_path, {
template_id,
split,
cleanup,
});
const payload = toBatchResultPayload(result);
return toolResult(summarizeBatch(payload), payload);
} catch (error) {
return toolError(error);
} finally {
await client?.close();
}
},
);
server.registerTool(
"list_templates",
{
title: "List Templates",
description:
"List extraction templates for choosing an extraction template. Agents must use team_templates for processing. System templates are discovery-only in MCP: if a matching system template exists but no matching team template exists, ask the user to add that system template to their templates in the Suparse UI before processing. If no matching team or system template exists, ask the user to create a custom extraction schema for that document type in the Suparse UI.",
inputSchema: {
include_system: z
.boolean()
.optional()
.describe(
"Include discovery-only system templates in addition to team templates. System templates returned here are not directly usable for extraction through MCP until the user adds them to their templates in the Suparse UI.",
),
...clientOptionsSchema,
},
outputSchema: {
templates: z.array(templateSummarySchema),
team_templates: z.array(templateSummarySchema),
system_templates: z.array(templateSummarySchema),
agent_guidance: z.string(),
},
},
async ({ include_system, api_url }) => {
let client: SuparseNodeClient | undefined;
try {
client = createClient(api_url);
const templates = await client.listTemplates({ includeSystem: include_system });
const mappedTemplates = templates.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,
}),
);
const teamTemplates = mappedTemplates.filter((template) => !template.is_system_template);
const systemTemplates = mappedTemplates.filter((template) => template.is_system_template);
const payload: TemplatesPayload = {
templates: mappedTemplates,
team_templates: teamTemplates,
system_templates: systemTemplates,
agent_guidance: TEMPLATE_AGENT_GUIDANCE,
};
return toolResult(
JSON.stringify(
{
found: payload.templates.length,
team_templates: payload.team_templates.length,
system_templates: payload.system_templates.length,
agent_guidance: payload.agent_guidance,
},
null,
2,
),
payload,
);
} catch (error) {
return toolError(error);
} finally {
await client?.close();
}
},
);
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: {
document_ids: z.array(z.string().min(1)).min(1).describe("Suparse document IDs to export."),
...exportOptionsSchema,
...clientOptionsSchema,
},
outputSchema: {
format: z.literal("json"),
export_type: z.enum(["original", "unified"]),
results: z.array(taskExportSchema),
},
},
async ({ document_ids, export_type, api_url }) => {
let client: SuparseNodeClient | undefined;
try {
client = createClient(api_url);
const exportType = export_type ?? "unified";
const exportResult = await client.fetchResults(document_ids, {
format: "json",
export_type: exportType,
});
const payload: FetchResultsPayload = {
format: "json",
export_type: exportType,
results: exportResult,
};
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(["json", "csv", "xlsx", "google_sheets"]),
export_type: z.enum(["original", "unified"]),
output_path: z.string(),
document_ids: z.array(z.string()),
},
},
async ({ document_ids, output_path, format, export_type, api_url }) => {
let client: SuparseNodeClient | undefined;
try {
client = createClient(api_url);
const exportType = export_type ?? "unified";
const outputPath = await client.downloadResults(document_ids, output_path, {
format,
export_type: exportType,
});
const payload: DownloadResultsPayload = {
format,
export_type: exportType,
output_path: outputPath,
document_ids,
};
return toolResult(JSON.stringify(payload, null, 2), payload);
} catch (error) {
return toolError(error);
} finally {
await client?.close();
}
},
);
server.registerTool(
"delete_documents",
{
title: "Delete Documents",
description: "Delete one or more documents from Suparse by document ID.",
inputSchema: {
document_ids: z.array(z.string().min(1)).min(1).describe("Suparse document IDs to delete."),
...clientOptionsSchema,
},
outputSchema: {
deleted: z.boolean(),
document_ids: z.array(z.string()),
},
},
async ({ document_ids, api_url }) => {
let client: SuparseNodeClient | undefined;
try {
client = createClient(api_url);
const payload: DeleteDocumentsPayload = {
deleted: await client.deleteDocuments(document_ids),
document_ids,
};
return toolResult(JSON.stringify(payload, null, 2), payload);
} catch (error) {
return toolError(error);
} finally {
await client?.close();
}
},
);
return server;
}
export async function runMcpServer(): Promise<void> {
const server = createSuparseMcpServer();
await server.connect(new StdioServerTransport());
}
function isDirectRun(): boolean {
if (!process.argv[1]) return false;
return import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;
}
if (isDirectRun()) {
runMcpServer().catch((error: unknown) => {
console.error(error instanceof Error ? error.message : error);
process.exit(1);
});
}
+1
-1

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

{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;iBAkPgB,sBAAA,CAAA,GAA0B,SAAA;AAAA,iBAkUpB,YAAA,CAAA,GAAgB,OAAA"}
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;iBAqRgB,sBAAA,IAA0B,SAAS;AAAA,iBAgW7B,YAAA,IAAgB,OAAO"}

@@ -17,3 +17,3 @@ #!/usr/bin/env node

const extractOptionsSchema = {
template_id: z.string().optional().describe("Optional extraction template ID. Omit to let Suparse auto-detect."),
template_id: z.string().optional().describe("Optional extraction template ID. Use only a non-system team template ID from list_templates. Do not pass system template IDs directly; ask the user to add the matching system template to their templates first. Omit to let Suparse auto-detect."),
split: z.boolean().optional().describe("Enable auto-splitting of multi-page documents with mixed document types."),

@@ -44,2 +44,23 @@ 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."),

});
const taskExportSchema = z.object({
task_id: z.string(),
original_file: z.string(),
total_documents_extracted: z.number(),
documents: z.array(z.unknown())
}).passthrough();
const failedResultSchema = z.object({
file: z.string(),
error: z.string()
});
const deferredExtractionSuccessSchema = z.object({
file_path: z.string(),
task_id: z.string(),
document_ids: z.array(z.string())
});
const deferredExtractionFailureSchema = z.object({
file_path: z.string(),
task_id: z.string().nullable(),
error: z.string()
});
const TEMPLATE_AGENT_GUIDANCE = "Use team_templates for extraction. If no matching team template exists, check system_templates. When a matching system template exists, ask the user to add that system template to their templates in the Suparse UI before processing. If neither team_templates nor system_templates contains a matching template for the document type, ask the user to create a custom extraction schema for that document type in the Suparse UI.";
function getConfigApiKey() {

@@ -58,3 +79,4 @@ const configPath = path.join(os.homedir(), ".config", "suparse", "config.json");

const options = { apiKey: getApiKey() };
if (apiUrl) options.baseUrl = apiUrl;
const baseUrl = apiUrl ?? process.env.SUPARSE_API_URL;
if (baseUrl) options.baseUrl = baseUrl;
return new SuparseNodeClient(options);

@@ -130,8 +152,9 @@ }

total: z.number(),
succeeded: z.array(z.unknown()),
failed: z.array(z.unknown())
succeeded: z.array(z.union([deferredExtractionSuccessSchema, taskExportSchema])),
failed: z.array(z.union([deferredExtractionFailureSchema, failedResultSchema]))
}
}, async ({ file_path, template_id, split, cleanup, result_mode, api_url }) => {
const client = createClient(api_url);
let client;
try {
client = createClient(api_url);
if ((result_mode ?? "defer") === "defer") {

@@ -180,3 +203,3 @@ if (cleanup) throw new Error("cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.");

} finally {
await client.close();
await client?.close();
}

@@ -195,8 +218,9 @@ });

total: z.number(),
succeeded: z.array(z.unknown()),
failed: z.array(z.unknown())
succeeded: z.array(z.union([deferredExtractionSuccessSchema, taskExportSchema])),
failed: z.array(z.union([deferredExtractionFailureSchema, failedResultSchema]))
}
}, async ({ folder_path, template_id, split, cleanup, result_mode, api_url }) => {
const client = createClient(api_url);
let client;
try {
client = createClient(api_url);
if ((result_mode ?? "defer") === "defer") {

@@ -234,3 +258,3 @@ if (cleanup) throw new Error("cleanup is only valid with result_mode return_json. Use download_results first, then delete_documents.");

} finally {
await client.close();
await client?.close();
}

@@ -240,12 +264,18 @@ });

title: "List Templates",
description: "List extraction templates available to the authenticated Suparse account.",
description: "List extraction templates for choosing an extraction template. Agents must use team_templates for processing. System templates are discovery-only in MCP: if a matching system template exists but no matching team template exists, ask the user to add that system template to their templates in the Suparse UI before processing. If no matching team or system template exists, ask the user to create a custom extraction schema for that document type in the Suparse UI.",
inputSchema: {
include_system: z.boolean().optional().describe("Include all system templates, not only assigned account templates."),
include_system: z.boolean().optional().describe("Include discovery-only system templates in addition to team templates. System templates returned here are not directly usable for extraction through MCP until the user adds them to their templates in the Suparse UI."),
...clientOptionsSchema
},
outputSchema: { templates: z.array(templateSummarySchema) }
outputSchema: {
templates: z.array(templateSummarySchema),
team_templates: z.array(templateSummarySchema),
system_templates: z.array(templateSummarySchema),
agent_guidance: z.string()
}
}, async ({ include_system, api_url }) => {
const client = createClient(api_url);
let client;
try {
const payload = { templates: (await client.listTemplates({ includeSystem: include_system })).map(({ id, name, description, template_language, version, is_active, is_system_template, created_at }) => ({
client = createClient(api_url);
const mappedTemplates = (await client.listTemplates({ includeSystem: include_system })).map(({ id, name, description, template_language, version, is_active, is_system_template, created_at }) => ({
id,

@@ -259,8 +289,19 @@ name,

created_at
})) };
return toolResult(`Found ${payload.templates.length} templates.`, payload);
}));
const payload = {
templates: mappedTemplates,
team_templates: mappedTemplates.filter((template) => !template.is_system_template),
system_templates: mappedTemplates.filter((template) => template.is_system_template),
agent_guidance: TEMPLATE_AGENT_GUIDANCE
};
return toolResult(JSON.stringify({
found: payload.templates.length,
team_templates: payload.team_templates.length,
system_templates: payload.system_templates.length,
agent_guidance: payload.agent_guidance
}, null, 2), payload);
} catch (error) {
return toolError(error);
} finally {
await client.close();
await client?.close();
}

@@ -279,7 +320,8 @@ });

export_type: z.enum(["original", "unified"]),
results: z.array(z.unknown())
results: z.array(taskExportSchema)
}
}, async ({ document_ids, export_type, api_url }) => {
const client = createClient(api_url);
let client;
try {
client = createClient(api_url);
const exportType = export_type ?? "unified";

@@ -298,3 +340,3 @@ const payload = {

} finally {
await client.close();
await client?.close();
}

@@ -323,4 +365,5 @@ });

}, async ({ document_ids, output_path, format, export_type, api_url }) => {
const client = createClient(api_url);
let client;
try {
client = createClient(api_url);
const exportType = export_type ?? "unified";

@@ -340,3 +383,3 @@ const payload = {

} finally {
await client.close();
await client?.close();
}

@@ -356,4 +399,5 @@ });

}, async ({ document_ids, api_url }) => {
const client = createClient(api_url);
let client;
try {
client = createClient(api_url);
const payload = {

@@ -367,3 +411,3 @@ deleted: await client.deleteDocuments(document_ids),

} finally {
await client.close();
await client?.close();
}

@@ -370,0 +414,0 @@ });

@@ -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 { 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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"}
{"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(\n \"Optional extraction template ID. Use only a non-system team template ID from list_templates. Do not pass system template IDs directly; ask the user to add the matching system template to their templates first. Omit to let Suparse auto-detect.\",\n ),\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\nconst taskExportSchema = z\n .object({\n task_id: z.string(),\n original_file: z.string(),\n total_documents_extracted: z.number(),\n documents: z.array(z.unknown()),\n })\n .passthrough();\n\nconst failedResultSchema = z.object({\n file: z.string(),\n error: z.string(),\n});\n\nconst deferredExtractionSuccessSchema = z.object({\n file_path: z.string(),\n task_id: z.string(),\n document_ids: z.array(z.string()),\n});\n\nconst deferredExtractionFailureSchema = z.object({\n file_path: z.string(),\n task_id: z.string().nullable(),\n error: z.string(),\n});\n\nconst TEMPLATE_AGENT_GUIDANCE =\n \"Use team_templates for extraction. If no matching team template exists, check system_templates. When a matching system template exists, ask the user to add that system template to their templates in the Suparse UI before processing. If neither team_templates nor system_templates contains a matching template for the document type, ask the user to create a custom extraction schema for that document type in the Suparse UI.\";\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 team_templates: TemplateSummary[];\n system_templates: TemplateSummary[];\n agent_guidance: string;\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 const baseUrl = apiUrl ?? process.env.SUPARSE_API_URL;\n if (baseUrl) options.baseUrl = baseUrl;\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.union([deferredExtractionSuccessSchema, taskExportSchema])),\n failed: z.array(z.union([deferredExtractionFailureSchema, failedResultSchema])),\n },\n },\n async ({ file_path, template_id, split, cleanup, result_mode, api_url }) => {\n let client: SuparseNodeClient | undefined;\n try {\n client = createClient(api_url);\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.union([deferredExtractionSuccessSchema, taskExportSchema])),\n failed: z.array(z.union([deferredExtractionFailureSchema, failedResultSchema])),\n },\n },\n async ({ folder_path, template_id, split, cleanup, result_mode, api_url }) => {\n let client: SuparseNodeClient | undefined;\n try {\n client = createClient(api_url);\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:\n \"List extraction templates for choosing an extraction template. Agents must use team_templates for processing. System templates are discovery-only in MCP: if a matching system template exists but no matching team template exists, ask the user to add that system template to their templates in the Suparse UI before processing. If no matching team or system template exists, ask the user to create a custom extraction schema for that document type in the Suparse UI.\",\n inputSchema: {\n include_system: z\n .boolean()\n .optional()\n .describe(\n \"Include discovery-only system templates in addition to team templates. System templates returned here are not directly usable for extraction through MCP until the user adds them to their templates in the Suparse UI.\",\n ),\n ...clientOptionsSchema,\n },\n outputSchema: {\n templates: z.array(templateSummarySchema),\n team_templates: z.array(templateSummarySchema),\n system_templates: z.array(templateSummarySchema),\n agent_guidance: z.string(),\n },\n },\n async ({ include_system, api_url }) => {\n let client: SuparseNodeClient | undefined;\n try {\n client = createClient(api_url);\n const templates = await client.listTemplates({ includeSystem: include_system });\n const mappedTemplates = 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 const teamTemplates = mappedTemplates.filter((template) => !template.is_system_template);\n const systemTemplates = mappedTemplates.filter((template) => template.is_system_template);\n const payload: TemplatesPayload = {\n templates: mappedTemplates,\n team_templates: teamTemplates,\n system_templates: systemTemplates,\n agent_guidance: TEMPLATE_AGENT_GUIDANCE,\n };\n return toolResult(\n JSON.stringify(\n {\n found: payload.templates.length,\n team_templates: payload.team_templates.length,\n system_templates: payload.system_templates.length,\n agent_guidance: payload.agent_guidance,\n },\n null,\n 2,\n ),\n payload,\n );\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(taskExportSchema),\n },\n },\n async ({ document_ids, export_type, api_url }) => {\n let client: SuparseNodeClient | undefined;\n try {\n client = createClient(api_url);\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 let client: SuparseNodeClient | undefined;\n try {\n client = createClient(api_url);\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 let client: SuparseNodeClient | undefined;\n try {\n client = createClient(api_url);\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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"}

@@ -21,2 +21,2 @@ MIT License

OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
{
"name": "@suparse/mcp",
"version": "1.2.0",
"version": "1.3.0",
"description": "MCP server for the Suparse Document Processing API",
"author": "Suparse <support@suparse.com>",
"homepage": "https://suparse.com",
"homepage": "https://github.com/suparse/suparse-mcp#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/suparse/suparse-mcp.git"
},
"bugs": {
"url": "https://github.com/suparse/suparse-mcp/issues"
},
"mcpName": "io.github.suparse/suparse-mcp",
"type": "module",

@@ -20,3 +28,7 @@ "bin": {

"files": [
"dist"
"dist",
"src",
"README.md",
"CHANGELOG.md",
"LICENSE"
],

@@ -39,16 +51,22 @@ "publishConfig": {

"@modelcontextprotocol/sdk": "^1.21.0",
"zod": "^3.25.0",
"@suparse/sdk": "1.2.0"
"@suparse/sdk": "^1.3.0",
"zod": "^3.25.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@types/node": "^22.0.0",
"eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8",
"tsdown": "^0.22.0",
"typescript": "^5.7.0"
"typescript": "^5.7.0",
"typescript-eslint": "^8.59.3",
"vitest": "^4.1.7"
},
"scripts": {
"build": "tsdown",
"typecheck": "pnpm --filter @suparse/sdk build && tsc --noEmit",
"lint": "eslint src/",
"typecheck": "tsc --noEmit",
"test": "vitest run",
"lint": "eslint src/ test/",
"clean": "rm -rf dist *.tsbuildinfo"
}
}

@@ -1,11 +0,13 @@

# @suparse/mcp
# Suparse MCP Server
MCP stdio server for the [Suparse](https://suparse.com) Document Processing API.
MCP stdio server for the [Suparse](https://suparse.com) Document Processing API. Use it from local MCP clients such as Claude Code and Codex to extract structured data from documents into JSON, CSV, XLSX, or Google Sheets; process single files or folders; let Suparse auto-detect extraction schemas or apply your team templates; split mixed multi-page documents; and download or clean up results by document ID.
Use this package to connect Suparse document extraction to local MCP clients such as Claude Code and Codex.
## Security Boundary
This is a local stdio MCP server. Connected MCP clients can ask it to read local document paths and write export files wherever the server process has permission. Only connect it to MCP clients and workspaces you trust.
## Requirements
- A Suparse API key
- Node.js 20+
- A Suparse API key

@@ -63,3 +65,3 @@ ## Authentication

- `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.
- `list_templates`: List summary metadata for templates, grouped into directly usable `team_templates` and discovery-only `system_templates`.
- `fetch_json_results`: Fetch JSON extraction results by document ID directly in the MCP response. Use only when full JSON is needed in context.

@@ -85,2 +87,13 @@ - `download_results`: Fetch an export by document ID and write it directly to local disk. Use this for `json`, `csv`, `xlsx`, and `google_sheets`.

## Template Selection for MCP Agents
MCP agents should use only `team_templates` when passing `template_id` to `extract_file` or `extract_folder`.
When a user asks to process a document type such as a receipt:
1. Check `team_templates` first and use the matching team template if present.
2. If no matching team template exists, call `list_templates` with `include_system: true` and check `system_templates`.
3. If a matching system template exists, ask the user to add that system template to their templates in the Suparse UI before processing. Do not pass the system template ID directly to extraction.
4. If no matching team or system template exists, ask the user to create a custom extraction schema for that document type in the Suparse UI.
## Development

@@ -91,3 +104,3 @@

```bash
pnpm --filter @suparse/mcp build
pnpm build
```

@@ -98,5 +111,5 @@

```bash
npx @modelcontextprotocol/inspector node packages/mcp/dist/index.mjs
npx @modelcontextprotocol/inspector node dist/index.mjs
```
The MCP server uses stdout for JSON-RPC protocol messages. Do not add `console.log` output to the server path; use stderr or MCP tool responses.