slide.md•26.7 kB
---
theme: default
class: text-center
highlighter: shiki
lineNumbers: false
colorSchema: auto
info: |
## From the Edge and Back
Turn Any REST API into an MCP Server
drawings:
persist: false
transition: fade
title: From the Edge and Back - REST API to MCP Server
mdc: true
---
# From the Edge and Back
## Turn Any REST API into an MCP Server
Deploy AI-ready APIs to Cloudflare Workers
<div class="pt-12">
<span @click="$slidev.nav.next" class="px-2 py-1 rounded cursor-pointer" hover="bg-white bg-opacity-10">
Press Space for next page <carbon:arrow-right class="inline"/>
</span>
</div>
---
layout: center
---
<img src="/aidd.png" class="mx-auto max-h-[500px]" />
---
layout: two-cols
---
# What We're Building
A real-world example: Products API + MCP Server
::right::
## What You'll Learn
- 🔧 **MCP Protocol** - Model Context Protocol basics
- 🌐 **REST Translation** - Converting REST to MCP tools
- ⚡ **Edge Deployment** - Cloudflare Workers at scale
- 🔄 **Dual Interface** - One API, two protocols
- 📦 **Real Code** - Actual working implementation
<br>
### Code-Heavy Session
Working code you can deploy today
---
layout: section
---
# The Problem
Why wrap REST APIs with MCP?
---
# AI Agents Need Context
<div class="grid grid-cols-2 gap-4">
<div>
## Traditional Approach
```typescript
// AI has to know your API
const response = await fetch(
'https://api.example.com/products?category=Electronics'
);
// AI has to parse response
const data = await response.json();
// AI has to handle errors
if (!response.ok) {
// what now?
}
```
- Manual integration for each API
- No standardization
- Limited discoverability
- Error handling varies
</div>
<div>
## MCP Approach
```typescript
// AI discovers available tools
const tools = await mcp.listTools();
// Returns: list_products, get_product, etc.
// AI calls tool with schema validation
const result = await mcp.callTool(
'list_products',
{ category: 'Electronics' }
);
// Structured error handling built-in
```
- Standard protocol
- Self-describing
- Type-safe
- Consistent errors
</div>
</div>
---
layout: section
---
# Our Example: Products API
Let's see what we're building
---
# The REST API
A simple products CRUD API
```typescript
// src/api.ts - In-memory product store
interface Product {
id: string;
name: string;
price: number;
description: string;
category: string;
inStock: boolean;
}
const products: Product[] = [
{
id: '1',
name: 'Wireless Headphones',
price: 99.99,
description: 'High-quality wireless headphones with noise cancellation',
category: 'Electronics',
inStock: true,
},
// ... more products
];
```
---
# REST Endpoints
Standard CRUD operations
```typescript
// GET /api/products - List all (with filtering)
apiRoutes.get('/products', (c) => {
const category = c.req.query('category');
const inStock = c.req.query('inStock');
// ... filter logic
});
// GET /api/products/:id - Get single product
apiRoutes.get('/products/:id', (c) => { /* ... */ });
// POST /api/products - Create new product
apiRoutes.post('/products', async (c) => { /* ... */ });
// PUT /api/products/:id - Update product
apiRoutes.put('/products/:id', async (c) => { /* ... */ });
// DELETE /api/products/:id - Delete product
apiRoutes.delete('/products/:id', (c) => { /* ... */ });
```
---
# Testing the REST API
```bash
# List all products
curl http://localhost:8787/api/products
# Filter by category
curl http://localhost:8787/api/products?category=Electronics
# Get single product
curl http://localhost:8787/api/products/1
# Create a product
curl -X POST http://localhost:8787/api/products \
-H "Content-Type: application/json" \
-d '{
"name": "Laptop",
"price": 999,
"description": "Powerful laptop",
"category": "Electronics"
}'
```
---
layout: section
---
# Adding MCP
Wrapping REST with Model Context Protocol
---
# What is MCP?
Model Context Protocol - A standard for AI-API communication
```mermaid
graph LR
A[AI Agent<br/>Claude] -->|MCP Protocol| B[MCP Server<br/>Your Worker]
B -->|Internal Call| C[REST API<br/>Products]
C -->|Response| B
B -->|Structured Result| A
style B fill:#f96,stroke:#333,stroke-width:4px
style A fill:#9cf,stroke:#333,stroke-width:2px
style C fill:#fc9,stroke:#333,stroke-width:2px
```
**Key Benefit**: AI agents can discover and use your API without custom integration
---
# MCP Tool Definitions
Each REST endpoint becomes a tool
```typescript {all|3-6|7-20|all}
// src/mcp.ts
const tools = [
{
name: 'list_products',
description: 'List all products with optional filtering by category or stock status',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Filter by product category (e.g., Electronics, Home, Sports)',
},
inStock: {
type: 'boolean',
description: 'Filter by stock availability',
},
},
},
},
// ... more tools
];
```
---
# More MCP Tools
```typescript
{
name: 'get_product',
description: 'Get details of a specific product by ID',
inputSchema: {
type: 'object',
properties: {
id: { type: 'string', description: 'The product ID' }
},
required: ['id']
}
},
{
name: 'create_product',
description: 'Create a new product',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Product name' },
price: { type: 'number', description: 'Product price' },
description: { type: 'string' },
category: { type: 'string' },
inStock: { type: 'boolean' }
},
required: ['name', 'price', 'description', 'category']
}
}
```
---
# Executing MCP Tools
Tools call the REST API internally
```typescript {all|1-5|7-17|19-25|all}
async function executeTool(name: string, args: any, baseUrl: string) {
let response: Response;
switch (name) {
case 'list_products': {
const params = new URLSearchParams();
if (args?.category) params.append('category', String(args.category));
if (args?.inStock !== undefined) params.append('inStock', String(args.inStock));
const url = `${baseUrl}/api/products${
params.toString() ? '?' + params.toString() : ''
}`;
response = await fetch(url);
break;
}
case 'get_product': {
response = await fetch(`${baseUrl}/api/products/${args?.id}`);
break;
}
case 'create_product': {
response = await fetch(`${baseUrl}/api/products`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(args),
});
break;
}
// ... more cases
}
return await response.json();
}
```
---
# MCP Request Handler
Handling MCP protocol requests
```typescript {all|1-8|10-15|17-30|all}
export async function mcpHandler(c: Context) {
const body = await c.req.json();
const requestId = body.id !== undefined ? body.id : 1;
const url = new URL(c.req.url);
const baseUrl = `${url.protocol}//${url.host}`;
// Handle tools/list request
if (body.method === 'tools/list') {
return c.json({ jsonrpc: '2.0', id: requestId, result: { tools } });
}
// Handle tools/call request
if (body.method === 'tools/call') {
const { name, arguments: args } = body.params;
try {
const result = await executeTool(name, args, baseUrl);
return c.json({
jsonrpc: '2.0',
id: requestId,
result: {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
}
});
} catch (error) {
return c.json({
jsonrpc: '2.0',
id: requestId,
error: { code: -32603, message: error.message }
});
}
}
// Handle initialize (MCP handshake)
if (body.method === 'initialize') {
const response = {
jsonrpc: '2.0',
id: requestId,
result: {
protocolVersion: '2024-11-05',
capabilities: { tools: {} },
serverInfo: { name: 'cf-mcp-server', version: '1.0.0' }
}
};
return c.json(response, 200, { 'Mcp-Session-Id': crypto.randomUUID() });
}
}
```
---
# Main Application
Bringing it all together
```typescript {all|1-4|6-8|10-12|14-16|18-29|all}
// src/index.ts
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { apiRoutes } from './api';
import { mcpHandler } from './mcp';
const app = new Hono();
app.use('/*', cors());
// REST API routes
app.route('/api', apiRoutes);
// MCP endpoint
app.post('/mcp', mcpHandler);
// Root endpoint
app.get('/', (c) => {
return c.json({
message: 'CF-MCP: REST API + MCP Server',
endpoints: {
rest: {
products: 'GET /api/products',
product: 'GET /api/products/:id',
create: 'POST /api/products',
update: 'PUT /api/products/:id',
delete: 'DELETE /api/products/:id',
},
mcp: 'POST /mcp (Model Context Protocol endpoint)',
},
});
});
export default app;
```
---
# Project Structure
Clean separation of concerns
```
cf-mcp/
├── src/
│ ├── index.ts # Main Hono app + routing
│ ├── api.ts # REST API endpoints + data
│ └── mcp.ts # MCP protocol handler
├── examples/
│ ├── rest-api.sh # REST API test script
│ └── mcp-api.sh # MCP test script
├── wrangler.toml # Cloudflare config
├── package.json
└── tsconfig.json
```
**Key Insight**: REST and MCP share the same business logic
---
layout: section
---
# Testing the MCP Server
---
# MCP Protocol Tests
```bash
# List available tools
curl -X POST http://localhost:8787/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/list",
"params": {}
}'
# Response:
{
"tools": [
{
"name": "list_products",
"description": "List all products with optional filtering...",
"inputSchema": { ... }
},
...
]
}
```
---
# Calling MCP Tools
```bash
# Call list_products tool
curl -X POST http://localhost:8787/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "list_products",
"arguments": {
"category": "Electronics"
}
}
}'
# Response:
{
"content": [
{
"type": "text",
"text": "{\n \"success\": true,\n \"data\": [...],\n \"count\": 1\n}"
}
]
}
```
---
# Using with Claude Code
Configure Claude Code to use your MCP server
```bash
# Add MCP server (local development)
claude mcp add --transport http products-api http://localhost:8788/mcp
# Or for production deployment
claude mcp add --transport http products-api https://cf-mcp.your-subdomain.workers.dev/mcp
# Verify it's connected
claude mcp list
```
Then in Claude:
- "List all products in the Electronics category"
- "Create a new product called Gaming Mouse for $49.99"
- "What products are out of stock?"
Claude will use your MCP tools automatically!
---
layout: section
---
# Key Patterns
REST to MCP Translation
---
# Pattern 1: CRUD → Tools
Each REST operation becomes a tool
<div class="grid grid-cols-2 gap-4">
<div>
## REST
```http
GET /api/products
GET /api/products/:id
POST /api/products
PUT /api/products/:id
DELETE /api/products/:id
```
</div>
<div>
## MCP Tools
```typescript
- list_products
- get_product
- create_product
- update_product
- delete_product
```
</div>
</div>
**Pattern**: Use `verb_noun` naming for tools
---
# Pattern 2: Query Params → Arguments
REST query parameters become tool arguments
<div class="grid grid-cols-2 gap-4">
<div>
## REST
```http
GET /api/products?category=Electronics&inStock=true
```
Query parameters:
- `category` (string)
- `inStock` (boolean)
</div>
<div>
## MCP Tool
```typescript
{
name: 'list_products',
inputSchema: {
properties: {
category: {
type: 'string'
},
inStock: {
type: 'boolean'
}
}
}
}
```
</div>
</div>
**Pattern**: Map query params to typed schema properties
---
# Pattern 3: Path Params → Required Args
URL path parameters become required arguments
<div class="grid grid-cols-2 gap-4">
<div>
## REST
```http
GET /api/products/:id
```
Path parameter:
- `id` (required)
</div>
<div>
## MCP Tool
```typescript
{
name: 'get_product',
inputSchema: {
properties: {
id: {
type: 'string',
description: 'Product ID'
}
},
required: ['id']
}
}
```
</div>
</div>
**Pattern**: Path params always go in `required` array
---
# Pattern 4: Request Body → Arguments
POST/PUT body becomes tool arguments
<div class="grid grid-cols-2 gap-4">
<div>
## REST
```http
POST /api/products
Content-Type: application/json
{
"name": "Laptop",
"price": 999,
"description": "...",
"category": "Electronics"
}
```
</div>
<div>
## MCP Tool
```typescript
{
name: 'create_product',
inputSchema: {
properties: {
name: { type: 'string' },
price: { type: 'number' },
description: { type: 'string' },
category: { type: 'string' }
},
required: ['name', 'price',
'description', 'category']
}
}
```
</div>
</div>
**Pattern**: Flatten JSON body into schema properties
---
# Pattern 5: Response Format
Wrap REST responses in MCP content format
```typescript
// REST API returns:
{
"success": true,
"data": [{ ... }],
"count": 3
}
// MCP wraps it:
{
"content": [
{
"type": "text",
"text": "{\"success\":true,\"data\":[{...}],\"count\":3}"
}
]
}
```
**Pattern**: Always return `content` array with `type` and `text`
---
# Pattern 6: Error Handling
Consistent JSON-RPC error responses
```typescript
try {
const result = await executeTool(name, args, baseUrl);
return c.json({
jsonrpc: '2.0',
id: requestId,
result: {
content: [{ type: 'text', text: JSON.stringify(result, null, 2) }]
}
});
} catch (error) {
return c.json({
jsonrpc: '2.0',
id: requestId,
error: {
code: -32603, // Internal error
message: error.message
}
});
}
```
**Pattern**: Use JSON-RPC error format with proper error codes
---
layout: section
---
# Deployment
Getting to the edge
---
# Cloudflare Workers Configuration
```toml
# wrangler.toml
name = "cf-mcp"
main = "src/index.ts"
compatibility_date = "2024-11-01"
[observability]
enabled = true
```
Simple and minimal - that's all you need!
---
# Local Development
```bash
# Install dependencies
npm install
# Start dev server
npm run dev
# Server runs at http://localhost:8787
```
Test both interfaces:
- REST: `http://localhost:8787/api/products`
- MCP: `http://localhost:8787/mcp`
---
# Deploy to Cloudflare
```bash
# Deploy to production
npm run deploy
# Output:
# Deployed to: https://cf-mcp.your-subdomain.workers.dev
```
Your API is now:
- ✅ Globally distributed on 300+ edge locations
- ✅ Automatically scaled
- ✅ Low latency worldwide
- ✅ Serving both REST and MCP
---
# Testing Deployed MCP
```bash
# Replace with your deployed URL
WORKER_URL="https://cf-mcp.your-subdomain.workers.dev"
# Test MCP endpoint
curl -X POST $WORKER_URL/mcp \
-H "Content-Type: application/json" \
-d '{
"method": "tools/call",
"params": {
"name": "list_products",
"arguments": {}
}
}'
```
Update Claude Desktop config with your production URL!
---
layout: section
---
# Architecture Deep Dive
How it all works together
---
# Request Flow - REST
```mermaid
sequenceDiagram
participant Client
participant Worker
participant API
Client->>Worker: GET /api/products?category=Electronics
Worker->>API: Route to apiRoutes
API->>API: Filter products
API->>Worker: JSON response
Worker->>Client: { success: true, data: [...] }
```
Direct REST request → Direct REST response
---
# Request Flow - MCP
```mermaid
sequenceDiagram
participant Claude
participant Worker
participant MCP
participant API
Claude->>Worker: POST /mcp (tools/call)
Worker->>MCP: Route to mcpHandler
MCP->>MCP: Parse tool name & args
MCP->>API: Internal fetch to /api/products
API->>MCP: REST response
MCP->>MCP: Wrap in MCP format
MCP->>Worker: MCP response
Worker->>Claude: { content: [{type: text, ...}] }
```
MCP wraps the REST API internally
---
# Architecture Diagram
```mermaid
graph TB
A[Client/Browser] -->|HTTP REST| B[Cloudflare Worker]
C[Claude Desktop] -->|MCP Protocol| B
B --> D{Router}
D -->|/api/*| E[REST API Handler]
D -->|/mcp| F[MCP Handler]
E --> G[(Products Data)]
F -->|Internal Fetch| E
style B fill:#f96,stroke:#333,stroke-width:4px
style E fill:#9cf,stroke:#333,stroke-width:2px
style F fill:#fc9,stroke:#333,stroke-width:2px
style G fill:#9f9,stroke:#333,stroke-width:2px
```
**One codebase, two interfaces, shared data**
---
layout: section
---
# Real-World Demo
Let's see it in action
---
# Demo Checklist
What we'll show:
1. **Start the local server**
- `npm run dev`
2. **Test REST API**
- List products
- Filter by category
- Create a product
3. **Test MCP endpoint**
- List available tools
- Call a tool
- See structured response
4. **Use with Claude Desktop**
- Configure MCP server
- Ask Claude to list products
- Ask Claude to create a product
5. **Deploy to Cloudflare**
- `npm run deploy`
- Test production endpoint
---
layout: section
---
# Advanced Topics
Going beyond the basics
---
# Adding Authentication
Secure your MCP server
```typescript
// Simple API key authentication
export async function mcpHandler(c: Context) {
const apiKey = c.req.header('X-API-Key');
if (apiKey !== c.env.API_KEY) {
return c.json({ error: 'Unauthorized' }, 401);
}
// ... rest of handler
}
```
Store API key in Cloudflare secrets:
```bash
wrangler secret put API_KEY
```
---
# Adding Persistent Storage
Use Cloudflare D1 (SQLite)
```typescript
// wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "products-db"
database_id = "your-database-id"
// src/api.ts
apiRoutes.get('/products', async (c) => {
const { results } = await c.env.DB.prepare(
'SELECT * FROM products'
).all();
return c.json({
success: true,
data: results
});
});
```
---
# Adding Rate Limiting
Protect your API from abuse
```typescript
// Using Cloudflare KV for rate limiting
async function checkRateLimit(c: Context, key: string) {
const count = await c.env.RATE_LIMIT.get(key);
if (count && parseInt(count) > 100) {
return c.json({ error: 'Rate limit exceeded' }, 429);
}
await c.env.RATE_LIMIT.put(
key,
String((parseInt(count || '0') + 1)),
{ expirationTtl: 60 } // 1 minute window
);
}
```
---
# Adding Caching
Speed up repeated requests
```typescript
// Cache product list for 5 minutes
apiRoutes.get('/products', async (c) => {
const cache = caches.default;
const cacheKey = new Request(c.req.url);
let response = await cache.match(cacheKey);
if (!response) {
const data = getProducts();
response = new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Cache-Control': 'max-age=300'
}
});
await cache.put(cacheKey, response.clone());
}
return response;
});
```
---
layout: section
---
# Best Practices
Lessons learned
---
# Design Principles
<div class="grid grid-cols-2 gap-4">
<div>
## REST API
- Clear resource naming
- RESTful conventions
- Consistent responses
- Proper HTTP status codes
- Filter with query params
- Paginate large results
</div>
<div>
## MCP Tools
- Descriptive tool names
- Rich input schemas
- Clear descriptions
- Type validation
- Proper error messages
- Document all fields
</div>
</div>
**Key**: Make both interfaces intuitive
---
# Tool Naming Conventions
Good vs bad tool names
✅ **Good Names**
- `list_products` - Clear verb + noun
- `get_product` - Specific action
- `create_product` - Obvious intent
- `update_product_stock` - Descriptive
❌ **Bad Names**
- `products` - Missing verb
- `fetch` - Too generic
- `doStuff` - Meaningless
- `api_call` - Not descriptive
**Pattern**: Always use `verb_noun` or `verb_noun_qualifier`
---
# Schema Best Practices
Write helpful schemas
```typescript
// ✅ Good schema
{
name: 'list_products',
description: 'List all products with optional filtering by category or stock status',
inputSchema: {
type: 'object',
properties: {
category: {
type: 'string',
description: 'Filter by product category (e.g., Electronics, Home, Sports)',
},
inStock: {
type: 'boolean',
description: 'Filter by stock availability (true = in stock, false = out of stock)',
}
}
}
}
```
**Tip**: AI agents read descriptions - make them helpful!
---
# Error Handling Best Practices
```typescript
// ✅ Good error handling
try {
const result = await executeTool(name, args, baseUrl);
return c.json({
content: [{
type: 'text',
text: JSON.stringify(result, null, 2)
}]
});
} catch (error) {
console.error('Tool execution failed:', error);
return c.json({
content: [{
type: 'text',
text: error instanceof Error
? `Error: ${error.message}`
: 'Unknown error occurred'
}],
isError: true
});
}
```
**Always**: Log errors and return user-friendly messages
---
# Testing Strategy
Test both interfaces
```bash
# Create test scripts
# examples/rest-api.sh
curl http://localhost:8787/api/products
curl http://localhost:8787/api/products/1
# ... more tests
# examples/mcp-api.sh
curl -X POST http://localhost:8787/mcp \
-d '{"method":"tools/list"}'
curl -X POST http://localhost:8787/mcp \
-d '{"method":"tools/call","params":{"name":"list_products"}}'
# ... more tests
```
**Run tests**: Before every deploy!
---
layout: section
---
# Common Challenges
And how to solve them
---
# Challenge: CORS Issues
Problem: Browser requests blocked
```typescript
// ❌ Without CORS
const app = new Hono();
app.post('/mcp', mcpHandler);
// ✅ With CORS
import { cors } from 'hono/cors';
const app = new Hono();
app.use('/*', cors()); // Enable for all routes
app.post('/mcp', mcpHandler);
```
**Solution**: Always enable CORS for edge APIs
---
# Challenge: Large Responses
Problem: MCP responses too big
```typescript
// ❌ Returning everything
const products = await getAllProducts(); // 10,000 items!
return c.json({ content: [{ type: 'text', text: JSON.stringify(products) }] });
// ✅ Paginate results
const limit = args.limit || 50;
const offset = args.offset || 0;
const products = await getProducts(limit, offset);
return c.json({
content: [{
type: 'text',
text: JSON.stringify({
data: products,
pagination: { limit, offset, total }
})
}]
});
```
**Solution**: Always paginate large datasets
---
# Challenge: Type Safety
Problem: Runtime errors from invalid data
```typescript
// ❌ No validation
async function executeTool(name: string, args: any) {
const response = await fetch(`/api/products/${args.id}`);
// What if args.id is undefined?
}
// ✅ With validation
async function executeTool(name: string, args: any) {
if (!args?.id) {
throw new Error('Product ID is required');
}
const response = await fetch(`/api/products/${args.id}`);
}
```
**Solution**: Validate inputs before using them
---
# Challenge: Testing MCP Locally
Problem: Hard to test MCP without Claude
```typescript
// Create a simple test client
async function testMCP() {
// Test tools/list
const listResponse = await fetch('http://localhost:8787/mcp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ method: 'tools/list', params: {} })
});
console.log('Tools:', await listResponse.json());
// Test tools/call
const callResponse = await fetch('http://localhost:8787/mcp', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
method: 'tools/call',
params: { name: 'list_products', arguments: {} }
})
});
console.log('Result:', await callResponse.json());
}
```
**Solution**: Write simple test scripts
---
layout: section
---
# Next Steps
Where to go from here
---
# Extend This Project
Ideas for enhancement:
1. **Add More Entities**
- Users, orders, categories
- Relationships between entities
2. **Add Search**
- Full-text search tool
- Complex filtering
3. **Add Analytics**
- Track tool usage
- Monitor performance
4. **Add Webhooks**
- Notify on events
- Real-time updates
5. **Add File Uploads**
- Product images
- Use Cloudflare R2
---
# Wrap Other APIs
Apply this pattern to:
- **GitHub API** → MCP tools for repos, issues, PRs
- **Stripe API** → MCP tools for payments, customers
- **SendGrid API** → MCP tools for emails
- **Weather API** → MCP tools for forecasts
- **Your internal APIs** → Make them AI-ready
**The pattern works for any REST API!**
---
# Resources
<div class="grid grid-cols-2 gap-4">
<div>
## This Project
- [GitHub: cf-mcp](https://github.com/heygarrison/cf-mcp)
- [Documentation](https://github.com/heygarrison/cf-mcp#readme)
- [Examples](https://github.com/heygarrison/cf-mcp/tree/main/examples)
## MCP Resources
- [MCP Specification](https://modelcontextprotocol.io/)
- [MCP Servers](https://github.com/modelcontextprotocol/servers)
- [Claude Desktop](https://claude.ai/desktop)
</div>
<div>
## Cloudflare Resources
- [Workers Docs](https://developers.cloudflare.com/workers/)
- [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/)
- [Hono Framework](https://hono.dev)
- [D1 Database](https://developers.cloudflare.com/d1/)
## Community
- [Cloudflare Discord](https://discord.gg/cloudflare)
- [Hono Discord](https://discord.gg/hono)
</div>
</div>
---
layout: center
class: text-center
---
# Questions?
Let's discuss your use cases
<div class="pt-12">
<span class="text-6xl">🚀</span>
</div>
---
layout: center
---
<img src="/aidd.png" class="mx-auto max-h-[500px]" />
---
layout: center
class: text-center
---
# Thank You!
## Get Started Today
```bash
git clone https://github.com/heygarrison/cf-mcp
cd cf-mcp
npm install
npm run dev
```
<div class="pt-8">
Deploy your first MCP server in minutes
</div>
<div class="pt-8 text-sm opacity-75">
From the Edge and Back: Turn Any REST API into an MCP Server
</div>