mcpField
O
openapi-contract

openapi-contract

github.com/claude-dev-suiteTesting & quality4638 installsUpdated 2d ago
Repository

About openapi-contract

A reference skill for checking whether frontend and backend API usage conforms to an OpenAPI contract. It helps developers find endpoint, type, request, and response mismatches and describes contract-first and automated testing approaches.

Key features

  1. 01Endpoint and method checks
  2. 02Request and response schema validation
  3. 03Common contract mismatch diagnosis
  4. 04Contract-first development guidance
  5. 05Automated testing and CI examples

Use cases

  1. 01Finding frontend paths that differ from the API spec
  2. 02Checking request fields and response types
  3. 03Adding contract tests to a CI pipeline

MCP support

The skill documents using the api-explorer MCP tools to search API paths and retrieve details for specific endpoints. It recommends targeted endpoint queries rather than loading a full schema.

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/claude-dev-suite/claude-dev-suite --skill openapi-contract

After installing, type/openapi-contractin Claude Code to confirm it loaded.

Details

From the project README

OpenAPI Contract Validation - Quick Reference

When NOT to Use This Skill

  • GraphQL APIs - Use graphql-contract skill
  • Authentication flows - Use auth-flow-validation skill
  • API versioning strategy - Use api-versioning skill
  • Type generation setup - Use type-generation skill

Deep Knowledge: Use mcp__api-explorer__get_api_endpoint_details for specific endpoint validation.

Contract Validation Overview

┌─────────────────────────────────────────────────────────────┐
│                    CONTRACT VALIDATION                       │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   Frontend Code          OpenAPI Spec           Backend      │
│   ┌──────────────┐      ┌────────────┐     ┌──────────────┐ │
│   │ fetch('/api/ │ ←──→ │ paths:     │ ←──→│ @Controller  │ │
│   │   users')    │      │  /users:   │     │ @GetMapping  │ │
│   │              │      │    get:    │     │              │ │
│   │ type User {  │ ←──→ │ schemas:   │ ←──→│ class UserDto│ │
│   │   id: number │      │  User:     │     │   Long id    │ │
│   │ }            │      │    id:int  │     │              │ │
│   └──────────────┘      └────────────┘     └──────────────┘ │
│                                                              │
│        MUST MATCH          SOURCE OF           MUST MATCH   │
│                            TRUTH                             │
└─────────────────────────────────────────────────────────────┘

Common Discrepancy Types

1. Path Mismatch
# OpenAPI Spec
paths:
  /users/{userId}:     # Backend path
    get: ...

# Frontend Code
fetch('/api/users/${id}')  # Frontend uses different path!

Detection:

# Find API calls in frontend
grep -r "fetch\|axios\|http\." src/

# Compare with OpenAPI paths
# Use api-explorer to list paths

Fix: Update frontend to use correct path or configure base URL.

2. Type Mismatch
# OpenAPI Spec
components:
  schemas:
    User:
      properties:
        age:
          type: string    # Backend uses string

# Frontend Code
interface User {
  age: number;            # Frontend expects number!
}

Detection:

// Frontend type
interface CreateUserDto {
  age: number;  // MISMATCH
}

// Should be
interface CreateUserDto {
  age: string;  // Match OpenAPI spec
}
3. Required Field Missing
# OpenAPI Spec
components:
  schemas:
    CreateUserRequest:
      required:
        - email
        - name
        - role          # Required!
      properties:
        email: { type: string }
        name: { type: string }
        role: { type: string }

# Frontend Code
const payload = {
  email: user.email,
  name: user.name,
  // role is missing!  <-- Will fail validation
};
4. Response Structure Mismatch
# OpenAPI Spec - Paginated response
paths:
  /users:
    get:
      responses:
        200:
          content:
            application/json:
              schema:
                type: object
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/User'
                  meta:
                    $ref: '#/components/schemas/PaginationMeta'

# Frontend Code - Expects array directly
const users: User[] = await response.json();  // WRONG!
// Should be
const { data, meta } = await response.json();
const users: User[] = data;

Validation Checklist

Per-Endpoint Validation
CheckFrontendBackend (OpenAPI)Status
Path/api/users/usersMISMATCH
MethodPOSTPOSTOK
Content-Typeapplication/jsonapplication/jsonOK
Request Bodymatches schemaCreateUserRequestCHECK
Response Typematches schemaUserCHECK
Status Codeshandles 400, 401200, 400, 401, 500PARTIAL
Request Body Validation
// OpenAPI Schema
interface CreateUserRequest {
  email: string;      // required
  name: string;       // required
  age?: number;       // optional
  role: UserRole;     // required, enum
}

// Validate frontend matches
interface FrontendPayload {
  email: string;      // OK - required
  name: string;       // OK - required
  age?: number;       // OK - optional
  role: 'admin' | 'user';  // CHECK - enum values match?
}
Response Handling Validation
// OpenAPI Response
interface ApiResponse<T> {
  data: T;
  meta?: {
    page: number;
    total: number;
  };
  error?: {
    code: string;
    message: string;
  };
}

// Frontend should handle all cases
async function fetchUsers(): Promise<User[]> {
  const response = await fetch('/api/users');

  if (!response.ok) {
    const error = await response.json();
    throw new ApiError(error.error.code, error.error.message);
  }

  const result: ApiResponse<User[]> = await response.json();
  return result.data;
}

Contract-First Development

1. Define OpenAPI Spec First
# openapi.yaml
openapi: 3.0.3
info:
  title: User API
  version: 1.0.0

paths:
  /users:
    post:
      operationId: createUser
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUserRequest'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

components:
  schemas:
    CreateUserRequest:
      type: object
      required: [email, name]
      properties:
        email:
          type: string
          format: email
        name:
          type: string
          minLength: 2
          maxLength: 100

    User:
      type: object
      properties:
        id:
          type: string
          format: uuid
        email:
          type: string
        name:
          type: string
        createdAt:
          type: string
          format: date-time
2. Generate Types for Both Ends
# Frontend (TypeScript)
npx openapi-typescript openapi.yaml -o src/api/types.ts

# Backend (Java/Spring)
npx @openapitools/openapi-generator-cli generate \
  -i openapi.yaml \
  -g spring \
  -o generated/
3. Implement Against Generated Types
// Frontend - uses generated types
import type { CreateUserRequest, User } from './api/types';

async function createUser(data: CreateUserRequest): Promise<User> {
  const response = await fetch('/api/users', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
  return response.json();
}
// Backend - uses generated DTOs
@PostMapping("/users")
public ResponseEntity<User> createUser(
    @Valid @RequestBody CreateUserRequest request) {
    // Implementation uses generated types
}

MCP api-explorer Usage

Efficient Validation Queries
// 1. Search for endpoint
await mcp__api_explorer__search_api({
  query: "users",
  searchIn: ["paths"],
  limit: 10
});

// 2. Get specific endpoint details
await mcp__api_explorer__get_api_endpoint_details({
  path: "/users",
  method: "POST"
});

// 3. Get request/response schemas
await mcp__api_explorer__get_api_models({
  model: "CreateUserRequest",
  compact: true
});
Avoid Token Waste
// DON'T - Loads entire spec
await mcp__api_explorer__get_api_schema({ format: "full" });

// DO - Query specific endpoints
await mcp__api_explorer__get_api_endpoint_details({
  path: "/users/{id}",
  method: "GET"
});

Automated Contract Testing

Prism (Mock Server)
# Start mock server from OpenAPI spec
npx @stoplight/prism-cli mock openapi.yaml

# Run frontend tests against mock
npm test -- --api-url=http://localhost:4010
Schemathesis (API Fuzzing)
# Test backend against OpenAPI spec
schemathesis run openapi.yaml --base-url=http://localhost:8080

# Validate all endpoints
schemathesis run openapi.yaml \
  --checks all \
  --validate-schema
Dredd (Contract Testing)
# Test implementation matches spec
dredd openapi.yaml http://localhost:8080

CI/CD Integration

GitHub Actions
name: Contract Validation
on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Validate OpenAPI spec
        run: npx @redocly/cli lint openapi.yaml

      - name: Generate types
        run: npx openapi-typescript openapi.yaml -o src/api/types.ts

      - name: Check types unchanged
        run: git diff --exit-code src/api/types.ts

      - name: Run contract tests
        run: npm run test:contract

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
Manual type syncDrift over timeGenerate from spec
Ignoring OpenAPI specNo source of truthContract-first development
Hardcoded URLsEnvironment issuesConfigure base URL
No validation in CIBreaks discovered lateAutomated contract tests
Any types for APINo type safetyGenerate proper types

Quick Troubleshooting

IssueLikely CauseSolution
Types out of syncManual updatesRegenerate from spec
404 errorsPath mismatchCheck OpenAPI paths
400 Bad RequestMissing required fieldValidate against schema
Unexpected responseResponse structure changedUpdate frontend types
CORS errorsBackend configCheck allowed origins

Related Skills

Comments

Markdown supported