@utdk/mcp-core 0.1.0-dev.37053ca

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.
@@ -0,0 +1,201 @@
1
+ import type { ProviderTool } from "./loader.js";
2
+ /**
3
+ * A text content block. Structurally compatible with the MCP SDK's text
4
+ * `ContentBlock` so handlers can be returned directly from a tools/call
5
+ * handler.
6
+ */
7
+ export interface TextContent {
8
+ type: "text";
9
+ text: string;
10
+ }
11
+ /**
12
+ * Result of a tools/call handler. Structurally compatible with the MCP SDK's
13
+ * `CallToolResult` so values returned from these handlers can be passed
14
+ * straight back to the SDK without an adapter: the stdio server returns them
15
+ * directly from its `tools/call` request handler, and the hosted gateway
16
+ * (Phase 2) serializes the same shape over HTTP. mcp-core deliberately does
17
+ * not depend on `@modelcontextprotocol/sdk`; the handlers return fresh object
18
+ * literals whose inferred types are assignable to the SDK's `ServerResult`
19
+ * union, and this interface is the documented shape of those literals.
20
+ */
21
+ export interface CallToolResult {
22
+ content: TextContent[];
23
+ isError?: boolean;
24
+ }
25
+ /**
26
+ * Ensure the input schema is a valid MCP tool inputSchema (type: "object").
27
+ */
28
+ export declare function normalizeInputSchema(schema: Record<string, unknown>): {
29
+ type: "object";
30
+ properties?: Record<string, object>;
31
+ required?: string[];
32
+ };
33
+ export declare const META_TOOLS: ({
34
+ name: string;
35
+ description: string;
36
+ inputSchema: {
37
+ type: "object";
38
+ properties: {
39
+ provider: {
40
+ type: string;
41
+ description: string;
42
+ };
43
+ group_by: {
44
+ type: string;
45
+ enum: string[];
46
+ description: string;
47
+ };
48
+ query?: undefined;
49
+ limit?: undefined;
50
+ tool_name?: undefined;
51
+ arguments?: undefined;
52
+ };
53
+ required?: undefined;
54
+ };
55
+ } | {
56
+ name: string;
57
+ description: string;
58
+ inputSchema: {
59
+ type: "object";
60
+ properties: {
61
+ query: {
62
+ type: string;
63
+ description: string;
64
+ };
65
+ provider: {
66
+ type: string;
67
+ description: string;
68
+ };
69
+ limit: {
70
+ type: string;
71
+ description: string;
72
+ };
73
+ group_by?: undefined;
74
+ tool_name?: undefined;
75
+ arguments?: undefined;
76
+ };
77
+ required: string[];
78
+ };
79
+ } | {
80
+ name: string;
81
+ description: string;
82
+ inputSchema: {
83
+ type: "object";
84
+ properties: {
85
+ tool_name: {
86
+ type: string;
87
+ description: string;
88
+ };
89
+ provider?: undefined;
90
+ group_by?: undefined;
91
+ query?: undefined;
92
+ limit?: undefined;
93
+ arguments?: undefined;
94
+ };
95
+ required: string[];
96
+ };
97
+ } | {
98
+ name: string;
99
+ description: string;
100
+ inputSchema: {
101
+ type: "object";
102
+ properties: {
103
+ tool_name: {
104
+ type: string;
105
+ description: string;
106
+ };
107
+ arguments: {
108
+ type: string;
109
+ description: string;
110
+ };
111
+ provider?: undefined;
112
+ group_by?: undefined;
113
+ query?: undefined;
114
+ limit?: undefined;
115
+ };
116
+ required: string[];
117
+ };
118
+ })[];
119
+ export declare function handleListTools(tools: ProviderTool[], args: Record<string, unknown>): {
120
+ content: {
121
+ type: "text";
122
+ text: string;
123
+ }[];
124
+ };
125
+ export declare function handleSearchTools(tools: ProviderTool[], args: Record<string, unknown>): {
126
+ content: {
127
+ type: "text";
128
+ text: string;
129
+ }[];
130
+ };
131
+ export declare function handleToolInfo(toolMap: Map<string, ProviderTool>, args: Record<string, unknown>): {
132
+ isError: boolean;
133
+ content: {
134
+ type: "text";
135
+ text: string;
136
+ }[];
137
+ } | {
138
+ content: {
139
+ type: "text";
140
+ text: string;
141
+ }[];
142
+ isError?: undefined;
143
+ };
144
+ /**
145
+ * Payload handed to an {@link Execute} callback: the resolved tool plus the
146
+ * arguments extracted from the `call_tool` meta-tool's `arguments` field.
147
+ */
148
+ export interface ExecuteInput {
149
+ tool: ProviderTool;
150
+ args: Record<string, unknown>;
151
+ }
152
+ /**
153
+ * Transport-agnostic result of executing a tool.
154
+ *
155
+ * - `ok: true` carries the parsed tool output (`data`); mcp-core serializes it
156
+ * as the `text` content of the {@link CallToolResult}.
157
+ * - `ok: false` carries an `error` message; mcp-core surfaces it as an
158
+ * `isError` {@link CallToolResult}.
159
+ *
160
+ * The stdio server wraps its `fetch`-based executor (which throws on HTTP
161
+ * errors) into this discriminated union; the hosted gateway (Phase 2) maps its
162
+ * `IsolateResult` onto the same shape. Keeping errors as values (instead of
163
+ * throwing) lets both transports share a single handler.
164
+ */
165
+ export type ExecuteResult = {
166
+ ok: true;
167
+ data: unknown;
168
+ } | {
169
+ ok: false;
170
+ error: string;
171
+ };
172
+ /**
173
+ * Callback that performs the actual tool execution. Injected into
174
+ * {@link handleCallTool} so the handler stays transport-agnostic: the stdio
175
+ * server passes a `fetch`-based implementation, the gateway passes its own
176
+ * executor.
177
+ */
178
+ export type Execute = (input: ExecuteInput) => Promise<ExecuteResult>;
179
+ /**
180
+ * Handle a `call_tool` meta-tool invocation.
181
+ *
182
+ * `tool` is the *resolved* tool (the caller looks it up in its tool map, which
183
+ * for the gateway is the per-caller filtered catalog); `undefined` produces an
184
+ * "Unknown tool" result, matching the prior stdio behavior. The injected
185
+ * `execute` callback is invoked with `{ tool, args }` (the arguments extracted
186
+ * from `args["arguments"]`) and its {@link ExecuteResult} is wrapped in a
187
+ * {@link CallToolResult}.
188
+ */
189
+ export declare function handleCallTool(tool: ProviderTool | undefined, args: Record<string, unknown>, execute: Execute): Promise<{
190
+ isError: boolean;
191
+ content: {
192
+ type: "text";
193
+ text: string;
194
+ }[];
195
+ } | {
196
+ content: {
197
+ type: "text";
198
+ text: string;
199
+ }[];
200
+ isError?: undefined;
201
+ }>;
@@ -0,0 +1,183 @@
1
+ import { searchTools, groupTools } from "./search.js";
2
+ // ---------------------------------------------------------------------------
3
+ // Schema normalization
4
+ // ---------------------------------------------------------------------------
5
+ /**
6
+ * Ensure the input schema is a valid MCP tool inputSchema (type: "object").
7
+ */
8
+ export function normalizeInputSchema(schema) {
9
+ return {
10
+ type: "object",
11
+ ...(schema["properties"]
12
+ ? { properties: schema["properties"] }
13
+ : {}),
14
+ ...(schema["required"] ? { required: schema["required"] } : {}),
15
+ };
16
+ }
17
+ // ---------------------------------------------------------------------------
18
+ // Meta-tool definitions (served by tools/list)
19
+ // ---------------------------------------------------------------------------
20
+ export const META_TOOLS = [
21
+ {
22
+ name: "list_tools",
23
+ description: "List available tool names without full schemas. Pass an optional provider to filter results, or group_by to organize by category.",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ provider: {
28
+ type: "string",
29
+ description: "Filter results to tools from this provider name (e.g. 'github')",
30
+ },
31
+ group_by: {
32
+ type: "string",
33
+ enum: ["provider", "tag"],
34
+ description: "Group results by 'provider' or by OpenAPI 'tag'. When omitted, returns a flat list of names.",
35
+ },
36
+ },
37
+ },
38
+ },
39
+ {
40
+ name: "search_tools",
41
+ description: "Find tools by keyword. Searches tool names, OpenAPI tags, and descriptions using TF-IDF relevance ranking so the LLM can locate relevant tools without browsing the full catalog.",
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: {
45
+ query: {
46
+ type: "string",
47
+ description: "Natural language or keyword search (all words must appear in name, tags, or description)",
48
+ },
49
+ provider: {
50
+ type: "string",
51
+ description: "Restrict search to tools from this provider name (e.g. 'github')",
52
+ },
53
+ limit: {
54
+ type: "number",
55
+ description: "Maximum number of results to return (default 10)",
56
+ },
57
+ },
58
+ required: ["query"],
59
+ },
60
+ },
61
+ {
62
+ name: "tool_info",
63
+ description: "Get the full schema and metadata for a specific tool by name. Use this after list_tools or search_tools to retrieve the complete input schema before calling the tool.",
64
+ inputSchema: {
65
+ type: "object",
66
+ properties: {
67
+ tool_name: {
68
+ type: "string",
69
+ description: "The MCP tool name (e.g. 'github__repos_list')",
70
+ },
71
+ },
72
+ required: ["tool_name"],
73
+ },
74
+ },
75
+ {
76
+ name: "call_tool",
77
+ description: "Execute any registered tool by name with the provided arguments. Use tool_info first to get the correct argument schema.",
78
+ inputSchema: {
79
+ type: "object",
80
+ properties: {
81
+ tool_name: {
82
+ type: "string",
83
+ description: "The MCP tool name to execute (e.g. 'github__repos_list')",
84
+ },
85
+ arguments: {
86
+ type: "object",
87
+ description: "Arguments to pass to the tool",
88
+ },
89
+ },
90
+ required: ["tool_name", "arguments"],
91
+ },
92
+ },
93
+ ];
94
+ // ---------------------------------------------------------------------------
95
+ // Meta-tool call handlers
96
+ //
97
+ // Each handler returns a fresh object literal (no explicit return annotation)
98
+ // so the inferred type stays assignable to the MCP SDK's `ServerResult`
99
+ // union, which includes an index-signature envelope that named interface types
100
+ // cannot satisfy. `type: "text" as const` keeps the content block's `type`
101
+ // literal so the result is still assignable to {@link CallToolResult}.
102
+ // ---------------------------------------------------------------------------
103
+ export function handleListTools(tools, args) {
104
+ const providerFilter = typeof args["provider"] === "string" ? args["provider"] : undefined;
105
+ const groupBy = args["group_by"] === "provider" || args["group_by"] === "tag"
106
+ ? args["group_by"]
107
+ : undefined;
108
+ const filtered = providerFilter
109
+ ? tools.filter((t) => t.providerName === providerFilter)
110
+ : tools;
111
+ const result = groupBy === "provider" || groupBy === "tag"
112
+ ? groupTools(filtered, groupBy)
113
+ : filtered.map((t) => t.mcpName);
114
+ return {
115
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
116
+ };
117
+ }
118
+ export function handleSearchTools(tools, args) {
119
+ const query = typeof args["query"] === "string" ? args["query"] : "";
120
+ const providerFilter = typeof args["provider"] === "string" ? args["provider"] : undefined;
121
+ const limit = typeof args["limit"] === "number" && args["limit"] > 0 ? Math.floor(args["limit"]) : 10;
122
+ const results = searchTools(tools, query, providerFilter, limit);
123
+ return {
124
+ content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
125
+ };
126
+ }
127
+ export function handleToolInfo(toolMap, args) {
128
+ const requestedName = typeof args["tool_name"] === "string" ? args["tool_name"] : "";
129
+ const tool = toolMap.get(requestedName);
130
+ if (!tool) {
131
+ return {
132
+ isError: true,
133
+ content: [{ type: "text", text: `Unknown tool: ${requestedName}` }],
134
+ };
135
+ }
136
+ return {
137
+ content: [
138
+ {
139
+ type: "text",
140
+ text: JSON.stringify({
141
+ name: tool.mcpName,
142
+ description: tool.description,
143
+ provider: tool.providerName,
144
+ inputSchema: normalizeInputSchema(tool.inputSchema),
145
+ }, null, 2),
146
+ },
147
+ ],
148
+ };
149
+ }
150
+ /**
151
+ * Handle a `call_tool` meta-tool invocation.
152
+ *
153
+ * `tool` is the *resolved* tool (the caller looks it up in its tool map, which
154
+ * for the gateway is the per-caller filtered catalog); `undefined` produces an
155
+ * "Unknown tool" result, matching the prior stdio behavior. The injected
156
+ * `execute` callback is invoked with `{ tool, args }` (the arguments extracted
157
+ * from `args["arguments"]`) and its {@link ExecuteResult} is wrapped in a
158
+ * {@link CallToolResult}.
159
+ */
160
+ export async function handleCallTool(tool, args, execute) {
161
+ const requestedName = typeof args["tool_name"] === "string" ? args["tool_name"] : "";
162
+ if (!tool) {
163
+ return {
164
+ isError: true,
165
+ content: [{ type: "text", text: `Unknown tool: ${requestedName}` }],
166
+ };
167
+ }
168
+ const toolArgs = typeof args["arguments"] === "object" && args["arguments"] !== null
169
+ ? args["arguments"]
170
+ : {};
171
+ const result = await execute({ tool, args: toolArgs });
172
+ if (!result.ok) {
173
+ return {
174
+ isError: true,
175
+ content: [
176
+ { type: "text", text: `Error calling ${tool.mcpName}: ${result.error}` },
177
+ ],
178
+ };
179
+ }
180
+ const text = typeof result.data === "string" ? result.data : JSON.stringify(result.data, null, 2);
181
+ return { content: [{ type: "text", text }] };
182
+ }
183
+ //# sourceMappingURL=meta-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"meta-tools.js","sourceRoot":"","sources":["../src/meta-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { ProviderTool } from "./loader.js";
2
+ /**
3
+ * Tokenize text into lowercase words, splitting on non-alphanumeric characters.
4
+ * Also splits camelCase boundaries for better matching of MCP tool names.
5
+ *
6
+ * Examples:
7
+ * "github__repos_list" → ["github", "repos", "list"]
8
+ * "List public repositories" → ["list", "public", "repositories"]
9
+ * "security-advisories" → ["security", "advisories"]
10
+ */
11
+ export declare function tokenize(text: string): string[];
12
+ /**
13
+ * Build a TF-IDF scoring function over a corpus of tools.
14
+ *
15
+ * Field weights applied during term-frequency calculation:
16
+ * - name × 3 (most signal)
17
+ * - tags × 2 (category signal)
18
+ * - description × 1
19
+ *
20
+ * IDF formula: log((N + 1) / df) — smoothed to avoid division-by-zero.
21
+ *
22
+ * Returns a function `score(tool, queryWords) → number` that can be called
23
+ * for each candidate. Build once per search call, reuse across candidates.
24
+ */
25
+ export declare function buildTfIdf(tools: ProviderTool[]): (tool: ProviderTool, words: string[]) => number;
26
+ /**
27
+ * Search tools by keyword using TF-IDF relevance ranking.
28
+ *
29
+ * Matching: ALL query words must appear in at least one of name, tags, or
30
+ * description (case-insensitive substring match used for filtering; TF-IDF
31
+ * token scoring used for ranking).
32
+ *
33
+ * Returns up to `limit` results in descending relevance order.
34
+ */
35
+ export declare function searchTools(tools: ProviderTool[], query: string, provider: string | undefined, limit: number): Array<{
36
+ name: string;
37
+ description: string;
38
+ tags: string[];
39
+ }>;
40
+ /**
41
+ * Group tools by provider name or by OpenAPI tag.
42
+ * Tools with no tags fall back to grouping under their provider name.
43
+ */
44
+ export declare function groupTools(tools: ProviderTool[], by: "provider" | "tag"): Record<string, string[]>;
package/dist/search.js ADDED
@@ -0,0 +1,137 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Tokenization
3
+ // ---------------------------------------------------------------------------
4
+ /**
5
+ * Tokenize text into lowercase words, splitting on non-alphanumeric characters.
6
+ * Also splits camelCase boundaries for better matching of MCP tool names.
7
+ *
8
+ * Examples:
9
+ * "github__repos_list" → ["github", "repos", "list"]
10
+ * "List public repositories" → ["list", "public", "repositories"]
11
+ * "security-advisories" → ["security", "advisories"]
12
+ */
13
+ export function tokenize(text) {
14
+ return text
15
+ .replace(/([a-z])([A-Z])/g, "$1 $2") // split camelCase boundaries
16
+ .toLowerCase()
17
+ .split(/[^a-z0-9]+/)
18
+ .filter(Boolean);
19
+ }
20
+ // ---------------------------------------------------------------------------
21
+ // TF-IDF scoring
22
+ // ---------------------------------------------------------------------------
23
+ /**
24
+ * Build a TF-IDF scoring function over a corpus of tools.
25
+ *
26
+ * Field weights applied during term-frequency calculation:
27
+ * - name × 3 (most signal)
28
+ * - tags × 2 (category signal)
29
+ * - description × 1
30
+ *
31
+ * IDF formula: log((N + 1) / df) — smoothed to avoid division-by-zero.
32
+ *
33
+ * Returns a function `score(tool, queryWords) → number` that can be called
34
+ * for each candidate. Build once per search call, reuse across candidates.
35
+ */
36
+ export function buildTfIdf(tools) {
37
+ const N = tools.length;
38
+ if (N === 0)
39
+ return () => 0;
40
+ // Precompute weighted token bags for each tool
41
+ const bags = tools.map((tool) => {
42
+ const bag = new Map();
43
+ const add = (tokens, weight) => {
44
+ for (const tok of tokens) {
45
+ bag.set(tok, (bag.get(tok) ?? 0) + weight);
46
+ }
47
+ };
48
+ add(tokenize(tool.mcpName), 3);
49
+ for (const tag of tool.tags) {
50
+ add(tokenize(tag), 2);
51
+ }
52
+ add(tokenize(tool.description), 1);
53
+ return bag;
54
+ });
55
+ // Document frequency: how many tools contain each token
56
+ const df = new Map();
57
+ for (const bag of bags) {
58
+ for (const tok of bag.keys()) {
59
+ df.set(tok, (df.get(tok) ?? 0) + 1);
60
+ }
61
+ }
62
+ const bagIndex = new Map(tools.map((t, i) => [t, bags[i]]));
63
+ return (tool, words) => {
64
+ const bag = bagIndex.get(tool);
65
+ if (!bag)
66
+ return 0;
67
+ const totalWeight = [...bag.values()].reduce((s, v) => s + v, 0) || 1;
68
+ let score = 0;
69
+ for (const word of words) {
70
+ const tf = (bag.get(word) ?? 0) / totalWeight;
71
+ const docFreq = df.get(word) ?? 0;
72
+ if (docFreq === 0)
73
+ continue;
74
+ const idf = Math.log((N + 1) / docFreq);
75
+ score += tf * idf;
76
+ }
77
+ return score;
78
+ };
79
+ }
80
+ // ---------------------------------------------------------------------------
81
+ // searchTools
82
+ // ---------------------------------------------------------------------------
83
+ /**
84
+ * Search tools by keyword using TF-IDF relevance ranking.
85
+ *
86
+ * Matching: ALL query words must appear in at least one of name, tags, or
87
+ * description (case-insensitive substring match used for filtering; TF-IDF
88
+ * token scoring used for ranking).
89
+ *
90
+ * Returns up to `limit` results in descending relevance order.
91
+ */
92
+ export function searchTools(tools, query, provider, limit) {
93
+ const words = tokenize(query);
94
+ if (words.length === 0)
95
+ return [];
96
+ const source = provider ? tools.filter((t) => t.providerName === provider) : tools;
97
+ const scoreFn = buildTfIdf(source);
98
+ const candidates = [];
99
+ for (const tool of source) {
100
+ const nameLower = tool.mcpName.toLowerCase();
101
+ const tagsText = tool.tags.join(" ").toLowerCase();
102
+ const descLower = tool.description.toLowerCase();
103
+ const allMatch = words.every((word) => nameLower.includes(word) || tagsText.includes(word) || descLower.includes(word));
104
+ if (allMatch) {
105
+ candidates.push({ tool, score: scoreFn(tool, words) });
106
+ }
107
+ }
108
+ candidates.sort((a, b) => b.score - a.score);
109
+ return candidates.slice(0, limit).map(({ tool }) => ({
110
+ name: tool.mcpName,
111
+ description: tool.description,
112
+ tags: tool.tags,
113
+ }));
114
+ }
115
+ // ---------------------------------------------------------------------------
116
+ // groupTools
117
+ // ---------------------------------------------------------------------------
118
+ /**
119
+ * Group tools by provider name or by OpenAPI tag.
120
+ * Tools with no tags fall back to grouping under their provider name.
121
+ */
122
+ export function groupTools(tools, by) {
123
+ const grouped = {};
124
+ for (const tool of tools) {
125
+ if (by === "provider") {
126
+ (grouped[tool.providerName] ??= []).push(tool.mcpName);
127
+ }
128
+ else {
129
+ const keys = tool.tags.length > 0 ? tool.tags : [tool.providerName];
130
+ for (const key of keys) {
131
+ (grouped[key] ??= []).push(tool.mcpName);
132
+ }
133
+ }
134
+ }
135
+ return grouped;
136
+ }
137
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@utdk/mcp-core",
3
+ "version": "0.1.0-dev.37053ca",
4
+ "type": "module",
5
+ "description": "Shared tool catalog and meta-tool handlers for @utdk/mcp packages",
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "dependencies": {
15
+ "@utcp/http": "^1.1.1",
16
+ "@utdk/common": "0.1.0-dev.37053ca"
17
+ },
18
+ "optionalDependencies": {
19
+ "@utdk/datadog": "1.0.0-20260407.1-dev.37053ca",
20
+ "@utdk/figma": "0.37.0-20260408.5-dev.37053ca",
21
+ "@utdk/github": "1.1.4-20260608.2-dev.37053ca",
22
+ "@utdk/airtable": "0.1.0-20260530.2-dev.37053ca",
23
+ "@utdk/google": "0.0.1-20260407.8-dev.37053ca",
24
+ "@utdk/intercom": "2.15.0-20260530.1-dev.37053ca",
25
+ "@utdk/hubspot": "3.0.0-20260530.2-dev.37053ca",
26
+ "@utdk/jira": "1001.0.0-SNAPSHOT-500abd49de29b046db51cf0460caa503167075c0-20260530.2-dev.37053ca",
27
+ "@utdk/linear": "1.0.0-20260530.1-dev.37053ca",
28
+ "@utdk/notion": "1.0.0-20260530.1-dev.37053ca",
29
+ "@utdk/salesforce": "58.0.0-20260530.1-dev.37053ca",
30
+ "@utdk/openai": "2.3.0-20260407.1-dev.37053ca",
31
+ "@utdk/sendgrid": "1.0.0-20260530.1-dev.37053ca",
32
+ "@utdk/slack": "1.7.0-20260530.1-dev.37053ca",
33
+ "@utdk/spotify": "1.0.0-20260407.6-dev.37053ca",
34
+ "@utdk/twilio": "1.0.0-20260530.2-dev.37053ca",
35
+ "@utdk/zendesk": "2.0.0-20260530.2-dev.37053ca",
36
+ "@utdk/stripe": "0.0.1-20260530.2-dev.37053ca"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.5.0",
40
+ "typescript": "^5.7.3",
41
+ "vitest": "2.1.5"
42
+ },
43
+ "engines": {
44
+ "node": ">=20.0.0"
45
+ },
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.json",
48
+ "check-types": "tsc -p tsconfig.json --noEmit",
49
+ "typecheck": "tsc -p tsconfig.json --noEmit",
50
+ "clean": "rm -rf dist",
51
+ "test": "vitest run"
52
+ }
53
+ }