Skip to content

futuritywork/plugins

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@futuritywork/plugins

A minimal MCP (Model Context Protocol) server library for Bun with auth forwarding support.

Installation

bun add git+ssh://github.com/futuritywork/plugins.git#v0.0.1

Quick Start

import { z } from "zod";
import { mcp } from "@futuritywork/plugins";

const app = mcp({
  name: "my-server",
  version: "1.0.0",
});

app.tool("greet", {
  description: "Greet a user",
  input: z.object({
    name: z.string(),
  }),
  handler: async ({ name }) => {
    return { message: `Hello, ${name}!` };
  },
});

app.listen(3000);
bun run server.ts
# MCP server listening on http://localhost:3000/mcp

API

mcp(options)

Create an MCP application.

const app = mcp({
  name: "my-server",           // required
  version: "1.0.0",            // required
  path: "/mcp",                // default: "/mcp"
  instructions: "...",         // optional system instructions
  capabilities: { ... },       // optional MCP capabilities
  pluginManifest: { ... },     // optional auth forwarding manifest
});

app.tool(name, options)

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" };
  },
});

app.resource(uri, options)

Register a resource.

app.resource("config://settings", {
  description: "Application settings",
  fetch: async () => {
    return { theme: "dark", language: "en" };
  },
});

app.use(plugin)

Apply a plugin.

import { cors } from "@futuritywork/plugins";

app.use(
  cors({
    allowOrigin: "*",
    allowMethods: ["GET", "POST", "DELETE", "OPTIONS"],
  })
);

app.middleware(fn)

Add middleware directly.

app.middleware(async (req, next) => {
  console.log(`${req.method} ${req.url}`);
  return next(req);
});

app.listen(port, transport?)

Start the server.

// HTTP (default)
await app.listen(3000);

// WebSocket
await app.listen(3000, "websocket");

CORS

import { mcp, cors } from "@futuritywork/plugins";

const app = 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);

OAuth

Configure OAuth metadata for /.well-known/oauth-authorization-server:

const app = 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"],
  },
});

Authentication

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 (v1)

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.

1. Generate a signing keypair

bun run keygen

This prints an Ed25519 keypair. Keep the private key secret; register the public key with the Futurity API.

2. Configure the plugin manifest

const app = 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",
  },
});

3. Serve the manifest

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.

Chained Auth (v2)

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.

const app = 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

Signing utilities

import { generateKeyPair, signPayload, verifyPayload } from "@futuritywork/plugins";

const { privateKey, publicKey } = generateKeyPair();
const jws = signPayload('{"hello":"world"}', privateKey);
const valid = verifyPayload('{"hello":"world"}', jws, publicKey); // true

Authentication

Custom auth middleware:

const app = mcp({
  name: "server",
  version: "1.0.0",
  auth: async (req) => {
    const token = req.headers.get("authorization")?.replace("Bearer ", "");
    if (!token) return false;
    return await validateToken(token);
  },
});

Stateful Patterns

const state = {
  counter: 0,
  items: new Map<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 };
  },
});

Multi-Session Support

The HTTP transport supports multiple concurrent client sessions.

const app = mcp({ name: "server", version: "1.0.0" });

app.tool("example", { handler: async () => ({ ok: true }) });

await app.listen(3000);

console.log(app.activeSessions);
await app.stop();

Direct Transport Usage

import { StreamableHttpServer, StreamableHttpTransport } from "@futuritywork/plugins";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const server = new StreamableHttpServer({
  port: 3000,
  path: "/mcp",
  onSession: async (transport: StreamableHttpTransport) => {
    const mcp = new McpServer({ name: "server", version: "1.0.0" });
    await mcp.connect(transport);
  },
});

await server.start();

Examples

Run examples from the examples/ directory:

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 forwarding

monday.com Integration Example

A complete monday.com MCP server is included demonstrating auth forwarding:

# Set environment variables
export FUTURITY_SIGNING_KEY="your-private-key-base64"

# Run the server
bun examples/monday/index.ts

Features:

  • Boards: List and get board details
  • Items: Full CRUD operations
  • Updates: Read and create comments
  • Groups: Create new groups

Types

import type {
  // App
  McpApp,
  McpAppOptions,
  ToolOptions,
  ResourceOptions,
  Middleware,
  AuthMiddleware,
  WellKnownEntry,

  // Plugin Manifest
  PluginManifest,
  PluginManifestOptions,
  Auth,
  AuthForwarding,
  ChainedAuth,

  // Chained Auth
  Session,
  PendingSession,
  SessionStore,
  UserContext,
  ChainedAuthConfig,
  ChainedAuthHandlers,
  PlatformAssertionClaims,
  VerifiedPlatformAssertion,

  // Transports
  StreamableHttpServer,
  StreamableHttpServerOptions,
  StreamableHttpTransport,
  WebSocketTransport,
  WebSocketTransportOptions,
} from "@futuritywork/plugins";

Chained Auth Utilities

import {
  // Session management
  createPendingSession,
  activateSession,
  expireSession,
  revokeSession,
  isSessionValid,
  InMemorySessionStore,

  // Token generation
  generatePluginToken,
  validatePluginToken,
  generateRefreshToken,

  // State management
  generateChainedState,
  parseChainedState,

  // Request binding
  hashPluginToken,
  hashRequest,
  validatePlatformAssertion,
  validateAuthenticatedRequest,

  // JWT validation
  validateUserContextJwt,
  clearJwksCache,
} from "@futuritywork/plugins";

License

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.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •