MCP Masterclass — Part 4 of 5

Multi-Tool Servers — Building a Real Platform

One tool is a demo. Five tools is a platform. This part covers designing composable tool APIs, authentication patterns, npm packaging, cross-IDE deployment, and distributing your MCP server to the world.

Multi-Tool Design Authentication npm Packaging Cross-IDE
MCP from A to Z4/5
API DesignComposabilityAuthenticationnpm PackagingCross-IDE DeployFull Example
Design Principles

Designing Multi-Tool APIs

When your server grows beyond 2-3 tools, design decisions matter. Poor naming, inconsistent parameter styles, and unclear boundaries between tools lead to AI confusion and unreliable behavior.

Naming Conventions

The Golden Rule
verb_noun[_qualifier]
Good NameBad NameWhy
get_stock_quotestockQuoteMissing verb; AI doesn't know if it reads or writes
search_articlesarticlesToo vague; is this list, search, or create?
get_earnings_calendargetEarningsCalendarForNextWeekByDateToo long; parameters handle specifics
create_alertalertNo verb = unclear action
list_watchlist_itemsget_watchlistlist_ for collections, get_ for single items

Parameter Design Principles

How Many Tools is Too Many?

The 7-15 Sweet Spot

Research shows AI models perform best with 7-15 tools. Fewer than 7 limits capability. More than 20 leads to tool confusion — the model starts picking wrong tools or hallucinating tool names. If you need more, consider splitting into multiple focused servers.

Composability

Composable Tools — Referencing Each Other's Output

The real power of multi-tool servers is composability — tools whose outputs become inputs for other tools. The AI orchestrates the chain automatically.

Design Pattern: Layered Tools

Composable Design
// Layer 1: Data fetching (primitive tools)
server.tool("get_quote", "Get stock price", ...);
server.tool("get_financials", "Get financial statements", ...);
server.tool("get_news", "Get recent news", ...);

// Layer 2: Analysis (uses Layer 1 data internally)
server.tool(
  "analyze_stock",
  "Complete stock analysis combining price, fundamentals, and news",
  { symbol: z.string() },
  async ({ symbol }) => {
    // Internally calls the same APIs as Layer 1 tools
    const [quote, financials, news] = await Promise.all([
      fetchQuote(symbol),
      fetchFinancials(symbol),
      fetchNews(symbol)
    ]);

    return { content: [{ type: "text", text: JSON.stringify({
      quote, financials, news,
      // Pre-computed insights the AI can use directly
      signals: {
        pe_vs_sector: quote.pe / financials.sector_avg_pe,
        revenue_growth: financials.revenue_yoy_pct,
        sentiment: news.filter(n => n.sentiment > 0).length / news.length
      }
    }, null, 2) }] };
  }
);

// Layer 3: Workflow (orchestrates multiple analyses)
server.tool(
  "compare_stocks",
  "Compare 2-5 stocks side by side on key metrics",
  {
    symbols: z.array(z.string()).min(2).max(5)
      .describe("List of stock tickers to compare")
  },
  async ({ symbols }) => {
    const analyses = await Promise.all(
      symbols.map(s => analyzeStock(s))
    );
    return { content: [{ type: "text",
      text: JSON.stringify({ comparison: analyses }, null, 2)
    }] };
  }
);

The AI uses these layers naturally. For a simple question ("What's AAPL's price?"), it calls get_quote. For a complex one ("Compare AAPL, MSFT, and GOOGL"), it calls compare_stocks. The key: each layer's description clearly signals its scope.

Security

Authentication Patterns

MCP servers need authentication at two levels: authenticating to external APIs (the server's credentials) and authenticating users who connect to the server (for multi-user deployments).

Level 1: API Keys via Environment Variables

index.js — Startup Validation
// Validate required env vars at startup — fail fast
const REQUIRED_KEYS = ["ALPHA_VANTAGE_KEY", "FINNHUB_KEY"];
const missing = REQUIRED_KEYS.filter(k => !process.env[k]);

if (missing.length > 0) {
  console.error(`Missing required env vars: ${missing.join(", ")}`);
  console.error("Set them in .mcp.json under \"env\" or export them.");
  process.exit(1);
}

Level 2: OAuth 2.1 for Remote Servers

For HTTP-transport servers that multiple users connect to, MCP supports OAuth 2.1 authorization. The flow:

1

Discovery

Client requests /.well-known/oauth-authorization-server

2

Authorization

User redirected to auth page, grants permission

3

Token Exchange

Auth code exchanged for access token via PKCE

4

Authenticated

Token sent in headers for all subsequent MCP calls

stdio vs HTTP Auth

For stdio servers (local), environment variables are sufficient. The server runs on the user's machine with their credentials. For HTTP servers (shared/cloud), OAuth 2.1 is the proper mechanism. Most MCP servers today use stdio + env vars. OAuth is mainly for enterprise and SaaS deployments.

Distribution

npm Packaging — Distributing Your Server

The standard way to distribute MCP servers is via npm. Users install with npx and the host spawns the process automatically. Zero manual setup.

Package Structure

finance-mcp-server/
  package.json — bin entry, dependencies
  index.js — server entry point (#!/usr/bin/env node)
  lib/
    api-client.js — HTTP client with cache/retry
    cache.js — TTL cache
  tools/
    stocks.js — Stock tools (get_quote, get_financials)
    crypto.js — Crypto tools (get_crypto_price)
    news.js — News tools (search_news, get_headlines)
  resources/
    watchlist.js — Watchlist resource
  README.md — npm page docs
  LICENSE — MIT

package.json for Distribution

package.json
{
  "name": "@yourorg/finance-mcp-server",
  "version": "1.0.0",
  "description": "MCP server for financial data — stocks, crypto, news",
  "type": "module",
  "bin": {
    "finance-mcp": "./index.js"
  },
  "keywords": ["mcp", "mcp-server", "finance", "stocks", "claude"],
  "files": ["index.js", "lib/", "tools/", "resources/"],
  "engines": { "node": ">=18" },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.12.0",
    "zod": "^3.23.0"
  }
}

Publish and Use

Terminal
# Publish to npm
npm publish --access public

# Users run with npx (no install needed)
npx @yourorg/finance-mcp-server

# Or in .mcp.json
{
  "mcpServers": {
    "finance": {
      "command": "npx",
      "args": ["@yourorg/finance-mcp-server"],
      "env": { "ALPHA_VANTAGE_KEY": "..." }
    }
  }
}
Universal Access

Cross-IDE Deployment

The whole point of MCP is "build once, use anywhere." Let's set up your server for every major AI tool simultaneously.

Configuration Files for Each Host

.mcp.json — Claude Code
{
  "mcpServers": {
    "finance": {
      "command": "npx",
      "args": ["@yourorg/finance-mcp-server"],
      "env": { "ALPHA_VANTAGE_KEY": "${ALPHA_VANTAGE_KEY}" }
    }
  }
}
.cursor/mcp.json — Cursor
{
  "mcpServers": {
    "finance": {
      "command": "npx",
      "args": ["@yourorg/finance-mcp-server"],
      "env": { "ALPHA_VANTAGE_KEY": "${ALPHA_VANTAGE_KEY}" }
    }
  }
}
.vscode/mcp.json — VS Code (Copilot)
{
  "servers": {
    "finance": {
      "type": "stdio",
      "command": "npx",
      "args": ["@yourorg/finance-mcp-server"],
      "env": { "ALPHA_VANTAGE_KEY": "${ALPHA_VANTAGE_KEY}" }
    }
  }
}

One-Command Setup Script

setup.sh — Auto-generate all config files
#!/bin/bash
# Auto-setup MCP config for all supported IDEs

CONFIG='{
  "mcpServers": {
    "finance": {
      "command": "npx",
      "args": ["@yourorg/finance-mcp-server"],
      "env": { "ALPHA_VANTAGE_KEY": "'$ALPHA_VANTAGE_KEY'" }
    }
  }
}'

# Claude Code
echo "$CONFIG" > .mcp.json

# Cursor
mkdir -p .cursor && echo "$CONFIG" > .cursor/mcp.json

# Windsurf
mkdir -p .windsurf && echo "$CONFIG" > .windsurf/mcp.json

# VS Code
mkdir -p .vscode && echo "$CONFIG" > .vscode/mcp.json

echo "MCP configured for Claude Code, Cursor, Windsurf, and VS Code!"
IDE / ToolConfig PathFormatAuto-discovery
Claude Code.mcp.jsonStandard MCPYes (project root)
Claude Desktop~/Library/.../claude_desktop_config.jsonStandard MCPYes (global)
Cursor.cursor/mcp.jsonStandard MCPYes (project root)
Windsurf.windsurf/mcp.jsonStandard MCPYes (project root)
VS Code.vscode/mcp.jsonExtendedYes (project root)
Gemini CLI.gemini/settings.jsonExtendedYes (project root)
Complete Example

Complete Multi-Tool Server — 7 Tools

index.js — Production Server
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { TTLCache } from "./lib/cache.js";
import { fetchAPI, safeTool } from "./lib/utils.js";

const cache = new TTLCache();
const server = new McpServer({
  name: "finance-platform",
  version: "2.0.0",
  description: "Financial data platform — stocks, crypto, news, earnings, screener"
});

// ─── Tool 1: Stock Quote ────────────────────────────────────
server.tool("get_stock_quote", "Get real-time stock price and key metrics",
  { symbol: z.string().describe("Ticker (AAPL, MSFT)") },
  safeTool(async ({ symbol }) => { /* ... */ })
);

// ─── Tool 2: Crypto Price ───────────────────────────────────
server.tool("get_crypto_price", "Get cryptocurrency price and market data",
  { coin: z.string().describe("Coin ID (bitcoin, ethereum)") },
  safeTool(async ({ coin }) => { /* ... */ })
);

// ─── Tool 3: Search News ────────────────────────────────────
server.tool("search_news", "Search financial news by keyword or ticker",
  { query: z.string(), limit: z.number().optional().default(5) },
  safeTool(async ({ query, limit }) => { /* ... */ })
);

// ─── Tool 4: Earnings Calendar ──────────────────────────────
server.tool("get_earnings_calendar", "Get upcoming earnings dates",
  { days: z.number().optional().default(7).describe("Lookahead days") },
  safeTool(async ({ days }) => { /* ... */ })
);

// ─── Tool 5: Market Regime ──────────────────────────────────
server.tool("get_market_regime", "Detect current market regime (risk-on/off/neutral)",
  {},
  safeTool(async () => { /* VIX + breadth + momentum → regime */ })
);

// ─── Tool 6: Screen Stocks ──────────────────────────────────
server.tool("screen_stocks", "Screen stocks by technical and fundamental criteria",
  {
    strategy: z.enum(["oversold_bounce", "momentum", "breakout", "value"]),
    min_market_cap: z.number().optional().default(1e9)
  },
  safeTool(async ({ strategy, min_market_cap }) => { /* ... */ })
);

// ─── Tool 7: Compare Stocks ─────────────────────────────────
server.tool("compare_stocks", "Compare 2-5 stocks side by side",
  { symbols: z.array(z.string()).min(2).max(5) },
  safeTool(async ({ symbols }) => { /* ... */ })
);

// ─── Resource: Watchlist ────────────────────────────────────
server.resource("watchlist", "finance://watchlist",
  { description: "User's stock watchlist" },
  async () => ({ contents: [{ uri:"finance://watchlist",
    mimeType:"application/json",
    text: JSON.stringify(["AAPL","MSFT","GOOGL","NVDA","AMZN","BTC","ETH"])
  }] })
);

// ─── Start ──────────────────────────────────────────────────
await server.connect(new StdioServerTransport());
Knowledge Check

Quick Quiz

Why is the verb_noun naming convention important for tools?
AI models use tool names + descriptions to decide which tool to call. A clear verb_noun pattern (like get_stock_quote) immediately tells the model whether the tool reads or writes data, and what entity it operates on. Vague names like stocks force the model to rely solely on descriptions, increasing misuse.
What's the ideal number of tools for one MCP server?
7-15 tools. Under 7 is too limited. Over 20 causes tool confusion — the AI starts picking wrong tools or inventing tool names. If you need 30+ tools, split into multiple focused servers (e.g., stocks-mcp and crypto-mcp).
How do you distribute an MCP server to users?
Publish to npm with a "bin" entry in package.json. Users run npx @yourorg/server-name — no install needed. In their .mcp.json, they set "command": "npx" with the package name in "args". Environment variables for API keys go in "env".
What's the difference between Claude Code and VS Code MCP config formats?
Functionally similar. Claude Code uses .mcp.json at project root with "mcpServers". VS Code uses .vscode/mcp.json with "servers" and adds a "type": "stdio" field. The server definition (command, args, env) is identical across all hosts.

Key Takeaways — Part 4

Part 5 of 5
Case Study: The DailyTickers MCP Server

MCP from A to Z — Supercharging AI with Live Data

Complete series in 5 parts — From protocol theory to production deployment

Back to Home
MCP from A to Z4/5