Files
gh-jezweb-claude-skills-ski…/references/cloudflare-integration.md
2025-11-30 08:25:43 +08:00

9.0 KiB

Cloudflare Services Integration

Guide to integrating Cloudflare services (D1, KV, R2, Vectorize, Workers AI) with MCP servers.


D1 (SQL Database)

Setup

# Create database
wrangler d1 create my-database

# Add to wrangler.jsonc
{
  "d1_databases": [
    {
      "binding": "DB",
      "database_name": "my-database",
      "database_id": "YOUR_DATABASE_ID"
    }
  ]
}

MCP Tool Example

server.registerTool(
  'query-users',
  {
    description: 'Queries users from D1 database',
    inputSchema: z.object({
      email: z.string().optional(),
      limit: z.number().default(10)
    })
  },
  async ({ email, limit }, env) => {
    const query = email
      ? 'SELECT * FROM users WHERE email = ? LIMIT ?'
      : 'SELECT * FROM users LIMIT ?';

    const params = email ? [email, limit] : [limit];

    const result = await env.DB.prepare(query).bind(...params).all();

    return {
      content: [{
        type: 'text',
        text: JSON.stringify(result.results, null, 2)
      }]
    };
  }
);

Best Practices

  • Use parameterized queries (never string interpolation)
  • Add indexes for common queries
  • Limit result set size
  • Use transactions for multi-step operations

KV (Key-Value Store)

Setup

# Create namespace
wrangler kv namespace create CACHE

# Add to wrangler.jsonc
{
  "kv_namespaces": [
    {
      "binding": "CACHE",
      "id": "YOUR_NAMESPACE_ID"
    }
  ]
}

MCP Tool Example

server.registerTool(
  'cache-get',
  {
    description: 'Gets value from KV cache',
    inputSchema: z.object({ key: z.string() })
  },
  async ({ key }, env) => {
    const value = await env.CACHE.get(key);
    return {
      content: [{
        type: 'text',
        text: value || `Key "${key}" not found`
      }]
    };
  }
);

server.registerTool(
  'cache-set',
  {
    description: 'Sets value in KV cache',
    inputSchema: z.object({
      key: z.string(),
      value: z.string(),
      ttl: z.number().optional()
    })
  },
  async ({ key, value, ttl }, env) => {
    await env.CACHE.put(key, value, ttl ? { expirationTtl: ttl } : undefined);
    return {
      content: [{ type: 'text', text: `Cached "${key}"` }]
    };
  }
);

R2 (Object Storage)

Setup

# Create bucket
wrangler r2 bucket create my-bucket

# Add to wrangler.jsonc
{
  "r2_buckets": [
    {
      "binding": "BUCKET",
      "bucket_name": "my-bucket"
    }
  ]
}

MCP Tool Example

server.registerTool(
  'r2-upload',
  {
    description: 'Uploads file to R2',
    inputSchema: z.object({
      key: z.string(),
      content: z.string(),
      contentType: z.string().optional()
    })
  },
  async ({ key, content, contentType }, env) => {
    await env.BUCKET.put(key, content, {
      httpMetadata: {
        contentType: contentType || 'text/plain'
      }
    });

    return {
      content: [{ type: 'text', text: `Uploaded to ${key}` }]
    };
  }
);

server.registerTool(
  'r2-download',
  {
    description: 'Downloads file from R2',
    inputSchema: z.object({ key: z.string() })
  },
  async ({ key }, env) => {
    const object = await env.BUCKET.get(key);

    if (!object) {
      return {
        content: [{ type: 'text', text: `File "${key}" not found` }],
        isError: true
      };
    }

    const text = await object.text();
    return {
      content: [{
        type: 'text',
        text: `File: ${key}\nSize: ${object.size} bytes\n\n${text}`
      }]
    };
  }
);

Vectorize (Vector Database)

Setup

# Create index
wrangler vectorize create my-index --dimensions=768 --metric=cosine

# Add to wrangler.jsonc
{
  "vectorize": [
    {
      "binding": "VECTORIZE",
      "index_name": "my-index"
    }
  ]
}

MCP Tool Example

server.registerTool(
  'semantic-search',
  {
    description: 'Searches documents semantically',
    inputSchema: z.object({
      query: z.string(),
      topK: z.number().default(5)
    })
  },
  async ({ query, topK }, env) => {
    // Generate embedding (using Workers AI)
    const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
      text: query
    });

    // Search vector index
    const results = await env.VECTORIZE.query(embedding.data[0], {
      topK,
      returnMetadata: true
    });

    return {
      content: [{
        type: 'text',
        text: JSON.stringify(results.matches, null, 2)
      }]
    };
  }
);

server.registerTool(
  'index-document',
  {
    description: 'Indexes document for semantic search',
    inputSchema: z.object({
      id: z.string(),
      text: z.string(),
      metadata: z.record(z.any()).optional()
    })
  },
  async ({ id, text, metadata }, env) => {
    // Generate embedding
    const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
      text
    });

    // Insert into index
    await env.VECTORIZE.insert([{
      id,
      values: embedding.data[0],
      metadata: metadata || {}
    }]);

    return {
      content: [{ type: 'text', text: `Indexed document "${id}"` }]
    };
  }
);

Workers AI

Setup

{
  "ai": {
    "binding": "AI"
  }
}

MCP Tool Example

server.registerTool(
  'generate-text',
  {
    description: 'Generates text using LLM',
    inputSchema: z.object({
      prompt: z.string(),
      model: z.string().default('@cf/meta/llama-3-8b-instruct')
    })
  },
  async ({ prompt, model }, env) => {
    const response = await env.AI.run(model, {
      prompt
    });

    return {
      content: [{ type: 'text', text: response.response }]
    };
  }
);

server.registerTool(
  'generate-image',
  {
    description: 'Generates image from text',
    inputSchema: z.object({
      prompt: z.string()
    })
  },
  async ({ prompt }, env) => {
    const response = await env.AI.run(
      '@cf/stabilityai/stable-diffusion-xl-base-1.0',
      { prompt }
    );

    // Save to R2
    const imageKey = `generated/${Date.now()}.png`;
    await env.BUCKET.put(imageKey, response);

    return {
      content: [{
        type: 'text',
        text: `Image generated: ${imageKey}`
      }]
    };
  }
);

Queues

Setup

# Create queue
wrangler queues create my-queue

# Add to wrangler.jsonc
{
  "queues": {
    "producers": [
      {
        "binding": "MY_QUEUE",
        "queue": "my-queue"
      }
    ],
    "consumers": [
      {
        "queue": "my-queue",
        "max_batch_size": 10,
        "max_batch_timeout": 30
      }
    ]
  }
}

MCP Tool Example

server.registerTool(
  'enqueue-task',
  {
    description: 'Enqueues background task',
    inputSchema: z.object({
      task: z.string(),
      data: z.record(z.any())
    })
  },
  async ({ task, data }, env) => {
    await env.MY_QUEUE.send({
      task,
      data,
      timestamp: Date.now()
    });

    return {
      content: [{ type: 'text', text: `Task "${task}" enqueued` }]
    };
  }
);

Consumer handler:

export default {
  async fetch(request, env) {
    return app.fetch(request, env);
  },

  async queue(batch, env) {
    for (const message of batch.messages) {
      console.log('Processing:', message.body);
      // Process task...
    }
  }
};

Analytics Engine

Setup

{
  "analytics_engine_datasets": [
    {
      "binding": "ANALYTICS"
    }
  ]
}

MCP Tool Example

server.registerTool(
  'log-event',
  {
    description: 'Logs analytics event',
    inputSchema: z.object({
      event: z.string(),
      metadata: z.record(z.any()).optional()
    })
  },
  async ({ event, metadata }, env) => {
    env.ANALYTICS.writeDataPoint({
      blobs: [event],
      doubles: [Date.now()],
      indexes: [event]
    });

    return {
      content: [{ type: 'text', text: `Event "${event}" logged` }]
    };
  }
);

Combining Services

Example: RAG System

server.registerTool(
  'ask-question',
  {
    description: 'Answers question using RAG',
    inputSchema: z.object({ question: z.string() })
  },
  async ({ question }, env) => {
    // 1. Generate embedding
    const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
      text: question
    });

    // 2. Search vector index
    const results = await env.VECTORIZE.query(embedding.data[0], {
      topK: 3,
      returnMetadata: true
    });

    // 3. Get context from D1
    const context = await env.DB
      .prepare('SELECT content FROM documents WHERE id IN (?, ?, ?)')
      .bind(...results.matches.map(m => m.id))
      .all();

    // 4. Generate answer with LLM
    const prompt = `Context: ${JSON.stringify(context.results)}\n\nQuestion: ${question}`;
    const answer = await env.AI.run('@cf/meta/llama-3-8b-instruct', {
      prompt
    });

    // 5. Cache result
    await env.CACHE.put(`answer:${question}`, answer.response, {
      expirationTtl: 3600
    });

    return {
      content: [{ type: 'text', text: answer.response }]
    };
  }
);

Last Updated: 2025-10-28