jackspace/claudeskillz/typescript-mcp

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Building MCP (Model Context Protocol) servers in TypeScript on Cloudflare Workers with the official @modelcontextprotocol/sdk: registering tools, resources, and prompts, Hono HTTP routing, StreamableHTTPServerTransport setup, authentication (API keys, OAuth, Zero Trust), Cloudflare D1/KV/R2/Vectorize integration, wrangler deployment, and testing. Prevents common failures including export syntax errors, Zod schema validation failures, memory leaks from unclosed transports, CORS misconfiguration, and missing rate limiting. Use when exposing APIs, tools, or data to LLMs as a stateless serverless MCP server on the edge. Not for Python MCP servers (see FastMCP), stateful WebSocket agents (Cloudflare Agents SDK), or local stdio CLI tools.

Install

npx vskill@latest install jackspace/claudeskillz/typescript-mcp

SKILL.md

TypeScript MCP Server on Cloudflare Workers

Build production-ready Model Context Protocol (MCP) servers using TypeScript and deploy them on Cloudflare Workers. This skill covers the official @modelcontextprotocol/sdk, HTTP transport setup, authentication patterns, Cloudflare service integrations, and comprehensive error prevention.


When to Use This Skill

Use this skill when:

  • Building MCP servers to expose APIs, tools, or data to LLMs
  • Deploying serverless MCP endpoints on Cloudflare Workers
  • Integrating external APIs as MCP tools (REST, GraphQL, databases)
  • Creating stateless MCP servers for edge deployment
  • Exposing Cloudflare services (D1, KV, R2, Vectorize) via MCP protocol
  • Implementing authenticated MCP servers with API keys, OAuth, or Zero Trust
  • Building multi-tool MCP servers with resources and prompts
  • Needing production-ready templates that prevent common MCP errors

Do NOT use this skill when:

  • Building Python MCP servers (use FastMCP skill instead)
  • Needing stateful agents with WebSockets (use Cloudflare Agents SDK)
  • Wanting long-running persistent agents with SQLite storage (use Durable Objects)
  • Building local CLI tools (use stdio transport, not HTTP)

Core Concepts

MCP Protocol Components

1. Tools - Functions LLMs can invoke

  • Input/output schemas defined with Zod
  • Async handlers return structured content
  • Can call external APIs, databases, or computations

2. Resources - Static or dynamic data exposure

  • URI-based addressing (e.g., config://app/settings)
  • Templates support parameters (e.g., user://{userId})
  • Return text, JSON, or binary data

3. Prompts - Pre-configured prompt templates

  • Provide reusable conversation starters
  • Can include placeholders and dynamic content
  • Help standardize LLM interactions

4. Completions (Optional) - Argument auto-complete

  • Suggest valid values for tool arguments
  • Improve developer experience

Quick Start

1. Basic MCP Server Template

Use the basic-mcp-server.ts template for a minimal working server:

typescript
// See templates/basic-mcp-server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { Hono } from 'hono';
import { z } from 'zod';
const server = new McpServer({
name: 'my-mcp-server',
version: '1.0.0'
});
// Register a simple tool
server.registerTool(
'echo',
{
description: 'Echoes back the input text',
inputSchema: z.object({
text: z.string().describe('Text to echo back')
})
},
async ({ text }) => ({
content: [{ type: 'text', text }]
})
);
// HTTP endpoint setup
const app = new Hono();
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true
});
// CRITICAL: Close transport on response end to prevent memory leaks
c.res.raw.on('close', () => transport.close());
await server.connect(transport);
await transport.handleRequest(c.req.raw, c.res.raw, await c.req.json());
return c.body(null);
});
export default app;

Install dependencies:

bash
npm install @modelcontextprotocol/sdk hono zod
npm install -D @cloudflare/workers-types wrangler typescript

Deploy:

bash
wrangler deploy

2. Tool-Server Template

Use tool-server.ts for exposing multiple tools (API integrations, calculations):

typescript
// Example: Weather API tool
server.registerTool(
'get-weather',
{
description: 'Fetches current weather for a city',
inputSchema: z.object({
city: z.string().describe('City name'),
units: z.enum(['metric', 'imperial']).default('metric')
})
},
async ({ city, units }, env) => {
const response = await fetch(
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=${units}&appid=${env.WEATHER_API_KEY}`
);
const data = await response.json();
return {
content: [{
type: 'text',
text: `Temperature in ${city}: ${data.main.temp}°${units === 'metric' ? 'C' : 'F'}`
}]
};
}
);

3. Resource-Server Template

Use resource-server.ts for exposing data:

typescript
import { ResourceTemplate } from '@modelcontextprotocol/sdk/types.js';
// Static resource
server.registerResource(
'config',
new ResourceTemplate('config://app', { list: undefined }),
{ description: 'Application configuration' },
async (uri) => ({
contents: [{
uri: uri.href,
mimeType: 'application/json',
text: JSON.stringify({ version: '1.0.0', features: ['tool1', 'tool2'] })
}]
})
);
// Dynamic resource with parameter
server.registerResource(
'user-profile',
new ResourceTemplate('user://{userId}', { list: undefined }),
{ description: 'User profile data' },
async (uri, { userId }, env) => {
const user = await env.DB.prepare('SELECT * FROM users WHERE id = ?').bind(userId).first();
return {
contents: [{
uri: uri.href,
mimeType: 'application/json',
text: JSON.stringify(user)
}]
};
}
);

4. Authenticated Server Template

Use authenticated-server.ts for production security:

typescript
import { Hono } from 'hono';
const app = new Hono();
// API Key authentication middleware
app.use('/mcp', async (c, next) => {
const authHeader = c.req.header('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return c.json({ error: 'Unauthorized' }, 401);
}
const apiKey = authHeader.replace('Bearer ', '');
const isValid = await c.env.MCP_API_KEYS.get(`key:${apiKey}`);
if (!isValid) {
return c.json({ error: 'Invalid API key' }, 403);
}
await next();
});
app.post('/mcp', async (c) => {
// MCP server logic (user is authenticated)
// ... transport setup and handling
});

Authentication Patterns

Pattern 1: API Key (Recommended for Most Cases)

Setup:

  1. Create KV namespace: wrangler kv namespace create MCP_API_KEYS
  2. Add to wrangler.jsonc:
jsonc
{
"kv_namespaces": [
{ "binding": "MCP_API_KEYS", "id": "YOUR_NAMESPACE_ID" }
]
}

Implementation:

typescript
async function verifyApiKey(key: string, env: Env): Promise<boolean> {
const storedKey = await env.MCP_API_KEYS.get(`key:${key}`);
return storedKey !== null;
}

Manage keys:

bash
# Add key
wrangler kv key put --binding=MCP_API_KEYS "key:abc123" "true"
# Revoke key
wrangler kv key delete --binding=MCP_API_KEYS "key:abc123"
Pattern 2: Cloudflare Zero Trust Access
typescript
import { verifyJWT } from '@cloudflare/workers-jwt';
const jwt = c.req.header('Cf-Access-Jwt-Assertion');
if (!jwt) {
return c.json({ error: 'Access denied' }, 403);
}
const payload = await verifyJWT(jwt, c.env.CF_ACCESS_TEAM_DOMAIN);
// User authenticated via Cloudflare Access
Pattern 3: OAuth 2.0

See references/authentication-guide.md for complete OAuth implementation.


Cloudflare Service Integration

D1 Database Tool Example
typescript
server.registerTool(
'query-database',
{
description: 'Executes SQL query on D1 database',
inputSchema: z.object({
query: z.string(),
params: z.array(z.union([z.string(), z.number()])).optional()
})
},
async ({ query, params }, env) => {
const result = await env.DB.prepare(query).bind(...(params || [])).all();
return {
content: [{
type: 'text',
text: JSON.stringify(result.results, null, 2)
}]
};
}
);

Wrangler config:

jsonc
{
"d1_databases": [
{ "binding": "DB", "database_name": "my-db", "database_id": "..." }
]
}
KV Storage Tool Example
typescript
server.registerTool(
'get-cache',
{
description: 'Retrieves cached value by key',
inputSchema: z.object({ key: z.string() })
},
async ({ key }, env) => {
const value = await env.CACHE.get(key);
return {
content: [{ type: 'text', text: value || 'Key not found' }]
};
}
);
R2 Object Storage Tool Example
typescript
server.registerTool(
'upload-file',
{
description: 'Uploads file to R2 bucket',
inputSchema: z.object({
key: z.string(),
content: z.string(),
contentType: z.string().optional()
})
},
async ({ key, content, contentType }, env) => {
await env.BUCKET.put(key, content, {
httpMetadata: { contentType: contentType || 'text/plain' }
});
return {
content: [{ type: 'text', text: `File uploaded: ${key}` }]
};
}
);
Vectorize Search Tool Example
typescript
server.registerTool(
'semantic-search',
{
description: 'Searches vector database',
inputSchema: z.object({
query: z.string(),
topK: z.number().default(5)
})
},
async ({ query, topK }, env) => {
const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
text: query
});
const results = await env.VECTORIZE.query(embedding.data[0], {
topK,
returnMetadata: true
});
return {
content: [{
type: 'text',
text: JSON.stringify(results.matches, null, 2)
}]
};
}
);

Testing Strategies

1. Unit Testing with Vitest
typescript
import { describe, it, expect } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
describe('Calculator Tool', () => {
it('should add two numbers', async () => {
const server = new McpServer({ name: 'test', version: '1.0.0' });
server.registerTool(
'add',
{
description: 'Adds two numbers',
inputSchema: z.object({
a: z.number(),
b: z.number()
})
},
async ({ a, b }) => ({
content: [{ type: 'text', text: String(a + b) }]
})
);
// Test tool execution
const result = await server.callTool('add', { a: 5, b: 3 });
expect(result.content[0].text).toBe('8');
});
});

Install:

bash
npm install -D vitest @cloudflare/vitest-pool-workers

Run:

bash
npx vitest
2. Integration Testing with MCP Inspector
bash
# Run server locally
npm run dev
# In another terminal
npx @modelcontextprotocol/inspector
# Connect to: http://localhost:8787/mcp
3. E2E Testing with Claude Agent SDK

See references/testing-guide.md for comprehensive testing patterns.


Known Issues Prevention

This skill prevents 10+ production issues documented in official MCP SDK and Cloudflare repos:

Issue #1: Export Syntax Issues (CRITICAL)

Error: "Cannot read properties of undefined (reading 'map')" Source: honojs/hono#3955, honojs/vite-plugins#237 Why It Happens: Incorrect export format with Vite build causes cryptic errors Prevention:

typescript
// ❌ WRONG - Causes cryptic build errors
export default { fetch: app.fetch };
// ✅ CORRECT - Direct export
export default app;
Issue #2: Unclosed Transport Connections

Error: Memory leaks, hanging connections Source: Best practice from SDK maintainers Why It Happens: Not closing StreamableHTTPServerTransport on request end Prevention:

typescript
app.post('/mcp', async (c) => {
const transport = new StreamableHTTPServerTransport(/*...*/);
// CRITICAL: Always close on response end
c.res.raw.on('close', () => transport.close());
// ... handle request
});

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
10
Repository forks
4
7-day trend score
12

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 23 Mar 2026, 7 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/jackspace/claudeskillz/typescript-mcp/badge)](https://verified-skill.com/skills/jackspace/claudeskillz/typescript-mcp)

Something wrong with this skill or its page? Tell us.