Cloudflare MCP Server for Static Sites

Cloudflare MCP Server for Static Sites

Turns any static website into an MCP-searchable knowledge base by deploying a Cloudflare Worker that provides full-text search tools, enabling AI assistants to search and retrieve content from your site.

Category
访问服务器

README

Cloudflare MCP Server for Static Sites

Turn your static website into an AI-accessible knowledge base. This project deploys a Cloudflare Worker that implements the Model Context Protocol (MCP). AI tools like Claude can then search and retrieve your content directly. You can read more about this approach in my blog post.

Cloudflare is well-suited for hosting remote MCP servers — its Workers platform handles the transport layer, and Durable Objects maintain persistent client sessions.



Why This Matters

AI assistants answer questions based on their training data, which may be outdated or incomplete. They can't search your website unless you give them a way to do so. This MCP server can be an AI-native bridge that allows these tools to get up-to-date information when they need it.

You might use this to:

  • Help users find answers in your documentation
  • Give AI assistants access to your blog's content
  • Let AI tools cite your articles with accurate, up-to-date information

How It Works

┌─────────────────────────────────────────────────────────────────────────┐
│                           Your Static Site                              │
│                    (Markdown files with frontmatter)                    │
└──────────────────────────────┬──────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                             Adapter                                     │
│         (Astro, Hugo, or Generic — runs at build time)                  │
│                                                                         │
│   Scans your content files, extracts metadata from frontmatter,         │
│   and generates a search-index.json file.                               │
└──────────────────────────────┬──────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         Cloudflare R2                                   │
│                                                                         │
│   Stores the search index. Only your Worker can access it.              │
│   The Worker caches the index in memory for one hour.                   │
└──────────────────────────────┬──────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                       Cloudflare Worker                                 │
│                                                                         │
│   Implements the MCP server. Uses Fuse.js for fuzzy search.             │
│   Durable Objects maintain persistent sessions with MCP clients.        │
└──────────────────────────────┬──────────────────────────────────────────┘
                               │
                               ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                          MCP Clients                                    │
│           (Claude Desktop, Claude Code, Cursor, etc.)                   │
│                                                                         │
│   Tools available to the AI:                                            │
│   • search_<prefix> — Find content by keywords                          │
│   • get_article     — Retrieve a specific page by URL                   │
│   • get_index_info  — Get index statistics                              │
└─────────────────────────────────────────────────────────────────────────┘

Prerequisites

Requirement What It's For
Cloudflare account Hosts the Worker and R2 bucket. The free tier is sufficient.
Node.js 18+ or Bun Runs the adapter that generates your search index.
Wrangler CLI Deploys the Worker and manages R2. Installed via bun install.

Quick Start

You can follow these steps manually or point an AI coding tool (Claude Code, Cursor, etc.) at this repo and ask it to set things up. Either way, you'll need a Cloudflare account and these details about your site:

  • Site name and domain (e.g., "My Blog" and "blog.example.com")
  • Content directory path to your markdown files
  • Tool prefix for MCP tool names (e.g., "myblog" → search_myblog)
  • MCP endpoint domain (e.g., "mcp.example.com")

1. Clone and Install

git clone https://github.com/lennyzeltser/cloudflare-mcp-for-static-sites.git my-site-mcp
cd my-site-mcp
bun install

2. Configure

Edit wrangler.jsonc:

{
  "name": "my-site-mcp-server",
  "routes": [
    { "pattern": "mcp.example.com", "custom_domain": true }
  ],
  "r2_buckets": [
    { "binding": "SEARCH_BUCKET", "bucket_name": "my-site-mcp-data" }
  ]
}

3. Create R2 Bucket

npx wrangler r2 bucket create my-site-mcp-data

4. Generate and Upload Index

Pick an adapter for your site (see Adapters):

node adapters/generic/generate-index.js \
  --content-dir=../my-site/content \
  --site-name="My Site" \
  --site-domain="example.com" \
  --tool-prefix="mysite"

npx wrangler r2 object put my-site-mcp-data/search-index.json \
  --file=./search-index.json \
  --content-type=application/json

5. Deploy

bun run deploy

Your MCP server is now running. Connect an MCP client to start searching.

CI/CD: The included GitHub Actions workflow (.github/workflows/deploy.yml) is set to manual trigger only. To deploy via GitHub Actions, go to Actions → Deploy → Run workflow. To enable auto-deploy on push, edit the workflow and add push: branches: [main] to the triggers.


MCP Client Setup

Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "my-site": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp.example.com/sse"]
    }
  }
}

Claude Code

claude mcp add my-site --transport sse https://mcp.example.com/sse

Cursor

Add to your Cursor mcp.json:

{
  "mcpServers": {
    "my-site": {
      "url": "https://mcp.example.com/sse"
    }
  }
}

Other Clients

Use the mcp-remote package to connect via the /sse endpoint (SSE transport) or /mcp endpoint (streamable HTTP).

Available Tools

Tool Description
search_<prefix> Search by keywords. Returns titles, URLs, dates, and summaries.
get_article Retrieve full content by URL path (e.g., /about).
get_index_info Get page count, generation date, and tool names.

Threat Model

This MCP server is designed for public content only. Consider these security characteristics before deploying:

What's Exposed

Exposure Mechanism
All indexed content get_article retrieves full text by URL path
Content enumeration search_* with broad queries reveals page titles and summaries
Site metadata / endpoint and get_index_info reveal page count, domain, and tool names

Assumptions

  • Your content is already public. The indexed pages come from a public website. This server makes them AI-searchable, not newly public.
  • R2 is not the security boundary. While the R2 bucket is private, the Worker exposes its contents through MCP tools. Anyone with the endpoint URL can query all indexed content.
  • No authentication. The MCP server accepts connections from any client. There's no API key, OAuth, or access control.

Not Designed For

  • Private or internal documentation
  • Content requiring authentication or authorization
  • Partial access control (all-or-nothing visibility)

Recommendations

If you need access control, consider:

  • Cloudflare Access for authentication at the Worker level
  • A separate private deployment for internal content
  • Excluding sensitive pages from the search index

Adapters

An adapter generates the search index from your content. It scans your files, extracts frontmatter metadata, and outputs search-index.json.

Each adapter handles the specifics of a particular static site generator.

Generic (Markdown)

Works with any site that uses markdown files with YAML frontmatter.

node adapters/generic/generate-index.js \
  --content-dir=./content \
  --site-name="My Website" \
  --site-domain="example.com" \
  --tool-prefix="mysite" \
  --output=./search-index.json

See adapters/generic/README.md.

Astro

An Astro integration that generates the index at build time.

// astro.config.mjs
import { searchIndexIntegration } from './src/integrations/search-index.mjs';

export default defineConfig({
  integrations: [
    searchIndexIntegration({
      siteName: 'My Blog',
      siteDomain: 'blog.example.com',
      toolPrefix: 'myblog',
    }),
  ],
});

See adapters/astro/README.md.

Hugo

A Node.js script that handles both TOML and YAML frontmatter.

node adapters/hugo/generate-index.js \
  --content-dir=./content \
  --site-name="My Hugo Site" \
  --site-domain="example.com"

See adapters/hugo/README.md.

Writing Your Own Adapter

If your static site generator isn't listed, you can write an adapter. It just needs to output JSON in the v3.0 format.

Your adapter should:

  1. Find your content files (markdown, MDX, HTML, etc.)
  2. Extract metadata from frontmatter (title, date, tags)
  3. Extract body text for search
  4. Map file paths to URLs
  5. Write search-index.json

Here's a template:

import { writeFileSync } from 'fs';

const pages = [/* your content processing logic */];

const index = {
  version: "3.0",
  generated: new Date().toISOString(),
  site: {
    name: "My Site",
    domain: "example.com",
    description: "Brief description for the MCP tool",
    toolPrefix: "mysite",
  },
  pageCount: pages.length,
  pages: pages.map(page => ({
    url: page.url,           // Required: starts with /
    title: page.title,       // Required
    abstract: page.summary,  // Optional
    date: page.date,         // Optional: YYYY-MM-DD
    topics: page.tags,       // Optional: array
    body: page.content,      // Recommended for search quality
  })),
};

writeFileSync("search-index.json", JSON.stringify(index, null, 2));

Validate your index:

bun scripts/validate-index.ts ./search-index.json

Upload to R2:

npx wrangler r2 object put my-site-mcp-data/search-index.json \
  --file=./search-index.json \
  --content-type=application/json

Configuration

wrangler.jsonc

Field Description
name Worker name in Cloudflare dashboard
routes[].pattern Your custom domain
r2_buckets[].bucket_name R2 bucket name

For testing, you can use a workers.dev subdomain instead of a custom domain:

"workers_dev": true,
// Comment out "routes"

Index Format

The search index follows the v3.0 schema:

{
  "version": "3.0",
  "generated": "2025-01-15T12:00:00.000Z",
  "site": {
    "name": "My Website",
    "domain": "example.com",
    "description": "A site about interesting topics",
    "toolPrefix": "mysite"
  },
  "pageCount": 42,
  "pages": [
    {
      "url": "/about",
      "title": "About Us",
      "abstract": "Learn about our team.",
      "date": "2025-01-01",
      "topics": ["about", "team"],
      "body": "Full page content..."
    }
  ]
}
Field Required Description
version Yes Schema version ("3.0")
generated Yes ISO 8601 timestamp
site.name Yes Site name
site.domain Yes Domain without protocol
site.description No Shown in MCP tool description
site.toolPrefix No Tool name prefix (default: website)
pageCount Yes Number of pages
pages[].url Yes Path starting with /
pages[].title Yes Page title
pages[].body No Full text (recommended)

Development

bun run dev          # Local development server
bun run type-check   # TypeScript checking
bun run lint:fix     # Lint and fix
bun run format       # Format code
bun run deploy       # Deploy to Cloudflare

Note: This is a template repository. The bun run deploy command is for users who clone this template to deploy their own MCP server. To contribute to this template itself, use standard git workflows (git push).


Troubleshooting

"Search index not found in R2 bucket"

  1. Check the bucket exists: npx wrangler r2 bucket list
  2. Check the file was uploaded: npx wrangler r2 object list my-site-mcp-data
  3. Verify the bucket name in wrangler.jsonc matches

MCP client won't connect

  1. Use the correct endpoint: /sse for SSE, /mcp for HTTP
  2. Visit your worker URL in a browser — you should see JSON
  3. Make sure the URL includes https://

Search returns no results

  1. Validate your index: bun scripts/validate-index.ts ./search-index.json
  2. Check that pages have body content
  3. Try broader search terms

Wrong tool names

Tool names come from toolPrefix in your search index. Regenerate and re-upload the index with the correct value.

Local development

You need a local copy of the search index:

mkdir -p .wrangler/state/r2/my-site-mcp-data
cp search-index.json .wrangler/state/r2/my-site-mcp-data/search-index.json

Examples

Two sites using this approach:

REMnux Documentation

MCP server for REMnux, the Linux toolkit for malware analysis.

Repo: github.com/REMnux/remnux-docs-mcp-server

# Claude Code
claude mcp add remnux-docs --transport sse https://docs-mcp.remnux.org/sse

Lenny Zeltser's Website

MCP server for zeltser.com, covering malware analysis, incident response, and security leadership.

# Claude Code
claude mcp add zeltser-search --transport sse https://website-mcp.zeltser.com/sse

Author

Lenny Zeltser is a cybersecurity leader who builds security programs, tools, and educational content. He serves as CISO at Axonius, created the REMnux malware analysis toolkit, and authored SANS courses on reverse-engineering malware and cybersecurity writing. He holds an MBA from MIT Sloan and a Computer Science degree from the University of Pennsylvania. More at zeltser.com.

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选