Securing Your AI Toolkit: MCP Version Control Strategy - Dean Keesey's Blog
10 min read

Securing Your AI Toolkit: MCP Version Control Strategy

How security-first thinking shaped my approach to managing AI development tools and MCP server version control to prevent potential disasters.

ai-development
security
mcp-servers
version-control
development-workflow
Cover image for Securing Your AI Toolkit: MCP Version Control Strategy

Securing Your AI Toolkit: MCP Version Control Strategy

How a security-first approach to MCP server management prevented potential disasters

When Model Context Protocol (MCP) emerged in late 2024, it felt like the Wild West. Powerful new capabilities, rapid community development, and the usual early-adopter trade-offs between innovation and stability. Having watched the evolution of package managers and open-source ecosystems, I knew that a security-first approach would be essential from day one.

The MCP Security Landscape

Understanding the Risk Surface

MCP servers run with significant privileges:

  • File System Access: Most MCP servers can read and write files across your development environment
  • Network Access: Many servers make external API calls with your credentials
  • Process Execution: Terminal and command-line MCPs can execute arbitrary commands
  • Data Access: Database MCPs connect to your production and development systems

Unlike traditional package dependencies that run in more constrained environments, MCP servers operate with the full privileges of your development environment. This makes security considerations paramount.

The Convenience vs Security Tension

The easiest way to install MCP servers is using npx to download and run them on-demand:

npx @modelcontextprotocol/server-filesystem ~/Documents

This approach offers:

  • Immediate gratification: Install and run any MCP server instantly
  • Automatic updates: Always get the latest version
  • Low maintenance: No local installation management

But it also creates significant security risks:

  • Supply chain attacks: Compromised packages could execute malicious code
  • Version instability: Automatic updates might break your workflows
  • Network dependencies: Can’t work offline or in restricted environments
  • Audit difficulties: Hard to track what code actually ran

The Local Installation Philosophy

After evaluating the trade-offs, I adopted a strict local installation strategy for all MCP servers. Here’s how and why:

Version Pinning Strategy

Instead of npx downloads, I install specific versions locally:

# Instead of: npx @modelcontextprotocol/server-filesystem
# Do this:
npm install -g @modelcontextprotocol/server-filesystem@1.0.2

Benefits:

  • Reproducible environments: Exact same versions across all development machines
  • Change control: Updates happen deliberately, not automatically
  • Offline capability: Can work without internet access
  • Security review: Can audit specific versions before adoption

The Local MCP Registry

I maintain a controlled registry of approved MCP servers:

~/Workspace/tools/mcp/
├── approved-mcps.json          # Registry of vetted servers
├── install-approved-mcps.sh    # Batch installation script
├── version-lock.json           # Exact version pins
└── security-audit.md           # Security review notes

The approval process for new MCPs:

  1. Security review: Code inspection for obvious red flags
  2. Provenance check: Verify author and maintenance status
  3. Test installation: Install and test in isolated environment
  4. Version pinning: Lock to specific, tested version
  5. Documentation: Record security considerations and update procedures

Wrapper Scripts for Problematic Servers

Some MCP servers had issues that required custom solutions. Rather than abandoning them, I created wrapper scripts:

Example: SQLite MCP Path Issues The SQLite MCP had path resolution problems. Instead of hoping for upstream fixes, I created a wrapper:

#!/bin/bash
# fix-sqlite-mcp-path.sh
export SQLITE_DB_PATH="/absolute/path/to/database.db"
exec node /usr/local/lib/node_modules/mcp-server-sqlite/dist/index.js "$@"

This approach provided:

  • Immediate problem resolution: Don’t wait for upstream fixes
  • Custom configuration: Tailor servers to specific environments
  • Debugging capability: Add logging and error handling
  • Transition strategy: Easy to remove wrappers when upstream fixes arrive

Provider Hierarchy: Trust-Based Decision Making

Not all MCP servers are created equal. I developed a provider hierarchy based on trust and maintenance quality:

Tier 1: First-Party Providers

Highest Trust: MCPs maintained by the service provider themselves

  • Azure MCP by Microsoft: Official Microsoft implementation
  • Notion MCP by Notion: Direct from Notion’s team
  • GitHub MCP by GitHub: Official GitHub tooling

Why Tier 1 matters:

  • Aligned incentives: Providers have strong motivation to maintain quality
  • Security resources: Large companies have dedicated security teams
  • Support responsibility: Clear ownership when issues arise
  • API compatibility: Most likely to stay current with API changes

Tier 2: Official MCP Repositories

High Trust: MCPs in official repositories with clear governance

  • @modelcontextprotocol/server-*: Official MCP implementations
  • Community-maintained servers with MCP organization oversight

Evaluation criteria:

  • Consistent commit history
  • Multiple contributors
  • Clear documentation
  • Security-conscious development practices

Real-World Security Incidents

The Malformed JSON Incident

One of my local MCP servers started producing malformed JSON responses after a system sleep/wake cycle. This could have been a sign of compromise, but investigation revealed:

  • Root cause: Memory corruption after system suspend
  • Solution: MCP server restart protocols
  • Prevention: Systematic restart procedures after system wake

This incident validated the local installation approach - I could quickly isolate and analyze the problem without worrying about upstream package modifications.

The Dependency Confusion Attempt

While monitoring npm packages, I noticed a suspicious package with a similar name to a legitimate MCP server:

  • Legitimate: @modelcontextprotocol/server-github
  • Suspicious: @model-context-protocol/server-github (note the hyphens)

This typosquatting attempt reinforced the importance of:

  • Careful package name verification
  • Official repository preference
  • Local installation for audit capability

Lessons Learned

Security Pays Dividends

The extra effort in security-conscious MCP management paid off:

  • Zero security incidents: No compromised servers or data breaches
  • Stable development environment: Predictable behavior from known versions
  • Faster debugging: Known configurations made troubleshooting easier
  • Audit capability: Could verify what code was actually running

Balance Pragmatism with Security

The strategy evolved to balance security with productivity:

  • Quick experimentation: Isolated test environment for trying new MCPs
  • Graduated trust: Move promising servers through trust tiers over time
  • Community engagement: Contribute back to improve security practices

Key Takeaways

  • Local installation provides control and security over convenience and automatic updates
  • Provider hierarchy helps make trust-based decisions about which MCPs to adopt
  • Wrapper scripts solve immediate problems without waiting for upstream fixes
  • Security monitoring must be proactive, not reactive
  • Documentation prevents configuration drift and preserves security rationale

Implementation Checklist

  • Establish local installation procedures for all MCP servers
  • Define provider trust hierarchy for your organization
  • Create version pinning and update control processes
  • Implement security monitoring and audit procedures
  • Document security decisions and rationale
  • Create backup and recovery procedures for MCP configurations

Next in this series: “When AI Tools Talk to Each Other: Multi-LLM Integration Lessons” - Real-world challenges of orchestrating multiple AI services and the surprising overhead of context translation.

← Back to all posts

Written by Dean Keesey