Rebasely Beta: data may be purged

examples/MCP-tools Public

Markdown · 146 lines · 4.8 KiB · at fcaf000

# mcp-toolsA small [Model Context Protocol](https://modelcontextprotocol.io) server on Cloudflare Workers. It gives models three exact, read-only tools for things they tend to get wrong: unit arithmetic, the current time somewhere else, and counting words.The server speaks Streamable HTTP at `/mcp`, holds no sessions, and counts tool calls per day in a KV namespace.## Tools### `convert_units`Converts between units of length, mass, volume (US customary) and temperature. Units can be symbols or names: `km`, `kilometres`, `°F`, `pounds`.```json{ "value": 26.2, "from": "miles", "to": "km" }``````json{ "quantity": "length", "value": 26.2, "from": "mi", "result": 42.1648128, "to": "km" }```Converting between quantities, an unknown unit, or a temperature below absolute zero returns an error result that says what to send instead.### `time_in_zone`The local date, time, weekday and UTC offset in an IANA time zone, now or at a given instant. A city that names one zone (`tokyo`, `new york`) works too.```json{ "zone": "tokyo", "at": "2026-10-14T17:00:00Z" }``````json{  "zone": "Asia/Tokyo",  "instant": "2026-10-14T17:00:00.000Z",  "date": "2026-10-15",  "time": "02:00:00",  "weekday": "Thursday",  "utc_offset": "+09:00",  "utc_offset_minutes": 540,  "formatted": "Thursday, October 15, 2026 at 2:00:00 AM GMT+9"}```Leave out `at` for the current time. An instant without `Z` or an offset is refused, since it would be read in the server's own zone.### `text_stats`Characters (as a reader sees them, so an emoji counts once), words, sentences, paragraphs and reading time.```json{ "text": "MCP gives models tools. This server has three of them." }``````json{  "characters": 54,  "characters_excluding_spaces": 45,  "words": 10,  "sentences": 2,  "paragraphs": 1,  "reading_time_seconds": 3,  "reading_time": "under a minute"}````words_per_minute` sets the reading speed (default 238).## Connecting a clientThe endpoint is `http://localhost:8787/mcp` locally, or `https://mcp-tools.<your subdomain>.workers.dev/mcp` once deployed. No auth.**MCP Inspector.** Run `npx @modelcontextprotocol/inspector`, choose the Streamable HTTP transport, and enter the URL.**Claude Code.**```shclaude mcp add --transport http mcp-tools http://localhost:8787/mcp```**Claude Desktop.** Desktop starts local servers over stdio, so bridge with `mcp-remote` in `claude_desktop_config.json`:```json{  "mcpServers": {    "mcp-tools": { "command": "npx", "args": ["-y", "mcp-remote", "http://localhost:8787/mcp"] }  }}```**curl.** Each message is its own POST, and `Accept` must list both types:```shcurl -s http://localhost:8787/mcp \  -H 'content-type: application/json' \  -H 'accept: application/json, text/event-stream' \  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"convert_units","arguments":{"value":100,"from":"C","to":"F"}}}'````GET /stats?days=7` returns calls and errors per tool for the last seven UTC days.## Running itNode 24 or later.```shnpm installnpm test            # node:test, no Workers runtime needednpm run typechecknpm run dev         # wrangler dev on http://localhost:8787````npx wrangler deploy` deploys it. The `USAGE` namespace in `wrangler.jsonc` has no id, so the first deploy creates it.## Design notes- **Stateless.** Every POST gets a fresh `McpServer` and a `WebStandardStreamableHTTPServerTransport` with no session id and JSON responses. Any isolate can answer any request; nothing needs a Durable Object.- **Tools are pure.** Each tool in `src/tools/` is a name, a description written for the model, zod input and output schemas, and a function from input to output. `src/server.ts` is the only file that knows about MCP.- **Errors are results.** A tool throws `ToolError` with advice ("Use an IANA name such as Europe/Paris"), and the server returns it with `isError: true` so the model can read it and try again.- **Counting stays off the request path.** Usage writes go through `ctx.waitUntil`, after the response. The counts are approximate, which is fine for seeing what gets used.The longer versions are in the knowledge base under `.rebasely/kb/`, and the component map is `.rebasely/architecture/system.yaml`.## Layout```src/  index.ts            Worker entry  worker.ts           routes: /mcp, /stats, CORS  server.ts           createServer(): registers the tools with McpServer  usage.ts            per-day counters in KV  tools/    convert-units.ts    time-in-zone.ts    text-stats.ts    errors.ts         ToolError    types.ts          defineTool and the Tool typetest/                 node:test suites, plus an in-memory client and KV fakes.rebasely/  architecture/       components, views and flows  kb/                 concepts, decisions and guides  workflows/ci.yml    typecheck and tests on every push```## LicenseMIT