Rebasely Beta: data may be purged

examples/MCP-tools Public

TypeScript · 59 lines · 2.5 KiB · at 7ee1343

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";import { convertUnits } from "./tools/convert-units.ts";import { ToolError } from "./tools/errors.ts";import { textStats } from "./tools/text-stats.ts";import { timeInZone } from "./tools/time-in-zone.ts";import type { AnyTool } from "./tools/types.ts";export const SERVER_INFO = { name: "mcp-tools", version: "0.1.0" };// Every tool the server offers, in the order tools/list returns them.export const TOOLS: AnyTool[] = [convertUnits, timeInZone, textStats];export type ServerOptions = {  // The clock tools read; tests pin it.  now?: () => Date;};// Builds an MCP server with every tool registered. The server holds no state between requests,// so src/worker.ts makes one per request and each test makes its own.export function createServer(options: ServerOptions = {}): McpServer {  const server = new McpServer(SERVER_INFO, {    instructions: "Small, exact helpers: unit conversion, the time in any time zone and text statistics. Each tool is read-only and answers from its input alone.",  });  for (const tool of TOOLS) register(server, tool, options);  return server;}function register(server: McpServer, tool: AnyTool, options: ServerOptions): void {  server.registerTool(    tool.name,    {      title: tool.title,      description: tool.description,      inputSchema: tool.input,      outputSchema: tool.output,      annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },    },    (args) => call(tool, args, options),  );}// The SDK has already checked `args` against the input schema; a mismatch never gets here and// comes back to the model as an isError result, the same as a ToolError.function call(tool: AnyTool, args: Record<string, unknown>, options: ServerOptions): CallToolResult {  try {    const result = tool.run(args, { now: options.now?.() ?? new Date() });    // Structured output for clients that read it, and the same JSON as text for those that don't.    return { content: [{ type: "text", text: JSON.stringify(result) }], structuredContent: result };  } catch (err) {    if (err instanceof ToolError) return failure(err.message);    console.error(`${tool.name} failed`, err);    return failure(`${tool.name} failed on the server. The input looked valid, so retrying with the same input will fail the same way.`);  }}function failure(text: string): CallToolResult {  return { isError: true, content: [{ type: "text", text }] };}