The MCP content type system (text, image, resource) tells clients how to render tool results. Returning a raw string instead of a content array means clients receive output they cannot reliably parse or display. Image tools that omit mimeType from data content leave clients unable to decode the bytes. These are protocol conformance failures — a client following the MCP spec will reject or misrender non-conformant responses, turning working tool calls into user-visible errors.
Low because malformed return structures cause display or parse failures in clients but do not expose data or enable attacks; the impact is limited to incorrect rendering.
Return a content array with the correct type field from every tool handler. Never return a raw string.
// src/tools/screenshot.ts
server.tool('screenshot', ..., async () => ({
content: [{
type: 'image',
data: base64ImageData, // required for image
mimeType: 'image/png', // required for image
}]
}))
// Text result
server.tool('read_file', ..., async ({ path }) => ({
content: [{ type: 'text', text: await fs.readFile(path, 'utf-8') }]
}))
For tools returning multiple items, include them as separate entries in the content array — not as a concatenated string.
ID: mcp-server.tool-definitions.return-content-types
Severity: low
What to look for: Count all tool return statements. Enumerate which specify content types (text, image, resource) vs. which return untyped content. Examine the return values of all tool handlers. Check that handlers return content arrays with proper type fields (text, image, or resource). For text content, verify the text field is a string. For image content, verify data (base64) and mimeType are set. For resource content, verify uri and mimeType are set. Check that tools don't return raw objects or non-content structures.
Pass criteria: All tool handlers return properly structured content arrays with correct type fields. Image and resource content types include required metadata (mimeType, data/uri). 100% of tool returns must specify a valid MCP content type.
Fail criteria: Tool handlers return raw strings instead of content arrays, or content objects have incorrect/missing type fields, or image/resource content lacks required metadata.
Skip (N/A) when: The server registers no tools. All checks skip when no MCP server is detected.
Cross-reference: For resource URI definitions, see resource-uris.
Detail on fail: "Tool 'screenshot' returns image content but mimeType is missing — clients cannot render the image correctly" or "Tool 'read_file' returns a raw string instead of a content array with type: 'text'"
Remediation: Content types tell clients how to render tool results. Use the correct structure:
// src/tools/search.ts — typed return content
return { content: [{ type: "text", text: JSON.stringify(results) }] }
// Text content
server.tool('read_file', ..., async ({ path }) => ({
content: [{ type: 'text', text: await fs.readFile(path, 'utf-8') }]
}))
// Image content
server.tool('screenshot', ..., async () => ({
content: [{
type: 'image',
data: base64Data,
mimeType: 'image/png',
}]
}))
// Multiple content items
server.tool('analyze', ..., async ({ path }) => ({
content: [
{ type: 'text', text: 'Analysis results:' },
{ type: 'text', text: JSON.stringify(results, null, 2) },
]
}))