Backend

This folder contains all backend code for the MCP server and widget serving.

Structure

  • index.ts - Main Hono application entrypoint
  • database/ - SQLite database schema and queries
  • mcp/ - MCP server with ChatGPT tools and widget resources

Hono

This app uses Hono as the web framework. Hono is designed for serverless environments like Val Town and Cloudflare Workers. If you're familiar with Express, Flask, or Sinatra, Hono follows similar patterns.

Routes

The backend serves three types of content:

1. Root Route (GET /)

Serves the setup instructions page with the dynamically injected MCP endpoint URL.

2. MCP Endpoint (ALL /mcp)

The Model Context Protocol server endpoint that ChatGPT connects to. This handles:

  • Tool discovery and execution
  • Widget resource serving
  • Streaming HTTP transport for MCP messages

3. Widget Assets (GET /widget-assets/**)

Serves the React widget files with automatic TypeScript/TSX transpilation. The backend maps requests from /widget-assets/* to /frontend/widgets/* and uses Val Town's serveFile utility to:

  • Read project files across branches and forks
  • Automatically transpile TSX → JS
  • Set correct content-types

Example: GET /widget-assets/widget.tsx → serves transpiled JS from /frontend/widgets/widget.tsx

MCP Server

The MCP server (backend/mcp/server.ts) defines:

  1. Widget Resource - The HTML template that ChatGPT loads in an iframe
  2. Tools - Functions that ChatGPT can call (list_messages, add_message, get_message)
  3. Widget Metadata - Configuration for widget behavior, CSP, loading states

Each tool returns structured data with a kind discriminator that the widget router uses for navigation.

Serving Widget HTML

The widget HTML template (frontend/widgets/index.html) contains __VAL_TOWN_URL__ placeholders that get replaced with the actual deployment URL when the widget resource is requested. This ensures widget assets load from the correct domain across forks and branches.

Error Handling

By default, Hono swallows errors and returns "Internal Server Error". We override this behavior to see full stack traces:

app.onError((err, c) => { throw err; });

This makes debugging easier during development. You can customize this for production.