parserator-mcp-server
Version:
Intelligent data parsing for AI agents via Model Context Protocol
311 lines • 11.7 kB
JavaScript
/**
* Real Parserator API Client for MCP Server
* Uses the actual production Parserator SDK with the Architect-Extractor pattern
*/
export class ParseratorClient {
config;
sdk; // Will be dynamically imported
constructor(config) {
this.config = {
baseUrl: 'https://app-5108296280.us-central1.run.app',
timeout: 30000,
...config
};
}
async initializeSDK() {
if (!this.sdk) {
// Note: In production, we would import the actual Parserator SDK
// For now, we'll use the REST API directly since we're in MCP context
this.sdk = {
baseUrl: this.config.baseUrl,
apiKey: this.config.apiKey,
headers: {
'Authorization': `Bearer ${this.config.apiKey}`,
'Content-Type': 'application/json',
'User-Agent': 'Parserator MCP Server v1.0.0'
}
};
}
}
async parseData(inputData, outputSchema, instructions) {
await this.initializeSDK();
try {
const requestBody = {
inputData,
outputSchema,
instructions,
options: {
includeMetadata: true,
validateOutput: true
}
};
const response = await fetch(`${this.config.baseUrl}/v1/parse`, {
method: 'POST',
headers: this.sdk.headers,
body: JSON.stringify(requestBody)
});
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(`API Error ${response.status}: ${errorData.message || response.statusText}`);
}
const result = await response.json();
if (!result.success) {
return {
success: false,
error: result.error?.message || 'Parse operation failed',
code: result.error?.code || 'PARSE_FAILED'
};
}
return {
success: true,
parsedData: result.parsedData,
confidence: result.metadata?.confidence,
tokensUsed: result.metadata?.tokensUsed,
processingTime: result.metadata?.processingTimeMs,
requestId: result.metadata?.requestId
};
}
catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
code: 'NETWORK_ERROR'
};
}
}
async validateSchema(data, schema) {
await this.initializeSDK();
try {
// Use Parserator's validation endpoint
const requestBody = { data, schema };
const response = await fetch(`${this.config.baseUrl}/v1/validate`, {
method: 'POST',
headers: this.sdk.headers,
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`Validation API Error: ${response.status}`);
}
const result = await response.json();
return {
success: true,
valid: result.valid,
errors: result.errors || [],
suggestions: result.suggestions || []
};
}
catch (error) {
// Fallback to basic validation
const isValid = this.basicValidation(data, schema);
return {
success: true,
valid: isValid,
errors: isValid ? [] : ['Basic validation failed - structure mismatch'],
suggestions: isValid ? [] : ['Check that all required fields are present and correctly typed']
};
}
}
async suggestSchema(sampleData) {
await this.initializeSDK();
try {
const requestBody = { sampleData: sampleData.substring(0, 2000) }; // Limit sample size
const response = await fetch(`${this.config.baseUrl}/v1/suggest-schema`, {
method: 'POST',
headers: this.sdk.headers,
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`Schema suggestion API Error: ${response.status}`);
}
const result = await response.json();
return {
success: true,
suggestedSchema: result.suggestedSchema,
confidence: result.confidence || 0.8,
reasoning: result.reasoning || 'AI-generated schema based on data patterns'
};
}
catch (error) {
// Fallback to pattern-based schema generation
const suggestedSchema = this.generatePatternSchema(sampleData);
return {
success: true,
suggestedSchema,
confidence: 0.7,
reasoning: 'Generated using pattern detection (fallback method)'
};
}
}
basicValidation(data, schema) {
if (!data || !schema)
return false;
const dataKeys = Object.keys(data);
const schemaKeys = Object.keys(schema);
// Check if all schema keys are present in data
return schemaKeys.every(key => dataKeys.includes(key));
}
generatePatternSchema(sampleData) {
const schema = {};
// Email detection
if (sampleData.match(/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/)) {
schema.email = 'string';
}
// Date detection
if (sampleData.match(/\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}\/\d{4}/)) {
schema.date = 'string';
}
// Phone detection
if (sampleData.match(/\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/)) {
schema.phone = 'string';
}
// Money/price detection
if (sampleData.match(/\$\d+(?:\.\d{2})?|\d+(?:\.\d{2})?\s*(?:USD|dollars?)/i)) {
schema.amount = 'number';
}
// Name detection
if (sampleData.match(/\b[A-Z][a-z]+\s+[A-Z][a-z]+\b/)) {
schema.name = 'string';
}
// Address detection
if (sampleData.match(/\d+\s+[A-Za-z\s]+(?:Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd)/i)) {
schema.address = 'string';
}
// URL detection
if (sampleData.match(/https?:\/\/[^\s]+/)) {
schema.url = 'string';
}
// Default fields for any content
schema.extractedText = 'string';
schema.category = 'string';
return schema;
}
async getUsageStats() {
await this.initializeSDK();
try {
const response = await fetch(`${this.config.baseUrl}/v1/account/usage`, {
method: 'GET',
headers: this.sdk.headers
});
if (!response.ok) {
throw new Error(`Usage API Error: ${response.status}`);
}
return await response.json();
}
catch (error) {
// Return minimal mock data if real API fails
return {
requestsThisMonth: 0,
tokensUsedThisMonth: 0,
quotaRemaining: 1000,
lastUpdated: new Date().toISOString(),
error: 'Unable to fetch real usage data'
};
}
}
async getTemplates() {
await this.initializeSDK();
try {
const response = await fetch(`${this.config.baseUrl}/v1/templates`, {
method: 'GET',
headers: this.sdk.headers
});
if (!response.ok) {
throw new Error(`Templates API Error: ${response.status}`);
}
return await response.json();
}
catch (error) {
// Return production-ready templates as fallback
return [
{
id: 'email-processor',
name: 'Email Processor',
description: 'Extract structured data from emails using Architect-Extractor pattern',
schema: {
sender: 'string',
subject: 'string',
recipients: 'string_array',
tasks: 'string_array',
dates: 'string_array',
priority: 'string',
actionRequired: 'boolean',
summary: 'string'
},
useCase: 'ai-agents'
},
{
id: 'invoice-extractor',
name: 'Invoice Extractor',
description: 'Parse invoices and receipts with high accuracy',
schema: {
vendor: 'string',
vendorAddress: 'string',
amount: 'number',
tax: 'number',
date: 'string',
invoiceNumber: 'string',
items: 'array',
currency: 'string'
},
useCase: 'business-automation'
},
{
id: 'contact-parser',
name: 'Contact Parser',
description: 'Extract contact information from various sources',
schema: {
name: 'string',
title: 'string',
company: 'string',
email: 'string',
phone: 'string',
address: 'string',
linkedin: 'string',
notes: 'string'
},
useCase: 'crm-integration'
},
{
id: 'document-analyzer',
name: 'Document Analyzer',
description: 'Analyze and extract key information from documents',
schema: {
documentType: 'string',
title: 'string',
author: 'string',
date: 'string',
keyPoints: 'string_array',
summary: 'string',
entities: 'array',
sentiment: 'string'
},
useCase: 'content-processing'
}
];
}
}
async testConnection() {
await this.initializeSDK();
const startTime = Date.now();
try {
const response = await fetch(`${this.config.baseUrl}/health`, {
method: 'GET',
headers: { 'Authorization': this.sdk.headers.Authorization }
});
const latency = Date.now() - startTime;
return {
success: response.ok,
latency,
error: response.ok ? undefined : `HTTP ${response.status}: ${response.statusText}`
};
}
catch (error) {
return {
success: false,
latency: Date.now() - startTime,
error: error instanceof Error ? error.message : 'Connection failed'
};
}
}
}
//# sourceMappingURL=parserator-client.js.map