Fixing MCP Serialization: JSON.stringify & React Server Errors
Quick answer (featured-snippet ready): The common MCP (Magic Component Platform) JSON.stringify error originates from non-serializable values (functions, DOM nodes, class instances, circular references) in the props or the MCP output. Fix by converting non-serializable values to plain objects, using safe serializers (serialize-javascript or a replacer), or adapting the MCP output to produce a serializable shape before sending the server response. For a concrete reproduction, inspect the MCP output at the project issue link below.
Direct link for reproduction and log capture: mcp__magic__21st_magic_component_builder issue — use this when filing patches or creating tests. Another helpful anchor for debugging is magic-mcp project debugging.
Why MCP serialization breaks (and how to find the culprit)
MCP serializes component metadata and props so the server can return hydrated responses or record component trees. JSON.stringify is strict: it fails or silently drops values that are functions, Symbols, DOM nodes, or objects with circular references. MCP outputs often include runtime helpers, class instances, or closure-bound functions that look fine in memory but explode when stringifying for transport or logging.
Start by determining the failing payload. Capture the exact object passed into JSON.stringify (or the framework serializer). If the error is «TypeError: Converting circular structure to JSON», the stack will point to the path. If the failure is a semantic mismatch (e.g., unexpected types in schema), logs that show the shape make diagnosis immediate. Use structured logging right before serialization to snapshot the object shape and the types of its properties.
Use these quick probes in Node or in the MCP server handler to narrow the location:
// shallow inspector
function inspectShape(obj, depth = 2) {
const seen = new WeakSet();
function typ(v) {
if (v === null) return 'null';
if (Array.isArray(v)) return 'array';
if (v instanceof Date) return 'date';
if (v instanceof RegExp) return 'regexp';
if (typeof v === 'object') return v.constructor?.name || 'object';
return typeof v;
}
function walk(o, d) {
if (o === null || typeof o !== 'object' || d < 0) return typ(o);
if (seen.has(o)) return '[circular]';
seen.add(o);
const keys = Object.keys(o).slice(0, 20);
const out = {};
for (const k of keys) out[k] = walk(o[k], d - 1);
return out;
}
return walk(obj, depth);
}
Common fixes and safe serialization patterns
There are three practical approaches: (1) sanitize MCP outputs before serialization, (2) use a safe serializer that supports certain edge cases, or (3) change how data flows so only serializable primitives reach the serializer.
Sanitization is the preferred long-term fix. Replace functions, DOM nodes, and large class instances with plain objects that have explicit, serializable fields: e.g., replace a React ref object with an id string or a status flag. If MCP injects helpers (like event callback references or an internal renderer instance), map those away in an explicit "serialization layer" within the MCP pipeline.
When sanitization is impossible (third-party data, unknown runtime shapes), use a controlled serializer. serialize-javascript is robust for functions and RegExp but beware of security: it produces code strings that may be unsafe for client eval. For circular references, a replacer that outputs "[Circular]" or path traces is preferable, e.g.:
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, function(key, value) {
if (typeof value === 'function') return `[Function:${value.name || 'anonymous'}]`;
if (typeof value === 'symbol') return value.toString();
if (value && typeof value === 'object') {
if (seen.has(value)) return `[Circular]`;
seen.add(value);
}
return value;
}, 2);
}
Repairing MCP tool output: concrete steps
1) Reproduce deterministically. Load the failing commit or snapshot that generates the MCP output preserved at the link and run the MCP emitter with debugging enabled. Capture the exact object that fails to stringify — this is the artifact to test against.
2) Create a minimal transform that maps non-serializable values to serializable placeholders. For example, convert React element instances to their type+props summary, strip internal renderer hooks, and remove prototype methods from component metadata. Persist the transform in the MCP serializer so the MCP artifacts become stable across environments.
3) Add unit tests that assert serialization round-trips: serialize -> parse -> validate shape. Tests should assert no functions, no circular markers, and expected schema keys. Add CI checks that run the MCP output generator and assert JSON.parse(serialized) succeeds and meets shape criteria.
Server-side React component handling with MCP (patterns & code)
Server-side rendering paths commonly flow like: React render -> MCP extract metadata -> MCP serialize -> HTTP response. The failure often occurs when metadata retains non-serializable runtime references (e.g., props with methods or component instances). Ensure the extraction step copies only the data required for client hydration: props (primitives/POJOs), markup (string), and a compact descriptor for components.
Recommended pattern: define a "hydrate descriptor" interface and implement a mapper in MCP that reduces complex props to descriptors. Example descriptor:
{
type: 'ComponentDescriptor',
name: 'UserCard',
props: { id: 123, displayName: 'Ada' },
markup: '<div ...>...