@chrischall/mcp-utils 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +235 -0
  2. package/dist/auth/index.d.ts +223 -0
  3. package/dist/auth/index.d.ts.map +1 -0
  4. package/dist/auth/index.js +267 -0
  5. package/dist/auth/index.js.map +1 -0
  6. package/dist/config/index.d.ts +86 -0
  7. package/dist/config/index.d.ts.map +1 -0
  8. package/dist/config/index.js +121 -0
  9. package/dist/config/index.js.map +1 -0
  10. package/dist/errors/index.d.ts +90 -0
  11. package/dist/errors/index.d.ts.map +1 -0
  12. package/dist/errors/index.js +157 -0
  13. package/dist/errors/index.js.map +1 -0
  14. package/dist/fetchproxy/index.d.ts +156 -0
  15. package/dist/fetchproxy/index.d.ts.map +1 -0
  16. package/dist/fetchproxy/index.js +197 -0
  17. package/dist/fetchproxy/index.js.map +1 -0
  18. package/dist/html/index.d.ts +142 -0
  19. package/dist/html/index.d.ts.map +1 -0
  20. package/dist/html/index.js +321 -0
  21. package/dist/html/index.js.map +1 -0
  22. package/dist/http/index.d.ts +202 -0
  23. package/dist/http/index.d.ts.map +1 -0
  24. package/dist/http/index.js +341 -0
  25. package/dist/http/index.js.map +1 -0
  26. package/dist/index.d.ts +23 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +23 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/response/index.d.ts +22 -0
  31. package/dist/response/index.d.ts.map +1 -0
  32. package/dist/response/index.js +61 -0
  33. package/dist/response/index.js.map +1 -0
  34. package/dist/server/index.d.ts +109 -0
  35. package/dist/server/index.d.ts.map +1 -0
  36. package/dist/server/index.js +95 -0
  37. package/dist/server/index.js.map +1 -0
  38. package/dist/session/index.d.ts +233 -0
  39. package/dist/session/index.d.ts.map +1 -0
  40. package/dist/session/index.js +404 -0
  41. package/dist/session/index.js.map +1 -0
  42. package/dist/test/index.d.ts +124 -0
  43. package/dist/test/index.d.ts.map +1 -0
  44. package/dist/test/index.js +181 -0
  45. package/dist/test/index.js.map +1 -0
  46. package/dist/zod/index.d.ts +130 -0
  47. package/dist/zod/index.d.ts.map +1 -0
  48. package/dist/zod/index.js +184 -0
  49. package/dist/zod/index.js.map +1 -0
  50. package/package.json +77 -0
@@ -0,0 +1,181 @@
1
+ /**
2
+ * `@chrischall/mcp-utils/test` — the in-memory vitest harness shared by every
3
+ * MCP in the fleet. Devtime-only: this subpath pulls test scaffolding and is
4
+ * never imported by runtime server code.
5
+ *
6
+ * It consolidates four copy-pasted pieces from ~19 sibling repos:
7
+ * - `createTestHarness` / `parseToolResult` — a connected `McpServer` + `Client`
8
+ * pair over `InMemoryTransport`, plus the trivial JSON-body extractor.
9
+ * - `versionSyncTest` — the release-please drift guard (`x-release-please-version`
10
+ * annotations vs `package.json#version`).
11
+ * - `mockFetchproxyBootstrap` / `setupClientMocks` — mock `@fetchproxy/bootstrap`
12
+ * at the module boundary and spy an API client's request methods.
13
+ */
14
+ import { readFileSync, readdirSync, statSync } from 'node:fs';
15
+ import { join, relative } from 'node:path';
16
+ import { vi } from 'vitest';
17
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
18
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
19
+ import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
20
+ /**
21
+ * Create a connected `McpServer` + `Client` pair wired over
22
+ * `InMemoryTransport`. The byte-identical helper every MCP's `tests/helpers.ts`
23
+ * defines — register your tools, then drive them through the real client RPC
24
+ * path (schema validation, content envelopes, isError, and all).
25
+ */
26
+ export async function createTestHarness(registerFn) {
27
+ const server = new McpServer({ name: 'test', version: '0.0.0' });
28
+ await registerFn(server);
29
+ const client = new Client({ name: 'test-client', version: '0.0.0' });
30
+ const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
31
+ await Promise.all([server.connect(serverTransport), client.connect(clientTransport)]);
32
+ let closed = false;
33
+ return {
34
+ client,
35
+ server,
36
+ callTool: (name, args) => client.callTool({ name, arguments: args ?? {} }),
37
+ listTools: async () => {
38
+ const result = await client.listTools();
39
+ return result.tools.map((t) => ({ name: t.name }));
40
+ },
41
+ close: async () => {
42
+ if (closed)
43
+ return;
44
+ closed = true;
45
+ await client.close();
46
+ await server.close();
47
+ },
48
+ };
49
+ }
50
+ /**
51
+ * Parse the JSON body out of a tool's `CallToolResult`. Fleet tools return a
52
+ * single text block of `JSON.stringify(data, null, 2)`; this is the inverse.
53
+ * Throws a contextual error (rather than a bare `TypeError`/`SyntaxError`) when
54
+ * the result is empty, non-text, or not valid JSON — those are the test
55
+ * failures you actually want to read.
56
+ */
57
+ export function parseToolResult(result) {
58
+ const block = result.content?.[0];
59
+ if (!block || block.type !== 'text' || typeof block.text !== 'string') {
60
+ throw new Error(`parseToolResult: result has no text content block (got ${block ? `type='${block.type}'` : 'empty content'})`);
61
+ }
62
+ try {
63
+ return JSON.parse(block.text);
64
+ }
65
+ catch (err) {
66
+ const reason = err instanceof Error ? err.message : String(err);
67
+ throw new Error(`parseToolResult: text block is not valid JSON (${reason}): ${block.text}`);
68
+ }
69
+ }
70
+ const SEMVER_LITERAL = /['"]([0-9]+\.[0-9]+\.[0-9]+(?:-[A-Za-z0-9.]+)?)['"]/;
71
+ function walkTs(dir) {
72
+ const out = [];
73
+ for (const entry of readdirSync(dir)) {
74
+ const p = join(dir, entry);
75
+ if (statSync(p).isDirectory())
76
+ out.push(...walkTs(p));
77
+ else if (p.endsWith('.ts'))
78
+ out.push(p);
79
+ }
80
+ return out;
81
+ }
82
+ /**
83
+ * The release-please drift guard. Walks `srcDir` for every line carrying an
84
+ * `x-release-please-version` annotation and asserts the version literal on that
85
+ * line matches `package.json#version`.
86
+ *
87
+ * Why this exists: a recurring footgun where a `VERSION` constant (the MCP's
88
+ * self-reported version + fetchproxy bridge identity) silently drifts from
89
+ * `package.json` because release-please's `extra-files` registration lacks the
90
+ * marker. resy-mcp v0.2.0 and opentable-mcp shipped this bug repeatedly.
91
+ *
92
+ * Returns the list of mismatch descriptions (`file:line → found (expected X)`).
93
+ * An empty array means in sync — callers assert `expect(result).toEqual([])`.
94
+ * Marker lines with no version literal (e.g. a docstring describing the
95
+ * convention) are intentionally skipped, so the test never trips on itself.
96
+ */
97
+ export function versionSyncTest({ srcDir, pkgPath }) {
98
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
99
+ const root = join(srcDir, '..');
100
+ const mismatches = [];
101
+ for (const f of walkTs(srcDir)) {
102
+ const lines = readFileSync(f, 'utf8').split('\n');
103
+ lines.forEach((line, i) => {
104
+ if (!line.includes('x-release-please-version'))
105
+ return;
106
+ const match = line.match(SEMVER_LITERAL);
107
+ if (!match)
108
+ return;
109
+ const ver = match[1];
110
+ if (ver !== pkg.version) {
111
+ mismatches.push(`${relative(root, f)}:${i + 1} → ${ver} (expected ${pkg.version})`);
112
+ }
113
+ });
114
+ }
115
+ return mismatches;
116
+ }
117
+ /**
118
+ * Build a fully-shaped {@link BootstrapResult}, with empty maps as defaults and
119
+ * `overrides` shallow-merged on top. Keeps tests from re-declaring the four
120
+ * empty maps every time they only care about, say, `cookies`.
121
+ */
122
+ export function makeBootstrapResult(overrides = {}) {
123
+ return {
124
+ cookies: {},
125
+ localStorage: {},
126
+ sessionStorage: {},
127
+ capturedHeaders: {},
128
+ ...overrides,
129
+ };
130
+ }
131
+ /**
132
+ * Mock `@fetchproxy/bootstrap` at the module boundary so tests never open a
133
+ * real WebSocket to a browser bridge. Returns a spy that resolves a default
134
+ * {@link BootstrapResult} (overridable per call via `bootstrap.mockResolvedValue`).
135
+ *
136
+ * Usage:
137
+ * ```ts
138
+ * const fp = mockFetchproxyBootstrap({ cookies: { SID: 'x' } });
139
+ * vi.mock('@fetchproxy/bootstrap', fp.module);
140
+ * // ...import the SUT, then assert on fp.bootstrap
141
+ * ```
142
+ * Because `vi.mock` is hoisted, declare the mock handle and the `vi.mock` call
143
+ * before importing the system under test.
144
+ */
145
+ export function mockFetchproxyBootstrap(defaultResult = {}) {
146
+ const base = makeBootstrapResult(defaultResult);
147
+ const bootstrap = vi.fn(async (..._args) => base);
148
+ return {
149
+ bootstrap,
150
+ module: () => ({ bootstrap: (...args) => bootstrap(...args) }),
151
+ reset: () => {
152
+ bootstrap.mockReset();
153
+ bootstrap.mockResolvedValue(base);
154
+ },
155
+ };
156
+ }
157
+ /**
158
+ * Spy on an API client's async request methods and (optionally) stub each one's
159
+ * resolved value. Mirrors the `vi.spyOn(client, 'request').mockResolvedValue(...)`
160
+ * boilerplate every tool-level test repeats.
161
+ *
162
+ * For each entry in `returns`: a spy is installed on `client[method]`. If the
163
+ * value is `undefined`, the spy passes through to the real implementation;
164
+ * otherwise it `mockResolvedValue`s the provided value. Remember to
165
+ * `vi.restoreAllMocks()` in `afterEach`.
166
+ *
167
+ * @returns A map of method name → installed spy, for call assertions.
168
+ */
169
+ export function setupClientMocks(client, returns) {
170
+ const spies = {};
171
+ for (const method of Object.keys(returns)) {
172
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
173
+ const spy = vi.spyOn(client, method);
174
+ const value = returns[method];
175
+ if (value !== undefined)
176
+ spy.mockResolvedValue(value);
177
+ spies[method] = spy;
178
+ }
179
+ return spies;
180
+ }
181
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * `zod` — schema atoms, pagination, and tool-annotation helpers shared across
3
+ * the MCP fleet. Intentionally small: atoms + annotations, not a schema
4
+ * framework. Built on zod v4.
5
+ *
6
+ * The raw-shape style (`inputSchema: { foo: z.string() }`) used by
7
+ * `server.registerTool` is preserved — these atoms are plain `ZodType`s you
8
+ * drop straight into a shape object.
9
+ */
10
+ import { z } from 'zod';
11
+ /** A strictly positive integer (`> 0`). */
12
+ export declare const PositiveInt: z.ZodNumber;
13
+ /** A non-negative integer (`>= 0`). */
14
+ export declare const NonNegInt: z.ZodNumber;
15
+ /** A non-empty string (after no trimming — at least one character). */
16
+ export declare const NonEmptyString: z.ZodString;
17
+ /**
18
+ * An ISO calendar date, `YYYY-MM-DD`. Rejects impossible dates (e.g.
19
+ * `2026-13-01`) via zod v4's `z.iso.date()`.
20
+ */
21
+ export declare const IsoDate: z.ZodISODate;
22
+ /**
23
+ * A 24-hour wall-clock time `HH:MM` (no seconds, no offset). This is the
24
+ * fleet convention (resy notify/book, slot windows) — restaurant-local times
25
+ * carried as bare `HH:MM`, never parsed through `Date` so they aren't shifted
26
+ * by the server's timezone. Accepts `9:05` and `09:05`; rejects `24:00`,
27
+ * `19:60`, and anything with seconds.
28
+ */
29
+ export declare const IsoTime: z.ZodString;
30
+ /**
31
+ * Optional portal-origin selector (e.g. `https://<vendor>.hbportal.co`).
32
+ * Disambiguates which signed-in session a tool routes through when more than
33
+ * one is active. Optional — when a single session is active it can be omitted.
34
+ * (honeybook / disk-session MCPs)
35
+ */
36
+ export declare const schemaOrigin: z.ZodOptional<z.ZodString>;
37
+ /**
38
+ * The write-confirmation gate shared by mutating tools: without
39
+ * `confirm: true` the tool returns a preview instead of performing the action.
40
+ * (honeybook pay_invoice / sign_contract)
41
+ */
42
+ export declare const schemaConfirm: z.ZodOptional<z.ZodBoolean>;
43
+ /**
44
+ * Offset/limit pagination shape (raw, for spreading into an `inputSchema`).
45
+ * `offset` defaults to 0, `limit` is bounded `1..200` and defaults to 50 —
46
+ * the bounds standardized across the fleet's search tools.
47
+ */
48
+ export declare const paginationSchema: {
49
+ readonly offset: z.ZodDefault<z.ZodNumber>;
50
+ readonly limit: z.ZodDefault<z.ZodNumber>;
51
+ };
52
+ /**
53
+ * 1-based page pagination shape (`page_num` / `page_size`). `page_num`
54
+ * defaults to 1, `page_size` is bounded `1..200` and defaults to 50.
55
+ * Mirrors onehome-mcp's search pagination, normalized to 1-based.
56
+ */
57
+ export declare const pageSchema: {
58
+ readonly page_num: z.ZodDefault<z.ZodNumber>;
59
+ readonly page_size: z.ZodDefault<z.ZodNumber>;
60
+ };
61
+ /**
62
+ * Convert a 1-based page + page size into a zero-based offset.
63
+ *
64
+ * Defensive against bad callers: page and size are floored, page is clamped to
65
+ * `>= 1` and size to `>= 0`, so the result is always a non-negative integer.
66
+ *
67
+ * @example calculateOffset(1, 50) // 0
68
+ * @example calculateOffset(3, 50) // 100
69
+ */
70
+ export declare function calculateOffset(page: number, size: number): number;
71
+ /** The MCP tool-annotation block, as consumed by `server.registerTool`. */
72
+ export interface ToolAnnotations {
73
+ title: string;
74
+ readOnlyHint: boolean;
75
+ idempotentHint: boolean;
76
+ openWorldHint: boolean;
77
+ }
78
+ /** Options for {@link toolAnnotations}. */
79
+ export interface ToolAnnotationsInput {
80
+ /** Human-readable tool title (shown in clients). */
81
+ title: string;
82
+ /** Tool does not modify state. Default `true` (most fleet tools are reads). */
83
+ readOnly?: boolean;
84
+ /** Repeated identical calls have the same effect. Default `true`. */
85
+ idempotent?: boolean;
86
+ /**
87
+ * Tool reaches an open/unbounded external world (the live web/API) rather
88
+ * than a closed local computation. Default `true`.
89
+ */
90
+ openWorld?: boolean;
91
+ }
92
+ /**
93
+ * Build the `annotations` block repeated across ~15 tools per MCP. Defaults
94
+ * encode the fleet's common case (a read-only, idempotent, open-world fetch);
95
+ * mutating/local tools override the relevant hint.
96
+ *
97
+ * @example toolAnnotations({ title: 'Search properties' })
98
+ * // { title, readOnlyHint: true, idempotentHint: true, openWorldHint: true }
99
+ * @example toolAnnotations({ title: 'Book', readOnly: false, idempotent: false })
100
+ */
101
+ export declare function toolAnnotations(opts: ToolAnnotationsInput): ToolAnnotations;
102
+ /**
103
+ * Extract bare `HH:MM` from an ISO-ish datetime such as
104
+ * `2026-05-01T19:00:00` or a wire time `19:00:00`. Deliberately avoids
105
+ * `new Date()` so times aren't shifted by the server's timezone (resy returns
106
+ * restaurant-local times with no offset). Returns `''` when no time is found.
107
+ *
108
+ * @example extractTime('2026-05-01T19:30:00') // '19:30'
109
+ * @example extractTime('19:30:00') // '19:30'
110
+ * @example extractTime(undefined) // ''
111
+ */
112
+ export declare function extractTime(input: string | undefined | null): string;
113
+ /**
114
+ * Normalize a loose caller-supplied time to canonical 24-hour `HH:MM`.
115
+ *
116
+ * Accepts:
117
+ * - `HH:MM` / `H:MM` → zero-padded `HH:MM`
118
+ * - `HH:MM:SS` → seconds trimmed
119
+ * - `7pm`, `7:30 PM`, `12 am` → 12h clock converted (12am→00, 12pm→12)
120
+ *
121
+ * Returns `undefined` when the input can't be understood, so callers can fall
122
+ * through to a default rather than book at a garbage time.
123
+ *
124
+ * @example normalizeTime('7:30 PM') // '19:30'
125
+ * @example normalizeTime('9:5') // '09:05'
126
+ * @example normalizeTime('12am') // '00:00'
127
+ * @example normalizeTime('nope') // undefined
128
+ */
129
+ export declare function normalizeTime(input: string | undefined | null): string | undefined;
130
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/zod/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,2CAA2C;AAC3C,eAAO,MAAM,WAAW,aAA8B,CAAC;AAEvD,uCAAuC;AACvC,eAAO,MAAM,SAAS,aAAiC,CAAC;AAExD,uEAAuE;AACvE,eAAO,MAAM,cAAc,aAAoB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,OAAO,cAAe,CAAC;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,aAEqD,CAAC;AAE1E;;;;;GAKG;AACH,eAAO,MAAM,YAAY,4BAKtB,CAAC;AAEJ;;;;GAIG;AACH,eAAO,MAAM,aAAa,6BAGuD,CAAC;AAMlF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;CASnB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;CASb,CAAC;AAEX;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAIlE;AAMD,2EAA2E;AAC3E,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,2CAA2C;AAC3C,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qEAAqE;IACrE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,oBAAoB,GAAG,eAAe,CAO3E;AAMD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,CAOpE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAuBlF"}
@@ -0,0 +1,184 @@
1
+ /**
2
+ * `zod` — schema atoms, pagination, and tool-annotation helpers shared across
3
+ * the MCP fleet. Intentionally small: atoms + annotations, not a schema
4
+ * framework. Built on zod v4.
5
+ *
6
+ * The raw-shape style (`inputSchema: { foo: z.string() }`) used by
7
+ * `server.registerTool` is preserved — these atoms are plain `ZodType`s you
8
+ * drop straight into a shape object.
9
+ */
10
+ import { z } from 'zod';
11
+ // ---------------------------------------------------------------------------
12
+ // Atoms
13
+ // ---------------------------------------------------------------------------
14
+ /** A strictly positive integer (`> 0`). */
15
+ export const PositiveInt = z.number().int().positive();
16
+ /** A non-negative integer (`>= 0`). */
17
+ export const NonNegInt = z.number().int().nonnegative();
18
+ /** A non-empty string (after no trimming — at least one character). */
19
+ export const NonEmptyString = z.string().min(1);
20
+ /**
21
+ * An ISO calendar date, `YYYY-MM-DD`. Rejects impossible dates (e.g.
22
+ * `2026-13-01`) via zod v4's `z.iso.date()`.
23
+ */
24
+ export const IsoDate = z.iso.date();
25
+ /**
26
+ * A 24-hour wall-clock time `HH:MM` (no seconds, no offset). This is the
27
+ * fleet convention (resy notify/book, slot windows) — restaurant-local times
28
+ * carried as bare `HH:MM`, never parsed through `Date` so they aren't shifted
29
+ * by the server's timezone. Accepts `9:05` and `09:05`; rejects `24:00`,
30
+ * `19:60`, and anything with seconds.
31
+ */
32
+ export const IsoTime = z
33
+ .string()
34
+ .regex(/^([01]?\d|2[0-3]):[0-5]\d$/, 'must be HH:MM (24h), e.g. 19:30');
35
+ /**
36
+ * Optional portal-origin selector (e.g. `https://<vendor>.hbportal.co`).
37
+ * Disambiguates which signed-in session a tool routes through when more than
38
+ * one is active. Optional — when a single session is active it can be omitted.
39
+ * (honeybook / disk-session MCPs)
40
+ */
41
+ export const schemaOrigin = z
42
+ .string()
43
+ .optional()
44
+ .describe('Portal origin (e.g. https://<vendor>.example.co) selecting which active session to use. Optional when only one session is active.');
45
+ /**
46
+ * The write-confirmation gate shared by mutating tools: without
47
+ * `confirm: true` the tool returns a preview instead of performing the action.
48
+ * (honeybook pay_invoice / sign_contract)
49
+ */
50
+ export const schemaConfirm = z
51
+ .boolean()
52
+ .optional()
53
+ .describe('Must be true to proceed. Without this, the tool returns a preview.');
54
+ // ---------------------------------------------------------------------------
55
+ // Pagination
56
+ // ---------------------------------------------------------------------------
57
+ /**
58
+ * Offset/limit pagination shape (raw, for spreading into an `inputSchema`).
59
+ * `offset` defaults to 0, `limit` is bounded `1..200` and defaults to 50 —
60
+ * the bounds standardized across the fleet's search tools.
61
+ */
62
+ export const paginationSchema = {
63
+ offset: NonNegInt.default(0).describe('Number of items to skip (0-based).'),
64
+ limit: z
65
+ .number()
66
+ .int()
67
+ .min(1)
68
+ .max(200)
69
+ .default(50)
70
+ .describe('Maximum number of items to return (1-200).'),
71
+ };
72
+ /**
73
+ * 1-based page pagination shape (`page_num` / `page_size`). `page_num`
74
+ * defaults to 1, `page_size` is bounded `1..200` and defaults to 50.
75
+ * Mirrors onehome-mcp's search pagination, normalized to 1-based.
76
+ */
77
+ export const pageSchema = {
78
+ page_num: PositiveInt.default(1).describe('1-based page number.'),
79
+ page_size: z
80
+ .number()
81
+ .int()
82
+ .min(1)
83
+ .max(200)
84
+ .default(50)
85
+ .describe('Number of items per page (1-200).'),
86
+ };
87
+ /**
88
+ * Convert a 1-based page + page size into a zero-based offset.
89
+ *
90
+ * Defensive against bad callers: page and size are floored, page is clamped to
91
+ * `>= 1` and size to `>= 0`, so the result is always a non-negative integer.
92
+ *
93
+ * @example calculateOffset(1, 50) // 0
94
+ * @example calculateOffset(3, 50) // 100
95
+ */
96
+ export function calculateOffset(page, size) {
97
+ const p = Math.max(1, Math.floor(page));
98
+ const s = Math.max(0, Math.floor(size));
99
+ return (p - 1) * s;
100
+ }
101
+ /**
102
+ * Build the `annotations` block repeated across ~15 tools per MCP. Defaults
103
+ * encode the fleet's common case (a read-only, idempotent, open-world fetch);
104
+ * mutating/local tools override the relevant hint.
105
+ *
106
+ * @example toolAnnotations({ title: 'Search properties' })
107
+ * // { title, readOnlyHint: true, idempotentHint: true, openWorldHint: true }
108
+ * @example toolAnnotations({ title: 'Book', readOnly: false, idempotent: false })
109
+ */
110
+ export function toolAnnotations(opts) {
111
+ return {
112
+ title: opts.title,
113
+ readOnlyHint: opts.readOnly ?? true,
114
+ idempotentHint: opts.idempotent ?? true,
115
+ openWorldHint: opts.openWorld ?? true,
116
+ };
117
+ }
118
+ // ---------------------------------------------------------------------------
119
+ // Time normalization (resy)
120
+ // ---------------------------------------------------------------------------
121
+ /**
122
+ * Extract bare `HH:MM` from an ISO-ish datetime such as
123
+ * `2026-05-01T19:00:00` or a wire time `19:00:00`. Deliberately avoids
124
+ * `new Date()` so times aren't shifted by the server's timezone (resy returns
125
+ * restaurant-local times with no offset). Returns `''` when no time is found.
126
+ *
127
+ * @example extractTime('2026-05-01T19:30:00') // '19:30'
128
+ * @example extractTime('19:30:00') // '19:30'
129
+ * @example extractTime(undefined) // ''
130
+ */
131
+ export function extractTime(input) {
132
+ if (!input)
133
+ return '';
134
+ // Prefer the time component after a `T`; fall back to a leading HH:MM[:SS].
135
+ const afterT = /T(\d{2}):(\d{2})/.exec(input);
136
+ if (afterT)
137
+ return `${afterT[1]}:${afterT[2]}`;
138
+ const leading = /^(\d{2}):(\d{2})/.exec(input);
139
+ return leading ? `${leading[1]}:${leading[2]}` : '';
140
+ }
141
+ /**
142
+ * Normalize a loose caller-supplied time to canonical 24-hour `HH:MM`.
143
+ *
144
+ * Accepts:
145
+ * - `HH:MM` / `H:MM` → zero-padded `HH:MM`
146
+ * - `HH:MM:SS` → seconds trimmed
147
+ * - `7pm`, `7:30 PM`, `12 am` → 12h clock converted (12am→00, 12pm→12)
148
+ *
149
+ * Returns `undefined` when the input can't be understood, so callers can fall
150
+ * through to a default rather than book at a garbage time.
151
+ *
152
+ * @example normalizeTime('7:30 PM') // '19:30'
153
+ * @example normalizeTime('9:5') // '09:05'
154
+ * @example normalizeTime('12am') // '00:00'
155
+ * @example normalizeTime('nope') // undefined
156
+ */
157
+ export function normalizeTime(input) {
158
+ if (!input)
159
+ return undefined;
160
+ const s = input.trim().toLowerCase();
161
+ if (!s)
162
+ return undefined;
163
+ const m = /^(\d{1,2})(?::(\d{1,2}))?(?::\d{1,2})?\s*(am|pm)?$/.exec(s);
164
+ if (!m)
165
+ return undefined;
166
+ let hour = Number(m[1]);
167
+ const minute = m[2] === undefined ? 0 : Number(m[2]);
168
+ const meridiem = m[3];
169
+ if (minute > 59)
170
+ return undefined;
171
+ if (meridiem) {
172
+ if (hour < 1 || hour > 12)
173
+ return undefined;
174
+ if (meridiem === 'am')
175
+ hour = hour === 12 ? 0 : hour;
176
+ else
177
+ hour = hour === 12 ? 12 : hour + 12;
178
+ }
179
+ else if (hour > 23) {
180
+ return undefined;
181
+ }
182
+ return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
183
+ }
184
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/zod/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@chrischall/mcp-utils",
3
+ "version": "0.1.0",
4
+ "description": "Shared scaffolding for the chrischall MCP fleet — server bootstrap, tool-result formatting, helpful errors, hardened env/config, a bearer API-client kit, zod atoms, session registries, a fetchproxy transport adapter, auth resolver skeletons, an in-memory test harness, and opt-in HTML helpers. The generic MCP glue hoisted out of ~19 sibling servers.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Claude Code (AI) <https://www.anthropic.com/claude>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/chrischall/mcp-utils.git"
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": "./dist/index.js",
17
+ "types": "./dist/index.d.ts"
18
+ },
19
+ "./test": {
20
+ "import": "./dist/test/index.js",
21
+ "types": "./dist/test/index.d.ts"
22
+ },
23
+ "./fetchproxy": {
24
+ "import": "./dist/fetchproxy/index.js",
25
+ "types": "./dist/fetchproxy/index.d.ts"
26
+ },
27
+ "./session": {
28
+ "import": "./dist/session/index.js",
29
+ "types": "./dist/session/index.d.ts"
30
+ },
31
+ "./html": {
32
+ "import": "./dist/html/index.js",
33
+ "types": "./dist/html/index.d.ts"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "provenance": true
42
+ },
43
+ "scripts": {
44
+ "build": "tsc -b",
45
+ "typecheck": "tsc -b --noEmit false",
46
+ "test": "vitest run",
47
+ "test:watch": "vitest"
48
+ },
49
+ "peerDependencies": {
50
+ "@modelcontextprotocol/sdk": "^1.29.0",
51
+ "zod": "^4.4.0",
52
+ "@fetchproxy/server": "*",
53
+ "node-html-parser": "*",
54
+ "vitest": "*"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "@fetchproxy/server": {
58
+ "optional": true
59
+ },
60
+ "node-html-parser": {
61
+ "optional": true
62
+ },
63
+ "vitest": {
64
+ "optional": true
65
+ }
66
+ },
67
+ "devDependencies": {
68
+ "@fetchproxy/server": "^0.11.0",
69
+ "@modelcontextprotocol/sdk": "^1.29.0",
70
+ "@types/node": "^24.0.0",
71
+ "@vitest/coverage-v8": "^4.1.0",
72
+ "node-html-parser": "^7.1.0",
73
+ "typescript": "^5.6.0",
74
+ "vitest": "^4.1.0",
75
+ "zod": "^4.4.0"
76
+ }
77
+ }