Skip to content

Latest commit

 

History

History
312 lines (243 loc) · 10.6 KB

File metadata and controls

312 lines (243 loc) · 10.6 KB

Tool Definition Fingerprinting

MCPLint provides a dual-tier fingerprinting system for MCP tool definitions that enables:

  • Schema Change Detection: Track meaningful changes to tool schemas over time
  • Audit Trail: Maintain cryptographic evidence of tool definitions for compliance
  • CI/CD Integration: Detect breaking changes before deployment

Overview

The fingerprinting system generates two types of hashes for each tool:

Hash Type Purpose Ignores
Semantic Hash Detect functional changes Descriptions, whitespace, property ordering
Full Hash Audit trail / tamper detection Nothing (captures everything)

Quick Start

CLI Usage

# Generate fingerprints for all tools on a server
mcplint fingerprint generate my-server

# Generate and save to file
mcplint fingerprint generate my-server --output fingerprints.json

# Compare against a baseline
mcplint fingerprint compare my-server --baseline baseline.json

# Output as JSON
mcplint fingerprint generate my-server --format json

Programmatic Usage

use mcplint::fingerprinting::{FingerprintHasher, FingerprintComparator, ChangeSeverity};
use mcplint::protocol::mcp::Tool;

// Generate a fingerprint from a tool definition
let fingerprint = FingerprintHasher::fingerprint(&tool)?;
println!("Tool: {}", fingerprint.tool_name);
println!("Semantic hash: {}", fingerprint.semantic_hash);

// Compare two fingerprints
let diff = FingerprintComparator::compare(&old_fingerprint, &new_fingerprint);
println!("Severity: {:?}", diff.severity);

if diff.severity >= ChangeSeverity::Major {
    println!("Breaking changes detected!");
    for change in &diff.changes {
        println!("  - {:?}", change);
    }
}

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                     Fingerprinting System                        │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────────┐  │
│  │    Tool      │───▶│  Normalizer  │───▶│     Hasher       │  │
│  │  Definition  │    │              │    │                  │  │
│  └──────────────┘    └──────────────┘    └──────────────────┘  │
│                             │                     │              │
│                             ▼                     ▼              │
│                      ┌──────────────┐    ┌──────────────────┐  │
│                      │  Normalized  │    │  ToolFingerprint │  │
│                      │   Schema     │    │                  │  │
│                      └──────────────┘    └──────────────────┘  │
│                                                   │              │
│                                                   ▼              │
│                                          ┌──────────────────┐  │
│                                          │   Comparator     │  │
│                                          │                  │  │
│                                          └──────────────────┘  │
│                                                   │              │
│                                                   ▼              │
│                                          ┌──────────────────┐  │
│                                          │ FingerprintDiff  │  │
│                                          │  + Severity      │  │
│                                          │  + Changes       │  │
│                                          │  + Recommendations│  │
│                                          └──────────────────┘  │
└─────────────────────────────────────────────────────────────────┘

Components

SchemaNormalizer

Canonicalizes JSON schemas for consistent fingerprinting:

  • Property Ordering: Sorts properties alphabetically
  • Type Canonicalization: Normalizes type aliases (Stringstring, boolboolean)
  • Required Sorting: Sorts required field arrays
  • Description Handling: Removes for semantic hash, normalizes for full hash

FingerprintHasher

Generates SHA-256 hashes from normalized schemas:

// Generate fingerprint for a single tool
let fp = FingerprintHasher::fingerprint(&tool)?;

// Batch fingerprint multiple tools
let fingerprints = FingerprintHasher::fingerprint_all_ok(&tools);

FingerprintComparator

Compares fingerprints and generates detailed diff reports:

let diff = FingerprintComparator::compare(&old_fp, &new_fp);

// Check severity
match diff.severity {
    ChangeSeverity::Breaking => panic!("Breaking changes!"),
    ChangeSeverity::Major => warn!("Major changes detected"),
    ChangeSeverity::Minor => info!("New features added"),
    ChangeSeverity::Patch => debug!("Documentation changes"),
    ChangeSeverity::None => {}
}

Change Types

Change Type Severity Description
ParameterRemoved (required) Breaking Required parameter was removed
TypeChanged Breaking Parameter type incompatibility
RequiredChanged (→ required) Major Optional became required
ParameterAdded (required) Major New required parameter
ParameterAdded (optional) Minor New optional parameter
ConstraintAdded Minor New validation constraint
ConstraintRemoved Minor Validation relaxed
DescriptionChanged Patch Documentation only

Severity Levels

Breaking > Major > Minor > Patch > None

Breaking (✗): Requires client updates, may break existing integrations
Major    (!): Significant changes, review recommended
Minor    (+): New features, backward compatible
Patch    (~): Documentation/metadata only
None     (✓): No changes detected

Data Structures

ToolFingerprint

pub struct ToolFingerprint {
    pub tool_name: String,           // Tool identifier
    pub semantic_hash: String,       // SHA-256 (64 chars) - functional changes
    pub full_hash: String,           // SHA-256 (64 chars) - all changes
    pub schema_version: Option<String>,
    pub created_at: DateTime<Utc>,
    pub mcplint_version: String,
    pub metadata: FingerprintMetadata,
}

FingerprintMetadata

pub struct FingerprintMetadata {
    pub parameter_count: usize,
    pub required_params: Vec<String>,
    pub param_types: HashMap<String, String>,
    pub complexity_score: u32,
}

FingerprintDiff

pub struct FingerprintDiff {
    pub tool_name: String,
    pub old_semantic_hash: String,
    pub new_semantic_hash: String,
    pub old_full_hash: String,
    pub new_full_hash: String,
    pub changes: Vec<ChangeType>,
    pub severity: ChangeSeverity,
    pub summary: String,
    pub recommendations: Vec<String>,
}

Baseline Integration

Fingerprints integrate with MCPLint's baseline system for incremental scanning:

use mcplint::baseline::Baseline;

// Create baseline with fingerprints
let baseline = Baseline::from_results(&scan_results)
    .with_fingerprints(fingerprints);

// Save for future comparison
baseline.save("baseline.json")?;

// Load and check for fingerprint changes
let loaded = Baseline::load("baseline.json")?;
if loaded.has_fingerprints() {
    let stored_fp = loaded.get_fingerprint("my_tool");
}

CI/CD Integration

GitHub Actions Example

name: MCP Schema Check

on: [push, pull_request]

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

      - name: Install MCPLint
        run: cargo install mcplint

      - name: Generate fingerprints
        run: mcplint fingerprint generate ./my-server --output current.json

      - name: Compare with baseline
        run: |
          mcplint fingerprint compare ./my-server --baseline baseline.json
          EXIT_CODE=$?
          if [ $EXIT_CODE -eq 1 ]; then
            echo "::error::Breaking changes detected!"
            exit 1
          elif [ $EXIT_CODE -eq 2 ]; then
            echo "::warning::Major changes detected"
          fi

Exit Codes

Code Meaning
0 No changes or minor/patch changes
1 Breaking changes detected
2 Major changes detected

JSON Output Format

{
  "tool_name": "read_file",
  "semantic_hash": "a1b2c3d4e5f6...",
  "full_hash": "f6e5d4c3b2a1...",
  "schema_version": null,
  "created_at": "2025-01-15T10:30:00Z",
  "mcplint_version": "0.1.0",
  "metadata": {
    "parameter_count": 2,
    "required_params": ["path"],
    "param_types": {
      "path": "string",
      "encoding": "string"
    },
    "complexity_score": 5
  }
}

Best Practices

  1. Store baselines in version control - Track schema evolution over time
  2. Run fingerprint checks in CI - Catch breaking changes before merge
  3. Use semantic hash for compatibility - Ignore non-functional changes
  4. Use full hash for audit - Cryptographic proof of exact definitions
  5. Review Major changes - May require client updates even if not breaking
  6. Document intentional changes - Add context when updating baselines

Troubleshooting

"Baseline does not contain tool fingerprints"

The baseline was created before fingerprinting was added. Regenerate with:

mcplint fingerprint generate my-server --output new-baseline.json

Hash mismatch on identical tools

Ensure both environments use the same MCPLint version. Schema normalization may differ between versions.

Slow fingerprinting

Fingerprinting is CPU-bound (SHA-256). For many tools, consider:

  • Running in parallel across servers
  • Caching fingerprints between runs
  • Using --output to persist results