Skip to content

Commit 17e5ff7

Browse files
committed
refactor(mcp): centralize presentation contract markers and document versioning
This refactor enhances the maintainability of the MCP stdio safety contract: - Centralized markers in `src/constants/markers.ts` (single source of truth) - Updated renderer and tests to import markers, eliminating drift risk - Documented marker versioning rules in README to prevent accidental breaking changes
1 parent 0640fd9 commit 17e5ff7

4 files changed

Lines changed: 40 additions & 7 deletions

File tree

packages/mcp-server/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,17 @@ The server implements a hierarchical logging system to ensure production safety
900900

901901
**Note:** All logs are strictly routed to `stderr` to prevent interference with the MCP protocol on `stdout`.
902902

903+
## Presentation Contract
904+
905+
The server output embeds stable, hidden contract markers to facilitate automated testing and client integration without relying on brittle markdown styling.
906+
907+
- **`<!-- kind:pattern-index:v1 -->`**: Indicates the presence of the search results index table.
908+
- **`<!-- kind:pattern-card:v1 -->`**: Indicates the start of a pattern detail card.
909+
910+
These markers are guaranteed to be present in the raw tool output content, enabling robust regression testing of the presentation layer.
911+
912+
**Versioning Rule:** If marker semantics change, the version suffix (e.g., `v1`) must be incremented, and backward compatibility should be maintained where feasible.
913+
903914
## CI/CD Requirements
904915

905916
Integration tests (`test:integration`) require specific environment variables in your CI/CD pipeline:
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* Presentation Contract Markers
3+
*
4+
* These hidden HTML comments serve as stable contract markers for automated testing
5+
* and client integration. They ensure that even if the visual markdown styling changes,
6+
* the structural semantics of the output remain verifiable.
7+
*
8+
* Usage:
9+
* - Renderers include these markers in the output.
10+
* - Tests assert the presence and count of these markers.
11+
*/
12+
13+
export const MARKER_PATTERN_INDEX_V1 = "<!-- kind:pattern-index:v1 -->";
14+
export const MARKER_PATTERN_CARD_V1 = "<!-- kind:pattern-card:v1 -->";

packages/mcp-server/src/mcp-content-builders.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
*/
1313

1414
import type { TextContent } from "@modelcontextprotocol/sdk/shared/messages.js";
15+
import {
16+
MARKER_PATTERN_CARD_V1,
17+
MARKER_PATTERN_INDEX_V1,
18+
} from "./constants/markers.js";
1519

1620
/**
1721
* MCP 2.0 Annotation structure
@@ -678,7 +682,7 @@ function buildScanFirstPatternContent(pattern: PatternData): TextContent[] {
678682

679683
// Hidden presentation marker for contract testing
680684
content.push(
681-
createTextBlock("<!-- kind:pattern-card:v1 -->", {
685+
createTextBlock(MARKER_PATTERN_CARD_V1, {
682686
priority: 1,
683687
audience: ["user"],
684688
})
@@ -815,7 +819,7 @@ function buildIndexTable(patterns: readonly PatternData[]): string {
815819

816820
// Pre-allocate buffer and use single join pass (O(N) instead of O(N²))
817821
const rows: string[] = [];
818-
rows.push("<!-- kind:pattern-index:v1 -->"); // Hidden marker for contract testing
822+
rows.push(MARKER_PATTERN_INDEX_V1); // Hidden marker for contract testing
819823
rows.push("| Pattern | Category | Difficulty | Tags |");
820824
rows.push("| :--- | :--- | :--- | :--- |");
821825

packages/mcp-server/tests/mcp-protocol/local.test.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@
1919
import { describe, it, expect, beforeAll, afterEach } from "vitest";
2020
import { MCPTestClient, createMCPTestClient } from "./helpers/mcp-test-client";
2121
import { getMCPEnvironmentConfig } from "../../src/config/mcp-environments";
22+
import {
23+
MARKER_PATTERN_CARD_V1,
24+
MARKER_PATTERN_INDEX_V1,
25+
} from "../../src/constants/markers";
2226

2327
function extractContentText(
2428
content: Array<{ type: string; text?: string }> | { type: string; text?: string }
@@ -129,11 +133,11 @@ describe("Local MCP Server", () => {
129133
}
130134

131135
// Positive assertion: Check for contractual structural markers (stable IDs and markers)
132-
expect(searchText).toContain("<!-- kind:pattern-index:v1 -->"); // Index contract marker
133-
expect(searchText).toContain("<!-- kind:pattern-card:v1 -->"); // Card contract marker
136+
expect(searchText).toContain(MARKER_PATTERN_INDEX_V1); // Index contract marker
137+
expect(searchText).toContain(MARKER_PATTERN_CARD_V1); // Card contract marker
134138

135139
// Ensure cards are rendered (K=1 requested in args)
136-
const cardMatches = searchText.match(/<!-- kind:pattern-card:v1 -->/g);
140+
const cardMatches = searchText.match(new RegExp(MARKER_PATTERN_CARD_V1, "g"));
137141
expect(cardMatches?.length).toBe(1);
138142

139143
// Test 2: Get pattern
@@ -144,9 +148,9 @@ describe("Local MCP Server", () => {
144148

145149
// Structural markers for single pattern card
146150
expect(getText).toMatch(/^# /m); // Markdown H1 title
147-
expect(getText).toContain("<!-- kind:pattern-card:v1 -->"); // Contract marker
151+
expect(getText).toContain(MARKER_PATTERN_CARD_V1); // Contract marker
148152

149-
const getCardMatches = getText.match(/<!-- kind:pattern-card:v1 -->/g);
153+
const getCardMatches = getText.match(new RegExp(MARKER_PATTERN_CARD_V1, "g"));
150154
expect(getCardMatches?.length).toBe(1);
151155

152156
expect(getText).toContain("**API:**");

0 commit comments

Comments
 (0)