npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mcp-s-oauth

v1.0.5

Published

Express middleware library for MCP (Model Context Protocol) OAuth authentication

Readme

MCP S OAuth

Universal OAuth middleware library for MCP (Model Context Protocol) servers with support for any OAuth provider.

Overview

This library provides OAuth authentication for MCP servers using a flexible connector pattern. It handles the complete OAuth flow for any OAuth provider and provides an authenticated /mcp endpoint where you create your own MCP server with tools. Built using the official MCP TypeScript SDK.

Features

  • 🔐 Universal OAuth Support - Works with 20+ OAuth providers including GitHub, Google, Slack, and more
  • 🔌 Connector Pattern - Pre-built connectors for popular services + easily add support for new OAuth providers
  • 🚀 Express Middleware - Simple integration with app.use()
  • 🛠️ Bring Your Own MCP Server - Create MCP server in your handler function
  • 📊 Session Management - Automatic session handling via MCP SDK
  • 🌐 Authenticated Context - Access OAuth tokens in your tools
  • 🎯 Production Ready - Built with official MCP SDK components
  • 🔑 Minimal Scopes - Uses minimal OAuth scopes for enhanced security

Installation

npm install

Quick Start

Basic Usage

import express from "express"
import { Server } from "@modelcontextprotocol/sdk/server/index.js"
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"
import { McpOAuth } from "mcp-s-oauth"
import type { McpOAuthConfig } from "mcp-s-oauth"

const app = express()

// Choose your OAuth provider connector
import { githubConnector } from "mcp-s-oauth"
// or import { googleConnector } from "mcp-s-oauth"
// or create your own custom connector

// Configure OAuth with any provider
const config: McpOAuthConfig = {
  baseUrl: "http://localhost:3000",
  clientId: "your-oauth-client-id",
  clientSecret: "your-oauth-client-secret",
  connector: githubConnector  // or any other connector
}

// Create your MCP handler - this is where YOU create the MCP server
const mcpHandler = async (req: express.Request, res: express.Response, { authInfo }) => {
  // Access the OAuth token from any provider
  const oauthToken = authInfo.token
  
  // Create transport and your MCP server
  const transport = new StreamableHTTPServerTransport(/* options */)
  const mcpServer = new Server({ name: "my-server", version: "1.0.0" })
  
  // Register your tools (customize based on your OAuth provider)
  mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
    tools: [{
      name: "get_profile",
      description: "Get authenticated user profile",
      inputSchema: { type: "object" }
    }]
  }))
  
  mcpServer.setRequestHandler(CallToolRequestSchema, async (request) => {
    if (request.params.name === "get_profile") {
      // Call your OAuth provider's API using the token
      // Example for GitHub: https://api.github.com/user
      // Example for Google: https://www.googleapis.com/oauth2/v2/userinfo
      const apiUrl = "https://api.github.com/user" // adjust for your provider
      
      const response = await fetch(apiUrl, {
        headers: { "Authorization": `Bearer ${authInfo.token}` }
      })
      const userData = await response.json()
      
      return {
        content: [{ type: "text", text: `Profile: ${JSON.stringify(userData, null, 2)}` }]
      }
    }
  })
  
  // Handle the MCP request
  await mcpServer.connect(transport)
  await transport.handleRequest(req, res, req.body)
}

// Use the library
const mcpOAuth = McpOAuth(config, mcpHandler)
app.use("/", mcpOAuth.router)

app.listen(3000)

OAuth Provider Setup

GitHub OAuth App Setup

  1. Go to GitHub Developer Settings
  2. Create a new OAuth App:
    • Authorization callback URL: ${baseUrl}/oauth/callback (e.g., http://localhost:3000/oauth/callback)
  3. Use the Client ID and Secret in your config

Google OAuth App Setup

  1. Go to Google Cloud Console
  2. Create a new project or select existing
  3. Enable the "Google+ API" or "People API"
  4. Create OAuth 2.0 credentials:
    • Authorized redirect URIs: ${baseUrl}/oauth/callback (e.g., http://localhost:3000/oauth/callback)
  5. Use the Client ID and Secret in your config

Custom OAuth Provider Setup

For any OAuth 2.0 provider, you'll need:

  1. Client ID and Client Secret from your provider
  2. Authorization callback URL set to: ${baseUrl}/oauth/callback

Available Connectors

This library includes pre-built connectors for popular OAuth providers with minimal scopes for enhanced security:

| Service | Connector | |---------|-----------| | Slack | slackConnector | | Gmail | gmailConnector | | Google Calendar | googleCalendarConnector | | Jira | jiraConnector | | Trello | trelloConnector | | Asana | asanaConnector | | Notion | notionConnector | | Monday | mondayConnector | | GitHub | githubConnector | | GitLab | gitlabConnector | | Google Workspace | googleWorkspaceConnector | | Google Drive | googleDriveConnector | | Google Sheets | googleSheetsConnector | | Google Forms | googleFormsConnector | | Google Slides | googleSlidesConnector | | Salesforce | salesforceConnector | | Figma | figmaConnector | | Zeplin | zeplinConnector | | Amplitude | amplitudeConnector | | Google Analytics | googleAnalyticsConnector | | Google Maps | googleMapsConnector | | Discord | discordConnector | | Spotify | spotifyConnector | | Twitter | twitterConnector |

Example Usage with GitHub

import { githubConnector } from "mcp-s-oauth"

const config: McpOAuthConfig = {
  baseUrl: "http://localhost:3000",
  clientId: process.env.GITHUB_CLIENT_ID!,
  clientSecret: process.env.GITHUB_CLIENT_SECRET!,
  connector: githubConnector,
}

Custom Connector

For any OAuth provider not listed above:

import type { Connector } from "mcp-s-oauth"

const myCustomConnector: Connector = {
  authUrl: "https://your-provider.com/oauth/authorize",
  tokenUrl: "https://your-provider.com/oauth/token",
  scopes: ["read", "write"],
  codeExchangeConfig: {
    isForm: true,
    modelCredentialsMapping: `{
      "access_token": access_token, 
      "expires_at": $fromMillis($millis() + expires_in * 1000),
      "refresh_token": refresh_token,
      "scope": scope,
      "token_type": token_type
    }`,
  },
  authInitUrlParams: {
    prompt: "consent",
  },
}

Creating Custom Connectors

The Connector interface allows you to integrate any OAuth 2.0 provider. Here's how to create your own:

Connector Interface

export interface Connector {
  authUrl?: string                    // OAuth authorization endpoint
  tokenUrl?: string                   // OAuth token exchange endpoint  
  refreshTokenUrl?: string            // Token refresh endpoint (defaults to tokenUrl)
  scopes?: string[] | readonly string[] // OAuth scopes to request
  codeExchangeConfig?: {
    modelCredentialsMapping?: JsonataString<OAuthCredentials> | ((config: any) => OAuthCredentials)
    isForm?: boolean                  // Use form encoding vs JSON for token exchange
    authorizationMapping?: JsonataString<string> | ((config: any) => string)
  }
  authInitUrlParams?: Record<string, string> // Additional OAuth params
}

Step-by-Step Guide

  1. Find OAuth Documentation for your provider (authorization URL, token URL, scopes)

  2. Create Connector File:

// src/connectors/my-provider.ts
import type { Connector } from "../types/connector.types.js"

export const myProviderConnector: Connector = {
  authUrl: "https://api.myprovider.com/oauth/authorize",
  tokenUrl: "https://api.myprovider.com/oauth/token", 
  scopes: ["read", "write"],
  codeExchangeConfig: {
    isForm: true, // Most providers use form encoding
    modelCredentialsMapping: `{
      "access_token": access_token, 
      "expires_at": $fromMillis($millis() + expires_in * 1000),
      "refresh_token": refresh_token,
      "scope": scope,
      "token_type": token_type
    }`,
  },
}
  1. Handle Special Cases:
// Provider requires special auth parameters
export const specialProviderConnector: Connector = {
  authUrl: "https://special.com/oauth/authorize",
  tokenUrl: "https://special.com/oauth/token",
  scopes: ["user:read"],
  authInitUrlParams: {
    access_type: "offline",
    prompt: "consent",
    response_mode: "query",
  },
  codeExchangeConfig: {
    isForm: false, // This provider uses JSON
  },
}
  1. Use Function for Complex Mapping:
export const complexProviderConnector: Connector = {
  authUrl: "https://complex.com/oauth/authorize",
  tokenUrl: "https://complex.com/oauth/token",
  scopes: ["api"],
  codeExchangeConfig: {
    isForm: true,
    // Use function for complex response mapping
    modelCredentialsMapping: (tokenResponse) => ({
      access_token: tokenResponse.accessToken, // Different field name
      expires_at: new Date(Date.now() + tokenResponse.expiresIn * 1000).toISOString(),
      refresh_token: tokenResponse.refreshToken,
      refresh_token_expires_at: null,
      scope: tokenResponse.scope,
      token_type: "Bearer",
    }),
  },
}

Common OAuth Patterns

| Provider | Auth URL | Token URL | Form Encoding | Special Notes | |----------|----------|-----------|---------------|---------------| | GitHub | /login/oauth/authorize | /login/oauth/access_token | ❌ JSON | Simple flow | | Google | /o/oauth2/v2/auth | /oauth2/token | ✅ Form | Use access_type: offline | | Discord | /api/oauth2/authorize | /api/oauth2/token | ✅ Form | Standard OAuth | | Twitter | /i/oauth2/authorize | /2/oauth2/token | ✅ Form | Requires PKCE | | LinkedIn | /authorization | /accessToken | ✅ Form | Different field names |

Testing Your Connector

  1. Create test config:
const config: McpOAuthConfig = {
  baseUrl: "http://localhost:3000",
  clientId: process.env.MY_PROVIDER_CLIENT_ID!,
  clientSecret: process.env.MY_PROVIDER_CLIENT_SECRET!,
  connector: myProviderConnector,
}
  1. Test OAuth flow:

    • Visit /auth/authorize
    • Complete OAuth on provider
    • Check console logs for token exchange
    • Test API calls with the token
  2. Common Issues:

    • Form vs JSON: Check provider docs for token endpoint format
    • Field Names: Response might use different field names
    • Scopes: Ensure scopes are valid for your provider
    • Callback URL: Must match OAuth app configuration

Example Tools Implementation

The tools you create depend on your OAuth provider and their APIs. The library provides the OAuth token - you implement the tools. Here's a complete GitHub example:

GitHub Tools Example

// In your mcpHandler function
mcpServer.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "github_me",
      description: "Get authenticated user's GitHub profile",
      inputSchema: { type: "object", properties: {} }
    },
    {
      name: "github_repos",
      description: "List user's repositories",
      inputSchema: { 
        type: "object",
        properties: {
          type: { type: "string", enum: ["all", "owner", "member"] }
        }
      }
    },
    {
      name: "github_issues",
      description: "List user's issues",
      inputSchema: { 
        type: "object",
        properties: {
          state: { type: "string", enum: ["open", "closed", "all"] }
        }
      }
    }
  ]
}))

mcpServer.setRequestHandler(CallToolRequestSchema, async (request, { authInfo }) => {
  if (request.params.name === "github_me") {
    const response = await fetch("https://api.github.com/user", {
      headers: { "Authorization": `Bearer ${authInfo.token}` }
    })
    return { content: [{ type: "text", text: await response.text() }] }
  }
  
  if (request.params.name === "github_repos") {
    const { type = "all" } = request.params.arguments || {}
    const response = await fetch(`https://api.github.com/user/repos?type=${type}`, {
      headers: { "Authorization": `Bearer ${authInfo.token}` }
    })
    return { content: [{ type: "text", text: await response.text() }] }
  }

  if (request.params.name === "github_issues") {
    const { state = "open" } = request.params.arguments || {}
    const response = await fetch(`https://api.github.com/issues?state=${state}`, {
      headers: { "Authorization": `Bearer ${authInfo.token}` }
    })
    return { content: [{ type: "text", text: await response.text() }] }
  }
})

Other Provider Examples

For other OAuth providers, follow the same pattern using their respective APIs:

  • Slack: Use https://slack.com/api/ endpoints
  • Google Services: Use Google API endpoints (Gmail, Drive, Sheets, etc.)
  • Notion: Use https://api.notion.com/v1/ endpoints
  • Jira: Use Atlassian REST API endpoints
  • And more... - Each connector works with the provider's standard OAuth API

MCP Client Usage

Using with MCP Clients

Your OAuth-authenticated MCP server implements the MCP (Model Context Protocol) and can be used with any MCP-compatible client:

import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"

const client = new Client({
  name: "oauth-mcp-client",
  version: "1.0.0"
})

const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:3000/mcp")
)

await client.connect(transport)

// List available tools (depends on your implementation)
const tools = await client.listTools()
console.log("Available tools:", tools)

// Call any tool you've implemented
const result = await client.callTool({
  name: "get_profile", // or any tool name you've implemented
  arguments: {}
})
console.log("Result:", result)

Authentication Flow

  1. Start OAuth Flow: Make a request to /auth/authorize to initiate OAuth with your provider
  2. User Authorization: User is redirected to OAuth provider to authorize your application
  3. Token Exchange: Provider redirects back with an authorization code
  4. MCP Access: Use the obtained token to make authenticated MCP requests to /mcp

API Reference

Server Information

  • GET / - Server information, available tools, and authentication endpoints
  • GET /health - Health check endpoint

Authentication Endpoints

  • POST /auth/authorize - Start OAuth flow with configured provider
  • POST /auth/token - Exchange authorization code for access token
  • GET /oauth/callback - OAuth callback handler (used by the provider)

MCP Protocol

  • ALL /mcp - Main MCP endpoint supporting all MCP protocol methods
    • Requires Bearer token authentication
    • Supports session management with mcp-session-id header
    • Handles tools/list and tools/call requests
    • Returns tools you've implemented in your mcpHandler

Testing the Server

Test the MCP server using the official SDK client:

import { Client } from "@modelcontextprotocol/sdk/client/index.js"
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"

// Connect to the server
const client = new Client({ name: "test-client", version: "1.0.0" })
const transport = new StreamableHTTPClientTransport(
  new URL("http://localhost:3000/mcp"),
  {
    requestInit: {
      headers: {
        'Authorization': 'Bearer your-oauth-token-here'
      }
    }
  }
)

await client.connect(transport)

// Test listing tools
const tools = await client.listTools()
console.log("Available tools:", tools.tools)

// Test calling any tool you've implemented
const result = await client.callTool({
  name: "get_profile", // or any tool name you've implemented
  arguments: {}
})
console.log("Result:", result.content)

Development

Project Structure

src/
├── index.ts               # Library exports
├── server.ts              # MCP server implementation  
├── lib.ts                 # Core utilities
├── connectors/            # OAuth provider connectors
├── services/              # Database & auth services
└── types/                 # TypeScript types

Building and Running

# Install dependencies
npm install

# Development mode with auto-reload
npm run dev

# Build for production
npm run build

# Run production build
npm start

# Run tests
npm test

# Lint code
npm run lint

# Format code
npm run format

Adding New Tools

To add new MCP tools to your server:

  1. Update the tools list in ListToolsRequestSchema handler
  2. Add tool implementation in CallToolRequestSchema handler
  3. Use the OAuth token to call your provider's APIs

Example (provider-agnostic):

// In ListToolsRequestSchema handler
{
  name: "get_user_data",
  description: "Get user data from OAuth provider",
  inputSchema: {
    type: "object",
    properties: {
      fields: {
        type: "array",
        items: { type: "string" },
        description: "Fields to retrieve"
      }
    }
  }
}

// In CallToolRequestSchema handler
if (name === "get_user_data") {
  const { fields = [] } = request.params.arguments || {}
  
  // Use the OAuth token to call your provider's API
  const apiUrl = "https://api.your-provider.com/user" // adjust for your provider
  const response = await fetch(apiUrl, {
    headers: {
      "Authorization": `Bearer ${authInfo.token}`,
      "Accept": "application/json"
    }
  })
  
  const userData = await response.json()
  
  return {
    content: [{ 
      type: "text", 
      text: JSON.stringify(userData, null, 2)
    }]
  }
}

Security Considerations

  1. OAuth Secrets: Never commit OAuth client secrets to version control
  2. HTTPS: Use HTTPS in production for secure OAuth flows
  3. Environment Variables: Store all sensitive configuration in environment variables
  4. Token Storage: Tokens are managed by the MCP SDK OAuth provider
  5. OAuth App Configuration: Configure your OAuth app with correct callback URLs
  6. Scope Limitation: Request only the OAuth scopes your application needs
  7. Token Validation: The library handles token validation automatically

Troubleshooting

Common Issues

  1. "Missing required environment variables"

    • Ensure your OAuth provider's CLIENT_ID and CLIENT_SECRET are set
    • Check environment variable names match your configuration
  2. OAuth callback errors

    • Verify your BASE_URL matches your OAuth app configuration
    • Check that Authorization callback URL is set to ${baseUrl}/oauth/callback
    • Ensure your OAuth provider app is configured correctly
  3. Token exchange errors

    • Check isForm setting in your connector - most providers use form encoding
    • Verify tokenUrl is correct for your provider
    • Check if provider requires special headers or parameters
  4. API rate limits

    • Authenticated requests usually have higher rate limits
    • Consider implementing caching for frequently accessed data
    • Check your provider's rate limiting documentation
  5. MCP client connection issues

    • Ensure you're using the correct MCP endpoint URL: /mcp
    • Verify Bearer token authentication is properly configured
    • Check that your tools are properly registered in the mcpHandler
  6. Connector issues

    • Verify OAuth URLs are correct for your provider
    • Check scopes are valid for your provider
    • Test OAuth flow manually in browser first

Debug Mode

Enable detailed logging by setting environment variables:

DEBUG=mcp:*
NODE_ENV=development

Contributing

Contributions are welcome! Please read our contributing guidelines and submit pull requests to our repository.

License

MIT License - see LICENSE file for details.

Support

Related Projects