编程 Val Town深度实战:当「让代码动起来」成为AI编程的新范式——从Deno Runtime到MCP驱动的Serverless JavaScript全栈指南(2026)

2026-06-20 10:26:44 +0800 CST views 9

Val Town深度实战:当「让代码动起来」成为AI编程的新范式——从Deno Runtime到MCP驱动的Serverless JavaScript全栈指南(2026)

引言:代码是惰性的,这是个问题

"Code is inert. How do you make it ert?"
— Paul Ford, What is Code

2015年,Paul Ford在Bloomberg上发表那篇著名的长文《What is Code》时,提出了一个深刻的问题:代码天生是惰性的——它躺在磁盘里,不运行,不响应,不与任何人交互。作为程序员,我们花了大量时间去"部署"代码,让它从静态文件变成活的服务。这个过程涉及服务器、容器、域名、DNS、CI/CD流水线……每一步都是摩擦力,都是障碍。

2026年的今天,这个问题的严重性被放大了无数倍。

当AI开始大规模生成代码,代码的生产速度已经远远超过人类部署代码的速度。据Paul Kinlan的估算,GitHub上约5%的公开提交已经由语言模型贡献——而且这只是未提交、未署名的LLM代码的冰山一角。更可怕的是,AI生成的代码比人类写的代码更容易成为"惰性代码":人类程序员至少知道代码在本地怎么跑,而AI生成的代码往往在"生成-复制-粘贴-放弃"的生命周期里迅速死去。

Val Town(val.town)正是为解决这个矛盾而生的平台。它的核心主张简单而有力:Val Town让代码动起来(makes code ert)。本文将深入剖析这个平台的技术架构、MCP集成、AI编程工作流,以及它如何重新定义"部署"这个概念。


一、Val Town是什么:重新定义Serverless JavaScript

1.1 产品定位

Val Town是一个运行在浏览器中的JavaScript代码编辑器和Serverless运行时。用户在这个平台上写代码、保存代码、运行代码——三件事是同一个动作。没有任何"部署"步骤,代码保存后100毫秒内就在云端运行了。

这不是一个玩具。Val Town支持:

  • HTTP处理函数:写出export default就能接收HTTP请求
  • 定时任务(Cron Jobs):像写普通函数一样定义定时任务
  • 数据库:内置SQLite和Blob存储
  • 电子邮件处理:接收和发送邮件
  • WebSocket:双向实时通信
  • AI Agent集成:通过MCP协议与Claude Code等AI编程工具深度集成

从技术栈看,Val Town基于Deno运行时,支持现代JavaScript/TypeScript标准。这意味着:

// val.town 上的一个HTTP处理函数——保存即上线
export default async function(req: Request): Promise<Response> {
  const { name } = await req.json();
  return Response.json({ 
    message: `Hello, ${name}! The code is ert.`,
    timestamp: Date.now(),
    runtime: "Deno"
  });
}

保存这个函数后,立即可以通过https://yourname.val.town/访问。

1.2 技术架构解析

Val Town的架构设计非常精妙,它的每一层都服务于同一个目标:消除代码运行的摩擦

运行时层:Deno

Val Town选择Deno而非Node.js作为底层运行时,是经过深思熟虑的决定。Deno的几个特性与Val Town的需求完美契合:

  1. 原生TypeScript支持:无需构建步骤,TypeScript代码直接运行
  2. 安全沙箱:默认不允许文件、网络、环境的任意访问,适合多租户Serverless场景
  3. 标准库完整:Deno标准库的质量和覆盖度相当高,减少了对npm生态的依赖
  4. 现代ES模块系统:Deno原生支持ES模块,URL导入和版本管理更清晰
// Deno原生支持ES模块,URL直接导入
import { Hono } from "https://deno.land/x/hono@v4.3.0/mod.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

const app = new Hono();

app.get("/api/users/:id", async (c) => {
  const id = c.req.param("id");
  return c.json({ id, platform: "val.town", runtime: "deno" });
});

export default app.fetch;

执行模型:保存即运行

传统Serverless平台(Cloudflare Workers、Vercel Functions等)的执行模型是:写代码 → 构建 → 部署 → 等待生效。这个流程引入了不可忽视的延迟和不确定性。

Val Town的执行模型是:

  1. 用户在浏览器编辑器中编辑代码
  2. 每次保存(Ctrl/Cmd+S),代码通过WebSocket同步到服务器
  3. 服务器在100毫秒内完成代码的验证和加载
  4. 下一个HTTP请求即命中新代码

这个"即时生效"的特性,使得Val Town的开发体验非常接近本地开发,但又完全在云端运行。对于需要快速迭代的AI Agent工作流来说,这个特性简直是量身定做——AI可以在循环中不断修改代码,每次修改立即可见。

存储层:SQLite + Blob Storage

Val Town为每个val提供了持久化存储能力:

  • SQLite:每个val有独立的SQLite数据库,可以存储结构化数据
  • Blob Storage:大文件存储,适合AI Agent缓存、生成的图片等二进制数据
// 访问当前val的SQLite数据库
import {.Sqlite } from "https://esm.town/vrt/sqlite.ts";

export default async function(req: Request): Promise<Response> {
  const db = await Sqlite.open("myapp");
  
  // 初始化表
  await db.execute(`
    CREATE TABLE IF NOT EXISTS visits (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      timestamp INTEGER,
      user_agent TEXT
    )
  `);
  
  // 记录访问
  await db.execute({
    sql: "INSERT INTO visits (timestamp, user_agent) VALUES (?, ?)",
    args: [Date.now(), req.headers.get("user-agent") ?? "unknown"]
  });
  
  // 查询统计
  const result = await db.select<{count: number}[]>(
    "SELECT COUNT(*) as count FROM visits"
  );
  
  return Response.json({ visits: result[0].count });
}

1.3 与传统平台的对比

特性Val TownCloudflare WorkersVercel FunctionsReplit
部署延迟<100ms数秒数秒秒级
原生TypeScript
内置数据库SQLite + BlobD1 (SQLite)PostgresPostgres
MCP集成✅✅
协作编辑✅ (即将)
免费额度足够个人用100k req/天100k req/月0.5GB RAM
定价策略按量计费按量计费按量计费订阅制

二、MCP协议:Val Town与AI Agent的深度集成

2.1 什么是MCP

MCP(Model Context Protocol)是Anthropic在2024年末推出的开放协议,用于让AI编程助手(如Claude Code)与外部工具和数据源进行标准化交互。MCP的核心价值是:让AI能够真正操作系统,而不只是生成代码文本。

打个比方:如果说传统AI编程助手是"用嘴指挥别人写代码",那么MCP加持的AI助手就是"直接用手写代码"——它不仅能生成代码,还能调用工具执行代码、读写文件、运行测试、管理数据库。

2.2 Val Town MCP Server的架构

Val Town在2025-2026年期间投入大量工程资源开发了功能完整的MCP Server,这个Server提供了40+个工具,涵盖了Val Town平台的全部核心能力:

MCP Server 工具集
├── 代码操作
│   ├── val_town_readCode    — 读取指定val的源代码
│   ├── val_town_writeCode   — 写入/更新指定val的代码
│   ├── val_town_runCode     — 立即运行val并获取输出
│   └── val_town_remix       — 复制一个val作为新项目起点
│
├── 存储操作
│   ├── val_town_sqliteRead  — 查询SQLite数据库
│   ├── val_town_sqliteWrite — 执行SQL写入
│   └── val_town_blobRead/Write — Blob文件读写
│
├── 运行时控制
│   ├── val_town_cronCreate  — 创建定时任务
│   ├── val_town_cronDelete  — 删除定时任务
│   └── val_town_logs        — 获取运行日志
│
└── 外部交互
    ├── val_town_httpGet/Post — 发起HTTP请求
    └── val_town_visitUrl    — 访问并解析URL内容

这个工具集的广度和深度令人印象深刻。基本上,在Val Town Web UI上能做的所有事情,都可以通过MCP协议远程完成。

2.3 Claude Code + Val Town:AI编程的完美闭环

在Val Town的2026年5月更新中,最重要的变化之一是将Claude Code推到了产品核心位置。现在,每个val的主页上都提供了一个可复制的命令,用于快速在Claude Code中连接Val Town MCP:

# 在Claude Code中执行这条命令,即可在对话中操作Val Town
npx @valTown/mcp-cli connect --token YOUR_TOKEN

连接建立后,Claude Code就可以:

场景一:AI帮你构建后端API

用户描述需求:"帮我做一个URL缩短服务,存储在SQLite里,支持自定义短码"——

用户 → Claude Code: 帮我做一个URL缩短服务
Claude Code → Val Town MCP: 
  1. 调用 val_town_writeCode 创建 urlShortener.ts
  2. 调用 val_town_sqliteWrite 初始化数据库表
  3. 调用 val_town_runCode 测试基本功能
  4. 调用 val_town_httpPost 测试HTTP端点
Claude Code → 用户: "已创建 https://you.val.town/urlShortener"

场景二:AI直接修改运行中的代码

// 原始代码
export default async function(req: Request): Promise<Response> {
  const name = await req.json();
  return Response.json({ greeting: `Hello ${name}` });
}

// AI通过MCP修改后的代码(直接在生产环境)
export default async function(req: Request): Promise<Response> {
  const { name, language = "en" } = await req.json();
  
  const greetings: Record<string, string> = {
    en: `Hello ${name}`,
    zh: `你好,${name}`,
    ja: `こんにちは、${name}さん`,
    fr: `Bonjour ${name}`
  };
  
  return Response.json({ 
    greeting: greetings[language] ?? greetings.en,
    languages: Object.keys(greetings)
  });
}

这个修改在保存后立即生效,无需任何构建、部署流程。AI可以像使用本地编辑器一样使用Val Town,唯一的区别是代码在云端运行。

2.4 Townie:Val Town原生的AI助手

除了MCP集成,Val Town还内置了一个名为Townie的AI助手。Townie直接集成在Val Town的编辑器界面中,用户可以在编写代码时随时召唤AI协助。

2026年5月的更新中,Townie迎来了重大升级:

  • 默认模型升级到Claude Opus 4.6,支持Opus 4.7 + xhigh effort level
  • 新增"Allow all"模式(aka YOLO模式):Townie在明确授权后可执行所有操作,无需逐次确认
  • 按量计费:推理费用按成本价收取,无任何溢价
  • 模型选择器:支持Opus 4.7、Sonnet 4.6、Haiku 4.5,满足不同场景的速度-成本权衡
// Townie模式下,AI可以直接修改代码——这是Val Town特有的能力
// 在编辑器中右键 → "Ask Townie" → "帮我把这个API改成支持分页"
// Townie会直接修改代码并保存

三、核心能力深度拆解

3.1 HTTP处理函数:Serverless端点的极简写法

Val Town的HTTP处理函数是平台最核心的能力。与传统框架的路由系统不同,Val Town鼓励一个val = 一个HTTP端点的极简设计哲学。

// 一个完整的RESTful API — 所有路由在同一个文件
import { Hono } from "https://deno.land/x/hono@v4.3.0/mod.ts";
import { Sqlite } from "https://esm.town/vrt/sqlite.ts";

const app = new Hono();
const db = await Sqlite.open("blog");

// 初始化数据库
await db.execute(`
  CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    content TEXT NOT NULL,
    created_at INTEGER DEFAULT (unixepoch())
  )
`);

// GET /posts — 列出所有文章
app.get("/posts", async (c) => {
  const posts = await db.select<{id: number; title: string; created_at: number}[]>(
    "SELECT id, title, created_at FROM posts ORDER BY created_at DESC"
  );
  return c.json({ posts });
});

// GET /posts/:id — 获取单篇文章
app.get("/posts/:id", async (c) => {
  const id = c.req.param("id");
  const post = await db.select<{id: number; title: string; content: string}[]>(
    "SELECT * FROM posts WHERE id = ?", [id]
  );
  if (post.length === 0) {
    return c.json({ error: "Post not found" }, 404);
  }
  return c.json({ post: post[0] });
});

// POST /posts — 创建文章
app.post("/posts", async (c) => {
  const { title, content } = await c.req.json();
  if (!title || !content) {
    return c.json({ error: "title and content required" }, 400);
  }
  const result = await db.execute({
    sql: "INSERT INTO posts (title, content) VALUES (?, ?)",
    args: [title, content]
  });
  return c.json({ id: result.lastInsertId, title, content }, 201);
});

export default app.fetch;

这种写法的优雅之处在于:没有任何服务器配置,没有package.json,没有Dockerfile,没有CI/CD。你写的代码,就是运行中的API。

3.2 定时任务(Cron Jobs):优雅的后台处理

Val Town的Cron Job与HTTP处理函数共用同一套代码模型。要创建一个每小时执行的任务,只需:

// val.town 会自动识别这个函数为定时任务
// 通过 @cron 装饰器指定执行频率
export async function hourlyReport() {
  const db = await Sqlite.open("analytics");
  
  // 统计过去一小时的访问数据
  const oneHourAgo = Date.now() - 3600 * 1000;
  const stats = await db.select<{count: number}[]>(
    "SELECT COUNT(*) as count FROM page_views WHERE timestamp > ?",
    [oneHourAgo]
  );
  
  // 发送邮件报告(Val Town内置邮件功能)
  await fetch("https://api.val.town/v1/sendEmail", {
    method: "POST",
    headers: { "Authorization": `Bearer ${Deno.env.get("SMTP_PASS")}` },
    body: JSON.stringify({
      to: "dev@example.com",
      subject: `Hourly Report: ${stats[0].count} views`,
      text: `过去1小时共 ${stats[0].count} 次页面访问`
    })
  });
  
  console.log(`[${new Date().toISOString()}] Report sent: ${stats[0].count} views`);
}

在Val Town UI中,可以直观地设置Cron表达式(支持标准5段式Cron和自然语言描述):

hourlyReport — "Every hour"      — ✅ Active
dailyCleanup  — "Every day 3am"  — ✅ Active  
weeklyDigest  — "Every Monday"    — ⏸ Paused

3.3 Blob存储:AI Agent的数据后盾

Val Town的Scoped Blob Storage是2026年6月新增的功能。每个val现在都有自己独立的Blob存储空间,这意味着AI Agent可以在运行过程中存储和读取任意数据。

// 使用Blob存储AI生成的图片
import { BlobStorage } from "https://esm.town/vrt/blob.ts";

export async function generateAndStoreImage(prompt: string): Promise<string> {
  // 调用AI图像生成API
  const imageResponse = await fetch("https://api.stability.ai/v1/generation", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${Deno.env.get("STABILITY_API_KEY")}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({ prompt, width: 1024, height: 1024 })
  });
  
  const imageBuffer = await imageResponse.arrayBuffer();
  
  // 存储到当前val的Blob空间
  const blob = new BlobStorage();
  const filename = `generated_${Date.now()}.png`;
  await blob.set(filename, new Uint8Array(imageBuffer), "image/png");
  
  // 获取访问URL
  const url = await blob.url(filename);
  return url;
}

3.4 邮件处理:Serverless邮件服务

Val Town还支持接收和处理邮件,这使得构建自动化邮件服务变得异常简单:

// 处理收到的邮件
export async function handleEmail(email: {
  from: string;
  subject: string;
  text: string;
  html: string;
  attachments: { filename: string; content: Uint8Array }[]
}): Promise<{ action: string }> {
  // 解析邮件内容,提取命令
  const command = email.text.trim().toLowerCase();
  
  if (command === "status") {
    // 回复状态
    const db = await Sqlite.open("monitor");
    const stats = await db.select<{metric: string; value: number}[]>(
      "SELECT metric, value FROM system_stats ORDER BY timestamp DESC LIMIT 5"
    );
    return {
      action: "reply",
      body: `System Status:\n${stats.map(s => `• ${s.metric}: ${s.value}`).join("\n")}`
    };
  }
  
  if (command.startsWith("deploy ")) {
    // 触发部署流程
    const service = command.substring(7);
    return {
      action: "trigger_deploy",
      service
    };
  }
  
  return { action: "no_action" };
}

四、生产级项目实战:构建一个AI驱动的个人助手

4.1 项目需求

让我们用一个完整项目来展示Val Town的生产级使用方式:构建一个AI个人助手,它能够:

  1. 接收邮件指令
  2. 在数据库中管理任务列表
  3. 每天定时汇总任务状态并发送邮件报告
  4. AI Agent可以通过MCP随时查询和修改任务

4.2 项目结构

my-ai-assistant/
├── main.ts              — HTTP入口,接收Web/API请求
├── email_handler.ts     — 邮件处理逻辑
├── task_manager.ts      — 任务CRUD操作
├── daily_report.ts      — 每日报告生成和发送
└── db_schema.ts         — 数据库初始化

4.3 数据库设计

// db_schema.ts
import { Sqlite } from "https://esm.town/vrt/sqlite.ts";

export async function initDatabase() {
  const db = await Sqlite.open("ai_assistant");
  
  await db.execute(`
    CREATE TABLE IF NOT EXISTS tasks (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      title TEXT NOT NULL,
      description TEXT,
      status TEXT DEFAULT 'pending' CHECK(status IN ('pending', 'in_progress', 'done', 'cancelled')),
      priority INTEGER DEFAULT 3 CHECK(priority BETWEEN 1 AND 5),
      created_at INTEGER DEFAULT (unixepoch()),
      updated_at INTEGER DEFAULT (unixepoch()),
      completed_at INTEGER
    )
  `);
  
  await db.execute(`
    CREATE TABLE IF NOT EXISTS task_history (
      id INTEGER PRIMARY KEY AUTOINCREMENT,
      task_id INTEGER REFERENCES tasks(id),
      action TEXT NOT NULL,
      old_value TEXT,
      new_value TEXT,
      timestamp INTEGER DEFAULT (unixepoch())
    )
  `);
  
  return db;
}

export type Task = {
  id: number;
  title: string;
  description: string | null;
  status: "pending" | "in_progress" | "done" | "cancelled";
  priority: number;
  created_at: number;
  updated_at: number;
  completed_at: number | null;
};

4.4 任务管理核心逻辑

// task_manager.ts
import { Sqlite } from "https://esm.town/vrt/sqlite.ts";
import { initDatabase, type Task } from "./db_schema.ts";

export async function createTask(title: string, description?: string, priority = 3): Promise<Task> {
  const db = await initDatabase();
  
  const result = await db.execute({
    sql: "INSERT INTO tasks (title, description, priority) VALUES (?, ?, ?)",
    args: [title, description ?? null, priority]
  });
  
  const task = await db.select<Task[]>(
    "SELECT * FROM tasks WHERE id = ?", [result.lastInsertId]
  );
  
  await logHistory(db, result.lastInsertId, "created", null, { title, priority });
  
  return task[0];
}

export async function updateTaskStatus(
  taskId: number, 
  newStatus: Task["status"]
): Promise<Task | null> {
  const db = await initDatabase();
  
  const current = await db.select<Task[]>("SELECT * FROM tasks WHERE id = ?", [taskId]);
  if (current.length === 0) return null;
  
  const completedAt = newStatus === "done" ? Date.now() : null;
  
  await db.execute({
    sql: "UPDATE tasks SET status = ?, updated_at = unixepoch(), completed_at = ? WHERE id = ?",
    args: [newStatus, completedAt, taskId]
  });
  
  await logHistory(db, taskId, "status_changed", current[0].status, newStatus);
  
  const updated = await db.select<Task[]>("SELECT * FROM tasks WHERE id = ?", [taskId]);
  return updated[0];
}

export async function listTasks(filters?: {
  status?: Task["status"];
  priority?: number;
  limit?: number;
}): Promise<Task[]> {
  const db = await initDatabase();
  
  let sql = "SELECT * FROM tasks WHERE 1=1";
  const args: (string | number)[] = [];
  
  if (filters?.status) {
    sql += " AND status = ?";
    args.push(filters.status);
  }
  if (filters?.priority) {
    sql += " AND priority = ?";
    args.push(filters.priority);
  }
  
  sql += " ORDER BY priority ASC, created_at DESC";
  if (filters?.limit) {
    sql += ` LIMIT ${filters.limit}`;
  }
  
  return db.select<Task[]>(sql, args);
}

export async function generateDailyReport(): Promise<string> {
  const db = await initDatabase();
  
  const stats = await db.select<{
    total: number;
    pending: number;
    in_progress: number;
    done: number;
    high_priority_pending: number;
  }[]>(`
    SELECT 
      COUNT(*) as total,
      SUM(status = 'pending') as pending,
      SUM(status = 'in_progress') as in_progress,
      SUM(status = 'done') as done,
      SUM(status = 'pending' AND priority <= 2) as high_priority_pending
    FROM tasks
  `);
  
  const highPriorityTasks = await db.select<Task[]>(`
    SELECT * FROM tasks 
    WHERE status = 'pending' AND priority <= 2
    ORDER BY priority ASC
    LIMIT 10
  `);
  
  const todayStart = new Date();
  todayStart.setHours(0, 0, 0, 0);
  const todayTasks = await db.select<{count: number}[]>(`
    SELECT COUNT(*) as count FROM tasks WHERE created_at >= ?
  `, [Math.floor(todayStart.getTime() / 1000)]);
  
  return `📊 AI Assistant 日报 — ${new Date().toLocaleDateString("zh-CN")}
  
📈 今日概况:
• 新建任务: ${todayTasks[0].count}
• 高优先级待办: ${stats[0].high_priority_pending}
• 进行中: ${stats[0].in_progress}
• 已完成: ${stats[0].done}
• 合计: ${stats[0].total}

🔥 高优先级待办:
${highPriorityTasks.map(t => `  [P${t.priority}] ${t.title}`).join("\n") || "  无"}`;
}

async function logHistory(
  db: Awaited<ReturnType<typeof initDatabase>>,
  taskId: number,
  action: string,
  oldValue: string | null,
  newValue: unknown
) {
  await db.execute({
    sql: "INSERT INTO task_history (task_id, action, old_value, new_value) VALUES (?, ?, ?, ?)",
    args: [taskId, action, oldValue, JSON.stringify(newValue)]
  });
}

4.5 HTTP入口整合

// main.ts
import { Hono } from "https://deno.land/x/hono@v4.3.0/mod.ts";
import { createTask, updateTaskStatus, listTasks, generateDailyReport } from "./task_manager.ts";

const app = new Hono();

// POST /tasks — 创建任务
app.post("/tasks", async (c) => {
  const { title, description, priority } = await c.req.json();
  if (!title) return c.json({ error: "title is required" }, 400);
  
  const task = await createTask(title, description, priority);
  return c.json(task, 201);
});

// PATCH /tasks/:id — 更新状态
app.patch("/tasks/:id", async (c) => {
  const id = parseInt(c.req.param("id"));
  const { status } = await c.req.json();
  
  if (!["pending", "in_progress", "done", "cancelled"].includes(status)) {
    return c.json({ error: "Invalid status" }, 400);
  }
  
  const task = await updateTaskStatus(id, status);
  if (!task) return c.json({ error: "Task not found" }, 404);
  
  return c.json(task);
});

// GET /tasks — 列出任务
app.get("/tasks", async (c) => {
  const status = c.req.query("status") as Task["status"] | undefined;
  const priority = c.req.query("priority") ? parseInt(c.req.query("priority")!) : undefined;
  const limit = c.req.query("limit") ? parseInt(c.req.query("limit")!) : undefined;
  
  const tasks = await listTasks({ status, priority, limit });
  return c.json({ tasks });
});

// GET /report — 生成日报
app.get("/report", async (c) => {
  const report = await generateDailyReport();
  return c.json({ report });
});

export default app.fetch;

五、AI Agent集成实战:用Claude Code驱动Val Town

5.1 工作流程

这是Val Town最激动人心的使用场景:AI Agent通过MCP协议完整控制Val Town上的应用。以下是典型的工作流:

用户 → Claude Code: "帮我创建一个图片水印服务,支持设置文字水印和透明度"
    ↓
Claude Code(思考):
  1. 设计API接口(接收图片 + 水印文字 + 透明度)
  2. 编写图像处理代码(使用Canvas/DENO)
  3. 创建SQLite表存储使用记录
  4. 部署到Val Town
    ↓
Claude Code → MCP工具链:
  val_town_writeCode("watermark.ts", imageProcessingCode)
  val_town_sqliteWrite(initTable)
  val_town_runCode("watermark.ts") 
  val_town_httpPost("https://you.val.town/watermark", testData)
    ↓
Claude Code → 用户: "服务已上线: https://you.val.town/watermark"

5.2 完整AI Agent脚本示例

以下是一个Claude Code脚本,展示如何通过MCP批量操作Val Town vals:

/**
 * Claude Code MCP Script: 批量构建AI工具集
 * 在Claude Code中运行此脚本,自动在Val Town上部署多个AI工具
 */

// MCP工具引用(Claude Code会自动解析这些函数调用)
// const { val_town_writeCode, val_town_runCode, val_town_sqliteWrite } = mcpTools;

// 工具1: 智能摘要服务
const summarizerCode = `
import { Hono } from "https://deno.land/x/hono@v4.3.0/mod.ts";
import { Sqlite } from "https://esm.town/vrt/sqlite.ts";

const app = new Hono();
const db = await Sqlite.open("summarizer");

await db.execute(\`
  CREATE TABLE IF NOT EXISTS summaries (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    original_text TEXT,
    summary TEXT,
    model TEXT,
    created_at INTEGER DEFAULT (unixepoch())
  )
\`);

app.post("/", async (c) => {
  const { text, model = "claude-3-5-sonnet" } = await c.req.json();
  if (!text) return c.json({ error: "text required" }, 400);
  
  // 调用AI API进行摘要
  const response = await fetch("https://api.anthropic.com/v1/messages", {
    method: "POST",
    headers: {
      "x-api-key": Deno.env.get("ANTHROPIC_API_KEY")!,
      "anthropic-version": "2023-06-01",
      "content-type": "application/json"
    },
    body: JSON.stringify({
      model,
      max_tokens: 1024,
      messages: [{
        role: "user",
        content: \`请用中文简洁地总结以下内容(100字以内):\n\n\${text}\`
      }]
    })
  });
  
  const result = await response.json();
  const summary = result.content?.[0]?.text ?? "摘要生成失败";
  
  await db.execute({
    sql: "INSERT INTO summaries (original_text, summary, model) VALUES (?, ?, ?)",
    args: [text.slice(0, 500), summary, model]
  });
  
  return c.json({ summary, id: db.lastInsertId });
});

app.get("/history", async (c) => {
  const limit = parseInt(c.req.query("limit") ?? "10");
  const history = await db.select("SELECT * FROM summaries ORDER BY created_at DESC LIMIT ?", [limit]);
  return c.json({ history });
});

export default app.fetch;
`;

// 工具2: URL监控服务
const urlMonitorCode = `
import { Hono } from "https://deno.land/x/hono@v4.3.0/mod.ts";
import { Sqlite } from "https://esm.town/vrt/sqlite.ts";

const app = new Hono();
const db = await Sqlite.open("url_monitor");

await db.execute(\`
  CREATE TABLE IF NOT EXISTS monitors (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    url TEXT NOT NULL,
    interval_minutes INTEGER DEFAULT 60,
    last_check INTEGER,
    last_status INTEGER,
    last_response_time_ms INTEGER,
    is_alive BOOLEAN DEFAULT 1
  )
\`);

// 添加监控目标
app.post("/monitors", async (c) => {
  const { url, interval_minutes = 60 } = await c.req.json();
  
  const result = await db.execute({
    sql: "INSERT INTO monitors (url, interval_minutes) VALUES (?, ?)",
    args: [url, interval_minutes]
  });
  
  return c.json({ id: result.lastInsertId, url, interval_minutes });
});

// 执行健康检查
app.post("/monitors/:id/check", async (c) => {
  const id = parseInt(c.req.param("id"));
  const monitors = await db.select<{url: string}[]>("SELECT url FROM monitors WHERE id = ?", [id]);
  
  if (monitors.length === 0) return c.json({ error: "Not found" }, 404);
  
  const start = Date.now();
  try {
    const response = await fetch(monitors[0].url, { 
      method: "HEAD",
      signal: AbortSignal.timeout(10000)
    });
    const responseTime = Date.now() - start;
    const isAlive = response.ok;
    
    await db.execute({
      sql: \`UPDATE monitors SET last_check = unixepoch(), 
          last_status = ?, last_response_time_ms = ?, is_alive = ? WHERE id = ?\`,
      args: [response.status, responseTime, isAlive ? 1 : 0, id]
    });
    
    return c.json({ is_alive: isAlive, status: response.status, response_time_ms: responseTime });
  } catch (e) {
    await db.execute({
      sql: "UPDATE monitors SET last_check = unixepoch(), is_alive = 0 WHERE id = ?",
      args: [id]
    });
    return c.json({ is_alive: false, error: String(e) });
  }
});

// 获取所有监控状态
app.get("/monitors", async (c) => {
  const monitors = await db.select("SELECT * FROM monitors ORDER BY id");
  return c.json({ monitors });
});

export default app.fetch;
`;

console.log("🚀 开始批量部署AI工具集...");
// await val_town_writeCode("aiSummarizer", summarizerCode);
// await val_town_writeCode("urlMonitor", urlMonitorCode);
// await val_town_sqliteWrite(initDatabases);
// await val_town_runCode("aiSummarizer");
// await val_town_runCode("urlMonitor");
console.log("✅ 所有工具已部署:");
console.log("   摘要服务: https://yourname.val.town/aiSummarizer");
console.log("   URL监控: https://yourname.val.town/urlMonitor");

六、适用场景与局限性分析

6.1 Val Town最擅长的场景

AI Agent的专属后端:当AI需要存储数据、执行定时任务、发送通知时,Val Town是最便捷的后端选择。相比手动创建云服务,MCP集成让AI能自主完成这一切。

快速原型和小工具:不需要完整的项目管理,不需要Git仓库,几个val就能搭建一个完整的服务。非常适合hackathon和个人工具。

个人自动化:定时爬取数据、聚合RSS、处理邮件、自动发帖——这些"脚本级"需求Val Town比任何框架都更轻便。

团队共享工具:Val Town的vals可以设置为公开或私有,团队成员可以复用彼此的工具,形成一个"工具市场"。

6.2 Val Town的局限性

冷启动延迟:虽然运行延迟低,但Val Town作为Serverless平台,函数在闲置后再次调用时会有冷启动延迟。对于延迟敏感的实时应用,需要注意。

计算资源限制:Val Town没有公开具体的资源限制文档,但在高并发场景下可能出现资源争抢。生产级高流量应用仍建议使用传统云服务。

供应商锁定:运行在Val Town专有平台上,迁移到其他平台需要重写代码。相比使用标准框架(Express/Fastify)和标准云服务(Cloudflare/AWS),有一定锁定风险。

调试工具链:虽然日志功能在持续改进,但与本地开发相比,生产环境调试仍然不便。


七、与其他AI编程平台的横评

2026年的AI编程平台战场已经相当拥挤,Val Town需要与多个强劲对手竞争:

Val Town vs. Replit Agent

Replit是最早做AI编程的平台之一,优势在于完整的开发环境(IDE + 运行时 + 部署)。但Replit的Agent更偏向"全栈替代",而Val Town的MCP更偏向"工具扩展"。如果你需要AI在你的基础设施上操作,Val Town更合适。

Val Town vs. Cursor

Cursor是目前最火的AI IDE,但它是本地编辑器+云端AI的组合。Val Town的优势在于不需要本地环境——纯浏览器即可工作,而且部署和运行是无缝的。

Val Town vs. Bolt (Linear)

Bolt.diy是另一个纯浏览器AI编程平台,定位与Val Town接近。Val Town的优势在于MCP工具链的完整性和Deno运行时的成熟度;Bolt的优势在于更激进的AI全栈生成能力。

Val Town vs. Cloudflare Workers + Workers AI

这是"Serverless + AI"的组合方案。Val Town的优势是开发体验更流畅,MCP集成更原生;Cloudflare Workers的优势是全球CDN边缘部署,性能更强。


八、性能与安全:生产使用的关键考量

8.1 冷启动性能

Val Town的冷启动时间通常在200-500ms之间,相比传统容器Serverless(Cloudflare Workers在50ms左右)略慢,但比Lambda(经常1s+)快很多。对于非延迟敏感的API,这个差距感知不明显。

8.2 安全模型

Val Town的安全模型基于Deno的权限系统。每个val都运行在独立的沙箱中:

// Deno的权限模型 — Val Town在底层使用相同的安全策略
// 默认情况下,val中的代码无法:
// - 访问文件系统(除非通过Val Town提供的API)
// - 发起任意网络请求(只能访问显式授权的URL)
// - 读取环境变量(除非通过Deno.env.get()并已配置)

// 用户需要通过Val Town UI授权敏感权限

8.3 成本分析

Val Town采用按量计费模式:

  • 推理费用:与AI模型的官方定价一致(无溢价)
  • 运行费用:根据执行时间计费
  • 存储费用:SQLite和Blob存储按容量计费
  • 传出流量:按GB计费

对于个人用户和小型项目,免费额度通常足够使用。


九、未来展望:Val Town的演进方向

从Val Town官方博客2026年的更新来看,平台正在向以下几个方向演进:

9.1 协作编辑

Val Town正在开发多人实时协作编辑功能,类似于Figma或飞书文档的协作体验。这意味着Val Town不仅是一个代码平台,还可能成为团队共建AI Agent的协作空间。

9.2 更强的MCP工具

随着MCP协议的普及,Val Town正在扩展MCP工具集,包括:

  • 更细粒度的版本控制(diff查看、单行回滚)
  • 更好的调试工具(性能分析、内存使用可视化)
  • 更丰富的AI集成(支持更多模型、更复杂的Agent工作流)

9.3 Teams与企业功能

2026年Val Town开始推出团队功能,支持:

  • 团队vals(私有/公开/内部共享)
  • 权限管理(谁可以修改、谁只能查看)
  • 团队用量统计和计费

结语:让代码动起来

Paul Ford问的那个问题——"Code is inert. How do you make it ert?"——Val Town给出了一个相当有说服力的答案。

Val Town的核心价值不是它用了什么新技术(Web编辑器、Deno、MCP都不是新概念),而是它将这些技术以正确的方式组合在一起,创造了一种零摩擦的开发体验。代码保存即运行,AI能直接操作系统,协作可以像在线文档一样自然——这些特性单独看都不稀奇,但组合在一起,就形成了一个真正有差异化的产品。

2026年,当AI开始大规模参与代码生成时,"让代码动起来"的成本变得前所未有的重要。GitHub上5%的提交由AI贡献,但那5%中又有多少进入了生产环境?Val Town正是为了解决这个问题而生的:不仅让AI生成代码,更让AI生成的那些代码能够真正运行起来、真正服务于用户。

从这个角度看,Val Town代表的不仅是一个技术产品,更是一种关于"代码应该如何存在"的哲学立场:代码不应是躺在磁盘里的静态文本,而应是随时可以响应世界召唤的活的存在。

对于正在构建AI Agent工作流的开发者来说,Val Town是一个值得认真研究的平台。它可能不是所有场景的最佳选择,但在"MCP驱动的AI后端"这个赛道上,它目前是最成熟、最完整的解决方案之一。

让代码动起来,让AI帮你动起来。


参考资源

标签:Val Town | Deno | Serverless | MCP | AI编程 | JavaScript | TypeScript | Claude Code | 2026技术

推荐文章

使用Ollama部署本地大模型
2024-11-19 10:00:55 +0800 CST
什么是Vue实例(Vue Instance)?
2024-11-19 06:04:20 +0800 CST
Vue3中如何使用计算属性?
2024-11-18 10:18:12 +0800 CST
CSS实现亚克力和磨砂玻璃效果
2024-11-18 01:21:20 +0800 CST
前端项目中图片的使用规范
2024-11-19 09:30:04 +0800 CST
程序员茄子在线接单