Skip to content

mozilla-ai/mcpd-sdk-javascript

mcpd-sdk-javascript

mcpd-sdk-javascript is a TypeScript/JavaScript SDK for interacting with the mcpd application.

A daemon that exposes MCP server tools via a simple HTTP API.

This SDK provides high-level and dynamic access to those tools, making it easy to integrate with scripts, applications, or agentic frameworks.

Features

  • Discover and list available mcpd hosted MCP servers
  • Retrieve tool, prompt, and resource definitions from individual servers
  • Dynamically invoke any tool using a clean, attribute-based syntax
  • Unified AI framework integration - works directly with LangChain JS and Vercel AI SDK via getAgentTools()
  • Generate self-contained, framework-compatible tool functions without conversion layers
  • Multiple output formats ('array', 'object', 'map') for different framework needs
  • Full TypeScript support with comprehensive type definitions and overloads
  • Minimal dependencies (lru-cache for caching, zod for schema validation)
  • Works in both Node.js and browser environments
  • Clean API wrapper over mcpd HTTP endpoints - no opinionated aggregation logic

Installation

npm install @mozilla-ai/mcpd
# or
yarn add @mozilla-ai/mcpd
# or
pnpm add @mozilla-ai/mcpd

Quick Start

Note: This SDK works seamlessly with both JavaScript and TypeScript. TypeScript users automatically get full type safety and autocomplete via the included .d.ts type definitions—no additional setup required.

JavaScript

const { McpdClient, McpdError } = require("@mozilla-ai/mcpd");

const client = new McpdClient({
  apiEndpoint: "http://localhost:8090",
});

// List available servers
const servers = await client.listServers();
console.log(servers);
// Example: ['time', 'fetch', 'git']

// List tool definitions for a specific server
const tools = await client.servers.time.getTools();
console.log(tools);

// Dynamically call a tool via the .tools namespace
try {
  const result = await client.servers.time.tools.get_current_time({
    timezone: "UTC",
  });
  console.log(result);
} catch (error) {
  if (error instanceof McpdError) {
    console.error("Error:", error.message);
  }
}

TypeScript

import { McpdClient, McpdError, Tool } from "@mozilla-ai/mcpd";

const client = new McpdClient({
  apiEndpoint: "http://localhost:8090",
  apiKey: "optional-key", // Optional API key
  healthCacheTtl: 10, // Cache health checks for 10 seconds
  serverCacheTtl: 60, // Cache server/tool metadata for 60 seconds
});

// Full type safety and autocomplete
const servers: string[] = await client.listServers();

// Get tools with proper typing
const tools: Tool[] = await client.servers.time.getTools();

// Dynamic tool invocation with error handling via .tools namespace
try {
  const result = await client.servers.time.tools.get_current_time({
    timezone: "UTC",
  });
  console.log(result);
} catch (error) {
  if (error instanceof McpdError) {
    console.error("Operation failed:", error.message);
  }
}

API

Initialization

import { McpdClient } from "@mozilla-ai/mcpd";

// Initialize the client with your mcpd API endpoint
const client = new McpdClient({
  apiEndpoint: "http://localhost:8090", // Required
  apiKey: "optional-key", // Optional: API key for authentication
  healthCacheTtl: 10, // Optional: TTL in seconds for health cache (default: 10)
  serverCacheTtl: 60, // Optional: TTL in seconds for server/tools cache (default: 60)
  timeout: 30000, // Optional: Request timeout in ms (default: 30000)
});

Core Methods

client.listServers()

Returns a list of all configured server names.

const servers = await client.listServers();
// Returns: ['time', 'fetch', 'git']

client.servers.<server>.getTools()

Returns tool schemas for a specific server.

// Get tools for a specific server
const timeTools = await client.servers.time.getTools();
// Returns: [{ name: 'get_current_time', description: '...', inputSchema: {...} }]

client.servers.<server>.tools.<tool>(args)

Dynamically invoke any tool using natural syntax via the .tools namespace. Tool names must match exactly as returned by the MCP server.

// Call a tool with parameters using property access (recommended)
const result = await client.servers.weather.tools.get_forecast({
  city: "Tokyo",
  days: 3,
});

// Call without parameters
const time = await client.servers.time.tools.get_current_time();

client.servers.<server>.getTools()

Get all tools available on a specific server.

// List tools for a server using property access
const tools = await client.servers.time.getTools();
for (const tool of tools) {
  console.log(`${tool.name}: ${tool.description}`);
}

// Useful in loops with dynamic server names
const servers = await client.listServers();
for (const serverName of servers) {
  const tools = await client.servers[serverName].getTools();
  console.log(`${serverName}: ${tools.length} tools`);
}

client.servers.<server>.callTool(toolName, args?)

Call a tool by name with the given arguments. This is useful for programmatic tool invocation when the tool name is in a variable.

// Call with dynamic tool name
const toolName = "get_current_time";
const result = await client.servers.time.callTool(toolName, {
  timezone: "UTC",
});

// Using with dynamic server name too
const serverName = "time";
const result2 = await client.servers[serverName].callTool(toolName, {
  timezone: "UTC",
});

client.servers.<server>.hasTool(toolName)

Check if a specific tool exists on a server. Tool names must match exactly as returned by the MCP server.

// Check if tool exists before calling it
if (await client.servers.time.hasTool("get_current_time")) {
  const result = await client.servers.time.callTool("get_current_time", {
    timezone: "UTC",
  });
}

// Using with dynamic server names
const serverName = "time";
if (await client.servers[serverName].hasTool("get_current_time")) {
  const result = await client.servers[serverName].tools.get_current_time();
}

client.servers.<server>.getPrompts()

Returns prompt schemas for a specific server.

// Get prompts for a specific server
const githubPrompts = await client.servers.github.getPrompts();
// Returns: [{ name: 'create_pr', description: '...', arguments: [...] }]

client.servers.<server>.prompts.<prompt>(args)

Dynamically generate any prompt using natural syntax via the .prompts namespace. Prompt names must match exactly as returned by the MCP server.

// Generate a prompt with parameters using property access (recommended)
const result = await client.servers.github.prompts.create_pr({
  title: "Fix bug",
  description: "Fixed authentication issue",
});

// Generate without parameters (if prompt has no required args)
const result = await client.servers.templates.prompts.default_template();

client.servers.<server>.generatePrompt(promptName, args?)

Generate a prompt by name with the given arguments. This is useful for programmatic prompt generation when the prompt name is in a variable.

// Generate with dynamic prompt name
const promptName = "create_pr";
const result = await client.servers.github.generatePrompt(promptName, {
  title: "Fix bug",
  description: "Fixed authentication issue",
});

// Using with dynamic server name too
const serverName = "github";
const result2 = await client.servers[serverName].generatePrompt(promptName, {
  title: "Fix bug",
});

client.servers.<server>.hasPrompt(promptName)

Check if a specific prompt exists on a server. Prompt names must match exactly as returned by the MCP server.

// Check if prompt exists before generating it
if (await client.servers.github.hasPrompt("create_pr")) {
  const result = await client.servers.github.generatePrompt("create_pr", {
    title: "Fix bug",
  });
}

// Using with dynamic server names
const serverName = "github";
if (await client.servers[serverName].hasPrompt("create_pr")) {
  const result = await client.servers[serverName].prompts.create_pr({
    title: "Fix bug",
  });
}

client.servers.<server>.getResources()

Returns resource schemas for a specific server.

// Get resources for a specific server
const githubResources = await client.servers.github.getResources();
// Returns: [{ name: 'readme', uri: 'file:///repo/README.md', ... }]

client.servers.<server>.getResourceTemplates()

Returns resource template schemas for a specific server.

// Get resource templates for a specific server
const githubTemplates = await client.servers.github.getResourceTemplates();
// Returns: [{ name: 'file', uriTemplate: 'file:///{path}', ... }]

client.servers.<server>.readResource(uri)

Read resource content by URI from a specific server.

// Read resource content by URI
const contents = await client.servers.github.readResource(
  "file:///repo/README.md",
);
for (const content of contents) {
  if (content.text) {
    console.log(content.text);
  } else if (content.blob) {
    console.log("Binary content (base64):", content.blob);
  }
}

client.servers.<server>.hasResource(uri)

Check if a specific resource exists on a server. Resource URIs must match exactly as returned by the MCP server.

// Check if resource exists before reading it
if (await client.servers.github.hasResource("file:///repo/README.md")) {
  const contents = await client.servers.github.readResource(
    "file:///repo/README.md",
  );
}

// Using with dynamic server names
const serverName = "github";
if (await client.servers[serverName].hasResource("file:///repo/README.md")) {
  const contents = await client.servers[serverName].readResource(
    "file:///repo/README.md",
  );
}

client.getServerHealth(serverName?: string)

Get health information for one or all servers.

// Get health for all servers
const allHealth = await client.getServerHealth();
// Returns: { time: { status: 'ok' }, fetch: { status: 'ok' } }

// Get health for specific server
const timeHealth = await client.getServerHealth("time");
// Returns: { status: 'ok' }

client.isServerHealthy(serverName: string)

Check if a specific server is healthy.

if (await client.isServerHealthy("time")) {
  // Server is healthy, safe to use
  const result = await client.servers.time.tools.get_current_time({
    timezone: "UTC",
  });
}

client.getAgentTools(options?)

Generate callable functions that work directly with AI agent frameworks. No conversion layers needed.

// Options: { servers?: string[], format?: 'array' | 'object' | 'map' }
// Default format is 'array' (for LangChain)

// Use with LangChain JS (array format is default)
import { ChatOpenAI } from "@langchain/openai";

const langchainTools = await client.getAgentTools({ format: "array" });
// Or simply: const langchainTools = await client.getAgentTools();

// Bind tools to model
const model = new ChatOpenAI({ modelName: "gpt-4o-mini" });
const modelWithTools = model.bindTools(langchainTools);

// Or use with agents
import { createOpenAIToolsAgent } from "langchain/agents";
const agent = await createOpenAIToolsAgent({
  llm,
  tools: langchainTools,
  prompt,
});

// Use with Vercel AI SDK (expects object format)
import { generateText } from "ai";

const vercelTools = await client.getAgentTools({ format: "object" });
const result = await generateText({
  model: openai("gpt-4o-mini"),
  tools: vercelTools,
  prompt: "What time is it in Tokyo?",
});

// Filter to specific servers
const timeTools = await client.getAgentTools({
  servers: ["time"],
  format: "array",
});

// Use with Map for efficient lookups
const toolMap = await client.getAgentTools({ format: "map" });
const timeTool = toolMap.get("time__get_current_time");
if (timeTool) {
  const result = await timeTool({ timezone: "UTC" });
}

// Each function has metadata for both frameworks
const tools = await client.getAgentTools();
for (const tool of tools) {
  console.log(`${tool.name}: ${tool.description}`);
  console.log(`Server: ${tool._serverName}, Tool: ${tool._toolName}`);
  // LangChain properties: tool.schema, tool.invoke, tool.lc_namespace
  // Vercel AI properties: tool.inputSchema, tool.execute
}

client.clearAgentToolsCache()

Clear the cache of generated agent tools functions.

// Clear cache to regenerate tools with latest schemas
client.clearAgentToolsCache();
const freshTools = await client.getAgentTools();

client.clearServerHealthCache()

Clear the server health cache, forcing fresh health checks on next call.

// Force fresh health check
client.clearServerHealthCache();
const health = await client.getServerHealth("time");

Error Handling

The SDK provides a comprehensive error hierarchy for different failure scenarios:

import {
  McpdError, // Base error class
  ConnectionError, // Cannot connect to mcpd daemon
  AuthenticationError, // Auth failed (invalid API key)
  ServerNotFoundError, // Server doesn't exist
  ServerUnhealthyError, // Server is unhealthy
  ToolNotFoundError, // Tool doesn't exist
  ToolExecutionError, // Tool execution failed
  ValidationError, // Input validation failed
  TimeoutError, // Operation timed out
} from "@mozilla-ai/mcpd";

try {
  const result = await client.servers.unknown.tools.tool();
} catch (error) {
  if (error instanceof ToolNotFoundError) {
    console.error(
      `Tool not found: ${error.toolName} on server ${error.serverName}`,
    );
  } else if (error instanceof ConnectionError) {
    console.error("Cannot connect to mcpd daemon. Is it running?");
  } else if (error instanceof McpdError) {
    console.error("Operation failed:", error.message);
  }
}

Development

Setup

# Clone the repository
git clone https://github.com/mozilla-ai/mcpd-sdk-javascript.git
cd mcpd-sdk-javascript

# Install dependencies
npm install

Building

# Build the project
npm run build

# Build in watch mode
npm run dev

Testing

# Run tests
npm test

# Run tests with coverage
npm run test:coverage

Linting and Formatting

# Run all checks (format, lint, typecheck, test, build)
npm run check

# Run linter
npm run lint

# Fix linting issues
npm run lint:fix

# Format code
npm run format

# Check formatting
npm run format:check

License

Apache-2.0

Contributing

Please see CONTRIBUTING.md for details on how to contribute to this project.

Related Projects

About

JavaScript SDK for mcpd

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •