Skip to main content
TutorialCopy-Paste Ready

How to Build an MCP Server for Your Business: A 30-Minute Tutorial

Of the 500 businesses AgentHermes has scanned, only 2 ship a working MCP server. That is the gap. This tutorial closes it. Install the SDK, define your tools, deploy to Vercel or Cloudflare, register with an agent card — thirty minutes, zero guesswork.

AH
AgentHermes Research
April 15, 202611 min read
2 / 500
businesses with an MCP server
30 min
from zero to live endpoint
3
primitives: tools, resources, prompts
$0
hosting on Vercel/Cloudflare free tier

Step 1: Install the MCP SDK

The official SDK from Anthropic handles the protocol, schema validation, and SSE transport. Spin up a Node 20 project and pull in two packages.

mkdir my-business-mcp && cd my-business-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node tsx
npx tsc --init --target es2022 --module esnext --moduleResolution bundler

zod is used to describe input schemas in a way both humans and agents can reason about. The SDK turns the zod schema into a JSON Schema that agents fetch during discovery.

Step 2: Define Three Tools

Tools are the verbs of your business. Start with three that every business has: list what you sell, quote a price, and place an order. Each tool gets a name, a zod schema, and a handler.

list_products

Agent can enumerate what you sell with filters and pagination.

list_products({ category?: string, limit?: number }) → Product[]
get_pricing

Structured pricing for a specific product or service. Replaces "contact for quote".

get_pricing({ product_id: string, quantity?: number }) → Price
create_order

Mutating tool that places an order. Requires auth, returns idempotent result.

create_order({ items: Item[], customer: Customer, idempotency_key: string }) → Order
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { z } from 'zod'

const server = new McpServer({ name: 'acme-biz', version: '1.0.0' })

server.tool(
  'list_products',
  { category: z.string().optional(), limit: z.number().int().max(100).default(20) },
  async ({ category, limit }) => {
    const rows = await db.products.list({ category, limit })
    return { content: [{ type: 'text', text: JSON.stringify(rows) }] }
  }
)

server.tool(
  'get_pricing',
  { product_id: z.string(), quantity: z.number().int().positive().default(1) },
  async ({ product_id, quantity }) => {
    const price = await db.pricing.compute(product_id, quantity)
    return { content: [{ type: 'text', text: JSON.stringify(price) }] }
  }
)

server.tool(
  'create_order',
  {
    items: z.array(z.object({ product_id: z.string(), qty: z.number().int().positive() })),
    customer_email: z.string().email(),
    idempotency_key: z.string().uuid(),
  },
  async ({ items, customer_email, idempotency_key }) => {
    const order = await db.orders.createIdempotent({ items, customer_email, idempotency_key })
    return { content: [{ type: 'text', text: JSON.stringify(order) }] }
  }
)

Step 3: Add Resources (Static Docs the Agent Can Read)

Resources are read-only documents agents load once and reason over. Business hours, refund policy, terms of service. Expose them as MCP resources instead of making the agent scrape HTML.

server.resource(
  'business-info',
  'resource://acme/business-info',
  async () => ({
    contents: [{
      uri: 'resource://acme/business-info',
      mimeType: 'application/json',
      text: JSON.stringify({
        legal_name: 'Acme Inc.',
        hours: { mon_fri: '9:00-18:00', sat: '10:00-14:00', sun: 'closed' },
        phone: '+1-555-0100',
        address: '100 Market St, San Francisco, CA',
        refund_policy_url: 'https://acme.com/refunds',
      }),
    }],
  })
)

Step 4: Define a Prompt Workflow

Prompts are pre-built interaction templates. They guide an agent through multi-step flows — onboarding, quote-to-order, return-a-product. One prompt can orchestrate three tools in the right sequence.

server.prompt(
  'place_order_flow',
  'Guide a customer from product search through checkout',
  { customer_intent: z.string() },
  async ({ customer_intent }) => ({
    messages: [
      { role: 'user', content: { type: 'text', text:
        'Customer said: ' + customer_intent + '\n\n' +
        '1. Call list_products to find matches.\n' +
        '2. Call get_pricing on top 3 candidates.\n' +
        '3. Confirm with customer, then call create_order with a fresh UUID idempotency_key.'
      }}
    ]
  })
)

Step 5: Deploy

Wire your McpServer to an SSE transport and expose it at /api/mcp. Three hosting paths, ranked by effort.

Vercel

Push to GitHub → Vercel auto-deploys. Runtime: Node 20. Add route /api/mcp with SSE headers. Set VERCEL_URL env var so agent-card.json reports the right endpoint.

Cloudflare Workers

Use the @modelcontextprotocol/sdk/server/sse export. wrangler.toml with [vars] for env. 100K requests/day free. Lowest p50 latency for global agents.

AgentHermes Hosted

Skip the whole build. Visit /connect, pick your vertical, AgentHermes provisions a hosted MCP server at agenthermes.ai/api/mcp/hosted/{slug} with dynamic tools wired to your business data.

For the Vercel path, add a single route file at app/api/mcp/route.ts that imports your server and pipes it through SSEServerTransport. Enable runtime = 'nodejs' and set a generous maxDuration for long-lived streams.

Step 6: Register in agent-card.json

A deployed MCP endpoint is useless if no agent can find it. The agent-card.json file at /.well-known/agent-card.json is the discovery hook agents like Claude, ChatGPT, and AgentHermes use to enumerate your capabilities.

{
  "protocolVersion": "0.3",
  "name": "Acme Inc.",
  "description": "Online retailer of industrial supplies",
  "url": "https://acme.com",
  "mcp_server": {
    "transport": "sse",
    "endpoint": "https://acme.com/api/mcp",
    "auth": { "type": "oauth2", "authorization_url": "https://acme.com/oauth/authorize" }
  },
  "skills": [
    { "id": "browse",  "tool": "list_products" },
    { "id": "quote",   "tool": "get_pricing" },
    { "id": "checkout","tool": "create_order" }
  ]
}

Test It Before You Ship

Run the MCP Inspector — the official debugging UI — against your deployed URL. It enumerates every tool, validates schemas, and lets you fire test calls. If the Inspector shows your three tools with green checkmarks, Claude and ChatGPT will see the same.

npx @modelcontextprotocol/inspector https://acme.com/api/mcp

Then run an AgentHermes scan at /audit against your domain. A working MCP endpoint plus a linked agent card lifts D2 API Quality and D9 Agent Experience — up to 12 points combined. That is enough to move most businesses from Bronze to Silver in a single deploy.

Not a Developer? Skip to AgentHermes /connect

This tutorial is aimed at engineers. If you run a business and do not write code, AgentHermes /connect auto-generates everything described above — tools tuned to your vertical, resources populated from your business profile, a hosted MCP endpoint, and a linked agent card. The output is the same MCP server this tutorial builds. You just skip the 30 minutes.

Why so few businesses have one: The 33-million-business MCP gap is not a technology problem. The SDK is stable, hosting is free, the protocol works. The gap is that most businesses have never heard the acronym. Every tutorial like this one closes the gap by a few percent.

Frequently Asked Questions

Do I need Node.js to build an MCP server?

The fastest path is Node.js using the official @modelcontextprotocol/sdk package. There are also Python and Go SDKs. This tutorial uses TypeScript on Node 20+. If you deploy to Cloudflare Workers you stay on the JavaScript runtime, just without raw Node APIs.

Which transport should I use — stdio or HTTP/SSE?

For a remote MCP server that hosted agents like Claude, ChatGPT, and AgentHermes connect to over the internet, use HTTP with SSE transport. Stdio transport is only for local desktop agents that spawn your server as a child process. Every example in this tutorial uses SSE so agents can reach you over the public internet.

Does my MCP server need authentication?

Yes, if your tools mutate state (create_order, book_appointment, cancel_subscription). Read-only tools (list_products, get_business_hours) can be public. Use Bearer tokens via OAuth 2.0 with PKCE, the same pattern as the Stripe and GitHub REST APIs. Scanning 500 businesses shows 401 with a structured JSON envelope scores 87% of a 200 response on D7 Security.

How do agents discover my MCP server once it is live?

Publish the URL in three places. First, your agent-card.json at /.well-known/agent-card.json. Second, your agent-hermes.json if you follow the AgentHermes standard. Third, an MCP registry like the AgentHermes registry at /registry. Agents fetch these files, parse the mcp_server entry, and connect.

How much does it cost to host an MCP server?

On Vercel, a simple MCP server fits inside the free tier. On Cloudflare Workers, the first 100K requests per day are free. Egress is the only real variable. If you are a local business without engineering time, AgentHermes /connect auto-generates and hosts the server for you at agenthermes.ai/api/mcp/hosted/your-slug.


Ship an MCP server in 30 minutes — or 30 seconds

Run the scanner to baseline your score, then pick the path: build it yourself with this tutorial, or let AgentHermes /connect generate it for you.


Share this article: