@mcpfn/testing 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # McpFn Testing
2
+
3
+ `@mcpfn/testing` provides deterministic MCP regression testing over the official SDK's in-memory transport. It tests the protocol boundary rather than calling handlers directly.
4
+
5
+ It includes:
6
+
7
+ - an official MCP client fixture;
8
+ - exact manifest assertions across tools, resources, templates, and prompts;
9
+ - resource, prompt, completion, subscription, and task client methods;
10
+ - reusable API-key and OAuth resource-server regression matrices;
11
+ - an in-memory authorization-code, PKCE, refresh, and revocation server;
12
+ - extensible Client ID Metadata fixtures that include unrelated grant types;
13
+ - optional Playwright fixtures for real redirect and consent-page coverage;
14
+ - generic tools-only, full-protocol, and MCP Apps host profiles;
15
+ - structured/text response parity checks;
16
+ - declarative semantic scenarios;
17
+ - orchestration of the official `@modelcontextprotocol/conformance` runner.
18
+
19
+ Official conformance validates protocol behavior. McpFn scenarios validate product behavior. Production MCP servers should run both.
20
+
21
+ ```ts
22
+ import {
23
+ McpFnTestClient,
24
+ assertManifestContract,
25
+ runScenarios,
26
+ } from "@mcpfn/testing";
27
+
28
+ const server = createServer();
29
+ const client = await McpFnTestClient.connect(server);
30
+ try {
31
+ await assertManifestContract(client, server.manifest());
32
+ const results = await runScenarios(client, [
33
+ {
34
+ name: "returns one skill",
35
+ tool: "skill_get",
36
+ arguments: { slug: "work-linear-issue" },
37
+ expect: { isError: false, structuredTextParity: true },
38
+ },
39
+ ]);
40
+ if (results.some((result) => result.status === "failed")) {
41
+ throw new Error(JSON.stringify(results));
42
+ }
43
+ } finally {
44
+ await client.close();
45
+ }
46
+ ```
47
+
48
+ Scenarios run serially so stateful workflows and idempotency checks remain deterministic. `assertStructuredTextParity` requires a JSON text block; do not use it for intentionally human-readable text.
49
+
50
+ `checkHostCompatibility(manifest, profile)` returns `compatible`, `degraded`, or `incompatible`. Unsupported optional server surfaces are degraded; missing protocol overlap or required client-mediated features are incompatible. The built-in profiles are stable capability fixtures, not claims about the current behavior of named commercial hosts. Supply a custom profile for a captured host version.
51
+
52
+ ## Authentication regression suite
53
+
54
+ Import the transport-level testkit from `@mcpfn/testing/auth`. The application adapter owns only credential issuance and revocation; McpFn owns the common rejection and lifecycle matrix.
55
+
56
+ ```ts
57
+ import {
58
+ apiKeyCredential,
59
+ assertAuthRegressionSuite,
60
+ createFetchAuthTarget,
61
+ } from "@mcpfn/testing/auth";
62
+
63
+ await assertAuthRegressionSuite({
64
+ kind: "api-key",
65
+ target: createFetchAuthTarget({ url: "http://127.0.0.1:8787/mcp" }),
66
+ invalidCredentialHeaders: { "x-api-key": "invalid" },
67
+ provider: {
68
+ capabilities: { revocation: true },
69
+ async issue() {
70
+ const key = await skillplaneTestAuth.issueApiKey();
71
+ return apiKeyCredential(key.value, { headerName: "x-api-key", scheme: "" });
72
+ },
73
+ async revoke(credential) {
74
+ await skillplaneTestAuth.revokeApiKey(credential);
75
+ },
76
+ },
77
+ });
78
+ ```
79
+
80
+ OAuth adapters can additionally enable scope, expiry, resource-binding, and revocation scenarios. Rejected OAuth requests must include a Bearer challenge with `resource_metadata`; API keys intentionally do not inherit that OAuth-specific requirement.
81
+
82
+ `createOAuthClientMetadataVariants()` returns authorization-code clients with basic, JWT-bearer-extension, device-code-extension, and generic-extension metadata. The compatibility assertion requires authorization-code support while deliberately accepting unrelated grants, which catches closed-world Client ID Metadata validation regressions.
83
+
84
+ ## Playwright fixture
85
+
86
+ Install `@playwright/test` and import the ready-to-extend fixture from `@mcpfn/testing/playwright`:
87
+
88
+ ```ts
89
+ import { expect, test } from "@mcpfn/testing/playwright";
90
+
91
+ test("accepts extensible OAuth client metadata", async ({ page, mcpfnOAuth }) => {
92
+ const result = await mcpfnOAuth.authorize(page, {
93
+ authorizationEndpoint: skillplane.authorizationEndpoint,
94
+ clientId: mcpfnOAuth.server.clientMetadataUrl("jwtBearerExtension"),
95
+ redirectUri: mcpfnOAuth.server.callbackUrl,
96
+ scopes: ["mcp:read"],
97
+ beforeDecision: async (currentPage) => signInIfRequired(currentPage),
98
+ });
99
+ expect(result.callback.parameters.code).toBeTruthy();
100
+ });
101
+ ```
102
+
103
+ The fixture starts a local mock server that publishes authorization-server discovery, consent UI, callback capture, client metadata variants, PKCE token exchange, refresh rotation, revocation, and an SDK-compatible access-token verifier. Extend the exported `test` with Skillplane's signed-in page or database fixtures; do not copy the OAuth machinery into the application.
104
+
105
+ See [Testing and CI](https://github.com/21nCo/super-functions/blob/main/mcpfn/TESTING.md) for the complete layered strategy.