@frontmcp/testing
End-to-end tests for FrontMCP servers. It starts your server, gives each test mcp, an MCP client connected to it, and adds matchers like toBeSuccessful() and toContainTool() to expect. Tests run under Jest, with frontmcp test or your own Jest configuration. It also has the pieces to build tests of your own: a JSON Web Token (JWT) factory, mock OAuth and API servers, HTTP mocking and request interceptors.
Install
npm install -D @frontmcp/testing jest tsx
Requires Node.js 24 or later, and @frontmcp/sdk in the project (a peer dependency, as is @frontmcp/lazy-zod, which comes with the SDK). jest (29 or 30) runs the tests, and @jest/globals, which comes with it, provides expect; both are optional peers so that the token factory and mock servers work outside Jest. The library starts your server with tsx. @swc/jest, which compiles the tests, is installed with the package.
Usage
// e2e/tickets.e2e.spec.ts
import { expect, test } from '@frontmcp/testing';
test.use({ server: './src/main.ts' });
test('server exposes tools', async ({ mcp }) => {
const tools = await mcp.tools.list();
expect(tools).toContainTool('search_tickets');
});
test('tool execution works', async ({ mcp }) => {
const result = await mcp.tools.call('search_tickets', { query: 'log' });
expect(result).toBeSuccessful();
expect(result.json()).toEqual({ tickets: [{ id: 'T-1', title: 'Cannot log in' }] });
});
npx frontmcp test
frontmcp test (from the frontmcp CLI) runs src/**/*.spec.ts, __tests__/**/*.spec.ts and e2e/**/*.e2e.spec.ts with a Jest configuration it generates. With a jest.config.* of your own, start from one of the Jest presets. The server starts when the first test that needs it runs, and tests with the same test.use() options share it.
Jest presets
| Preset | Use it for |
|---|---|
preset: '@frontmcp/testing' | The configuration frontmcp test generates: unit and e2e specs, a 60-second timeout, and the matchers registered through @frontmcp/testing/setup |
preset: '@frontmcp/testing/jest-preset-unit' | Unit tests, in a CommonJS project too: Jest's default 5-second timeout, no setup file, and every *.spec.ts(x) except *.e2e.spec, *.perf.spec and *.pw.spec files. Add @frontmcp/testing/setup to setupFilesAfterEnv to use the matchers |
Both compile TypeScript and TSX with @swc/jest, and both compile jose, @noble/hashes and @noble/ciphers, ESM-only packages the SDK loads, which Jest's CommonJS runtime otherwise fails on with SyntaxError: Unexpected token 'export'. To compile your TypeScript with ts-jest, set a transform for .ts only, so the preset still compiles those packages:
// jest.config.js
module.exports = {
preset: '@frontmcp/testing/jest-preset-unit',
transform: { '^.+\\.tsx?$': 'ts-jest' },
};
testMatch and transformIgnorePatterns of your own replace the preset's lists, so copy its entries into yours.
Fixtures
| Fixture | Description |
|---|---|
mcp | A client connected to the server, new for each test: tools, resources, prompts, raw requests, notifications and logs |
server | The running server: info (baseUrl, port, pid), createClient(), createClientBuilder(), restart(), getLogs(), clearLogs() |
auth | Makes JWTs: createToken(), createExpiredToken(), createInvalidToken(), users (admin, user, readOnly), getJwks(), getIssuer(), getAudience() |
test.beforeEach() and test.afterEach() receive the same fixtures when their callback takes a parameter.
Matchers
Tools: toContainTool, toBeSuccessful, toBeError, toHaveTextContent, toHaveImageContent, toHaveResourceContent, toPassOutputSchemaWhole
Resources: toContainResource, toContainResourceTemplate, toHaveMimeType
Prompts: toContainPrompt, toHaveMessages
Protocol: toBeValidJsonRpc, toHaveResult, toHaveError, toHaveErrorCode
Tool UI: toHaveRenderedHtml, toContainHtmlElement, toContainBoundValue, toBeXssSafe, toHaveWidgetMetadata, toHaveCssClass, toNotContainRawContent, toHaveProperHtmlStructure
Use them through the expect this package exports, which is Jest's expect with these matchers typed. frontmcp test and the @frontmcp/testing preset register them, through @frontmcp/testing/setup, which you can add to setupFilesAfterEnv yourself.
Configuration
import { test } from '@frontmcp/testing';
test.use({
server: './src/main.ts', // the entry file, run with tsx
port: 3003, // default: a free port
transport: 'streamable-http', // or 'sse'
entryPath: '/mcp', // if the server sets http.entryPath
publicMode: true, // send no Authorization header, for auth: { mode: 'public' }
logLevel: 'debug', // the server's own log level
protocolVersion: '2026-07-28', // default: '2025-06-18'
mcpLogLevel: 'info', // the log messages the clients ask for; default: 'debug', false for none
env: { API_KEY: 'test' },
startupTimeout: 30_000,
});
Call test.use() at the top of a file to configure every test in it, or inside test.describe() to configure that block, which then gets a server of its own. baseUrl tests a server that's already running instead of starting one.
Notifications
A tool's progress and log messages reach mcp.notifications by the time mcp.tools.call() returns, from a FrontMCP server that sends them on the call's own response (1.11.3 and later). The client asks for every log message unless mcpLogLevel says otherwise, and each progress update keeps its message.
Protocol versions
The client speaks MCP 2025-06-18 by default, and any revision up to 2025-11-25 opens a session with initialize. With protocolVersion: '2026-07-28' it's stateless: it connects with server/discover, sends each request with its _meta and mirrored headers (MCP-Protocol-Version, Mcp-Method, Mcp-Name, Mcp-Param-*), answers a tool's this.elicit() through input_required with your mcp.onElicitation() handler, and listens for changes with subscriptions/listen. mcp.discover() sends server/discover from a client on any version.
Other exports
| Export | Use it to |
|---|---|
McpTestClient, TestServer | Start a server and connect a client yourself, without the fixtures |
TestTokenFactory, MockOAuthServer, MockCimdServer | Sign tokens, stand in for an OAuth provider, and serve client ID metadata documents |
MockAPIServer, httpMock, httpResponse | Answer the HTTP calls your tools make |
mockResponse, interceptors | Mock or change MCP requests on the mcp client |
perfTest, LeakDetector, MetricsCollector | Measure memory and time, and find leaks |
Docs
@frontmcp/testing:test, themcpclient, result wrappers and running tests- Fixtures: every
test.use()option, and themcp,serverandauthfixtures - Matchers: what each matcher checks
- Interceptors and HTTP mocking:
mcp.mock,mcp.intercept,MockAPIServerandhttpMock - Testing authentication: tokens, mock OAuth servers and signing in
- Testing your server: a guided introduction
Related packages
@frontmcp/sdk: the framework the servers under test are built withfrontmcp: the CLI, whosefrontmcp testruns your tests@frontmcp/observability: hascreateTestTracer()to check the spans a test records
Contributing
Source: libs/testing. Issues and pull requests are welcome; see CONTRIBUTING.md.
License
Apache-2.0. See LICENSE.