FF

fabiankuebler/fenced

Developer tools
96 stars Quality 64 Trend 15MIT

Agentic chat runtime where markdown is the protocol

Overview

A prototype of an agentic chat runtime where markdown is the protocol. The LLM streams markdown text, executes TypeScript code blocks server-side, and renders reactive UI components on the client—all within a single response. Markdown is the ideal protocol for agentic AI assistants. It's what LLMs already understand, mixing prose, executable code, and structured data in a format that requires no new training. 📖 The Future of Agentic AI Assistants Is... Markdown Open http://localhost:4000 in your browser. The mail, calendar, and contacts skills need Google OAuth credentials. Without them, set FENCED_DISABLE_GOOGLE_SKILLS=true in your .env to skip loading these skills. 1. Go to Google Cloud Console 2. Create a project (or select an existing one) 3. Enable the , , and 4. Configure the (External, add your email as test user) 5. Under , add these scopes: - https://www.googleapis.com/auth/gmail.modify - https://www.googleapis.com/auth/calendar - https://www.googleapis.

README

fenced

A prototype of an agentic chat runtime where markdown is the protocol. The LLM streams markdown text, executes TypeScript code blocks server-side, and renders reactive UI components on the client—all within a single response.

Thesis: Markdown is the ideal protocol for agentic AI assistants. It’s what LLMs already understand, mixing prose, executable code, and structured data in a format that requires no new training.

📖 Read the accompanying article: The Future of Agentic AI Assistants Is… Markdown

Quick Start with Docker

# Build the image
docker build -t fenced .

# Run with required environment variables
docker run -p 4000:4000 \
  -e OPENAI_API_KEY=your-openai-api-key \
  -e FENCED_DISABLE_GOOGLE_SKILLS=true \
  fenced

Open http://localhost:4000 in your browser.

Environment Variables

Variable Required Description
OPENAI_API_KEY Yes OpenAI API key for LLM generation
FENCED_DISABLE_GOOGLE_SKILLS No Set to 1 to disable Google skills (mail, contacts, calendar). Recommended when running without Google OAuth credentials.

Local Development

Prerequisites

Setup

# Install dependencies
bun install

# Create .env file
cp .env.example .env
# Edit .env with your OPENAI_API_KEY

# Run dev servers (two terminals)
bun run dev:server   # Bun API on port 4000
bun run dev:client   # Vite dev server with HMR

# Or use tmux to run both
bun run dev

Google Skills (Mail, Calendar, Contacts)

The mail, calendar, and contacts skills need Google OAuth credentials. Without them, set FENCED_DISABLE_GOOGLE_SKILLS=true in your .env to skip loading these skills.

The Protocol

Three block types flow through the system:

Block Syntax Purpose
Text Standard markdown Streams to user token-by-token
Code ```tsx agent.run Server-executed TypeScript with persistent context
Data ```json agent.data => "id" JSON streamed into named client targets

The feedback loop: user message → LLM generates markdown → code executes → console.log output feeds back to LLM → repeat until logs are empty.

Generative UI

The mount() primitive lets agents write React components on the fly:

const data = new Data({ progress: 0 });
const comp = mount({
  data,
  outputSchema: z.object({ name: z.string() }),
  ui: ({ data, output }) => (
    
      
      
      Submit
    
  )
});

data.progress = 50;  // Live update to client
const result = await comp.result;  // Wait for form submit

Four data-movement patterns:

  • Client → Server: Form submission with Zod validation
  • Server → Client: Live updates via Valtio proxy mutations
  • LLM → Client: json agent.data blocks stream JSON incrementally
  • Client → Server: Callbacks for interactive elements

Project Structure

apps/
  client/     # React + Vite frontend
  server/     # Bun HTTP/WebSocket API
packages/
  shared/     # Protocol types, constants
  channel/    # WebSocket transport layer
  session/    # Session model
  runtime/    # Interaction loop orchestration
  llm/        # LLM streaming (Vercel AI SDK)
  parser/     # Streaming markdown parser
  executor/   # VM execution, mount manager
  skills/     # Skill discovery and injection
  component-render/  # Client-side UI rendering

Scripts

bun run dev:client      # Vite dev server
bun run dev:server      # Bun API with hot reload
bun run build           # Build client for production
bun run lint            # ESLint (client) + tsc (server)
bun test                # Run all tests
bun test packages/parser  # Run tests in specific package

Adding Skills

Skills extend agent capabilities. Each skill lives in packages/skills/src/skills//:

File Purpose
SKILL.md Description injected into LLM prompt
index.d.ts TypeScript declarations shown to model
index.ts Runtime implementation (globals in agent.run)

Security

This is a developer prototype with no sandboxing. The LLM generates code that executes with full Bun/browser capabilities. Treat it as “RCE as a Service” until proper isolation is implemented.

Documentation

Notes

  • Single-session, single-user prototype
  • All code runs in one node:vm context per session (variables persist)
  • API on port 4000, Vite proxies /chat WebSocket
  • Max 60s per agent.run execution
View the full README on GitHub

Recommended Tools

Try a different keyword or remove a filter.

Install

npx skillfish add fabiankuebler/fenced
New to MCP? Start with the mcpskill.org guide →