Provides deployment infrastructure for MCP servers with dual authentication (OAuth + API Key), KV storage for sessions, and automatic builds via GitHub integration.
Used for automatic deployment triggers through Cloudflare Workers Builds integration and repository hosting.
Powers interactive SEP-1865 widgets with the useApp() hook for managing tool inputs, results, and lifecycle events.
Provides UI components for building interactive widgets with automatic dark mode support.
Provides styling system for widgets with automatic dark mode via host context.
Primary development language for both server-side MCP implementation and widget components.
Builds and bundles widget HTML with viteSingleFile to inline all JS/CSS into single deployable files.
Defines input schemas for tool validation and type safety in MCP tool registration.
{{SERVER_NAME}} MCP Server
MCP Apps server with SEP-1865 interactive widget support, dual authentication (OAuth + API Key), and Cloudflare Workers deployment.
Quick Start
1. Replace Placeholders
Search and replace these placeholders in all files:
Placeholder | Description | Example |
| Human-readable name | "Currency Converter" |
| kebab-case identifier | "currency-converter" |
| PascalCase class name | "CurrencyConverterMcp" |
| Brief description | "Convert currencies using real-time exchange rates" |
| Widget HTML title | "Currency Converter Widget" |
| GitHub organization | "your-org" |
2. Update wrangler.jsonc
Replace
{{SERVER_ID}}with your server IDReplace
{{McpAgentClassName}}with your class nameUpdate the custom domain pattern
3. Install and Build
4. Set Secrets
5. Deploy
Deployment is automatic via Cloudflare Workers Builds when you push to GitHub.
For manual deployment (not recommended):
Project Structure
Adding New Tools
When adding a tool, update these locations:
1. Tool Metadata (src/tools/descriptions.ts)
2. Server Registration (src/server.ts)
3. API Key Handler (src/api-key-handler.ts)
Duplicate the tool registration for API key authentication path.
SEP-1865 MCP Apps Pattern
This skeleton uses the Two-Part Registration pattern:
PART 1: Register Resource - UI HTML template from Assets
PART 2: Register Tool - Links to resource via
_meta[RESOURCE_URI_META_KEY]
Data flows:
Authentication
OAuth 2.1 (OAuth-capable clients)
Flow:
/authorize-> WorkOS AuthKit ->/callback-> ToolsPKCE support (RFC 7636)
Centralized login at panel.wtyczki.ai
Example clients: Claude Desktop
API Key (Non-OAuth clients)
Header:
Authorization: Bearer wtyk_xxxxxKeys generated via panel.wtyczki.ai
LRU cache prevents memory leaks
Example clients: AnythingLLM, Cursor
Widget Development
Key Concepts
React 18 with
useApp()hook from@modelcontextprotocol/ext-apps/reactTailwind CSS with automatic dark mode (via host context)
Fixed 600px height container (mandatory for MCP Apps)
viteSingleFile inlines all JS/CSS into single HTML
Development
Widget Lifecycle
Configuration Files
File | Purpose |
| Cloudflare Workers config (bindings, routes) |
| Widget build config |
| Dependencies and scripts |
| TypeScript config (server) |
| TypeScript config (widget) |
Environment Variables
Set via wrangler secret put:
Variable | Required | Description |
| Yes | WorkOS client ID |
| Yes | WorkOS API key (starts with sk_) |
| No | AI Gateway token (if using Workers AI) |
Common Issues
Widget not loading
Check
npm run build:widgetscompleted successfullyVerify
web/dist/widgets/widget.htmlexistsCheck ASSETS binding in wrangler.jsonc
Authentication failures
Verify WORKOS_CLIENT_ID and WORKOS_API_KEY secrets are set
Check USER_SESSIONS KV namespace is configured
Ensure custom domain is set up in Cloudflare
Tool not appearing
Check tool is registered in BOTH server.ts AND api-key-handler.ts
Verify tool name matches exactly in all locations
Check handleToolsList() includes the tool schema
Production Checklist
All
{{PLACEHOLDER}}values replacedwrangler.jsonc configured with correct IDs
Secrets set via
wrangler secret putCustom domain configured in Cloudflare
GitHub repository connected to Cloudflare Workers Builds
Widget builds successfully (
npm run build:widgets)Type checking passes (
npm run type-check)