Files
gh-hirefrank-hirefrank-mark…/agents/cloudflare/binding-context-analyzer.md
2025-11-29 18:45:50 +08:00

9.4 KiB

name, model, color
name model color
binding-context-analyzer haiku blue

Binding Context Analyzer

Purpose

Parses wrangler.toml to understand configured Cloudflare bindings and ensures code uses them correctly.

What Are Bindings?

Bindings connect your Worker to Cloudflare resources like KV namespaces, R2 buckets, Durable Objects, and D1 databases. They're configured in wrangler.toml and accessed via the env parameter.

This agent can use the Cloudflare MCP server for real-time binding information when available.

MCP-First Approach

If Cloudflare MCP server is available:

  1. Query real account state via MCP tools
  2. Get structured binding data with actual IDs, namespaces, and metadata
  3. Cross-reference with wrangler.toml to detect mismatches
  4. Warn if config references non-existent resources

If MCP server is not available:

  1. Fall back to manual wrangler.toml parsing (documented below)
  2. Parse config file using Glob and Read tools
  3. Generate TypeScript interface from config alone

MCP Tools Available

When the Cloudflare MCP server is configured, these tools become available:

// Get all configured bindings for project
cloudflare-bindings.getProjectBindings()  {
  kv: [{ binding: "USER_DATA", id: "abc123", title: "prod-users" }],
  r2: [{ binding: "UPLOADS", id: "def456", bucket: "my-uploads" }],
  d1: [{ binding: "DB", id: "ghi789", name: "production-db" }],
  do: [{ binding: "COUNTER", class: "Counter", script: "my-worker" }],
  vectorize: [{ binding: "VECTOR_INDEX", id: "jkl012", name: "embeddings" }],
  ai: { binding: "AI" }
}

// List all KV namespaces in account
cloudflare-bindings.listKV()  [
  { id: "abc123", title: "prod-users" },
  { id: "def456", title: "cache-data" }
]

// List all R2 buckets in account
cloudflare-bindings.listR2()  [
  { id: "def456", name: "my-uploads" },
  { id: "xyz789", name: "backups" }
]

// List all D1 databases in account
cloudflare-bindings.listD1()  [
  { id: "ghi789", name: "production-db" },
  { id: "mno345", name: "analytics-db" }
]

Benefits of Using MCP

Real account state - Know what resources actually exist, not just what's configured Detect mismatches - Find bindings in wrangler.toml that reference non-existent resources Suggest reuse - If user wants to add KV namespace, check if one already exists Accurate IDs - Get actual resource IDs without manual lookup Namespace discovery - Find existing resources that could be reused

Workflow with MCP

1. Check if Cloudflare MCP server is available
2. If YES:
   a. Call cloudflare-bindings.getProjectBindings()
   b. Parse wrangler.toml for comparison
   c. Cross-reference: warn if config differs from account
   d. Generate Env interface from real account state
3. If NO:
   a. Fall back to manual wrangler.toml parsing (see below)
   b. Generate Env interface from config file

Example MCP-Enhanced Analysis

// Step 1: Get real bindings from account (via MCP)
const accountBindings = await cloudflare-bindings.getProjectBindings();
// Returns: { kv: [{ binding: "USER_DATA", id: "abc123" }], ... }

// Step 2: Parse wrangler.toml
const wranglerConfig = parseWranglerToml();
// Returns: { kv: [{ binding: "USER_DATA", id: "abc123" }, { binding: "CACHE", id: "old456" }] }

// Step 3: Detect mismatches
const configOnlyBindings = wranglerConfig.kv.filter(
  configKV => !accountBindings.kv.some(accountKV => accountKV.binding === configKV.binding)
);
// Finds: CACHE binding exists in config but not in account

// Step 4: Warn user
console.warn(`⚠️ wrangler.toml references KV namespace 'CACHE' (id: old456) that doesn't exist in account`);
console.log(`💡 Available KV namespaces: ${accountBindings.kv.map(kv => kv.title).join(', ')}`);

Analysis Steps

1. Locate wrangler.toml

# Use Glob tool to find wrangler.toml
pattern: "**/wrangler.toml"

2. Parse Binding Types

Extract all bindings from the configuration:

KV Namespaces:

[[kv_namespaces]]
binding = "USER_DATA"
id = "abc123"

[[kv_namespaces]]
binding = "CACHE"
id = "def456"

R2 Buckets:

[[r2_buckets]]
binding = "UPLOADS"
bucket_name = "my-uploads"

Durable Objects:

[[durable_objects.bindings]]
name = "COUNTER"
class_name = "Counter"
script_name = "my-worker"

D1 Databases:

[[d1_databases]]
binding = "DB"
database_id = "xxx"
database_name = "production-db"

Service Bindings:

[[services]]
binding = "AUTH_SERVICE"
service = "auth-worker"

Queues:

[[queues.producers]]
binding = "TASK_QUEUE"
queue = "tasks"

Vectorize:

[[vectorize]]
binding = "VECTOR_INDEX"
index_name = "embeddings"

AI:

[ai]
binding = "AI"

3. Generate TypeScript Env Interface

Based on bindings found, suggest this interface:

interface Env {
  // KV Namespaces
  USER_DATA: KVNamespace;
  CACHE: KVNamespace;

  // R2 Buckets
  UPLOADS: R2Bucket;

  // Durable Objects
  COUNTER: DurableObjectNamespace;

  // D1 Databases
  DB: D1Database;

  // Service Bindings
  AUTH_SERVICE: Fetcher;

  // Queues
  TASK_QUEUE: Queue;

  // Vectorize
  VECTOR_INDEX: VectorizeIndex;

  // AI
  AI: Ai;

  // Environment Variables
  API_KEY?: string;
  ENVIRONMENT?: string;
}

4. Verify Code Uses Bindings Correctly

Check that code:

  • Accesses bindings via env parameter
  • Uses correct TypeScript types
  • Doesn't hardcode binding names incorrectly
  • Handles optional bindings appropriately

Common Issues

Issue 1: Hardcoded Binding Names

Wrong:

const data = await KV.get(key);  // Where does KV come from?

Correct:

const data = await env.USER_DATA.get(key);

Issue 2: Missing TypeScript Types

Wrong:

async fetch(request: Request, env: any) {
  // env is 'any' - no type safety
}

Correct:

interface Env {
  USER_DATA: KVNamespace;
}

async fetch(request: Request, env: Env) {
  // Type-safe access
}

Issue 3: Undefined Binding References

Problem:

// Code uses env.CACHE
// But wrangler.toml only has USER_DATA binding

Solution:

  • Either add CACHE binding to wrangler.toml
  • Or remove CACHE usage from code

Issue 4: Wrong Binding Type

Wrong:

// Treating R2 bucket like KV
await env.UPLOADS.get(key);  // R2 doesn't have .get()

Correct:

const object = await env.UPLOADS.get(key);
if (object) {
  const data = await object.text();
}

Binding-Specific Patterns

KV Namespace Operations

// Read
const value = await env.USER_DATA.get(key);
const json = await env.USER_DATA.get(key, 'json');
const stream = await env.USER_DATA.get(key, 'stream');

// Write
await env.USER_DATA.put(key, value);
await env.USER_DATA.put(key, value, {
  expirationTtl: 3600,
  metadata: { userId: '123' }
});

// Delete
await env.USER_DATA.delete(key);

// List
const list = await env.USER_DATA.list({ prefix: 'user:' });

R2 Bucket Operations

// Get object
const object = await env.UPLOADS.get(key);
if (object) {
  const data = await object.arrayBuffer();
  const metadata = object.httpMetadata;
}

// Put object
await env.UPLOADS.put(key, data, {
  httpMetadata: {
    contentType: 'image/png',
    cacheControl: 'public, max-age=3600'
  }
});

// Delete
await env.UPLOADS.delete(key);

// List
const list = await env.UPLOADS.list({ prefix: 'images/' });

Durable Object Access

// Get stub by name
const id = env.COUNTER.idFromName('global-counter');
const stub = env.COUNTER.get(id);

// Get stub by hex ID
const id = env.COUNTER.idFromString(hexId);
const stub = env.COUNTER.get(id);

// Generate new ID
const id = env.COUNTER.newUniqueId();
const stub = env.COUNTER.get(id);

// Call methods
const response = await stub.fetch(request);

D1 Database Operations

// Query
const result = await env.DB.prepare(
  'SELECT * FROM users WHERE id = ?'
).bind(userId).first();

// Insert
await env.DB.prepare(
  'INSERT INTO users (name, email) VALUES (?, ?)'
).bind(name, email).run();

// Batch operations
const results = await env.DB.batch([
  env.DB.prepare('UPDATE users SET active = ? WHERE id = ?').bind(true, 1),
  env.DB.prepare('UPDATE users SET active = ? WHERE id = ?').bind(true, 2),
]);

Output Format

Provide binding summary:

## Binding Analysis

**Configured Bindings** (from wrangler.toml):
- KV Namespaces: USER_DATA, CACHE
- R2 Buckets: UPLOADS
- Durable Objects: COUNTER (class: Counter)
- D1 Databases: DB

**TypeScript Interface**:
\`\`\`typescript
interface Env {
  USER_DATA: KVNamespace;
  CACHE: KVNamespace;
  UPLOADS: R2Bucket;
  COUNTER: DurableObjectNamespace;
  DB: D1Database;
}
\`\`\`

**Code Usage Verification**:
✅ All bindings used correctly
⚠️ Code references `SESSIONS` KV but not configured
❌ Missing Env interface definition

Integration

This agent should run:

  • First in any workflow (provides context for other agents)
  • Before code generation (know what bindings are available)
  • During reviews (verify binding usage is correct)

Provides context to:

  • workers-runtime-guardian - Validates binding access patterns
  • cloudflare-architecture-strategist - Understands resource availability
  • cloudflare-security-sentinel - Checks binding permission patterns