Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.agentfront.dev/llms.txt

Use this file to discover all available pages before exploring further.

@frontmcp/testing extends Jest’s expect with MCP-specific matchers that provide cleaner assertions and better error messages.

Tool Matchers

toContainTool

Check if a tools array contains a tool by name:
const tools = await mcp.tools.list();

expect(tools).toContainTool('create-note');
expect(tools).not.toContainTool('unknown-tool');
Error message example:
Expected tools to contain "create-note", but got: [list-notes, delete-note]

Result Matchers

toBeSuccessful

Check if a tool call or resource read succeeded:
const result = await mcp.tools.call('create-note', { title: 'Test' });

expect(result).toBeSuccessful();

toBeError

Check if a result is an error, optionally with a specific error code:
const result = await mcp.tools.call('create-note', {});

// Any error
expect(result).toBeError();

// Specific error code
expect(result).toBeError(-32602); // Invalid params
Common MCP Error Codes:
CodeNameDescription
-32700Parse errorInvalid JSON
-32600Invalid requestInvalid JSON-RPC
-32601Method not foundUnknown method
-32602Invalid paramsInvalid parameters
-32603Internal errorServer error
-32001UnauthorizedAuthentication required
-32002Resource not foundResource doesn’t exist
-32800Request cancelledRequest was cancelled

Content Matchers

toHaveTextContent

Check if a result contains text content:
const result = await mcp.tools.call('echo', { message: 'hello' });

// Has any text content
expect(result).toHaveTextContent();

// Has specific text content
expect(result).toHaveTextContent('hello');

// With partial match
expect(result).toHaveTextContent('ell');

toHaveImageContent

Check if a result contains image content:
const result = await mcp.tools.call('generate-chart', { data: [1, 2, 3] });

expect(result).toHaveImageContent();

toHaveResourceContent

Check if a result contains embedded resource content:
const result = await mcp.tools.call('fetch-document', { id: '123' });

expect(result).toHaveResourceContent();

toHaveMimeType

Check the MIME type of resource content:
const content = await mcp.resources.read('data://config');

expect(content).toHaveMimeType('application/json');
expect(content).toHaveMimeType('text/plain');

Resource Matchers

toContainResource

Check if a resources array contains a resource by URI:
const resources = await mcp.resources.list();

expect(resources).toContainResource('notes://all');
expect(resources).toContainResource('config://settings');

toContainResourceTemplate

Check if a resource templates array contains a template:
const templates = await mcp.resources.listTemplates();

expect(templates).toContainResourceTemplate('notes://note/{id}');
expect(templates).toContainResourceTemplate('users://user/{userId}/profile');

Prompt Matchers

toContainPrompt

Check if a prompts array contains a prompt by name:
const prompts = await mcp.prompts.list();

expect(prompts).toContainPrompt('summarize-notes');
expect(prompts).toContainPrompt('prioritize-tasks');

toHaveMessages

Check the number of messages in a prompt result:
const result = await mcp.prompts.get('multi-turn', { context: 'test' });

expect(result).toHaveMessages(3);

Inspecting message role / content

For role and text-content checks on prompt messages, read the message directly. The toHaveTextContent matcher works for tool results; for prompt messages use plain Jest assertions:
const result = await mcp.prompts.get('assistant-prompt', {});

expect(result.messages[0].role).toBe('user');
expect(result.messages[1].role).toBe('assistant');
expect(result.messages[0].content.text).toContain('TypeScript');

JSON-RPC Matchers

toBeValidJsonRpc

Validate JSON-RPC response structure:
const response = await mcp.raw.request({
  jsonrpc: '2.0',
  id: 1,
  method: 'tools/list',
  params: {},
});

expect(response).toBeValidJsonRpc();

toHaveResult

Check that a response has a result (not an error):
const response = await mcp.raw.request({
  jsonrpc: '2.0',
  id: 1,
  method: 'tools/list',
  params: {},
});

expect(response).toHaveResult();
expect(response.result.tools).toBeDefined();

toHaveError

Check that a response is an error:
const response = await mcp.raw.request({
  jsonrpc: '2.0',
  id: 1,
  method: 'unknown/method',
  params: {},
});

expect(response).toHaveError();

toHaveErrorCode

Check for a specific error code:
const response = await mcp.raw.sendRaw('not valid json');

expect(response).toHaveErrorCode(-32700); // Parse error

UI Matchers

These assert on the rendered UI metadata returned by tools that ship a UI template (see Building tool UI). They run against result._meta / result.content[0].text (the rendered HTML).
MatcherWhat it checks
toHaveRenderedHtml()Result includes a non-empty rendered HTML payload
toContainHtmlElement(tag)Rendered HTML contains an element of tag (e.g. 'button')
toContainBoundValue(v)Rendered HTML contains the bound input/output value
toBeXssSafe()Rendered HTML has been sanitized (no <script>, inline event attrs)
toHaveWidgetMetadata()Result advertises the MCP widget metadata block
toHaveCssClass(name)Rendered HTML contains an element with the given CSS class
toNotContainRawContent(s)Rendered HTML does not contain the raw (unsanitized) string s
toHaveProperHtmlStructure()Rendered HTML is well-formed (open/close, no obvious truncation)
toHavePlatformMeta(p)Result includes the platform-specific meta block (e.g. 'openai')
toHaveMetaKey(k)Result _meta contains key k
toHaveMetaValue(k, v)Result _meta[k] deep-equals v
toNotHaveMetaKey(k)Result _meta does NOT contain key k
toHaveOnlyNamespacedMeta(ns)All _meta keys are namespaced under ns
toHavePlatformMimeType(p)Result has the expected MIME type for platform p
toHavePlatformHtml(p)Result includes platform-targeted HTML for platform p
const result = await mcp.tools.call('show-card', { id: 'abc' });

expect(result).toHaveRenderedHtml();
expect(result).toContainHtmlElement('article');
expect(result).toBeXssSafe();
expect(result).toHavePlatformMeta('openai');

Using with Negation

All matchers support .not for negation:
const tools = await mcp.tools.list();
expect(tools).not.toContainTool('deleted-tool');

const result = await mcp.tools.call('safe-action', {});
expect(result).not.toBeError();

const resources = await mcp.resources.list();
expect(resources).not.toContainResource('private://secret');

TypeScript Support

The matchers are fully typed. TypeScript will autocomplete matcher names and validate arguments:
// ✓ TypeScript knows these matchers
expect(tools).toContainTool('name');
expect(result).toBeSuccessful();
expect(result).toBeError(-32602);

// ✗ TypeScript errors on invalid matchers
expect(tools).toContainsTool('name'); // Typo caught
expect(result).toBeError('invalid'); // Wrong argument type

Best Practices

Do:
  • Use specific matchers for clearer error messages
  • Combine matchers for comprehensive validation
  • Use error code constants instead of magic numbers
Don’t:
  • Check internal implementation details
  • Use generic .toBe(true) when specific matchers exist
  • Ignore error codes in failure tests
// Good: Specific and informative
expect(result).toBeSuccessful();
expect(result).toHaveTextContent();
expect(result.json()).toHaveProperty('id');

// Bad: Generic and unclear
expect(result.isSuccess).toBe(true);
expect(result.raw.content.length).toBeGreaterThan(0);