@agentauth/mcp
Version:
Universal payment-enabled MCP gateway for AI agents with native x402 protocol support.
133 lines (132 loc) • 5.54 kB
JavaScript
/*
* Copyright (c) 2025 AgentAuth
* SPDX-License-Identifier: MIT
*/
import { debugLog } from './utils.js';
/**
* Extract response data from a JSON-RPC message
* Enhanced to handle MCP content format with embedded JSON
*/
export function extractMCPResponseData(message) {
try {
debugLog('extractMCPResponseData - processing message:', {
hasError: 'error' in message,
hasResult: 'result' in message,
messageKeys: Object.keys(message)
});
// Check error field first (most common for payment required)
if ('error' in message && message.error) {
// Payment requirements can be in error.data or error itself
if (message.error.data) {
debugLog('Extracting response data from error.data');
return message.error.data;
}
debugLog('Extracting response data from error');
return message.error;
}
// Check result field - now with MCP content parsing
if ('result' in message && message.result) {
debugLog('Found result field, checking for MCP content format');
// Check if this is MCP content format
if (isMCPContentFormat(message.result)) {
debugLog('Detected MCP content format, parsing embedded JSON');
return extractFromMCPContent(message.result);
}
// Fallback to direct result (backward compatibility)
debugLog('Using direct result format (backward compatibility)');
return message.result;
}
debugLog('No extractable response data found in message');
return null;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
debugLog('Error extracting response data:', errorMessage);
return null;
}
}
/**
* Check if response follows MCP content format
*/
export function isMCPContentFormat(result) {
try {
const isValid = (result &&
typeof result === 'object' &&
Array.isArray(result.content) &&
result.content.length > 0 &&
result.content[0] &&
typeof result.content[0] === 'object' &&
result.content[0].type === 'text' &&
typeof result.content[0].text === 'string');
debugLog('MCP content format validation:', {
hasContent: Array.isArray(result?.content),
contentLength: result?.content?.length,
firstItemType: result?.content?.[0]?.type,
hasText: typeof result?.content?.[0]?.text === 'string',
isValid
});
return isValid;
}
catch (error) {
debugLog('Error validating MCP content format:', error);
return false;
}
}
/**
* Extract and parse JSON from MCP content format
*/
export function extractFromMCPContent(result) {
try {
const textContent = result.content[0].text;
debugLog('Parsing MCP text content:', {
textLength: textContent.length,
textPreview: textContent.substring(0, 100)
});
let parsedContent;
try {
// Try standard JSON parse first
parsedContent = JSON.parse(textContent);
}
catch (firstParseError) {
// Fallback: Handle Python-style dict strings (single quotes)
// This is a workaround for Gradio MCP which uses str() instead of json.dumps()
debugLog('Initial JSON parse failed, trying Python dict string conversion');
try {
// Convert Python dict format to JSON by replacing single quotes with double quotes
// This is a simple heuristic that works for most cases
const jsonified = textContent
.replace(/'/g, '"') // Replace single quotes with double quotes
.replace(/True/g, 'true') // Python True → JSON true
.replace(/False/g, 'false') // Python False → JSON false
.replace(/None/g, 'null'); // Python None → JSON null
parsedContent = JSON.parse(jsonified);
debugLog('Successfully parsed Python dict string after conversion');
}
catch (secondParseError) {
// If both attempts fail, re-throw the original error
throw firstParseError;
}
}
debugLog('Successfully parsed MCP content:', {
parsedKeys: Object.keys(parsedContent),
hasError: 'error' in parsedContent,
errorType: parsedContent.error,
hasErrorData: parsedContent.error?.data
});
// Smart extraction: return error.data if it contains payment protocol data,
// otherwise return the full parsed content
if (parsedContent.error?.data) {
debugLog('Returning error.data from parsed MCP content (x402 format)');
return parsedContent.error.data;
}
// For direct payment protocol data (AgentPay format), return full content
debugLog('Returning full parsed MCP content for direct protocol detection');
return parsedContent;
}
catch (parseError) {
const errorMessage = parseError instanceof Error ? parseError.message : 'Unknown parse error';
debugLog('Failed to parse MCP content as JSON:', errorMessage);
// Return the raw text as fallback
return result.content[0].text;
}
}