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

@jsr2npm/mcpc__cmcp

v0.0.3-beta-1

Published

[![JSR](https://jsr.io/badges/@mcpc/cmcp)](https://jsr.io/@mcpc/cmcp)

Readme

Client-Tool-Execution MCP Server 🚀

JSR

Truly Useful Anti-Patterns: Client-Tool-Execution MCP Server

Core Features 🎯

  • Dynamic Tool Registration: Clients connect and register their tools automatically
  • Client-Side Tool Execution: Tools execute on the client side, not the server - perfect for browser DOM manipulation, local file access, or environment-specific operations
  • Transparent Proxy: Server acts as a proxy, routing tool calls to the appropriate client for execution

This enables you to:

  • 🔄 Register custom tools dynamically when clients connect
  • ⚡ Execute tools directly on the client - not on the server
  • 🌐 Build tools that interact with client-specific environments (browser DOM, local files, etc.)
  • 🔗 Create flexible, client-driven AI tool ecosystems where execution happens where the data lives

Getting Started 🚀

Installation

# Using Deno (recommended)
import { createClientExecServer, createClientExecClient } from "jsr:@mcpc/cmcp";

# Or add to your deno.json
{
  "imports": {
    "@mcpc/cmcp": "jsr:@mcpc/cmcp@^0.0.2"
  }
}

Basic Setup

  1. Install the package: Add @mcpc/cmcp to your project
  2. Start the server demo: deno run --allow-net --allow-read --allow-env server.ts
  3. Run the client demo: deno run --allow-net client.ts

Complete Example

Here's a minimal working example:

Server Usage 📡

The server acts as a proxy and registry - it has no predefined tools and simply routes execution to clients:

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { createClientExecServer } from "@mcpc/cmcp";

// Server is just a proxy - no tools, no execution logic
const server = createClientExecServer(
  new Server({ name: "dynamic-mcp-server", version: "1.0.0" }),
  "dynamic-server",
);

// Server routes all tool calls to the appropriate client
// All execution happens on the client side

Client Usage 🖥️

Clients register tools with implementations that execute locally on the client:

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
import { type ClientToolDefinition, createClientExecClient } from "@mcpc/cmcp";

const client = createClientExecClient(
  new Client({ name: "browser-client", version: "1.0.0" }),
  "browser-client-001",
);

// Define tools with LOCAL implementations (executed on client)
const tools: ClientToolDefinition[] = [
  {
    name: "querySelector",
    description: "Query DOM elements using CSS selectors",
    inputSchema: {
      type: "object",
      properties: {
        selector: { type: "string", description: "CSS selector to query" },
        action: {
          type: "string",
          description: "Action to perform",
          enum: ["getText", "click", "getAttribute"],
        },
        attribute: { type: "string", description: "Attribute name" },
      },
      required: ["selector", "action"],
    },
    // 🔥 Implementation runs on CLIENT side - has access to DOM, local files, etc.
    implementation: async (args: Record<string, unknown>) => {
      const { selector, action, attribute } = args;
      const element = document.querySelector(selector as string);

      if (!element) {
        throw new Error(`Element not found: ${selector}`);
      }

      switch (action) {
        case "getText":
          return element.textContent || "";
        case "click":
          element.click();
          return `Clicked element: ${selector}`;
        case "getAttribute":
          return element.getAttribute(attribute as string);
        default:
          throw new Error(`Unknown action: ${action}`);
      }
    },
  },
];

// Register tools (stored locally until connection)
client.registerTools(tools);

// Connect and register tools to server
await client.connect(
  new SSEClientTransport(new URL("http://localhost:9000/sse")),
);

console.log("Client connected and tools registered!");
// Client stays connected to handle tool execution requests

Example Tool Call 🔧

// External MCP client connecting to the server
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";

const mcpClient = new Client({
  name: "external-client",
  version: "1.0.0",
});

await mcpClient.connect(
  new SSEClientTransport(new URL("http://localhost:9000/sse")),
);

// Call tools registered by connected clients
const result = await mcpClient.callTool({
  name: "querySelector",
  arguments: {
    selector: "#my-button",
    action: "click",
  },
});

console.log(result); // "Clicked element: #my-button"
// ✨ The actual DOM manipulation happened on the CLIENT side!

Why Client-Side Execution? 🤔

Traditional MCP: Tools execute on the server

  • ❌ Server needs access to all resources (files, DOM, APIs)
  • ❌ Security concerns with server-side execution
  • ❌ Limited to server environment capabilities

Client-Tool-Execution MCP: Tools execute on the client

  • ✅ Client has natural access to its own environment (DOM, local files, etc.)
  • ✅ Better security - no need to expose sensitive resources to server
  • ✅ Scalable - each client handles its own execution load
  • ✅ Environment-specific - browser clients can manipulate DOM, desktop clients can access files

Architecture Flow 🔄

  1. Server: Starts as an empty proxy with no predefined tools
  2. Client Connect: Client establishes SSE connection to server
  3. Tool Registration: Client sends tool definitions (schema only) via client/register_tools
  4. Server Registry: Server updates its tool registry with client's tool schemas
  5. MCP Call: External system discovers and calls tools via server
  6. Proxy Call: Server proxies call to appropriate client via notification
  7. Client Execution: 🔥 Tool runs on CLIENT side with full access to client environment
  8. Response: Result flows back through server to caller
  9. Client Disconnect: Server automatically removes client's tools

Key Point: The server never executes tools - it only routes calls to clients where the actual execution happens!