Skip to main content
All Projects
IN·PROGRESS

Outloud

Remote MCP server + web app that turns a developer's mid-session explanation of what they just shipped into voice-matched LinkedIn and X drafts, reviewed by a human before anything publishes

Outloud screenshot 1

Role

Full Stack Developer

Team

Solo

Stack
Frontend
React 19TanStack StartTanStack RouterTanStack QueryVite
Backend
Node.jsHonoBetter AuthDrizzleZod
Data
Neon Postgres
AI & Agents
MCP (Streamable HTTP)OAuth 2.1OpenRouter
Integrations
LinkedIn APIX APIDodo Payments
Infra
RailwayCloudflare Workerspnpm workspaces
Challenges
  • Remote MCP over Streamable HTTP with OAuth 2.1 dynamic client registration, so Claude Code and Cursor connect via a single URL with no pasted API keys
  • One Hono process serving two entry prefixes into the same core: /api REST with cookie sessions and /mcp with OAuth bearer tokens
  • Atomic generation-cap reservation before any LLM spend, since check-then-increment was racy across LLM latency
  • Ports-and-adapters core package that is fully transport-agnostic: no Hono, HTTP, or MCP framing leaks into business logic
  • Silent voice-learning loop: user edits to drafts are recorded as diffs and periodically folded back into the per-platform voice profile
  • Fail-fast boot sequence: env validation, cipher keys, and service construction all fail the deploy, never the first request
Insights
  • MCP authorization spec in practice: agent discovers OAuth metadata from the server URL, registers dynamically, and every tool call maps 1:1 to a user account
  • Shared core service layer consumed by both MCP and REST handlers avoids duplicating generation, usage, and publish logic across transports
  • Per-field Zod caps do not bound JSON parse cost, so body limits (256 KiB REST, 1 MiB MCP) sit in front of parsing
  • Monthly usage caps keyed by calendar month make resets implicit: the month key changes, no cron reset needed
  • Shared Zod schema package as the backend/frontend contract, so drift fails loudly at the boundary instead of rendering garbage
  • Capped-but-successful tool responses: at the generation cap the MCP tool returns capped: true instead of an error, so agents surface it as a normal message

Overview

Outloud turns a developer's own mid-session explanation of what they just built, captured live from inside their coding agent, into a voice-matched LinkedIn and X draft that a human reviews before anything publishes.

The insight: developers are told to "build in public," but the bottleneck is translation friction. The reasoning about what was built and why already happened inside the agent session. Outloud captures that reasoning as generation input instead of a git webhook or a web form. Nothing ever auto-posts by default. Auto-publish is Pro only, opt-in, per platform, with a countdown you can cancel.

<30s

Shipped to draft

1 URL

Agent connect, zero keys

2

Platforms, one inbox

$10/mo

Pro, BYOK on free


How It Works

  1. 1

    Connect your agent over remote MCP

    Claude Code, Cursor, or any MCP client connects to one URL. The agent discovers Outloud's OAuth metadata, registers itself, and runs a standard authorization-code flow. No API keys to generate or paste.

  2. 2

    Ask mid-session

    "I just shipped X, write about it." The agent calls the draft_post(context, platform) tool with the raw conversation excerpt. No client-side summarization: voice matching benefits from the actual reasoning trail.

  3. 3

    Voice-matched generation

    Outloud assembles a prompt from the per-platform voice profile plus the agent context, generates through OpenRouter, and returns the draft inline to the agent along with a link into the web app.

  4. 4

    Human review

    Edit, regenerate, or discard in the draft inbox. Regeneration never burns the monthly cap, only fresh context does. Every edit is silently recorded for voice learning.

  5. 5

    Publish

    Copy-paste on the free tier, or direct API publishing to LinkedIn and X on Pro. Auto-publish is opt-in, per platform, with a cancelable countdown.


Key Features

Remote MCP server

Streamable HTTP transport per the current MCP spec, so Claude Code, Claude Desktop, and Cursor all connect via a single URL rather than a locally-run stdio process. The same mechanism used by remote connectors like Notion and Linear.

OAuth 2.1, token per user

Dynamic client registration with every access token mapping 1:1 to an Outloud account, so the server always knows whose voice profile, cap, and drafts to operate on. Never a bare API key with no owner.

Per-platform voice profiles

Tone dials (technical depth, humor, formality) set separately for LinkedIn and X, plus pasted sample posts. No LinkedIn scraping, ever: pasted samples are the permanent path.

Silent voice-learning loop

Edits the user makes to a draft are logged as diffs and periodically folded back into that platform's voice profile as reference examples, so drafts converge on the user's real voice over time.

One draft inbox

List, edit inline, regenerate, publish, discard, across both platforms in one place. In-house LinkedIn and X OAuth publish integrations give full control of publish UX and clean Pro gating.

Atomic usage caps

A generation slot is reserved before any LLM spend and released if generation fails, because check-then-increment was racy across LLM latency. Caps are per calendar month (UTC) with an implicit reset.

Graceful cap handling

At the cap with no BYOK key, draft_post still returns successfully with capped: true instead of erroring, so the agent surfaces it as a normal message rather than a stack trace.

BYOK on the free tier

Bring your own API key to lift the 15/month cap and the model restriction. Pro adds unlimited generations, direct publish, auto-publish, and deeper voice analysis. Billing via Dodo Payments.


Architecture

A pnpm monorepo with one backend deploy and one frontend deploy:

apps/api          Hono on Node (Railway): one process, two entry prefixes
                    /api  REST for the web app (Better Auth cookie sessions)
                    /mcp  MCP Streamable HTTP for agents (OAuth 2.1 bearer)
apps/web          TanStack Start + React 19 on Cloudflare Workers
packages/core     Business logic + platform clients (LLM, LinkedIn, X, Dodo)
packages/schemas  Shared Zod schemas: the backend/frontend contract
ADR-13 · Single deployable unit

The MCP handler and the REST handlers need identical business logic: build a draft, check caps, publish, update a voice profile. Both are thin entry points into one internal service layer, one Hono app on two route prefixes rather than two separately-deployed services. A small team's v1: operational simplicity beats premature separation.

Ports and adapters. Every core service module ships ports.ts (the interfaces it needs) and service.ts (a factory taking those deps), and depends only on ports. The API app supplies Drizzle repos and real HTTP clients; tests supply in-memory fakes. Nothing in core opens a database connection or reads env vars.

Fail-fast boot. loadEnv() validates every env var up front, then logger, Neon pg pool wrapped by Drizzle, Better Auth with the MCP OAuth-provider plugin, then all core services. A bad cipher key or missing OpenRouter key fails the deploy, not the first request. SIGINT/SIGTERM stop background jobs, close the server, then drain the pool.

Transport hardening. Body limits sit in front of JSON parsing (256 KiB on /api/*, 1 MiB on /mcp) because per-field Zod caps do not bound the parse itself. Credentialed CORS restricted to the web app origin, plus a CSRF guard since CORS only limits who can read responses.

Frontend holds no business logic. TanStack Start with file-based routing, React Query, and typed fetch wrappers that validate every response against @outloud/schemas, so contract drift fails loudly at the boundary.

Background jobs run in-process: publish-token refresh, auto-publish countdowns, and folding edit diffs back into voice profiles.

The wedge

Win on the input mechanism, not generation quality or publishing infrastructure, both of which are commoditizing fast. Capture the reasoning, not the diff: a git webhook knows what changed, the agent conversation knows why it mattered.


Outcome

Outloud demonstrates a production remote-MCP product: a spec-compliant Streamable HTTP MCP server with OAuth 2.1 dynamic client registration, a transport-agnostic core shared between agent and web entry points, atomic usage accounting around LLM spend, and a voice-learning loop driven by real user edits. The wedge is the input mechanism, capturing the developer's live reasoning instead of a commit diff, with a human review step guaranteed before anything goes live.