A minimal MCP (Model Context Protocol) server library for Bun with auth forwarding support.
bun create futurity-plugin my-plugin
cd my-plugin
bun run src/index.tsbun add @futurity/pluginsimport{mcp,t}from"@futurity/plugins";constapp=mcp({name: "my-plugin",version: "1.0.0",});app.tool("hello",{description: "Say hello",input: t.obj({name: t.str}),handler: async({ name })=>({greeting: `Hello, ${name}!`,}),});// Option A: Use the fetch handler (works in any runtime)Bun.serve({port: 3000,fetch: app.fetch});// Option B: Convenience method (Bun only)awaitapp.listen(3000);Note: The WebSocket transport (
app.listen(port, "websocket")) is Bun-only. The HTTP transport (app.fetch) works in any runtime that supports the Fetch API.
Create an MCP application.
constapp=mcp({name: "my-server",// requiredversion: "1.0.0",// requiredpath: "/mcp",// default: "/mcp"instructions: "...",// optional system instructionscapabilities: { ... },// optional MCP capabilitiespluginManifest: { ... },// optional auth forwarding manifest});Register a tool.
app.tool("add",{description: "Add two numbers",input: z.object({a: z.number(),b: z.number(),}),handler: async({ a, b })=>{return{sum: a+b};},});Tools without input:
app.tool("ping",{description: "Health check",handler: async()=>{return{status: "ok"};},});Register a resource.
app.resource("config://settings",{description: "Application settings",fetch: async()=>{return{theme: "dark",language: "en"};},});Apply a plugin.
import{cors}from"@futurity/plugins";app.use(cors({allowOrigin: "*",allowMethods: ["GET","POST","DELETE","OPTIONS"],}));Add middleware directly.
app.middleware(async(req,next)=>{console.log(`${req.method}${req.url}`);returnnext(req);});The standard Fetch API handler. Works in any runtime (Bun, Deno, Cloudflare Workers, Node with a fetch-compatible adapter, etc.):
Bun.serve({port: 3000,fetch: app.fetch});Convenience method to start the server. Requires Bun.
// HTTP (default)awaitapp.listen(3000);// WebSocket (Bun only)awaitapp.listen(3000,"websocket");import{mcp,cors}from"@futurity/plugins";constapp=mcp({name: "server",version: "1.0.0"});app.use(cors({allowOrigin: "*",allowMethods: ["GET","POST","DELETE","OPTIONS"],allowHeaders: ["Content-Type","Authorization","Accept","Mcp-Session-Id"],exposeHeaders: ["Mcp-Session-Id"],maxAge: 86400,credentials: false,}));app.listen(3000);Configure OAuth metadata for /.well-known/oauth-authorization-server:
constapp=mcp({name: "server",version: "1.0.0",oauth: {issuer: "https://auth.example.com",authorizationEndpoint: "https://auth.example.com/oauth/authorize",tokenEndpoint: "https://auth.example.com/oauth/token",jwksUri: "https://auth.example.com/.well-known/jwks.json",scopesSupported: ["openid","profile"],},});There are two auth modes for Futurity plugins:
| Mode | Description | Use Case |
|---|---|---|
| Auth Forwarding (v1) | Platform manages OAuth tokens | Simple integrations |
| Chained Auth (v2) | Plugin manages sessions & tokens | Complex integrations, multi-service |
Auth forwarding lets the Futurity platform manage OAuth tokens on behalf of your plugin. Instead of implementing OAuth end-to-end, you declare your auth requirements in a signed manifest.
bun run keygenThis prints an Ed25519 keypair. Keep the private key secret; register the public key with the Futurity API.
constapp=mcp({name: "my-plugin",version: "1.0.0",pluginManifest: {specVersion: 2,pluginId: "my-plugin",name: "My Plugin",version: "1.0.0",signingKey: process.env.FUTURITY_SIGNING_KEY!,auth: {type: "forwarding",tokenEndpoint: "https://auth.example.com/oauth2/token",authorizationEndpoint: "https://auth.example.com/oauth2/authorize",requiredScopes: ["read","write"],deliveryMethod: "header",// "header" (default) or "query"maxTokenTtl: 3600,// optional, seconds},mcpUrl: "https://my-plugin.example.com/mcp",},});The signed manifest is automatically served at:
GET /.well-known/futurity/plugin
The response includes an X-Futurity-Signature header with an Ed25519 JWS signature.
For plugins that need to manage their own sessions and store third-party tokens, use chained auth. The plugin controls the OAuth flow and issues its own tokens.
constapp=mcp({name: "my-plugin",version: "1.0.0",pluginManifest: {specVersion: 2,pluginId: "my-plugin",name: "My Plugin",version: "1.0.0",signingKey: process.env.PLUGIN_SIGNING_KEY!,auth: {type: "chained",authorizationEndpoint: "https://plugin.example.com/auth/authorize",callbackEndpoint: "https://plugin.example.com/auth/callback",tokenEndpoint: "https://plugin.example.com/auth/token",requiredUserContext: ["user_id","email"],},mcpUrl: "https://plugin.example.com/mcp",},chainedAuth: {sessionStore: mySessionStore,platformJwksUrl: "https://platform.example.com/.well-known/jwks.json",pluginSigningKey: process.env.PLUGIN_SIGNING_KEY!,handlers: {onAuthorize: async(userContext,platformState,platformCallback)=>{ ... },onCallback: async(req)=>{ ... },onToken: async(req)=>{ ... },},},});See docs/chained-auth.md for full documentation including:
- Complete implementation guide
- Request binding for anti-replay protection
- Session management
- Platform integration guide
import{generateKeyPair,signPayload,verifyPayload}from"@futurity/plugins";const{ privateKey, publicKey }=generateKeyPair();constjws=signPayload('{"hello":"world"}',privateKey);constvalid=verifyPayload('{"hello":"world"}',jws,publicKey);// trueCustom auth middleware:
constapp=mcp({name: "server",version: "1.0.0",auth: async(req)=>{consttoken=req.headers.get("authorization")?.replace("Bearer ","");if(!token)returnfalse;returnawaitvalidateToken(token);},});conststate={counter: 0,items: newMap<string,string>(),};app.tool("increment",{handler: async()=>{state.counter++;return{counter: state.counter};},});app.tool("set",{input: z.object({key: z.string(),value: z.string()}),handler: async({ key, value })=>{state.items.set(key,value);return{ key, value };},});The HTTP transport supports multiple concurrent client sessions.
constapp=mcp({name: "server",version: "1.0.0"});app.tool("example",{handler: async()=>({ok: true})});awaitapp.listen(3000);console.log(app.activeSessions);awaitapp.stop();import{StreamableHttpServer,StreamableHttpTransport}from"@futurity/plugins";import{McpServer}from"@modelcontextprotocol/sdk/server/mcp.js";constserver=newStreamableHttpServer({port: 3000,path: "/mcp",onSession: async(transport: StreamableHttpTransport)=>{constmcp=newMcpServer({name: "server",version: "1.0.0"});awaitmcp.connect(transport);},});awaitserver.start();The examples/ directory in this repo contains runnable examples. Clone the repo and run them directly:
git clone https://github.com/futuritywork/plugins.git
cd plugins
bun examples/calculator.ts # Math operations and unit conversion
bun examples/cors.ts # CORS configuration
bun examples/database.ts # Document database
bun examples/filesystem.ts # Virtual filesystem
bun examples/oauth.ts # OAuth metadata
bun examples/stateful.ts # Counter, notes, key-value store
bun examples/todo-app.ts # Todo list with CRUD
bun examples/weather-api.ts # Weather data API
bun examples/monday/index.ts # monday.com integration with auth forwardingA complete monday.com MCP server is included demonstrating auth forwarding:
# Set environment variablesexport FUTURITY_SIGNING_KEY="your-private-key-base64"# Run the server
bun examples/monday/index.tsFeatures:
- Boards: List and get board details
- Items: Full CRUD operations
- Updates: Read and create comments
- Groups: Create new groups
importtype{// AppMcpApp,McpAppOptions,ToolOptions,ResourceOptions,Middleware,AuthMiddleware,WellKnownEntry,// Plugin ManifestPluginManifest,PluginManifestOptions,Auth,AuthForwarding,ChainedAuth,// Chained AuthSession,PendingSession,SessionStore,UserContext,ChainedAuthConfig,ChainedAuthHandlers,PlatformAssertionClaims,VerifiedPlatformAssertion,// TransportsStreamableHttpServer,StreamableHttpServerOptions,StreamableHttpTransport,WebSocketTransport,WebSocketTransportOptions,}from"@futurity/plugins";import{// Session managementcreatePendingSession,activateSession,expireSession,revokeSession,isSessionValid,InMemorySessionStore,// Token generationgeneratePluginToken,validatePluginToken,generateRefreshToken,// State managementgenerateChainedState,parseChainedState,// Request bindinghashPluginToken,hashRequest,validatePlatformAssertion,validateAuthenticatedRequest,// JWT validationvalidateUserContextJwt,clearJwksCache,}from"@futurity/plugins";Copyright © 2025 Futurity Technologies Pte Ltd
This software is licensed under the Business Source License 1.1 (BSL). You may use, copy, modify, and redistribute for non-production purposes. Production use requires a commercial license until the Change Date (2030-01-01), after which the software becomes available under the GNU General Public License v3.0 or later.
See LICENSE for full terms.