Mcpster
Agnostic TypeScript SDK for building MCP servers
Ask AI about Mcpster
Powered by Claude Β· Grounded in docs
I know everything about Mcpster. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
mcpster
A TypeScript SDK for building MCP servers β removes the boilerplate so you focus on what you expose, not how.
Overview
mcpster is an agnostic TypeScript SDK for building Model Context Protocol (MCP) servers. It wraps @modelcontextprotocol/sdk behind a fluent, chainable API with Zod-first schema validation, URI template parsing, and consistent error handling. Servers built with mcpster start as local stdio processes and have a clear migration path to remote hosting and public npm distribution β without rewriting any business logic.
Features
createServer()+ chainabledefineTool/defineResource/definePromptAPI- Zod schema validation enforced at tool registration time
- URI template parameter extraction for resources (
templates://{name}) - Automatic error wrapping β handlers throw, mcpster returns MCP-compliant error responses
- Scope-aware server naming (project, user, or public scope)
- stdio and Streamable HTTP transports β switch with a single config field
- OAuth 2.1 (PKCE) support for protected remote servers β enable with
auth: true - Designed for progressive deployment: local stdio β remote Streamable HTTP β hosted β npm package
Installation
npm install mcpster
Usage
stdio (default)
import { createServer } from 'mcpster'
import { z } from 'zod'
createServer({ name: 'my-server', version: '1.0.0' })
.defineTool({
name: 'get_template',
description: 'Retrieve a template by name',
schema: z.object({ name: z.string() }),
handler: async ({ name }) => { /* ... */ },
})
.defineResource({
uri: 'templates://{name}',
description: 'Template content by name',
resolver: async ({ name }) => { /* ... */ },
})
.definePrompt({
name: 'summarize',
handler: async (args) => { /* ... */ },
})
.start() // stdio transport
Register the server per-project:
claude mcp add my-server -- npx mcpster start
Streamable HTTP transport
import { createServer } from 'mcpster'
createServer({
name: 'my-server',
version: '1.0.0',
transport: 'http',
http: { port: 3000, path: '/mcp' },
})
.defineTool({ /* ... */ })
.start() // listens on http://localhost:3000/mcp
Register with Claude Code:
claude mcp add --transport http my-server http://localhost:3000/mcp
OAuth 2.1 (protected remote server)
Enable auth: true to require clients to complete an OAuth 2.1 PKCE flow before accessing the MCP endpoint. Set baseUrl to your public server URL so OAuth discovery metadata points to the right place.
createServer({
name: 'my-server',
version: '1.0.0',
transport: 'http',
http: {
port: 3000,
path: '/mcp',
auth: true,
baseUrl: 'https://my-server.example.com',
},
}).start()
mcpster handles the full OAuth flow: dynamic client registration (/register), authorization (/authorize), token issuance (/token), and bearer token verification on the MCP endpoint. All dynamically registered clients are treated as public PKCE clients β no pre-shared secrets required.
For persistence across restarts, point clientsFile at a file on a mounted volume:
http: {
auth: true,
baseUrl: 'https://my-server.example.com',
clientsFile: '/data/oauth-clients.json',
}
Configuration
| Option | Default | Description |
|---|---|---|
name | required | Server name, used for MCP registration |
version | required | Semver string |
scope | process.cwd() | Project root; determines scope for naming and resource resolution |
transport | 'stdio' | Transport to use: 'stdio' or 'http' |
http.port | 3000 | Port to listen on (HTTP transport only) |
http.path | '/mcp' | Request path to handle (HTTP transport only) |
http.auth | false | Enable OAuth 2.1 PKCE protection on the MCP endpoint |
http.baseUrl | http://localhost:<port> | Public base URL β used for OAuth metadata and token endpoints |
http.clientsFile | (in-memory) | Path to persist registered OAuth clients across restarts |
Project Structure
mcpster/
βββ src/
β βββ index.ts # Public API β re-exports createServer and types
β βββ server.ts # McpsterServer class β core orchestrator
β βββ tool.ts # defineTool β schema validation + handler wiring
β βββ resource.ts # defineResource β URI template matching + resolver
β βββ prompt.ts # definePrompt β prompt template registration
β βββ transport/
β β βββ stdio.ts # stdio transport adapter
β β βββ http.ts # Streamable HTTP transport adapter
β βββ deploy/
β β βββ types.ts # Shared DeployConfig, DeployResult, DeployAdapter types
β β βββ railway.ts # Railway adapter (generateManifest, deploy)
β β βββ fly.ts # Fly.io adapter (generateManifest, generateDockerfile, deploy)
β β βββ cloudflare.ts # Cloudflare Workers adapter (generateManifest, generateWorkerShim, deploy)
β β βββ cli.ts # mcpster-deploy CLI entry point
β βββ types.ts # Shared TypeScript types and interfaces
βββ tests/
β βββ server.test.ts
β βββ tool.test.ts
β βββ resource.test.ts
β βββ prompt.test.ts
β βββ transport.test.ts
β βββ deploy.test.ts
βββ examples/
β βββ minimal/ # hello-mcp β tool + resource + prompt, runnable reference
βββ package.json
βββ tsconfig.json
Deploy (v3)
mcpster ships a deploy kit that pushes your Streamable HTTP server to Railway, Fly.io, or Cloudflare Workers with a single command. The Streamable HTTP transport must already be configured on your server before deploying β the deploy adapters wrap the existing transport: 'http' path.
Prerequisites
| Target | Prerequisite |
|---|---|
| Railway | railway CLI installed and authenticated (railway login) |
| Fly.io | flyctl installed and authenticated (fly auth login) |
| Cloudflare Workers | wrangler installed and authenticated (wrangler login) |
Usage
# Dry-run: print the generated manifest without deploying
npx mcpster-deploy --target railway --dry-run
npx mcpster-deploy --target fly --dry-run
npx mcpster-deploy --target cloudflare --dry-run
# Deploy
npx mcpster-deploy --target railway
npx mcpster-deploy --target fly --region lhr
npx mcpster-deploy --target cloudflare
All options:
-t, --target <target> Deploy target (railway | fly | cloudflare)
-n, --name <name> Server name (default: read from package.json)
-v, --version <version> Server version (default: read from package.json)
-p, --port <port> HTTP port (default: 3000)
-r, --region <region> Preferred region β Fly.io only (default: iad)
-d, --dry-run Print the generated manifest without deploying
-h, --help Show help
Local β Hosted migration path
Stage 1 β Local stdio (no infrastructure)
createServer({ name: 'my-server', version: '1.0.0' }).start()
Stage 2 β Local Streamable HTTP (same server, different transport)
createServer({
name: 'my-server',
version: '1.0.0',
transport: 'http',
http: { port: 3000, path: '/mcp' },
}).start()
Stage 3 β Hosted (one command, no code changes)
npx mcpster-deploy --target railway
# β Deployed to Railway: https://my-server.up.railway.app
Then connect Claude Code to the deployed server:
claude mcp add --transport http my-server https://my-server.up.railway.app/mcp
The server code is identical across all three stages.
License
MIT
