DEV Community

Edison Flores
Edison Flores

Posted on

How to build an MCP server with HTTP transport (not just stdio)

The problem

Most MCP (Model Context Protocol) servers only support stdio transport — you run them as a subprocess:

{
  "mcpServers": {
    "myserver": {
      "command": "npx",
      "args": ["-y", "my-mcp-server"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

But what if you want to expose your MCP server over HTTP? So that:

  • Remote clients can connect without installing anything locally
  • Multiple agents can share the same server instance
  • You can deploy it on Vercel/Cloudflare/AWS Lambda

The MCP spec supports HTTP transport (SSE + JSON-RPC), but there's barely any documentation on how to implement it.

This post is the guide I wish I had when building MarketNow's MCP endpoint.

The architecture

Client (Claude Desktop, Cursor, etc.)
    │
    ├── POST /api/mcp  → JSON-RPC 2.0 request/response
    │
    └── GET  /api/mcp  → SSE stream (Server-Sent Events)
Enter fullscreen mode Exit fullscreen mode
  • POST handles initialize, tools/list, tools/call (JSON-RPC 2.0)
  • GET opens an SSE stream for server-to-client messages

The code (Vercel serverless function)

// api/mcp.js — Vercel serverless function

export default async function handler(req, res) {
  // CORS
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

  if (req.method === 'OPTIONS') {
    res.status(200).end();
    return;
  }

  // GET → SSE stream
  if (req.method === 'GET') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive',
    });
    res.write(`data: ${JSON.stringify({ type: 'connected' })}\n\n`);
    return;
  }

  // POST → JSON-RPC
  if (req.method === 'POST') {
    const { jsonrpc, id, method, params } = req.body;
    let result;

    switch (method) {
      case 'initialize':
        result = {
          protocolVersion: '2024-11-05',
          capabilities: { tools: {} },
          serverInfo: { name: 'my-server', version: '1.0.0' },
        };
        break;

      case 'tools/list':
        result = {
          tools: [{
            name: 'hello',
            description: 'Say hello',
            inputSchema: { type: 'object', properties: { name: { type: 'string' } } },
          }],
        };
        break;

      case 'tools/call':
        result = {
          content: [{ type: 'text', text: `Hello, ${params.arguments.name}!` }],
        };
        break;

      default:
        res.status(200).json({
          jsonrpc: '2.0', id,
          error: { code: -32601, message: `Method not found: ${method}` },
        });
        return;
    }

    res.status(200).json({ jsonrpc: '2.0', id, result });
    return;
  }

  res.status(405).json({ error: 'Method not allowed' });
}
Enter fullscreen mode Exit fullscreen mode

The .well-known/mcp.json

Clients discover your MCP server via .well-known/mcp.json:

{
  "name": "my-server",
  "version": "1.0.0",
  "transport": {
    "type": "http+sse",
    "http_url": "https://yourdomain.com/api/mcp",
    "sse_url": "https://yourdomain.com/api/mcp"
  },
  "tools": [
    { "name": "hello", "description": "Say hello" }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Testing it

# Test initialize
curl -X POST https://yourdomain.com/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{}}}'

# Test tools/list
curl -X POST https://yourdomain.com/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# Test tools/call
curl -X POST https://yourdomain.com/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"hello","arguments":{"name":"World"}}}'
Enter fullscreen mode Exit fullscreen mode

Deploy to Vercel

  1. Create api/mcp.js in your project
  2. Deploy: vercel --prod
  3. Your MCP server is live at https://yourproject.vercel.app/api/mcp

Why this matters

HTTP transport means:

  • No installation required — clients connect via URL
  • Multi-tenant — one server serves many agents
  • Scalable — runs on serverless (Vercel, Cloudflare Workers, AWS Lambda)
  • Stateless — each request is independent

This is how MarketNow works — one Vercel serverless function serves all MCP clients.

Try it

MarketNow's MCP endpoint is live:

curl -X POST https://marketnow.site/api/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Enter fullscreen mode Exit fullscreen mode

Returns 4 tools: search_skills, get_skill, list_categories, health.


This post was written while building MarketNow — the trust layer for agent commerce. 8,764 MCP servers, each security-audited.

Top comments (0)