Johnny Butler

November 27, 2025

Migrating an MCP Server from Cloudflare Workers into a Rails Application

Over the past few weeks we’ve been evolving our AI-driven tooling at Materials Market, including the Model Context Protocol (MCP) services that power our agents. Our first iteration ran inside a Cloudflare Worker — a lightweight, globally distributed runtime that made iteration fast and frictionless.

As our MCP usage matured, however, we reached a natural point where keeping the MCP server outside our core Rails application created more overhead than benefits. The next logical step was to migrate the MCP server into the Rails codebase itself, giving us a unified, maintainable, and scalable foundation.

This post outlines why we made the move, how we approached it, and what benefits we gained.

Why We Started With Cloudflare Workers

Cloudflare Workers were a great choice early on:
  • Instant deploys made experimentation easy
  • Global edge routing provided low-latency access
  • Isolated environment let us iterate on the MCP protocol without touching Rails
  • Durable Objects let us maintain state across MCP calls if needed

For prototyping, this was perfect. We were able to build a functional MCP server in days, not weeks.

But as the MCP endpoints matured and became more tightly coupled with our Rails domain, several challenges emerged.

Why We Migrated MCP Into Rails

As soon as MCP endpoints started interacting with authenticated data, internal business rules, and database queries, the Worker became an unnecessary middle layer.

Key reasons for migrating:

1. Eliminate duplicated logic
Certain validations, permission checks, and product lookups needed to be recreated in the Worker. Moving MCP into Rails ensures that all business logic comes from a single canonical source.

2. Better internal security
The Worker required custom authentication plumbing and secret handling.
Rails already has secure sessions, API authentication, and encrypted credentials.

3. Observability + debugging
Cloudflare logs are lightweight but limited.
In Rails we gain:
  • Structured logs
  • Full exception tracking
  • Instrumentation
  • The ability to trace MCP requests across the stack

4. Simpler deployments
 
Previously:
  • MCP code deployed through Cloudflare
  • Backend code deployed via our Rails CI/CD
  • Coordination required between them
Now:
  • MCP deploys automatically with every Rails release
  • No mismatch between environments
  • No “stale Worker vs fresh backend” issues

5. Moving toward fast-mcp (native server-side MCP)

Rails gives us better control of long-lived connections, SSE streaming, and future extensions.

Cloudflare was great for experimentation — Rails is the right long-term home.

How We Kept the Migration Safe

Migrating an MCP server that clients depend on requires care, even if usage is internal.

Our approach:

1. Introduced Rails MCP endpoints behind a feature branch
We implemented:
  • /api/v1/mcp/ping
  • /api/v1/mcp/product_search
  • /api/v1/mcp/sse

and verified behaviour matched what the Worker currently returned.

2. Built a Cloudflare Worker “bridge”
Before cutting over, we updated the Worker so that its MCP tools simply proxied requests to Rails using a configurable BASE_API_URL.

This let us test the Rails MCP behind the Worker without affecting production.
If something went wrong, Rails MCP could be disabled instantly.

3. Created a test Worker (mcp-server-mcp-test)
This allowed us to:
  • Deploy the feature branch without touching main
  • Run MCP Inspector tools directly against Rails
  • Validate responses matched production Workers

4. Verified live parity
We compared:
  • Worker → Rails (via proxy)
  • Rails → Rails (direct MCP)

Tools behaved identically.

5. Switched traffic from Worker → Rails
Once confidence was high:
  • The Worker became a thin proxy
  • The Rails MCP endpoints became the source of truth
  • Eventually the Worker can be deleted entirely

Architecture Before and After

Before
MCP Client → Cloudflare Worker → (Custom Logic) → Materials Market API
Challenges: duplicated logic, separate deploys, inconsistent state.

After
MCP Client → Rails MCP Controller → Internal Services / DB
Benefits: unified logic, one deploy, full observability, simpler auth.

What We Gained

Faster development
One codebase. One environment. No Worker-specific runtime differences.

More reliable MCP behaviour
No accidental divergence between Worker and backend logic.

Stronger security
Rails sessions, signed tokens, encrypted secrets — already in place.

Easier testing
System specs can now exercise MCP endpoints directly.

Future-proofing for real-time tools
Rails ActionController::Live / async adapters give us a path toward robust SSE-based MCP tools.

Conclusion

Cloudflare Workers were the right tool for bootstrapping our MCP implementation — lightweight, quick, and flexible. But as MCP became more central to our platform, bringing it home into Rails was the natural evolution.

Now we have a unified system where AI agents can safely and directly interact with our domain logic, with full observability and consistency.

The result:
less duplication, fewer moving parts, and a far more maintainable MCP server.

If your team is experimenting with MCP and reaching the point where integration matters more than iteration speed, migrating your MCP server into your main application is absolutely worth considering