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 🙏

© 2025 – Pkg Stats / Ryan Hefner

dmcp

v0.0.14

Published

<div class="oranda-hide">

Downloads

4

Readme

🦑 dmcp 🦑

GitHub last commit  npm GitHub License

A TypeScript/JavaScript library for connecting to Model Context Protocol (MCP) servers. Provides OAuth authentication components and a React hook for easy integration with AI systems implementing the MCP standard.

Try it out: MCP Inspector | Cloudflare Workers AI Playground

Installation

npm install dmcp
# or
pnpm add dmcp
# or
yarn add dmcp

What's Included

  • 🔐 OAuth Authentication Components - Framework-agnostic browser OAuth flow handling
  • ⚛️ React Hook - Complete MCP client with connection management, tool calling, and state management
  • 🧰 TypeScript Types - Full type safety and editor support

Framework Support

| Framework | OAuth Components | MCP Client | Status | |-----------|:----------------:|:----------:|:------:| | React | ✅ | ✅ | Full Support | | Vue.js | ✅ | ❌ | OAuth Only | | Angular | ✅ | ❌ | OAuth Only | | Svelte | ✅ | ❌ | OAuth Only | | Vanilla JS | ✅ | ❌ | OAuth Only |

Note: Currently, only React has a complete MCP client implementation. Other frameworks can use the OAuth components to handle authentication, but you'll need to implement the MCP client logic using the @modelcontextprotocol/sdk directly.

Quick Start

React (Complete MCP Client)

import { useMcp } from 'dmcp/react'

function MyAIComponent() {
  const {
    state,          // Connection state
    tools,          // Available tools from MCP server
    error,          // Error message if connection failed
    callTool,       // Function to call tools
    retry,          // Retry connection
    authenticate,   // Manual authentication trigger
    clearStorage,   // Clear stored credentials
  } = useMcp({
    url: 'https://your-mcp-server.com',
    clientName: 'My App',
    autoReconnect: true,
  })

  // Handle different connection states
  if (state === 'failed') {
    return (
      <div>
        <p>Connection failed: {error}</p>
        <button onClick={retry}>Retry</button>
        <button onClick={authenticate}>Authenticate Manually</button>
      </div>
    )
  }

  if (state !== 'ready') {
    return <div>Connecting to AI service...</div>
  }

  // Use available tools
  const handleSearch = async () => {
    try {
      const result = await callTool('search', { query: 'example search' })
      console.log('Search results:', result)
    } catch (err) {
      console.error('Tool call failed:', err)
    }
  }

  return (
    <div>
      <h2>Available Tools: {tools.length}</h2>
      <ul>
        {tools.map(tool => (
          <li key={tool.name}>{tool.name}</li>
        ))}
      </ul>
      <button onClick={handleSearch}>Search</button>
    </div>
  )
}

Other Frameworks (OAuth Only)

For Vue.js, Angular, Svelte, and vanilla JavaScript, dmcp provides OAuth authentication components. You'll need to implement the MCP client functionality yourself using the @modelcontextprotocol/sdk.

Basic OAuth Setup

import { BrowserOAuthClientProvider, onMcpAuthorization } from 'dmcp'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'

// Initialize OAuth provider
const authProvider = new BrowserOAuthClientProvider('https://your-mcp-server.com', {
  clientName: 'My App',
  storageKeyPrefix: 'myapp:auth'
})

// Create MCP client
const client = new Client(
  { name: 'my-app', version: '1.0.0' },
  { capabilities: {} }
)

// Create transport with auth
const transport = new SSEClientTransport(
  new URL('https://your-mcp-server.com'),
  { authProvider }
)

// Connect and use
try {
  await client.connect(transport)
  const tools = await client.request({ method: 'tools/list' })
  console.log('Available tools:', tools)
} catch (error) {
  console.error('Connection failed:', error)
}

OAuth Callback Setup

All frameworks need to set up an OAuth callback route to handle authentication redirects.

React Router

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import { useEffect } from 'react'
import { onMcpAuthorization } from 'dmcp'

function OAuthCallback() {
  useEffect(() => {
    onMcpAuthorization()
  }, [])

  return (
    <div>
      <h1>Authenticating...</h1>
      <p>This window should close automatically.</p>
    </div>
  )
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/oauth/callback" element={<OAuthCallback />} />
        <Route path="/" element={<YourMainComponent />} />
      </Routes>
    </Router>
  )
}

Next.js Pages Router

// pages/oauth/callback.tsx
import { useEffect } from 'react'
import { onMcpAuthorization } from 'dmcp'

export default function OAuthCallbackPage() {
  useEffect(() => {
    onMcpAuthorization()
  }, [])

  return (
    <div>
      <h1>Authenticating...</h1>
      <p>This window should close automatically.</p>
    </div>
  )
}

Next.js App Router

// app/oauth/callback/page.tsx
'use client'
import { useEffect } from 'react'
import { onMcpAuthorization } from 'dmcp'

export default function OAuthCallbackPage() {
  useEffect(() => {
    onMcpAuthorization()
  }, [])

  return (
    <div>
      <h1>Authenticating...</h1>
      <p>This window should close automatically.</p>
    </div>
  )
}

Vue.js

<!-- OAuthCallback.vue -->
<template>
  <div>
    <h1>Authenticating...</h1>
    <p>This window should close automatically.</p>
  </div>
</template>

<script setup>
import { onMounted } from 'vue'
import { onMcpAuthorization } from 'dmcp'

onMounted(() => {
  onMcpAuthorization()
})
</script>
// router.js
import { createRouter, createWebHistory } from 'vue-router'
import OAuthCallback from './components/OAuthCallback.vue'
import Home from './components/Home.vue'

const routes = [
  { path: '/', component: Home },
  { path: '/oauth/callback', component: OAuthCallback }
]

export default createRouter({
  history: createWebHistory(),
  routes
})

Angular

// oauth-callback.component.ts
import { Component, OnInit } from '@angular/core'
import { onMcpAuthorization } from 'dmcp'

@Component({
  selector: 'app-oauth-callback',
  template: `
    <div>
      <h1>Authenticating...</h1>
      <p>This window should close automatically.</p>
    </div>
  `
})
export class OAuthCallbackComponent implements OnInit {
  ngOnInit() {
    onMcpAuthorization()
  }
}
// app-routing.module.ts
import { NgModule } from '@angular/core'
import { RouterModule, Routes } from '@angular/router'
import { OAuthCallbackComponent } from './oauth-callback.component'

const routes: Routes = [
  { path: 'oauth/callback', component: OAuthCallbackComponent },
  // other routes...
]

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Svelte/SvelteKit

<!-- src/routes/oauth/callback/+page.svelte -->
<script>
  import { onMount } from 'svelte'
  import { onMcpAuthorization } from 'dmcp'

  onMount(() => {
    onMcpAuthorization()
  })
</script>

<div>
  <h1>Authenticating...</h1>
  <p>This window should close automatically.</p>
</div>

Vanilla JavaScript

<!-- /oauth/callback.html -->
<!DOCTYPE html>
<html>
<head>
  <title>OAuth Callback</title>
</head>
<body>
  <h1>Authenticating...</h1>
  <p>This window should close automatically.</p>
  
  <script type="module">
    import { onMcpAuthorization } from 'dmcp'
    onMcpAuthorization()
  </script>
</body>
</html>

Framework-Specific Examples

Vue.js MCP Integration

<template>
  <div>
    <div v-if="connecting">Connecting to MCP server...</div>
    <div v-else-if="error" class="error">
      Error: {{ error }}
      <button @click="reconnect">Retry</button>
    </div>
    <div v-else>
      <h2>Available Tools: {{ tools.length }}</h2>
      <button @click="callExampleTool">Call Tool</button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { BrowserOAuthClientProvider } from 'dmcp'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'

const tools = ref([])
const error = ref(null)
const connecting = ref(true)

let client = null
let transport = null

const initMcp = async () => {
  try {
    const authProvider = new BrowserOAuthClientProvider('https://your-mcp-server.com', {
      clientName: 'Vue MCP App'
    })

    client = new Client(
      { name: 'vue-mcp-client', version: '1.0.0' },
      { capabilities: {} }
    )

    transport = new SSEClientTransport(
      new URL('https://your-mcp-server.com'),
      { authProvider }
    )

    await client.connect(transport)
    const toolsResponse = await client.request({ method: 'tools/list' })
    tools.value = toolsResponse.tools
    error.value = null
  } catch (err) {
    error.value = err.message
  } finally {
    connecting.value = false
  }
}

const callExampleTool = async () => {
  if (!client || tools.value.length === 0) return
  
  try {
    const result = await client.request({
      method: 'tools/call',
      params: {
        name: tools.value[0].name,
        arguments: {}
      }
    })
    console.log('Tool result:', result)
  } catch (err) {
    console.error('Tool call failed:', err)
  }
}

const reconnect = () => {
  connecting.value = true
  error.value = null
  initMcp()
}

onMounted(initMcp)

onUnmounted(() => {
  if (transport) {
    transport.close()
  }
})
</script>

<style>
.error {
  color: red;
  padding: 10px;
  border: 1px solid red;
  border-radius: 4px;
  margin: 10px 0;
}
</style>

Angular MCP Service

// mcp.service.ts
import { Injectable } from '@angular/core'
import { BehaviorSubject, Observable } from 'rxjs'
import { BrowserOAuthClientProvider } from 'dmcp'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'

export interface McpState {
  connected: boolean
  tools: any[]
  error: string | null
  loading: boolean
}

@Injectable({
  providedIn: 'root'
})
export class McpService {
  private client: Client | null = null
  private transport: any = null
  
  private stateSubject = new BehaviorSubject<McpState>({
    connected: false,
    tools: [],
    error: null,
    loading: false
  })

  public state$: Observable<McpState> = this.stateSubject.asObservable()

  async connect(serverUrl: string, clientName: string = 'Angular MCP App') {
    this.updateState({ loading: true, error: null })

    try {
      const authProvider = new BrowserOAuthClientProvider(serverUrl, {
        clientName
      })

      this.client = new Client(
        { name: 'angular-mcp-client', version: '1.0.0' },
        { capabilities: {} }
      )

      this.transport = new SSEClientTransport(
        new URL(serverUrl),
        { authProvider }
      )

      await this.client.connect(this.transport)
      const toolsResponse = await this.client.request({ method: 'tools/list' })
      
      this.updateState({
        connected: true,
        tools: toolsResponse.tools,
        loading: false,
        error: null
      })
    } catch (error) {
      this.updateState({
        connected: false,
        loading: false,
        error: error instanceof Error ? error.message : 'Connection failed'
      })
    }
  }

  async callTool(name: string, args: any = {}) {
    if (!this.client) {
      throw new Error('Not connected to MCP server')
    }

    return await this.client.request({
      method: 'tools/call',
      params: { name, arguments: args }
    })
  }

  disconnect() {
    if (this.transport) {
      this.transport.close()
    }
    this.updateState({
      connected: false,
      tools: [],
      error: null,
      loading: false
    })
  }

  private updateState(partialState: Partial<McpState>) {
    const currentState = this.stateSubject.value
    this.stateSubject.next({ ...currentState, ...partialState })
  }
}
// mcp.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
import { McpService, McpState } from './mcp.service'
import { Observable } from 'rxjs'

@Component({
  selector: 'app-mcp',
  template: `
    <div *ngIf="state$ | async as state">
      <div *ngIf="state.loading">Connecting to MCP server...</div>
      
      <div *ngIf="state.error" class="error">
        Error: {{ state.error }}
        <button (click)="connect()">Retry</button>
      </div>
      
      <div *ngIf="state.connected">
        <h2>Available Tools: {{ state.tools.length }}</h2>
        <ul>
          <li *ngFor="let tool of state.tools">
            {{ tool.name }}
            <button (click)="callTool(tool.name)">Call</button>
          </li>
        </ul>
        <button (click)="disconnect()">Disconnect</button>
      </div>
    </div>
  `,
  styles: [`
    .error {
      color: red;
      padding: 10px;
      border: 1px solid red;
      border-radius: 4px;
      margin: 10px 0;
    }
  `]
})
export class McpComponent implements OnInit, OnDestroy {
  state$: Observable<McpState>

  constructor(private mcpService: McpService) {
    this.state$ = this.mcpService.state$
  }

  ngOnInit() {
    this.connect()
  }

  ngOnDestroy() {
    this.mcpService.disconnect()
  }

  connect() {
    this.mcpService.connect('https://your-mcp-server.com')
  }

  disconnect() {
    this.mcpService.disconnect()
  }

  async callTool(toolName: string) {
    try {
      const result = await this.mcpService.callTool(toolName)
      console.log('Tool result:', result)
    } catch (error) {
      console.error('Tool call failed:', error)
    }
  }
}

Svelte MCP Store

// lib/mcp.js
import { writable } from 'svelte/store'
import { BrowserOAuthClientProvider } from 'dmcp'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'

export const mcpState = writable({
  connected: false,
  tools: [],
  error: null,
  loading: false
})

let client = null
let transport = null

export async function connectMcp(serverUrl, clientName = 'Svelte MCP App') {
  mcpState.update(state => ({ ...state, loading: true, error: null }))

  try {
    const authProvider = new BrowserOAuthClientProvider(serverUrl, {
      clientName
    })

    client = new Client(
      { name: 'svelte-mcp-client', version: '1.0.0' },
      { capabilities: {} }
    )

    transport = new SSEClientTransport(
      new URL(serverUrl),
      { authProvider }
    )

    await client.connect(transport)
    const toolsResponse = await client.request({ method: 'tools/list' })
    
    mcpState.set({
      connected: true,
      tools: toolsResponse.tools,
      loading: false,
      error: null
    })
  } catch (error) {
    mcpState.update(state => ({
      ...state,
      connected: false,
      loading: false,
      error: error.message
    }))
  }
}

export async function callTool(name, args = {}) {
  if (!client) {
    throw new Error('Not connected to MCP server')
  }

  return await client.request({
    method: 'tools/call',
    params: { name, arguments: args }
  })
}

export function disconnectMcp() {
  if (transport) {
    transport.close()
  }
  mcpState.set({
    connected: false,
    tools: [],
    error: null,
    loading: false
  })
}
<!-- McpComponent.svelte -->
<script>
  import { onMount, onDestroy } from 'svelte'
  import { mcpState, connectMcp, callTool, disconnectMcp } from './lib/mcp.js'

  onMount(() => {
    connectMcp('https://your-mcp-server.com')
  })

  onDestroy(() => {
    disconnectMcp()
  })

  async function handleToolCall(toolName) {
    try {
      const result = await callTool(toolName)
      console.log('Tool result:', result)
    } catch (error) {
      console.error('Tool call failed:', error)
    }
  }
</script>

<div>
  {#if $mcpState.loading}
    <div>Connecting to MCP server...</div>
  {:else if $mcpState.error}
    <div class="error">
      Error: {$mcpState.error}
      <button on:click={() => connectMcp('https://your-mcp-server.com')}>
        Retry
      </button>
    </div>
  {:else if $mcpState.connected}
    <div>
      <h2>Available Tools: {$mcpState.tools.length}</h2>
      <ul>
        {#each $mcpState.tools as tool (tool.name)}
          <li>
            {tool.name}
            <button on:click={() => handleToolCall(tool.name)}>
              Call
            </button>
          </li>
        {/each}
      </ul>
      <button on:click={disconnectMcp}>Disconnect</button>
    </div>
  {/if}
</div>

<style>
  .error {
    color: red;
    padding: 10px;
    border: 1px solid red;
    border-radius: 4px;
    margin: 10px 0;
  }
</style>

API Reference

useMcp React Hook

import { useMcp } from 'dmcp/react'

function useMcp(options: UseMcpOptions): UseMcpResult

Options

| Option | Type | Description | |--------|------|-------------| | url | string | Required. URL of your MCP server | | clientName | string | Name of your client for OAuth registration | | clientUri | string | URI of your client for OAuth registration | | callbackUrl | string | Custom callback URL for OAuth redirect (defaults to /oauth/callback on the current origin) | | storageKeyPrefix | string | Storage key prefix for OAuth data in localStorage (defaults to "mcp:auth") | | clientConfig | object | Custom configuration for the MCP client identity | | debug | boolean | Whether to enable verbose debug logging | | autoRetry | boolean \| number | Auto retry connection if initial connection fails, with delay in ms | | autoReconnect | boolean \| number | Auto reconnect if an established connection is lost, with delay in ms (default: 3000) | | transportType | 'auto' \| 'http' \| 'sse' | Transport type preference: 'auto' (HTTP with SSE fallback), 'http' (HTTP only), 'sse' (SSE only) (default: 'auto') |

Return Value

| Property | Type | Description | |----------|------|-------------| | state | string | Current connection state: 'discovering', 'authenticating', 'connecting', 'loading', 'ready', 'failed' | | tools | Tool[] | Available tools from the MCP server | | error | string \| undefined | Error message if connection failed | | authUrl | string \| undefined | Manual authentication URL if popup is blocked | | log | LogEntry[] | Array of log messages | | callTool | (name: string, args?: Record<string, unknown>) => Promise<any> | Function to call a tool on the MCP server | | retry | () => void | Manually attempt to reconnect | | disconnect | () => void | Disconnect from the MCP server | | authenticate | () => void | Manually trigger authentication | | clearStorage | () => void | Clear all stored authentication data |

OAuth Components

The main package exports OAuth authentication components that can be used in any framework:

import { BrowserOAuthClientProvider, onMcpAuthorization } from 'dmcp'

BrowserOAuthClientProvider

new BrowserOAuthClientProvider(serverUrl: string, options?: {
  storageKeyPrefix?: string
  clientName?: string
  clientUri?: string
  callbackUrl?: string
})

onMcpAuthorization

onMcpAuthorization(): Promise<void>

Call this function in your OAuth callback route to handle the authentication response.

License

MIT