sample-mcp

sample-mcp

A sample MCP server built with Node.js and TypeScript, demonstrating the three core primitives: Tools, Resources, and Prompts.

Category
访问服务器

README

Sample MCP Server + AI Hook Web Service

A sample MCP (Model Context Protocol) server and an AI Hook Web Service built with Node.js and TypeScript.

  • MCP Server — exposes Tools, Resources, and Prompts to AI clients (Claude Desktop, Claude Code, etc.)
  • AI Hook Web Service — HTTP service for logging AI usage events from Claude, Claude Code, OpenAI/Codex, OpenCode, and any other provider. Logs are kept in memory and persisted to a JSON file (no database required), with automatic cost estimation and a live web dashboard.

Table of Contents


Project Structure

sample-mcp-and-hook/
├── src/
│   ├── main.ts          # MCP server entry point
│   ├── tools.ts         # MCP Tools (greet, add)
│   ├── resources.ts     # MCP Resources (config, user-profile)
│   ├── prompts.ts       # MCP Prompts (explain-code, review-code)
│   └── web-server.ts    # AI Hook Web Service entry point
├── public/
│   └── index.html       # Static web dashboard (AI Hook Monitor)
├── scripts/
│   └── ai-hook.sh       # Enriches Claude Code Stop hooks with token usage
├── .claude/
│   └── settings.json    # Claude Code hooks + AI_HOOK_URL
├── .mcp.json            # MCP server registration for Claude Code
├── logs/                # Persisted hook logs (ai-hooks.json, git-ignored)
├── dist/                # Compiled JavaScript (after build)
├── package.json
├── tsconfig.json
└── README.md

Setup

Prerequisites: Node.js 18+ (and jq + curl for the Claude Code hooks)

# Install dependencies
pnpm install

# Build TypeScript
pnpm build

MCP Server

Running the MCP Server

# Production
pnpm start:mcp

# Development (auto-rebuild on change)
pnpm dev

MCP Core Primitives

Primitive Purpose Direction Example Use Case
Tools Execute actions AI → Server Calculations, API calls
Resources Provide data Server → AI Config, documents, user data
Prompts Template messages Server → AI Standardized instructions

Tools (src/tools.ts)

  • greet — greets a user by name
  • add — adds two numbers together

Resources (src/resources.ts)

  • config://app — static application configuration
  • users://{userId}/profile — dynamic user profile by ID

Prompts (src/prompts.ts)

  • explain-code — prompt template for explaining code
  • review-code — parameterized code review (language, focus area)

Connect to Claude Desktop

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "sample-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/sample-mcp-and-hook/dist/main.js"]
    }
  }
}

For Claude Code, the server is already registered in .mcp.json (relative path) and enabled in .claude/settings.local.json — no manual setup needed once you've run pnpm build.


AI Hook Web Service

A lightweight HTTP service that receives AI usage events, logs them to the console, and persists them to logs/ai-hooks.json. Logs survive server restarts — the file is loaded automatically on startup. Cost is estimated automatically from token counts, and a live dashboard is served at the root URL. No database needed.

Running the Web Service

# Production (build first)
pnpm build
pnpm start:hook

# Development (watches compiled output)
pnpm dev          # terminal 1 — recompile on change
pnpm dev:hook     # terminal 2 — restart server on change

Default port: 3000. Override with the PORT environment variable:

PORT=4000 pnpm start:hook

Log file: logs/ai-hooks.json (auto-created, already in .gitignore). Logs persist across restarts — the file is loaded on startup and rewritten after every hook received. Up to 1000 most-recent entries are retained in memory.

Web Dashboard

Once the service is running, open the dashboard in your browser:

http://localhost:3000/

The static dashboard (public/index.html) visualizes logged events and aggregate stats per provider and model — color-coded by provider (Claude, Claude Code, OpenAI, OpenCode) and event type.

Logging Claude Code Usage

This repo is preconfigured to log its own Claude Code usage to the web service.

Step 1 — Start the web service (keep it running in a separate terminal):

pnpm build
pnpm start:hook
# AI Hook Web Service running on http://localhost:3000

Step 2 — No model config needed. The hooks auto-detect your model at runtime:

  • On Stop events, scripts/ai-hook.sh reads the session transcript and extracts the exact model plus real token usage (input, output, cache read/write).
  • On SessionStart / UserPromptSubmit events (no token data available), an inline hook resolves the model from .claude/settings.json~/.claude/settings.json → falls back to "claude-code".

Whatever model you have configured in Claude Code is tracked automatically — no manual configuration required.

The hooks and endpoint are already wired up in .claude/settings.json:

{
  "env": {
    "AI_HOOK_URL": "http://localhost:3000/api/hook/claudecode"
  },
  "hooks": {
    "SessionStart":     [ /* inline curl — logs session_start */ ],
    "UserPromptSubmit": [ /* inline curl — logs prompt_submit */ ],
    "Stop":             [ { "command": "bash scripts/ai-hook.sh" } ]
  }
}

Step 3 — Open Claude Code in this project directory as usual:

claude

Every session start, user prompt, and Claude response is logged automatically (asynchronously, so Claude Code is never blocked).

Step 4 — View the logs in the dashboard or via the API:

# All events from Claude Code
curl "http://localhost:3000/api/logs?provider=claudecode"

# Aggregated stats
curl http://localhost:3000/api/stats

Note: The web service must be running on the configured port before Claude Code starts. If the service is down, hooks fail silently (Claude Code is not affected).

Endpoints

Method Path Description
GET / Static web dashboard
POST /api/hook Receive a hook (provider auto-detected from body)
POST /api/hook/:provider Receive a hook with explicit provider name
GET /api/logs List logs (newest first)
GET /api/stats Aggregate stats grouped by provider + model
DELETE /api/logs Clear all logs
GET /health Health check (log count, log file path, uptime)

Sending Hooks

The service auto-detects the provider from the request body. You can also send hooks to /api/hook/:provider to set the provider explicitly.

Claude

curl -X POST http://localhost:3000/api/hook \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "claude",
    "model": "claude-sonnet-4-6",
    "type": "message",
    "id": "msg_01abc",
    "session_id": "sess_xyz",
    "usage": {
      "input_tokens": 1200,
      "output_tokens": 350,
      "cache_read_input_tokens": 800
    }
  }'

Console output (cost is auto-estimated from tokens):

[HOOK] 2026-07-02T06:00:00.000Z | claude     | claude-sonnet-4-6              | in=1200 out=350 total=1550 cost=$0.008850 session=sess_xyz

OpenAI / Codex

curl -X POST http://localhost:3000/api/hook \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "openai",
    "model": "gpt-4o",
    "object": "chat.completion",
    "id": "chatcmpl-abc",
    "cost_usd": 0.00014,
    "usage": {
      "prompt_tokens": 500,
      "completion_tokens": 200,
      "total_tokens": 700
    }
  }'

OpenCode

# Using explicit provider path
curl -X POST http://localhost:3000/api/hook/opencode \
  -H "Content-Type: application/json" \
  -d '{
    "model": "claude-3-5-sonnet",
    "event": "completion",
    "input_tokens": 900,
    "output_tokens": 420,
    "total_tokens": 1320,
    "cost": 0.00031,
    "duration_ms": 1850,
    "session_id": "oc_sess_999"
  }'

Generic / Custom Provider

Any JSON body with a provider field works:

curl -X POST http://localhost:3000/api/hook \
  -H "Content-Type: application/json" \
  -d '{
    "provider": "my-custom-llm",
    "model": "my-model-v1",
    "event": "inference",
    "input_tokens": 300,
    "output_tokens": 150,
    "cost_usd": 0.00005,
    "user_id": "user_42",
    "metadata": { "region": "ap-southeast-1" }
  }'

Cost Estimation

When a hook does not include an explicit cost_usd (or cost), the service estimates it from token counts using built-in per-model pricing (input, output, cache-read, and cache-write rates per million tokens). Pricing is matched by substring against the model name, covering the Claude Opus/Sonnet/Haiku families (v3, v3.5, and v4).

  • If an explicit cost is provided in the payload, it is used as-is.
  • If no matching model pricing is found, cost is left unset.
  • Estimated cost appears in the console output, the cost_usd log field, and the aggregated stats.

Adjust the MODEL_PRICING table in src/web-server.ts to add models or update rates.


Query Logs

# All logs (newest first, default limit 50, max 500)
curl http://localhost:3000/api/logs

# Filter by provider (exact match)
curl "http://localhost:3000/api/logs?provider=claude"

# Filter by model name (partial match)
curl "http://localhost:3000/api/logs?model=sonnet"

# Combine filters and set limit
curl "http://localhost:3000/api/logs?provider=openai&limit=10"

# Clear all logs
curl -X DELETE http://localhost:3000/api/logs

Response shape:

{
  "total": 42,
  "returned": 10,
  "logs": [
    {
      "id": "log_1782972333250_96p2e",
      "received_at": "2026-07-02T06:05:33.250Z",
      "provider": "claude",
      "model": "claude-sonnet-4-6",
      "event": "message",
      "session_id": "sess_xyz",
      "tokens": {
        "input": 1200,
        "output": 350,
        "cache_read": 800,
        "total": 1550
      },
      "cost_usd": 0.00885,
      "request_id": "msg_01abc",
      "raw": { "...": "original payload" }
    }
  ]
}

View Stats

curl http://localhost:3000/api/stats

Stats are grouped by provider + model and sorted by call count (descending).

Response shape:

{
  "providers": ["claude", "openai", "opencode"],
  "stats": [
    {
      "provider": "claude",
      "model": "claude-sonnet-4-6",
      "calls": 15,
      "total_input_tokens": 18000,
      "total_output_tokens": 5250,
      "total_tokens": 23250,
      "total_cache_read_tokens": 9600,
      "total_cache_write_tokens": 0,
      "total_cost_usd": 0.1328,
      "total_duration_ms": 0
    },
    {
      "provider": "openai",
      "model": "gpt-4o",
      "calls": 8,
      "total_input_tokens": 4000,
      "total_output_tokens": 1600,
      "total_tokens": 5600,
      "total_cache_read_tokens": 0,
      "total_cache_write_tokens": 0,
      "total_cost_usd": 0.00112,
      "total_duration_ms": 0
    }
  ]
}

Dependencies

Package Purpose
@modelcontextprotocol/sdk Official MCP SDK
express HTTP server for the AI Hook Web Service
zod Schema validation for MCP tool inputs

Learn More

License

ISC

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选