Skip to main content
Glama
README.md
# mnehmos.trace.mcp

> Static analysis engine for detecting schema mismatches between data producers and consumers.

## What It Does

Trace MCP finds mismatches between:

- Backend API responses and frontend expectations
- MCP tool outputs and client code that uses them
- Service A's events and Service B's handlers
- REST endpoints and HTTP client calls
- GraphQL schemas and Apollo Client hooks

```
Producer returns:    { characterClass: "Fighter", hitPoints: 45 }
Consumer expects:    { class: "Fighter", hp: 45 }
Result:              ❌ Mismatch detected before runtime
```

## Features

### Core Capabilities

| Feature | Description |
|---------|-------------|
| **Schema Extraction** | Extract schemas from MCP tools, OpenAPI, TypeScript, tRPC, REST endpoints, GraphQL |
| **Usage Tracing** | Track how client code consumes schemas via property access patterns |
| **Mismatch Detection** | Compare producer schemas against consumer expectations |
| **Code Generation** | Scaffold consumer code from producer schemas (and vice versa) |
| **Watch Mode** | Continuous validation on file changes |

### Phase 2 Capabilities

| Feature | Description |
|---------|-------------|
| **Pattern Matcher** | Extensible pattern detection supporting call, decorator, property, export, and chain patterns |
| **Import Resolution** | Cross-file type resolution with import graph building and circular dependency handling |
| **REST Detection** | Express and Fastify endpoint extraction with validation middleware support |
| **HTTP Client Tracing** | fetch() and axios call detection with URL extraction and type inference |
| **GraphQL Support** | SDL schema parsing, Apollo Server resolvers, and Apollo Client hook tracing |

### Phase 3 Capabilities

| Feature | Description |
|---------|-------------|
| **Python AST Parser** | FastAPI, Flask, and MCP tool extraction with Pydantic model support |
| **Go Language Parser** | Struct/interface extraction with Chi, Gin, and stdlib HTTP handler detection |
| **gRPC/Protobuf Support** | Proto3 parsing with message, enum, service, and streaming RPC extraction |
| **Python HTTP Clients** | requests, httpx, and aiohttp library detection with response property tracing |

### Test Coverage

**1047 tests passing** across 16 test suites:

| Test Suite | Tests |
|------------|-------|
| Pattern Matcher | 85 |
| REST Detection | 87 |
| HTTP Client Tracing | 90 |
| GraphQL Support | 109 |
| Import Resolution | 56 |
| Core (adapters, OpenAPI, tRPC) | 234 |
| Python AST | 121 |
| gRPC/Protobuf | 124 |
| Go Parser | 106 |
| Python HTTP Clients | 35 |

## Installation

```bash
# Clone the repository
git clone https://github.com/Mnehmos/mnehmos.trace.mcp.git

# Navigate to the directory
cd mnehmos.trace.mcp

# Install dependencies
npm install

# Build the project
npm run build
```

## Configuration

Add to your MCP client configuration (e.g., `claude_desktop_config.json` or Roo-Code settings):

```json
{
  "mcpServers": {
    "trace-mcp": {
      "command": "node",
      "args": ["/path/to/trace-mcp/dist/index.js"],
      "env": {}
    }
  }
}
```

## Supported Formats

Trace MCP supports schema extraction and comparison across multiple specification formats through a pluggable adapter registry.

### Summary of Supported Frameworks

| Category | Frameworks |
|----------|------------|
| **API Specs** | OpenAPI 3.0+, Swagger |
| **RPC** | MCP (Zod), tRPC, gRPC/Protobuf |
| **REST Servers** | Express, Fastify, FastAPI, Flask, Chi, Gin, Go stdlib |
| **HTTP Clients** | fetch(), axios, requests, httpx, aiohttp |
| **GraphQL** | SDL schemas, Apollo Server, Apollo Client |
| **Type Systems** | TypeScript interfaces, Zod schemas, Pydantic models, Go structs |

### Supported Languages

| Language | Producer Detection | Consumer Tracing |
|----------|-------------------|------------------|
| **TypeScript** | MCP tools, tRPC, Express, Fastify, GraphQL resolvers | callTool(), fetch, axios, Apollo Client |
| **Python** | FastAPI, Flask, MCP tools, Pydantic models | requests, httpx, aiohttp |
| **Go** | Chi, Gin, stdlib handlers, structs, interfaces | — |
| **Protobuf** | Messages, enums, services, streaming RPCs | — |

---

### MCP Server Schemas (Zod)

Extract MCP tool definitions from server source code using Zod schemas.

```typescript
server.tool(
  "get_character",
  "Fetch character data",
  {
    characterId: z.string().describe("Character ID"),
  },
  async (args) => {
    // implementation
  }
);
```

**Schema ID Format**: `endpoint:GET:/tools/get_character@./server.ts`

---

### OpenAPI / Swagger Specifications

Extract schemas from OpenAPI 3.0+ specifications, supporting endpoints, request bodies, responses, and component schemas.

```yaml
openapi: 3.0.0
info:
  title: Character API
  version: 1.0.0
paths:
  /characters/{id}:
    get:
      parameters:
        - name: id
          in: path
          schema:
            type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Character'
components:
  schemas:
    Character:
      type: object
      properties:
        id:
          type: string
        name:
          type: string
        class:
          type: string
      required:
        - id
        - name
        - class
```

**Schema ID Format**: `endpoint:GET:/characters/{id}@./api.yaml`

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.openapi.yaml", "**/*.swagger.json"],
});
```

---

### TypeScript Interfaces & Types

Extract exported interfaces, type aliases, and enums from TypeScript source files. Supports utility types including `Pick`, `Omit`, `Partial`, `Required`, and `Record`.

```typescript
export interface Character {
  id: string;
  name: string;
  class: "Fighter" | "Wizard" | "Rogue";
  hitPoints: number;
  stats: {
    strength: number;
    dexterity: number;
    constitution: number;
  };
}

export type ReadonlyCharacter = Readonly<Character>;

export enum CharacterClass {
  Fighter = "Fighter",
  Wizard = "Wizard",
  Rogue = "Rogue",
}
```

**Schema ID Format**: `interface:Character@./types.ts`

**Supported Utility Types:**
- `Pick<T, K>` - Select properties from interface
- `Omit<T, K>` - Exclude properties from interface
- `Partial<T>` - Make all properties optional
- `Required<T>` - Make all properties required
- `Record<K, T>` - Object with specific keys and value type

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./shared",
  include: ["**/*.ts", "**/*.tsx"],
});
// Returns interfaces with ID format: interface:CharacterClass@./types.ts
```

---

### tRPC Routers

Extract procedure schemas from tRPC routers, including input/output types, query, mutation, and subscription handlers. Handles nested routers and middleware.

```typescript
import { z } from "zod";
import { publicProcedure, router } from "./trpc";

export const appRouter = router({
  users: router({
    getById: publicProcedure
      .input(z.string())
      .output(z.object({
        id: z.string(),
        name: z.string(),
        email: z.string().email(),
      }))
      .query(async ({ input }) => {
        // implementation
      }),
    
    create: publicProcedure
      .input(z.object({
        name: z.string(),
        email: z.string().email(),
      }))
      .output(z.object({
        id: z.string(),
        name: z.string(),
        email: z.string(),
      }))
      .mutation(async ({ input }) => {
        // implementation
      }),

    onChange: publicProcedure
      .output(z.object({
        userId: z.string(),
        action: z.enum(["created", "updated", "deleted"]),
      }))
      .subscription(async () => {
        // implementation
      }),
  }),
});
```

**Schema ID Format**: `trpc:users.getById@./router.ts`

**Detected Elements:**
- Router definitions (`router({ ... })`)
- Nested routers (`users: router({ ... })`)
- Procedures (`.query()`, `.mutation()`, `.subscription()`)
- Input schemas (`.input(zod_schema)`)
- Output schemas (`.output(zod_schema)`)

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./backend/trpc",
  include: ["**/*.router.ts"],
});
// Returns procedures with ID format: trpc:users.getById@./router.ts
```

---

### REST Endpoints (Express & Fastify)

Extract endpoint schemas from Express and Fastify applications, including route parameters, request bodies, response types, and validation middleware.

#### Express

```typescript
import express from "express";
import { z } from "zod";

const app = express();

// Basic route with typed response
app.get("/users/:id", (req, res) => {
  const user: User = getUserById(req.params.id);
  res.json(user);
});

// Route with Zod validation middleware
app.post("/users", 
  validate(z.object({
    name: z.string(),
    email: z.string().email(),
  })),
  (req, res) => {
    res.status(201).json({ id: "123", ...req.body });
  }
);

// Router-based routes
const router = express.Router();
router.get("/health", (req, res) => res.json({ status: "ok" }));
app.use("/api", router);
```

**Schema ID Format**: `rest:GET:/users/:id@./app.ts`

#### Fastify

```typescript
import Fastify from "fastify";

const fastify = Fastify();

// Route with JSON Schema validation
fastify.post("/users", {
  schema: {
    body: {
      type: "object",
      properties: {
        name: { type: "string" },
        email: { type: "string", format: "email" },
      },
      required: ["name", "email"],
    },
    response: {
      201: {
        type: "object",
        properties: {
          id: { type: "string" },
          name: { type: "string" },
        },
      },
    },
  },
}, async (request, reply) => {
  return { id: "123", name: request.body.name };
});

// Shorthand methods
fastify.get("/health", async () => ({ status: "ok" }));
```

**Schema ID Format**: `rest:POST:/users@./server.ts`

**Detected Elements:**
- HTTP methods: GET, POST, PUT, PATCH, DELETE
- Path parameters (`:id`, `:userId`)
- Request body schemas (Zod, Joi, celebrate, JSON Schema)
- Response type inference
- Router prefixes and mounting

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.ts"],
});
// Returns endpoints with ID format: rest:GET:/users/:id@./routes.ts
```

---

### HTTP Clients (fetch & axios)

Trace HTTP client calls to detect consumer expectations for API responses.

#### fetch() API

```typescript
// Basic fetch with type assertion
const response = await fetch("/api/users");
const users: User[] = await response.json();

// fetch with request options
const newUser = await fetch("/api/users", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ name: "Alice" }),
}).then(res => res.json()) as CreateUserResponse;

// Template literal URLs
const userId = "123";
const user = await fetch(`/api/users/${userId}`).then(r => r.json());

// Property access tracking
console.log(user.name, user.email, user.profile.avatar);
```

**Detected Elements:**
- URL extraction (static strings, template literals, variables)
- HTTP method detection
- Type assertions and generics
- Property access patterns on response data

#### axios

```typescript
import axios from "axios";

// Basic GET request
const { data: users } = await axios.get<User[]>("/api/users");

// POST with typed response
const response = await axios.post<CreateUserResponse>("/api/users", {
  name: "Bob",
  email: "bob@example.com",
});

// Instance with base URL
const api = axios.create({ baseURL: "https://api.example.com" });
const profile = await api.get<Profile>("/me");

// Destructured property access
const { name, email } = response.data;
```

**Schema ID Format**: `http-client:GET:/api/users@./client.ts`

**Detected Elements:**
- axios methods: `.get()`, `.post()`, `.put()`, `.patch()`, `.delete()`
- Generic type parameters (`axios.get<User>`)
- Instance creation with `axios.create()`
- Base URL resolution
- Response data property access

**Usage Example:**

```typescript
const result = await client.callTool("trace_usage", {
  rootDir: "./frontend/src",
  include: ["**/*.ts", "**/*.tsx"],
});
// Returns HTTP client calls with ID format: http-client:GET:/api/users@./api.ts
```

---

### GraphQL (SDL & Apollo)

Extract schemas from GraphQL SDL files and trace Apollo Server resolvers and Apollo Client hooks.

#### SDL Schema Files

```graphql
# schema.graphql
type User {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post {
  id: ID!
  title: String!
  content: String!
  author: User!
}

type Query {
  user(id: ID!): User
  users: [User!]!
  post(id: ID!): Post
}

type Mutation {
  createUser(name: String!, email: String!): User!
  createPost(title: String!, content: String!, authorId: ID!): Post!
}
```

**Schema ID Format**: `graphql:Query.user@./schema.graphql`

#### Apollo Server Resolvers

```typescript
import { ApolloServer } from "@apollo/server";

const resolvers = {
  Query: {
    user: async (_, { id }) => {
      return db.users.findById(id);
    },
    users: async () => {
      return db.users.findAll();
    },
  },
  Mutation: {
    createUser: async (_, { name, email }) => {
      return db.users.create({ name, email });
    },
  },
  User: {
    posts: async (parent) => {
      return db.posts.findByAuthor(parent.id);
    },
  },
};

const server = new ApolloServer({ typeDefs, resolvers });
```

**Schema ID Format**: `graphql-resolver:Query.user@./resolvers.ts`

#### Apollo Client Hooks

```typescript
import { useQuery, useMutation, gql } from "@apollo/client";

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
    }
  }
`;

const CREATE_USER = gql`
  mutation CreateUser($name: String!, $email: String!) {
    createUser(name: $name, email: $email) {
      id
      name
    }
  }
`;

function UserProfile({ userId }: { userId: string }) {
  const { data, loading, error } = useQuery(GET_USER, {
    variables: { id: userId },
  });

  const [createUser] = useMutation(CREATE_USER);

  if (loading) return <Spinner />;
  if (error) return <Error message={error.message} />;

  return <div>{data.user.name}</div>;
}
```

**Schema ID Format**: `graphql-client:GetUser@./UserProfile.tsx`

**Detected Elements:**
- SDL types: scalar, object, input, enum, interface, union
- Query and Mutation definitions
- Field arguments and return types
- Apollo Client hooks: `useQuery`, `useMutation`, `useLazyQuery`, `useSubscription`
- Operation names and variables
- Selected fields in queries

**Usage Example:**

```typescript
// Extract GraphQL schemas
const schemas = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.graphql", "**/resolvers.ts"],
});

// Trace Apollo Client usage
const usage = await client.callTool("trace_usage", {
  rootDir: "./frontend/src",
  include: ["**/*.tsx"],
});

// Compare for mismatches
const report = await client.callTool("compare", {
  producerDir: "./backend",
  consumerDir: "./frontend/src",
  format: "markdown",
});
```

---

### Python (FastAPI, Flask, MCP Tools)

Extract endpoint schemas from Python web frameworks and MCP tool definitions with full Pydantic model support.

#### FastAPI

```python
from fastapi import FastAPI, APIRouter
from pydantic import BaseModel
from typing import Optional, List

class Character(BaseModel):
    id: str
    name: str
    character_class: str
    level: int = 1
    skills: List[str] = []

class CreateCharacterRequest(BaseModel):
    name: str
    character_class: str
    background: Optional[str] = None

app = FastAPI()
router = APIRouter(prefix="/api/v1")

@app.get("/characters/{character_id}")
async def get_character(character_id: str) -> Character:
    return Character(id=character_id, name="Hero", character_class="Fighter")

@router.post("/characters")
async def create_character(request: CreateCharacterRequest) -> Character:
    return Character(id="123", name=request.name, character_class=request.character_class)

app.include_router(router)
```

**Schema ID Format**: `python:GET:/characters/{character_id}@./main.py`

#### Flask

```python
from flask import Flask, Blueprint, request, jsonify

app = Flask(__name__)
api = Blueprint("api", __name__, url_prefix="/api")

@app.route("/health")
def health_check():
    return jsonify({"status": "ok"})

@api.route("/users/<user_id>", methods=["GET"])
def get_user(user_id):
    return jsonify({"id": user_id, "name": "Alice"})

@api.route("/users", methods=["POST"])
def create_user():
    data = request.get_json()
    return jsonify({"id": "123", **data}), 201

app.register_blueprint(api)
```

**Schema ID Format**: `python:GET:/api/users/<user_id>@./app.py`

#### MCP Tools (Python)

```python
from mcp import Server

server = Server("character-tools")

@server.tool()
async def get_character(character_id: str) -> dict:
    """Fetch character data by ID."""
    return {"id": character_id, "name": "Hero", "class": "Fighter"}

@mcp.tool()
def roll_dice(dice: str, modifier: int = 0) -> dict:
    """Roll dice with optional modifier."""
    return {"result": 15, "expression": dice, "modifier": modifier}
```

**Schema ID Format**: `python-mcp:get_character@./tools.py`

#### Pydantic Models

```python
from pydantic import BaseModel, Field
from typing import Optional, List, Dict, Union, Literal
from enum import Enum

class CharacterClass(str, Enum):
    FIGHTER = "Fighter"
    WIZARD = "Wizard"
    ROGUE = "Rogue"

class Stats(BaseModel):
    strength: int = Field(ge=1, le=20)
    dexterity: int = Field(ge=1, le=20)
    constitution: int = Field(ge=1, le=20)

class Character(BaseModel):
    id: str
    name: str
    character_class: CharacterClass
    level: int = Field(default=1, ge=1, le=20)
    stats: Stats
    equipment: List[str] = []
    metadata: Optional[Dict[str, str]] = None
```

**Detected Elements:**
- Decorators: `@app.get()`, `@app.post()`, `@router.*`, `@app.route()`, `@blueprint.route()`
- MCP decorators: `@mcp.tool()`, `@server.tool()`
- Pydantic `BaseModel` classes with field extraction
- Type annotations: `Optional`, `Union`, `List`, `Dict`, `Literal`
- Enum types

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.py"],
});
// Returns endpoints with ID format: python:GET:/characters/{id}@./main.py
```

---

### Go Language (Chi, Gin, stdlib)

Extract struct definitions, interfaces, and HTTP endpoint schemas from Go source files.

#### Structs with JSON Tags

```go
package models

type Character struct {
    ID        string   `json:"id"`
    Name      string   `json:"name"`
    Class     string   `json:"class"`
    Level     int      `json:"level"`
    HitPoints int      `json:"hp"`
    Skills    []string `json:"skills,omitempty"`
}

type Stats struct {
    Strength     int `json:"str"`
    Dexterity    int `json:"dex"`
    Constitution int `json:"con"`
}

// Embedded struct
type CharacterWithStats struct {
    Character
    Stats Stats `json:"stats"`
}
```

**Schema ID Format**: `go-struct:Character@./models/character.go`

#### Interfaces

```go
package services

type CharacterService interface {
    GetByID(id string) (*Character, error)
    Create(req CreateRequest) (*Character, error)
    Update(id string, req UpdateRequest) (*Character, error)
    Delete(id string) error
}

type Repository interface {
    Find(query Query) ([]Character, error)
    Save(character *Character) error
}
```

**Schema ID Format**: `go-interface:CharacterService@./services/character.go`

#### stdlib HTTP Handlers

```go
package main

import (
    "encoding/json"
    "net/http"
)

func main() {
    http.HandleFunc("/health", healthHandler)
    http.HandleFunc("/api/characters", charactersHandler)
    http.HandleFunc("/api/characters/", characterByIDHandler)
    http.ListenAndServe(":8080", nil)
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    json.NewEncoder(w).Encode(map[string]string{"status": "ok"})
}

func charactersHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        // List characters
    case http.MethodPost:
        // Create character
    }
}
```

**Schema ID Format**: `go-http:GET:/health@./main.go`

#### Chi Router

```go
package main

import (
    "github.com/go-chi/chi/v5"
    "net/http"
)

func main() {
    r := chi.NewRouter()
    
    r.Get("/health", healthHandler)
    
    r.Route("/api/characters", func(r chi.Router) {
        r.Get("/", listCharacters)
        r.Post("/", createCharacter)
        r.Get("/{id}", getCharacter)        // Chi param: {id}
        r.Put("/{id}", updateCharacter)
        r.Delete("/{id}", deleteCharacter)
    })
    
    http.ListenAndServe(":8080", r)
}
```

**Schema ID Format**: `go-http:GET:/api/characters/{id}@./main.go`

#### Gin Framework

```go
package main

import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()
    
    r.GET("/health", healthHandler)
    
    api := r.Group("/api")
    {
        api.GET("/characters", listCharacters)
        api.POST("/characters", createCharacter)
        api.GET("/characters/:id", getCharacter)  // Gin param: :id
        api.PUT("/characters/:id", updateCharacter)
        api.DELETE("/characters/:id", deleteCharacter)
    }
    
    r.Run(":8080")
}
```

**Schema ID Format**: `go-http:GET:/api/characters/:id@./main.go`

**Detected Elements:**
- Struct definitions with JSON tags
- Embedded structs
- Interface definitions
- `http.HandleFunc()` patterns
- Chi router: `r.Get()`, `r.Post()`, `r.Route()`, `{param}` syntax
- Gin framework: `r.GET()`, `r.POST()`, `r.Group()`, `:param` syntax

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.go"],
});
// Returns structs, interfaces, and endpoints
```

---

### gRPC / Protobuf

Parse Protocol Buffer definitions (proto3) to extract message types, enums, services, and RPC methods.

#### Basic Messages

```protobuf
syntax = "proto3";

package character;

message Character {
    string id = 1;
    string name = 2;
    CharacterClass character_class = 3;
    int32 level = 4;
    Stats stats = 5;
    repeated string skills = 6;
}

message Stats {
    int32 strength = 1;
    int32 dexterity = 2;
    int32 constitution = 3;
}
```

**Schema ID Format**: `proto-message:character.Character@./character.proto`

#### Enums

```protobuf
enum CharacterClass {
    CHARACTER_CLASS_UNSPECIFIED = 0;
    CHARACTER_CLASS_FIGHTER = 1;
    CHARACTER_CLASS_WIZARD = 2;
    CHARACTER_CLASS_ROGUE = 3;
}

enum DamageType {
    DAMAGE_TYPE_UNSPECIFIED = 0;
    DAMAGE_TYPE_SLASHING = 1;
    DAMAGE_TYPE_PIERCING = 2;
    DAMAGE_TYPE_FIRE = 3;
}
```

**Schema ID Format**: `proto-enum:character.CharacterClass@./character.proto`

#### Oneof and Map Fields

```protobuf
message Equipment {
    string id = 1;
    string name = 2;
    
    oneof item_type {
        Weapon weapon = 10;
        Armor armor = 11;
        Consumable consumable = 12;
    }
}

message Inventory {
    string character_id = 1;
    map<string, int32> item_counts = 2;
    map<string, Equipment> equipped = 3;
}
```

**Schema ID Format**: `proto-message:character.Equipment@./equipment.proto`

#### Services and RPCs

```protobuf
service CharacterService {
    // Unary RPC
    rpc GetCharacter(GetCharacterRequest) returns (Character);
    
    // Server streaming
    rpc ListCharacters(ListRequest) returns (stream Character);
    
    // Client streaming
    rpc UploadInventory(stream Item) returns (UploadResponse);
    
    // Bidirectional streaming
    rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}

message GetCharacterRequest {
    string id = 1;
}

message ListRequest {
    int32 page_size = 1;
    string page_token = 2;
}
```

**Schema ID Format**: `proto-service:character.CharacterService@./character.proto`
**RPC Format**: `proto-rpc:CharacterService.GetCharacter@./character.proto`

#### Well-Known Types

```protobuf
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/any.proto";
import "google/protobuf/struct.proto";

message CharacterEvent {
    string character_id = 1;
    string event_type = 2;
    google.protobuf.Timestamp created_at = 3;
    google.protobuf.Duration duration = 4;
    google.protobuf.Any payload = 5;
    google.protobuf.Struct metadata = 6;
}
```

**Detected Elements:**
- Messages with all field types (scalar, message, enum, repeated)
- Enums with numeric values
- `oneof` field groups
- `map<K, V>` fields
- Nested message definitions
- Service definitions with all streaming modes:
  - Unary: `rpc Method(Request) returns (Response)`
  - Server streaming: `rpc Method(Request) returns (stream Response)`
  - Client streaming: `rpc Method(stream Request) returns (Response)`
  - Bidirectional: `rpc Method(stream Request) returns (stream Response)`
- Well-known types: `Timestamp`, `Duration`, `Any`, `Struct`

**Usage Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./proto",
  include: ["**/*.proto"],
});
// Returns messages, enums, and services
```

---

### Python HTTP Clients (requests, httpx, aiohttp)

Trace HTTP client calls in Python code to detect consumer expectations.

#### requests Library

```python
import requests

# Basic GET
response = requests.get("https://api.example.com/characters")
characters = response.json()

# GET with path parameter
character = requests.get(f"https://api.example.com/characters/{char_id}").json()

# POST with JSON body
new_char = requests.post(
    "https://api.example.com/characters",
    json={"name": "Hero", "class": "Fighter"}
).json()

# Session with base URL
session = requests.Session()
session.headers.update({"Authorization": "Bearer token"})
user = session.get("https://api.example.com/me").json()

# Property access tracking
print(character["name"], character["stats"]["strength"])
```

**Schema ID Format**: `python-http:GET:/characters@./client.py`

#### httpx Library

```python
import httpx

# Sync client
response = httpx.get("https://api.example.com/characters")
data = response.json()

# Async client
async with httpx.AsyncClient(base_url="https://api.example.com") as client:
    response = await client.get("/characters")
    characters = response.json()
    
    response = await client.post("/characters", json={"name": "Hero"})
    new_char = response.json()
```

**Schema ID Format**: `python-http:GET:/characters@./client.py`

#### aiohttp Library

```python
import aiohttp

async with aiohttp.ClientSession() as session:
    # GET request
    async with session.get("https://api.example.com/characters") as response:
        characters = await response.json()
    
    # POST request
    async with session.post(
        "https://api.example.com/characters",
        json={"name": "Hero", "class": "Fighter"}
    ) as response:
        new_char = await response.json()
    
    # Property access
    print(new_char["id"], new_char["name"])
```

**Schema ID Format**: `python-http:POST:/characters@./client.py`

**Detected Elements:**
- `requests.get()`, `requests.post()`, etc.
- `httpx.get()`, `httpx.post()`, `AsyncClient` methods
- `aiohttp.ClientSession` methods
- URL extraction (static strings, f-strings)
- HTTP method detection
- Response property access (dictionary key access)

**Usage Example:**

```typescript
const result = await client.callTool("trace_usage", {
  rootDir: "./python-client",
  include: ["**/*.py"],
});
// Returns HTTP client calls with property access patterns
```

---

## Architecture

### Pattern Matcher Framework

The pattern matcher provides an extensible system for detecting code patterns across different frameworks. Located in [`src/patterns/`](src/patterns/):

```
src/patterns/
├── base.ts          # BasePattern abstract class
├── types.ts         # PatternMatch, PatternContext interfaces
├── registry.ts      # PatternRegistry for plugin management
├── extractors.ts    # Node extractors for AST traversal
├── errors.ts        # Pattern-specific error types
├── rest/            # Express, Fastify patterns
├── http-clients/    # fetch, axios patterns
└── graphql/         # Apollo patterns
```

**Supported Pattern Types:**
- **Call patterns**: Function/method calls (`app.get()`, `fetch()`)
- **Decorator patterns**: TypeScript/Python decorators (`@Controller()`)
- **Property patterns**: Object property assignments
- **Export patterns**: Module exports (`export const router = ...`)
- **Chain patterns**: Method chaining (`router.get().post()`)

### Import Resolution

Cross-file type resolution with import graph building. Located in [`src/languages/import-resolver.ts`](src/languages/import-resolver.ts):

- Resolves `import { Type } from "./types"`
- Handles barrel exports (`export * from`)
- Supports path aliases via tsconfig.json
- Detects and handles circular dependencies
- Caches resolved types for performance

---

## Tools Reference

Trace MCP provides 11 tools organized into three categories:

### Core Analysis Tools

| Tool              | Description                                          |
| ----------------- | ---------------------------------------------------- |
| `extract_schemas` | Extract MCP tool definitions from server source code |
| `extract_file`    | Extract schemas from a single file                   |
| `trace_usage`     | Trace how client code uses MCP tools                 |
| `trace_file`      | Trace tool usage in a single file                    |
| `compare`         | Full pipeline: extract → trace → compare → report    |

### Code Generation Tools

| Tool                | Description                                     |
| ------------------- | ----------------------------------------------- |
| `scaffold_consumer` | Generate client code from producer schema       |
| `scaffold_producer` | Generate server stub from client usage          |
| `comment_contract`  | Add cross-reference comments to validated pairs |

### Project Management Tools

| Tool                 | Description                                             |
| -------------------- | ------------------------------------------------------- |
| `init_project`       | Initialize a trace project with `.trace-mcp` config     |
| `watch`              | Watch files for changes and auto-revalidate             |
| `get_project_status` | Get project config, cache state, and validation results |

---

## Tool Details

### `extract_schemas`

Extract MCP tool definitions (ProducerSchemas) from server source code. Scans for `server.tool()` calls and parses their Zod schemas. Also supports OpenAPI, TypeScript interfaces, tRPC routers, REST endpoints, and GraphQL schemas.

**Parameters:**

- `rootDir` (required): Root directory of server source code
- `include`: Glob patterns to include (default: `**/*.ts`)
- `exclude`: Glob patterns to exclude (default: `node_modules`, `dist`)

**Example:**

```typescript
const result = await client.callTool("extract_schemas", {
  rootDir: "./backend/src",
});
// Returns: { success: true, count: 12, schemas: [...] }
```

---

### `extract_file`

Extract MCP tool definitions from a single TypeScript file.

**Parameters:**

- `filePath` (required): Path to a TypeScript file

---

### `trace_usage`

Trace how client code uses MCP tools. Finds `callTool()` invocations, HTTP client calls, and GraphQL hooks, tracking which properties are accessed on results.

**Parameters:**

- `rootDir` (required): Root directory of consumer source code
- `include`: Glob patterns to include
- `exclude`: Glob patterns to exclude

---

### `trace_file`

Trace MCP tool usage in a single TypeScript file.

**Parameters:**

- `filePath` (required): Path to a TypeScript file

---

### `compare`

Full analysis pipeline: extract producer schemas, trace consumer usage, and compare them to find mismatches.

**Parameters:**

- `producerDir` (required): Path to MCP server source directory
- `consumerDir` (required): Path to consumer/client source directory
- `format`: Output format (`json`, `markdown`, `summary`)
- `strict`: Strict mode - treat missing optional properties as warnings
- `direction`: Data flow direction (`producer_to_consumer`, `consumer_to_producer`, `bidirectional`)

**Example Output (Markdown):**

```markdown
# mnehmos.trace.mcp Analysis Report

**Generated**: 2025-12-11T02:11:48.624Z

## Summary

| Metric      | Count |
| ----------- | ----- |
| Total Tools | 12    |
| Total Calls | 34    |
| Matches     | 31    |
| Mismatches  | 3     |

## Mismatches

### get_character

- **Type**: MISSING_PROPERTY
- **Description**: Consumer expects "characterClass" but producer has "class"
- **Consumer**: ./components/CharacterSheet.tsx:45
- **Producer**: ./tools/character.ts:23
```

---

### `scaffold_consumer`

Generate consumer code from a producer schema. Creates TypeScript functions, React hooks, or Zustand actions that correctly call MCP tools.

**Parameters:**

- `producerDir` (required): Path to MCP server source directory
- `toolName` (required): Name of the tool to scaffold
- `target`: Output format (`typescript`, `javascript`, `react-hook`, `zustand-action`)
- `includeErrorHandling`: Include try/catch error handling (default: true)
- `includeTypes`: Include TypeScript type definitions (default: true)

**Example Output:**

```typescript
/**
 * Get character data
 * @trace-contract CONSUMER
 * Producer: ./server/character-tools.ts:23
 */
export async function getCharacter(
  client: McpClient,
  args: GetCharacterArgs
): Promise<GetCharacterResult> {
  try {
    const result = await client.callTool("get_character", args);
    return JSON.parse(result.content[0].text);
  } catch (error) {
    console.error("Error calling get_character:", error);
    throw error;
  }
}
```

---

### `scaffold_producer`

Generate producer schema stub from consumer usage. Creates MCP tool definition based on how client code calls it.

**Parameters:**

- `consumerDir` (required): Path to consumer source directory
- `toolName` (required): Name of the tool to scaffold
- `includeHandler`: Include handler stub (default: true)

**Example Output:**

```typescript
import { z } from "zod";

// Tool: get_character
// Scaffolded from consumer at ./components/CharacterSheet.tsx:14
// @trace-contract PRODUCER (scaffolded)

server.tool(
  "get_character",
  "TODO: Add description",
  {
    characterId: z.string(),
  },
  async (args) => {
    // TODO: Implement handler
    // Consumer expects: name, race, level, stats, characterClass
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify({
            name: null, // TODO
            race: null, // TODO
            level: null, // TODO
          }),
        },
      ],
    };
  }
);
```

---

### `comment_contract`

Add cross-reference comments to validated producer/consumer pairs. Documents the contract relationship in both files.

**Parameters:**

- `producerDir` (required): Path to MCP server source directory
- `consumerDir` (required): Path to consumer source directory
- `toolName` (required): Name of the validated tool
- `dryRun`: Preview without writing (default: true)
- `style`: Comment style (`jsdoc`, `inline`, `block`)

**Example Preview:**

```javascript
// Producer comment:
/*
 * @trace-contract PRODUCER
 * Tool: get_character
 * Consumer: ./components/CharacterSheet.tsx:14
 * Args: characterId
 * Validated: 2025-12-11
 */

// Consumer comment:
/*
 * @trace-contract CONSUMER
 * Tool: get_character
 * Producer: ./server/character-tools.ts:23
 * Required Args: characterId
 * Validated: 2025-12-11
 */
```

---

### `init_project`

Initialize a trace project with `.trace-mcp` config directory for watch mode and caching.

**Parameters:**

- `projectDir` (required): Root directory for the trace project
- `producerPath` (required): Relative path to producer/server code
- `consumerPath` (required): Relative path to consumer/client code
- `producerLanguage`: Language (`typescript`, `python`, `go`, `rust`, `json_schema`)
- `consumerLanguage`: Language (`typescript`, `python`, `go`, `rust`, `json_schema`)

**Example:**

```typescript
const result = await client.callTool("init_project", {
  projectDir: "./my-app",
  producerPath: "./backend/src",
  consumerPath: "./frontend/src",
});
// Creates: ./my-app/.trace-mcp/config.json
```

---

### `watch`

Watch project files for changes and auto-revalidate contracts.

**Parameters:**

- `projectDir` (required): Root directory with `.trace-mcp` config
- `action`: `start`, `stop`, `status`, or `poll`

**Actions:**

- `start`: Begin watching for file changes
- `stop`: Stop watching
- `status`: Check current watcher state
- `poll`: Get pending events and last validation result

---

### `get_project_status`

Get the status of a trace project including config, cache state, and last validation result.

**Parameters:**

- `projectDir` (required): Root directory with `.trace-mcp` config

**Example Output:**

```json
{
  "success": true,
  "exists": true,
  "projectDir": "/path/to/project",
  "config": {
    "producer": { "path": "./server", "language": "typescript" },
    "consumer": { "path": "./client", "language": "typescript" }
  },
  "isWatching": true,
  "watcherStatus": { "running": true, "pendingChanges": 0 }
}
```

---

## Typical Workflow

### 1. Quick One-Off Analysis

```typescript
// Compare backend vs frontend, get markdown report
const result = await client.callTool("compare", {
  producerDir: "./backend/src",
  consumerDir: "./frontend/src",
  format: "markdown",
});
```

### 2. Continuous Validation (Watch Mode)

```typescript
// Initialize project
await client.callTool("init_project", {
  projectDir: ".",
  producerPath: "./server",
  consumerPath: "./client",
});

// Start watching
await client.callTool("watch", {
  projectDir: ".",
  action: "start",
});

// Later: poll for results
const status = await client.callTool("watch", {
  projectDir: ".",
  action: "poll",
});
```

### 3. Generate Missing Code

```typescript
// Generate client code from server schema
const consumer = await client.callTool("scaffold_consumer", {
  producerDir: "./server",
  toolName: "get_character",
  target: "react-hook",
});

// Or generate server stub from client usage
const producer = await client.callTool("scaffold_producer", {
  consumerDir: "./client",
  toolName: "save_settings",
});
```

### 4. Extract Multiple Formats

```typescript
// Extract from MCP server
const mcpSchemas = await client.callTool("extract_schemas", {
  rootDir: "./backend/mcp",
  include: ["**/*.ts"],
});

// Extract from OpenAPI specification
const openApiSchemas = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.openapi.yaml"],
});

// Extract from tRPC router
const trpcSchemas = await client.callTool("extract_schemas", {
  rootDir: "./backend/trpc",
  include: ["**/*.router.ts"],
});

// Extract TypeScript interfaces
const interfaceSchemas = await client.callTool("extract_schemas", {
  rootDir: "./shared",
  include: ["**/*.types.ts"],
});

// Extract REST endpoints (Express/Fastify)
const restSchemas = await client.callTool("extract_schemas", {
  rootDir: "./backend/routes",
  include: ["**/*.ts"],
});

// Extract GraphQL schemas
const graphqlSchemas = await client.callTool("extract_schemas", {
  rootDir: "./backend/graphql",
  include: ["**/*.graphql", "**/resolvers.ts"],
});
```

### 5. Full-Stack GraphQL Validation

```typescript
// Extract GraphQL schema and resolvers
const producer = await client.callTool("extract_schemas", {
  rootDir: "./backend",
  include: ["**/*.graphql", "**/resolvers/**/*.ts"],
});

// Trace Apollo Client hooks
const consumer = await client.callTool("trace_usage", {
  rootDir: "./frontend/src",
  include: ["**/*.tsx"],
});

// Compare for schema drift
const report = await client.callTool("compare", {
  producerDir: "./backend",
  consumerDir: "./frontend/src",
  format: "markdown",
});
```

---

## Roadmap

### Completed

- [x] MCP tool schema extraction
- [x] Consumer usage tracing
- [x] Basic mismatch detection
- [x] Code scaffolding (consumer & producer)
- [x] Contract comments
- [x] Watch mode with auto-revalidation
- [x] OpenAPI/Swagger adapter support
- [x] TypeScript interface extraction
- [x] tRPC router support
- [x] Pluggable adapter registry
- [x] Pattern Matcher abstraction (Phase 2)
- [x] Cross-file import resolution (Phase 2)
- [x] REST endpoint detection - Express & Fastify (Phase 2)
- [x] HTTP client tracing - fetch & axios (Phase 2)
- [x] GraphQL support - SDL, Apollo Server, Apollo Client (Phase 2)
- [x] Python language support - FastAPI, Flask, MCP tools, Pydantic (Phase 3)
- [x] Go language support - Chi, Gin, stdlib handlers, structs (Phase 3)
- [x] gRPC/Protobuf support - proto3, messages, services, streaming (Phase 3)
- [x] Python HTTP client tracing - requests, httpx, aiohttp (Phase 3)

### Planned

- [ ] JSON Schema adapter
- [ ] WebSocket message tracing
- [ ] OpenTelemetry integration
- [ ] Rust language support
- [ ] Java/Kotlin language support

## License

MIT

TDQS

A3.7/5.0

Scored across 11 tools

Disambiguation4/5

Tools are mostly distinct, but pairs like trace_file/trace_usage and extract_file/extract_schemas have overlapping purposes. Descriptions clarify the scope difference (single file vs. general), so ambiguity is low.

Naming Consistency5/5

All tool names follow a consistent verb_noun snake_case pattern (e.g., extract_file, trace_usage, init_project), making it easy to infer functionality.

Tool Count5/5

With 11 tools, the server covers extraction, tracing, validation, scaffolding, and project management without being bloated. The scope is well-defined.

Completeness5/5

The tool surface covers the full workflow: project init, schema extraction, usage tracing, contract comparison, cross-referencing comments, scaffolding, and watching. No obvious gaps for the intended domain.

Maintenance

ActivityInactive
ResponsivenessUnresponsive