Skip to main content
Glama

{{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

{{SERVER_NAME}}

Human-readable name

"Currency Converter"

{{SERVER_ID}}

kebab-case identifier

"currency-converter"

{{McpAgentClassName}}

PascalCase class name

"CurrencyConverterMcp"

{{SERVER_DESCRIPTION}}

Brief description

"Convert currencies using real-time exchange rates"

{{WIDGET_TITLE}}

Widget HTML title

"Currency Converter Widget"

{{GITHUB_ORG}}

GitHub organization

"your-org"

2. Update wrangler.jsonc

  1. Replace {{SERVER_ID}} with your server ID

  2. Replace {{McpAgentClassName}} with your class name

  3. Update the custom domain pattern

3. Install and Build

npm install npm run build:widgets

4. Set Secrets

wrangler secret put WORKOS_CLIENT_ID wrangler secret put WORKOS_API_KEY

5. Deploy

Deployment is automatic via Cloudflare Workers Builds when you push to GitHub.

For manual deployment (not recommended):

npm run deploy

Project Structure

src/ index.ts # Entry point with dual auth routing server.ts # McpAgent class (OAuth path) api-key-handler.ts # API key authentication with LRU cache types.ts # Environment bindings server-instructions.ts # LLM system prompt instructions auth/ authkit-handler.ts # WorkOS OAuth with PKCE apiKeys.ts # API key validation auth-utils.ts # User lookup, HTML pages props.ts # Auth context type session-types.ts # Session interfaces helpers/ assets.ts # loadHtml() for widget loading resources/ ui-resources.ts # SEP-1865 UI resource definitions tools/ descriptions.ts # Tool metadata (4-part pattern) shared/ logger.ts # Structured logging optional/ # Advanced features (delete if not needed) web/ widgets/ widget.html # Widget entry point widget.tsx # React widget component components/ # shadcn/ui components lib/ # Utilities (cn, types) styles/ # Tailwind CSS dist/widgets/ # Built output (gitignored)

Adding New Tools

When adding a tool, update these locations:

1. Tool Metadata (src/tools/descriptions.ts)

"your-tool": { title: "Your Tool", description: { part1_purpose: "What it does...", part2_returns: "Returns X, Y, Z...", part3_useCase: "Use when...", part4_constraints: "Note: limitations..." }, examples: [...] }

2. Server Registration (src/server.ts)

registerAppTool( this.server, "your-tool", { title: TOOL_METADATA["your-tool"].title, description: getToolDescription("your-tool"), inputSchema: { /* Zod schema */ }, _meta: { [RESOURCE_URI_META_KEY]: widgetResource.uri } }, async (args) => { /* implementation */ } );

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:

  1. PART 1: Register Resource - UI HTML template from Assets

  2. PART 2: Register Tool - Links to resource via _meta[RESOURCE_URI_META_KEY]

Data flows:

Tool Result -> structuredContent -> postMessage -> Widget State

Authentication

OAuth 2.1 (OAuth-capable clients)

  • Flow: /authorize -> WorkOS AuthKit -> /callback -> Tools

  • PKCE support (RFC 7636)

  • Centralized login at panel.wtyczki.ai

  • Example clients: Claude Desktop

API Key (Non-OAuth clients)

  • Header: Authorization: Bearer wtyk_xxxxx

  • Keys 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/react

  • Tailwind 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

# Build widget npm run build:widgets # Watch mode npm run dev:widget # Full dev (server + widget watch) npm run dev:full

Widget Lifecycle

const { app } = useApp({ onAppCreated: (app) => { app.ontoolinput = (params) => { /* tool called */ }; app.ontoolresult = (result) => { /* display result */ }; app.onhostcontextchanged = (ctx) => { /* theme change */ }; app.onteardown = async () => { /* cleanup */ }; } });

Configuration Files

File

Purpose

wrangler.jsonc

Cloudflare Workers config (bindings, routes)

vite.config.ts

Widget build config

package.json

Dependencies and scripts

tsconfig.json

TypeScript config (server)

web/tsconfig.json

TypeScript config (widget)

Environment Variables

Set via wrangler secret put:

Variable

Required

Description

WORKOS_CLIENT_ID

Yes

WorkOS client ID

WORKOS_API_KEY

Yes

WorkOS API key (starts with sk_)

AI_GATEWAY_TOKEN

No

AI Gateway token (if using Workers AI)

Common Issues

Widget not loading

  1. Check npm run build:widgets completed successfully

  2. Verify web/dist/widgets/widget.html exists

  3. Check ASSETS binding in wrangler.jsonc

Authentication failures

  1. Verify WORKOS_CLIENT_ID and WORKOS_API_KEY secrets are set

  2. Check USER_SESSIONS KV namespace is configured

  3. Ensure custom domain is set up in Cloudflare

Tool not appearing

  1. Check tool is registered in BOTH server.ts AND api-key-handler.ts

  2. Verify tool name matches exactly in all locations

  3. Check handleToolsList() includes the tool schema

Production Checklist

  • All {{PLACEHOLDER}} values replaced

  • wrangler.jsonc configured with correct IDs

  • Secrets set via wrangler secret put

  • Custom 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)

-
security - not tested
F
license - not found
-
quality - not tested

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cloudflare-mcp-apps/ads-roi'

If you have feedback or need assistance with the MCP directory API, please join our Discord server