Skip to main content

How to Build & Deploy an MCP Server with TypeScript

· 6 min read
Yassine El Haddad
Software Developer & Automation Specialist

I build production AI agents, web scrapers, and automation pipelines. Most of what I publish here comes from the actual problems they run into: proxies that get banned, anti-bot stacks that fingerprint your client, RAG that drifts when the underlying data moves. Stack: Python, TypeScript, Go, FastAPI, LangChain, Crawlee, Playwright, deployed on AWS, GCP, and Cloudflare.

The Model Context Protocol (MCP) standardizes how Large Language Models interface with external APIs and execution environments. By writing a single MCP server, your custom engineering tools instantly become compatible with Claude Desktop, Cursor, and any other MCP-compliant client, eliminating the need to maintain disparate plugin architectures.

While MCP servers can run locally via stdio, executing them exclusively on developer hardware severely limits agentic workflows. By deploying an MCP server to a cloud container platform like Apify, your AI agents gain persistent 24/7 access to your internal tools.

This technical guide demonstrates how to scaffold an MCP server in TypeScript, define a custom execution tool, and deploy it to a serverless container.

Why deploy MCP servers to the cloud?

Deploying MCP infrastructure to a cloud platform like Apify provides three distinct engineering advantages:

  1. Persistent Availability: Cloud deployment ensures your AI agent can process asynchronous background tasks (like nightly Web Scrape aggregations) without requiring your local machine to remain online.
  2. Infrastructure Abstraction: Running via an HTTP SSE (Server-Sent Events) endpoint allows you to share access with a broader engineering team via a stable https://... URL, bypassing complex local network tunneling.
  3. Automated Provisioning: Apify natively handles TLS termination, request load balancing, and container scaling.

Architectural constraints and failure modes

Before deploying an MCP server to production, engineers must account for inherent architectural limitations:

  1. Execution Timeouts: When an AI model calls an MCP tool, the client expects a relatively synchronous response. If your MCP tool triggers a deeply nested database query or a heavy web scraping operation that takes 5+ minutes, the SSE connection may time out, causing the LLM client to error. You must architect long-running tasks asynchronously.
  2. Context Window Bloat: Your MCP server must vigorously sanitize its output. If your tool returns a massive JSON payload (e.g., thousands of database rows), it will instantly max out the LLM's context window, resulting in massive API charges and token exhaustion.
  3. Debugging Opacity: When an MCP server runs locally, standard console.log debugging is straightforward. When deployed via SSE, you are reliant on the hosting platform's container logging telemetry to debug why a tool failed mid-execution.

Step 1: Initialize the project

We utilize the official Apify TypeScript MCP template, which comes pre-configured with Anthropic's SDK and the Apify deployment harness.

Execute the following in your terminal:

# Scaffold the project using the Apify CLI
apify create my-mcp-server --template ts-mcp-proxy
cd my-mcp-server

This generates an Actor directory structure properly configured to expose the @modelcontextprotocol/sdk.


Step 2: Define your executable tools

Navigate to src/main.ts. This file registers the tools your AI agent can invoke.

For demonstration, we will define a synchronous tool that calculates shipping constraints based on input parameters.

// src/tools/shipping.ts
import { Tool } from "@modelcontextprotocol/sdk/types.js";

// The schema is strictly parsed; LLMs read these descriptions to understand the tool's purpose
export const shippingTool: Tool = {
name: "calculate_shipping",
description: "Calculates enterprise shipping cost based on weight and precise destination",
inputSchema: {
type: "object",
properties: {
weight: { type: "number", description: "Package weight in kilograms" },
destination: { type: "string", description: "Strict ISO-3166 country code (e.g., US, DE)" }
},
required: ["weight", "destination"]
}
};

// The execution logic triggered when the LLM invokes the tool
export const calculateShipping = async ({ weight, destination }) => {
// In production, this would query a logistics API
const rateMatrix = { 'US': 5, 'DE': 8, 'UK': 10 };
const rate = rateMatrix[destination] || 15; // default fallback

return {
success: true,
data: { cost: weight * rate, currency: "USD" }
};
};

Step 3: Verify Actor configuration

Review the .actor/actor.json file. The critical parameter is webServerMcpPath, which instructs the Apify runtime to expose your server port via an SSE stream rather than a standard REST endpoint.

{
"actorSpecification": 1,
"name": "enterprise-shipping-mcp",
"usesStandbyMode": true,
"webServerMcpPath": "/sse"
}

Architectural Note: usesStandbyMode is mandatory for interactive MCP deployments. It instructs Apify to maintain a "warm" container state. If disabled, the container will cold-boot on every LLM request, introducing unacceptable latency into the chat interface.


Step 4: Deploy to the Apify cloud

Push your committed code to the Apify platform using the CLI:

# Authenticate if necessary
apify login

# Build the Docker container and push to the registry
apify push

Navigate to your Apify Console dashboard. Your new Actor will display a status of Ready.


Step 5: Connect Claude Desktop

  1. In the Apify Console, locate your Actor's Standby URL (typically formatted as https://[username]--[actorname].apify.actor/mcp).
  2. Open your local claude_desktop_config.json configuration file:
    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows: %APPDATA%\Claude\claude_desktop_config.json
  3. Append your new server credentials:
{
"mcpServers": {
"apify-shipping-tool": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://[YOUR_ACTOR_URL]/mcp"],
"env": {
"APIFY_TOKEN": "YOUR_PRIVATE_APIFY_TOKEN"
}
}
}
}

Restart the Claude Desktop application completely. You can now prompt the LLM directly: "Calculate the shipping cost for a 12kg package to DE," and Claude will route the execution to your Apify container.


Next steps for autonomous agents

With the foundational infrastructure deployed, you can begin exposing complex backend systems to your LLMs:

  • Database integrations: Expose secure, read-only PostgreSQL views to an AI analyst.
  • Legacy API wrapping: Wrap older SOAP or GraphQL endpoints in a single MCP tool for modern AI access.
  • Web Extraction: Integrate Apify's managed scraping Actors directly into your MCP server to give your agents live internet access.
Apify Affiliate Banner 728x90Apify Affiliate Banner 728x90Apify Affiliate Banner 300x50Apify Affiliate Banner 300x50
Frequently Asked Questions

Local MCP servers communicate via standard input/output (stdio) directly on your local CPU. Remote MCP servers, like those deployed on Apify, communicate via HTTP Server-Sent Events (SSE), allowing the LLM to interact with cloud infrastructure.

Yes. The Model Context Protocol is language-agnostic. Apify maintains a dedicated Python MCP template for data science teams who prefer building their agents in Python.

Apify utilizes a consumption-based pricing model. You are billed strictly for the Compute Units (CU) consumed while your server actively processes requests or maintains Standby mode. They offer a perpetual $5 monthly free tier for prototyping.