How to Add New Tools to the MCP Everything Reference Server

To add new tools to the MCP Everything reference server, create a TypeScript file in src/everything/tools/ that exports a registration function calling server.registerTool(), then import and invoke that function in src/everything/tools/index.ts to wire it into the startup lifecycle.

The Everything reference server in the modelcontextprotocol/servers repository demonstrates the canonical pattern for extending Model Context Protocol (MCP) servers with custom functionality. Adding new tools requires understanding the two-phase registration lifecycle that separates unconditional tool availability from capability-dependent conditional exposure.

Understanding the Tool Registration Architecture

The Everything server builds on the official MCP SDK, where the McpServer instance maintains the tool registry. Tool registration follows a strict lifecycle defined in src/everything/tools/index.ts: unconditional tools register immediately during server creation, while conditional tools register only after the client reports its capabilities via the notifications/initialized event.

According to the source code, every tool must provide a name, title, description, input schema (Zod-based), and an async handler that returns a CallToolResult. The server factory in src/everything/server/index.ts orchestrates these registration phases during the createServer initialization sequence.

Creating a New Tool Definition

Step 1: Implement the Tool Handler

Create a new file in src/everything/tools/ that defines your tool's schema and business logic. The file must export a registerXTool(server) function that invokes the SDK's registration API.

// src/everything/tools/multiply.ts
import { z } from "zod";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";

const MultiplySchema = z.object({
  a: z.number().describe("First factor"),
  b: z.number().describe("Second factor"),
});

const name = "multiply";
const config = {
  title: "Multiply Numbers",
  description: "Returns the product of two numbers",
  inputSchema: MultiplySchema,
};

export const registerMultiplyTool = (server: McpServer) => {
  server.registerTool(name, config, async (args): Promise<CallToolResult> => {
    const { a, b } = MultiplySchema.parse(args);
    const product = a * b;
    return {
      content: [{ type: "text", text: `🧮 ${a} × ${b} = ${product}` }],
    };
  });
};

Step 2: Export the Registration Function

The registerMultiplyTool function receives the McpServer instance and calls server.registerTool(name, config, handler). This pattern isolates tool implementation from server configuration, allowing the aggregation layer in src/everything/tools/index.ts to control when and how tools are exposed.

Wiring Tools into the Server Lifecycle

Unconditional Registration via registerTools()

Unconditional tools are always available regardless of client capabilities. The registerTools(server) function in src/everything/tools/index.ts (lines 25-38) runs once during server startup, immediately after the McpServer instantiation.

// src/everything/tools/index.ts
import { registerMultiplyTool } from "./multiply.js";

export const registerTools = (server: McpServer) => {
  // existing registrations …
  registerMultiplyTool(server);          // <-- new line
};

This aggregation pattern ensures that createServer in src/everything/server/index.ts can register all baseline tools with a single function call.

Conditional Registration via registerConditionalTools()

Some tools require specific client capabilities (e.g., sampling support or advanced math features). These register later via registerConditionalTools(server), which executes after the client sends its notifications/initialized event. As implemented in lines 44-53 of src/everything/tools/index.ts, this function checks server.server.getClientCapabilities() before exposing restricted functionality.

export const registerConditionalTools = (server: McpServer) => {
  // existing conditional tools …
  const caps = server.server.getClientCapabilities() ?? {};
  if (caps.advancedMath) {
    registerMultiplyTool(server);
  }
};

Server Startup Integration

The createServer function in src/everything/server/index.ts instantiates the McpServer and orchestrates the registration sequence. It immediately calls registerTools(server) after creation, then binds registerConditionalTools(server) to the server's oninitialized callback. This guarantees that tools appear at the correct moment in the MCP session lifecycle—unconditional tools at startup, conditional tools after capability negotiation.

The project documentation at src/everything/docs/extension.md provides additional guidance on the "Adding Tools" workflow, pointing developers to the exact file locations and patterns described above.

Summary

  • Create tool files in src/everything/tools/ exporting a registerXTool(server) function that calls server.registerTool(name, config, handler) with Zod-defined schemas
  • Import and invoke registration functions in registerTools() at lines 25-38 of src/everything/tools/index.ts for unconditional, always-available tools
  • Use registerConditionalTools() at lines 44-53 of the same file for capability-dependent tools, checking server.server.getClientCapabilities() before registration
  • The server factory in src/everything/server/index.ts orchestrates both phases during the createServer initialization sequence, ensuring tools appear at the correct lifecycle moment

Frequently Asked Questions

What is the difference between registerTools() and registerConditionalTools()?

registerTools() runs immediately during server startup and registers tools available to all clients regardless of capabilities. registerConditionalTools() executes only after the client sends the notifications/initialized event, allowing dynamic tool exposure based on reported client capabilities like sampling support or custom extensions.

Where should I place my new tool implementation files?

Create a new TypeScript file in src/everything/tools/ (e.g., src/everything/tools/multiply.ts) following the registerXTool(server) export pattern established by existing reference implementations like get-sum.ts. Each file should isolate one tool's schema, configuration, and handler logic.

How does the server know when to register conditional tools?

The createServer function in src/everything/server/index.ts binds registerConditionalTools(server) to the McpServer instance's oninitialized callback. This ensures the function only executes after the MCP client completes capability negotiation, preventing exposure of tools the client cannot support.

Can I use libraries like Zod for input validation?

Yes, the MCP SDK integrates natively with Zod for runtime input validation. Define your parameters using z.object() and pass the schema to the inputSchema property in the tool configuration. The SDK validates incoming arguments against this schema before invoking your handler, as demonstrated in the multiply.ts example.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →