@toolrank/mcp-server 0.1.0 → 0.1.2
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.
- package/README.md +24 -0
- package/mcp-worker/.wrangler/cache/wrangler-account.json +6 -0
- package/mcp-worker/worker.js +296 -0
- package/mcp-worker/wrangler.toml +3 -0
- package/package.json +7 -2
- package/src/index.ts +14 -1
- package/dist/index.d.ts +0 -13
- package/dist/index.js +0 -397
- package/smithery.yaml +0 -17
- package/tsconfig.json +0 -15
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# ToolRank MCP Server
|
|
2
|
+
|
|
3
|
+
Score and optimize MCP tool definitions for AI agent discovery. The first ATO (Agent Tool Optimization) platform.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
### toolrank_score
|
|
8
|
+
Analyzes MCP tool definitions and returns a ToolRank Score (0-100) measuring agent-readiness. Evaluates four dimensions: Findability (25%), Clarity (35%), Precision (25%), and Efficiency (15%). Returns per-tool scores, maturity level, specific issues found, and fix suggestions ranked by impact.
|
|
9
|
+
|
|
10
|
+
### toolrank_suggest
|
|
11
|
+
Generates specific improvement suggestions for MCP tool definitions. Returns rewritten descriptions, improved schemas, and estimated score improvements.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
```bash
|
|
15
|
+
npx @toolrank/mcp-server
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Links
|
|
19
|
+
|
|
20
|
+
- Website: https://toolrank.dev
|
|
21
|
+
- Framework: https://toolrank.dev/framework
|
|
22
|
+
- Ranking: https://toolrank.dev/ranking
|
|
23
|
+
- GitHub: https://github.com/imhiroki/toolrank
|
|
24
|
+
- License: MIT
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ToolRank MCP Server - Cloudflare Workers HTTP endpoint
|
|
3
|
+
*
|
|
4
|
+
* Wraps the ToolRank MCP scoring engine as a Streamable HTTP server
|
|
5
|
+
* for Smithery registration and remote MCP client access.
|
|
6
|
+
*
|
|
7
|
+
* Deploy: wrangler deploy
|
|
8
|
+
* URL: https://mcp.toolrank.dev/mcp
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Inline scoring engine (simplified Level A for Workers)
|
|
12
|
+
function scoreTool(tool) {
|
|
13
|
+
const name = tool.name || '';
|
|
14
|
+
const desc = tool.description || '';
|
|
15
|
+
const schema = tool.inputSchema || tool.input_schema || {};
|
|
16
|
+
const props = schema.properties || {};
|
|
17
|
+
const required = schema.required || [];
|
|
18
|
+
const issues = [];
|
|
19
|
+
|
|
20
|
+
// Clarity (max 35)
|
|
21
|
+
let clarity = 0;
|
|
22
|
+
if (!desc.trim()) {
|
|
23
|
+
issues.push({ dim: 'clarity', sev: 'critical', msg: 'No description', fix: 'Add description with purpose, context, return value', impact: 15 });
|
|
24
|
+
} else {
|
|
25
|
+
clarity += 5;
|
|
26
|
+
if (desc.length >= 80 && desc.length <= 250) clarity += 8;
|
|
27
|
+
else if (desc.length >= 50) { clarity += 5; issues.push({ dim: 'clarity', sev: 'warning', msg: `Description ${desc.length} chars (optimal: 80-200)`, fix: 'Expand description', impact: 5 }); }
|
|
28
|
+
else { clarity += 2; issues.push({ dim: 'clarity', sev: 'critical', msg: `Description too short (${desc.length} chars)`, fix: 'Expand to 80-200 chars', impact: 10 }); }
|
|
29
|
+
|
|
30
|
+
const dl = desc.toLowerCase();
|
|
31
|
+
if (/^(get|set|create|update|delete|search|find|list|fetch|send|retriev|return|provid)/.test(dl)) clarity += 6;
|
|
32
|
+
else { clarity += 3; issues.push({ dim: 'clarity', sev: 'warning', msg: 'No action verb at start', fix: "Start with verb like 'Searches for...', 'Creates...'", impact: 5 }); }
|
|
33
|
+
|
|
34
|
+
if (/use this|when|useful for|ideal for|designed for/.test(dl)) clarity += 6;
|
|
35
|
+
else { clarity += 2; issues.push({ dim: 'clarity', sev: 'warning', msg: 'No usage context', fix: "Add 'Use this when...'", impact: 6 }); }
|
|
36
|
+
|
|
37
|
+
if (/returns?|outputs?|produces|yields|result/.test(dl)) clarity += 6;
|
|
38
|
+
else { clarity += 3; issues.push({ dim: 'clarity', sev: 'info', msg: 'No return value described', fix: "Add 'Returns...'", impact: 3 }); }
|
|
39
|
+
|
|
40
|
+
clarity += 4; // name-desc alignment bonus
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Precision (max 25)
|
|
44
|
+
let precision = 0;
|
|
45
|
+
if (!schema.type) {
|
|
46
|
+
issues.push({ dim: 'precision', sev: 'critical', msg: 'No input schema', fix: 'Add inputSchema with types', impact: 12 });
|
|
47
|
+
} else {
|
|
48
|
+
precision += 5;
|
|
49
|
+
const pk = Object.keys(props);
|
|
50
|
+
if (pk.length > 0) {
|
|
51
|
+
const noType = pk.filter(k => !props[k].type);
|
|
52
|
+
precision += noType.length === 0 ? 5 : 2;
|
|
53
|
+
if (noType.length > 0) issues.push({ dim: 'precision', sev: 'warning', msg: `Missing types: ${noType.join(', ')}`, fix: 'Add type to each param', impact: 4 });
|
|
54
|
+
|
|
55
|
+
const noDesc = pk.filter(k => !props[k].description);
|
|
56
|
+
precision += noDesc.length === 0 ? 5 : 2;
|
|
57
|
+
if (noDesc.length > 0) issues.push({ dim: 'precision', sev: 'warning', msg: `Missing param descriptions: ${noDesc.join(', ')}`, fix: 'Add description to each param', impact: 5 });
|
|
58
|
+
|
|
59
|
+
precision += required.length > 0 ? 5 : 2;
|
|
60
|
+
if (required.length === 0) issues.push({ dim: 'precision', sev: 'info', msg: 'No required fields', fix: 'Add required array', impact: 3 });
|
|
61
|
+
|
|
62
|
+
precision += 5;
|
|
63
|
+
} else {
|
|
64
|
+
precision += 10;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Efficiency (max 15)
|
|
69
|
+
const tokens = JSON.stringify(tool).length / 4;
|
|
70
|
+
let efficiency = tokens > 2000 ? 4 : tokens > 1000 ? 8 : tokens > 500 ? 12 : 15;
|
|
71
|
+
if (tokens > 1000) issues.push({ dim: 'efficiency', sev: 'warning', msg: `~${Math.round(tokens)} tokens`, fix: 'Reduce definition size', impact: 3 });
|
|
72
|
+
|
|
73
|
+
// Findability (max 25)
|
|
74
|
+
let findability = name.length >= 4 ? 20 : 8;
|
|
75
|
+
if (name.length < 4) issues.push({ dim: 'findability', sev: 'warning', msg: `Name '${name}' too short`, fix: 'Use descriptive name', impact: 4 });
|
|
76
|
+
if (/^[a-z][a-zA-Z0-9_]*$/.test(name)) findability += 5;
|
|
77
|
+
else { issues.push({ dim: 'findability', sev: 'info', msg: 'Name not snake_case', fix: 'Use snake_case naming', impact: 2 }); }
|
|
78
|
+
|
|
79
|
+
const total = Math.round((findability + clarity + precision + efficiency) * 10) / 10;
|
|
80
|
+
let level, levelName;
|
|
81
|
+
if (total >= 85) { level = 4; levelName = 'Dominant'; }
|
|
82
|
+
else if (total >= 70) { level = 3; levelName = 'Preferred'; }
|
|
83
|
+
else if (total >= 50) { level = 2; levelName = 'Selectable'; }
|
|
84
|
+
else if (total >= 25) { level = 1; levelName = 'Visible'; }
|
|
85
|
+
else { level = 0; levelName = 'Absent'; }
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
name, total, level, levelName,
|
|
89
|
+
dimensions: { findability, clarity, precision, efficiency },
|
|
90
|
+
issues: issues.sort((a, b) => b.impact - a.impact),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Tool definitions (what MCP clients see)
|
|
95
|
+
const TOOLS = [
|
|
96
|
+
{
|
|
97
|
+
name: "toolrank_score",
|
|
98
|
+
description: "Analyzes MCP tool definitions and returns a ToolRank Score (0-100) measuring agent-readiness. Evaluates four dimensions: Findability (25%), Clarity (35%), Precision (25%), and Efficiency (15%). Use this when you want to check or improve the quality of your MCP tool definitions. Returns per-tool scores, maturity level, specific issues found, and fix suggestions ranked by impact.",
|
|
99
|
+
inputSchema: {
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
tools: {
|
|
103
|
+
type: "array",
|
|
104
|
+
description: "Array of MCP tool definition objects. Each must have 'name' and 'description' fields. 'inputSchema' is optional but improves Precision score.",
|
|
105
|
+
items: {
|
|
106
|
+
type: "object",
|
|
107
|
+
properties: {
|
|
108
|
+
name: { type: "string", description: "Tool name (snake_case recommended)" },
|
|
109
|
+
description: { type: "string", description: "Tool description" },
|
|
110
|
+
inputSchema: { type: "object", description: "JSON Schema for tool input parameters" }
|
|
111
|
+
},
|
|
112
|
+
required: ["name", "description"]
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
required: ["tools"]
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "toolrank_suggest",
|
|
121
|
+
description: "Generates specific improvement suggestions for MCP tool definitions to increase their ToolRank Score. Use this when you have a low score and want actionable fix recommendations. Returns rewritten description, improved schema, and estimated score improvement for each suggestion.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
tool: {
|
|
126
|
+
type: "object",
|
|
127
|
+
description: "Single MCP tool definition to improve",
|
|
128
|
+
properties: {
|
|
129
|
+
name: { type: "string", description: "Tool name" },
|
|
130
|
+
description: { type: "string", description: "Current tool description" },
|
|
131
|
+
inputSchema: { type: "object", description: "Current JSON Schema" }
|
|
132
|
+
},
|
|
133
|
+
required: ["name", "description"]
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
required: ["tool"]
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
// Handle MCP protocol messages
|
|
142
|
+
function handleMessage(msg) {
|
|
143
|
+
const { method, params, id } = msg;
|
|
144
|
+
|
|
145
|
+
switch (method) {
|
|
146
|
+
case "initialize":
|
|
147
|
+
return {
|
|
148
|
+
jsonrpc: "2.0", id,
|
|
149
|
+
result: {
|
|
150
|
+
protocolVersion: "2024-11-05",
|
|
151
|
+
capabilities: { tools: {} },
|
|
152
|
+
serverInfo: { name: "toolrank", version: "0.1.1" }
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
case "tools/list":
|
|
157
|
+
return { jsonrpc: "2.0", id, result: { tools: TOOLS } };
|
|
158
|
+
|
|
159
|
+
case "tools/call": {
|
|
160
|
+
const toolName = params?.name;
|
|
161
|
+
const args = params?.arguments || {};
|
|
162
|
+
|
|
163
|
+
if (toolName === "toolrank_score") {
|
|
164
|
+
const tools = args.tools || [];
|
|
165
|
+
const results = tools.map(t => scoreTool(t));
|
|
166
|
+
const avg = results.length > 0
|
|
167
|
+
? Math.round(results.reduce((s, r) => s + r.total, 0) / results.length * 10) / 10
|
|
168
|
+
: 0;
|
|
169
|
+
|
|
170
|
+
return {
|
|
171
|
+
jsonrpc: "2.0", id,
|
|
172
|
+
result: {
|
|
173
|
+
content: [{
|
|
174
|
+
type: "text",
|
|
175
|
+
text: JSON.stringify({ average_score: avg, tools: results }, null, 2)
|
|
176
|
+
}]
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (toolName === "toolrank_suggest") {
|
|
182
|
+
const tool = args.tool || {};
|
|
183
|
+
const scored = scoreTool(tool);
|
|
184
|
+
const suggestions = scored.issues.slice(0, 5).map(i => ({
|
|
185
|
+
dimension: i.dim,
|
|
186
|
+
issue: i.msg,
|
|
187
|
+
fix: i.fix,
|
|
188
|
+
estimated_impact: `+${i.impact} points`
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
// Generate rewrite
|
|
192
|
+
let newDesc = tool.description || '';
|
|
193
|
+
if (newDesc.length < 50) {
|
|
194
|
+
const parts = (tool.name || '').split(/[_\-]/).filter(p => p.length > 1);
|
|
195
|
+
newDesc = `${(parts[0] || 'Performs').charAt(0).toUpperCase() + (parts[0] || 'performs').slice(1)}s ${parts.slice(1).join(' ') || 'an operation'}. Use this when you need to ${parts.slice(1).join(' ') || 'perform this action'}. Returns the result as structured data.`;
|
|
196
|
+
} else {
|
|
197
|
+
if (!/use this|when|useful for/i.test(newDesc)) newDesc += ` Use this when you need to ${(tool.name || '').replace(/[_\-]/g, ' ')}.`;
|
|
198
|
+
if (!/returns?|outputs?/i.test(newDesc)) newDesc += ' Returns the result as structured data.';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
jsonrpc: "2.0", id,
|
|
203
|
+
result: {
|
|
204
|
+
content: [{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: JSON.stringify({
|
|
207
|
+
current_score: scored.total,
|
|
208
|
+
suggestions,
|
|
209
|
+
rewritten_description: newDesc
|
|
210
|
+
}, null, 2)
|
|
211
|
+
}]
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return {
|
|
217
|
+
jsonrpc: "2.0", id,
|
|
218
|
+
error: { code: -32601, message: `Unknown tool: ${toolName}` }
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
case "notifications/initialized":
|
|
223
|
+
case "ping":
|
|
224
|
+
return method === "ping" ? { jsonrpc: "2.0", id, result: {} } : null;
|
|
225
|
+
|
|
226
|
+
default:
|
|
227
|
+
return {
|
|
228
|
+
jsonrpc: "2.0", id,
|
|
229
|
+
error: { code: -32601, message: `Method not found: ${method}` }
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export default {
|
|
235
|
+
async fetch(request, env) {
|
|
236
|
+
const url = new URL(request.url);
|
|
237
|
+
|
|
238
|
+
// CORS
|
|
239
|
+
if (request.method === "OPTIONS") {
|
|
240
|
+
return new Response(null, {
|
|
241
|
+
headers: {
|
|
242
|
+
"Access-Control-Allow-Origin": "*",
|
|
243
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
244
|
+
"Access-Control-Allow-Headers": "Content-Type",
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Health check
|
|
250
|
+
if (request.method === "GET" && (url.pathname === "/" || url.pathname === "/mcp")) {
|
|
251
|
+
return new Response(JSON.stringify({
|
|
252
|
+
name: "toolrank",
|
|
253
|
+
version: "0.1.1",
|
|
254
|
+
description: "ToolRank MCP Server — Score and optimize MCP tool definitions for AI agent discovery.",
|
|
255
|
+
mcp_endpoint: `${url.origin}/mcp`,
|
|
256
|
+
tools: TOOLS.map(t => t.name),
|
|
257
|
+
}), {
|
|
258
|
+
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// MCP endpoint
|
|
263
|
+
if (request.method === "POST" && (url.pathname === "/mcp" || url.pathname === "/")) {
|
|
264
|
+
try {
|
|
265
|
+
const body = await request.json();
|
|
266
|
+
|
|
267
|
+
// Handle batch
|
|
268
|
+
if (Array.isArray(body)) {
|
|
269
|
+
const results = body.map(msg => handleMessage(msg)).filter(r => r !== null);
|
|
270
|
+
return new Response(JSON.stringify(results), {
|
|
271
|
+
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const result = handleMessage(body);
|
|
276
|
+
if (result === null) {
|
|
277
|
+
return new Response(null, { status: 204 });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return new Response(JSON.stringify(result), {
|
|
281
|
+
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "*" }
|
|
282
|
+
});
|
|
283
|
+
} catch (e) {
|
|
284
|
+
return new Response(JSON.stringify({
|
|
285
|
+
jsonrpc: "2.0",
|
|
286
|
+
error: { code: -32700, message: "Parse error" }
|
|
287
|
+
}), {
|
|
288
|
+
status: 400,
|
|
289
|
+
headers: { "Content-Type": "application/json" }
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return new Response("Not found", { status: 404 });
|
|
295
|
+
}
|
|
296
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolrank/mcp-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "ToolRank MCP Server — Score and optimize MCP tool definitions for AI agent discovery. The first ATO (Agent Tool Optimization) tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"start": "node dist/index.js",
|
|
13
13
|
"dev": "tsx src/index.ts"
|
|
14
14
|
},
|
|
15
|
-
"keywords": ["mcp"
|
|
15
|
+
"keywords": "[\"mcp\",\"ato\",\"agent-tool-optimization\",\"toolrank\",\"ai-agent\",\"tool-scoring\",\"mcp-server\"]",
|
|
16
16
|
"author": "Hiroki Honda",
|
|
17
17
|
"license": "MIT",
|
|
18
18
|
"dependencies": {
|
|
@@ -21,5 +21,10 @@
|
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"tsx": "^4.0.0",
|
|
23
23
|
"typescript": "^5.5.0"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://toolrank.dev",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/imhiroki/toolrank"
|
|
24
29
|
}
|
|
25
30
|
}
|
package/src/index.ts
CHANGED
|
@@ -437,4 +437,17 @@ async function main() {
|
|
|
437
437
|
console.error("ToolRank MCP Server running on stdio");
|
|
438
438
|
}
|
|
439
439
|
|
|
440
|
-
|
|
440
|
+
// Smithery sandbox export
|
|
441
|
+
export function createSandboxServer() {
|
|
442
|
+
return server;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Default export must be a function for Smithery shttp
|
|
446
|
+
export default function() {
|
|
447
|
+
return server;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Run stdio when executed directly
|
|
451
|
+
if (process.argv[1]?.includes('index')) {
|
|
452
|
+
main().catch(console.error);
|
|
453
|
+
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ToolRank MCP Server v0.1
|
|
3
|
-
*
|
|
4
|
-
* Provides AI agents with tools to score and optimize MCP tool definitions.
|
|
5
|
-
* This server's own tool definitions are designed to achieve ToolRank Score 100/100,
|
|
6
|
-
* serving as a live demonstration of ATO (Agent Tool Optimization) principles.
|
|
7
|
-
*
|
|
8
|
-
* Tools:
|
|
9
|
-
* toolrank_score — Analyze tool definitions and return quality scores
|
|
10
|
-
* toolrank_compare — Compare scores against category averages
|
|
11
|
-
* toolrank_suggest — Generate specific improvement suggestions
|
|
12
|
-
*/
|
|
13
|
-
export {};
|
package/dist/index.js
DELETED
|
@@ -1,397 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ToolRank MCP Server v0.1
|
|
3
|
-
*
|
|
4
|
-
* Provides AI agents with tools to score and optimize MCP tool definitions.
|
|
5
|
-
* This server's own tool definitions are designed to achieve ToolRank Score 100/100,
|
|
6
|
-
* serving as a live demonstration of ATO (Agent Tool Optimization) principles.
|
|
7
|
-
*
|
|
8
|
-
* Tools:
|
|
9
|
-
* toolrank_score — Analyze tool definitions and return quality scores
|
|
10
|
-
* toolrank_compare — Compare scores against category averages
|
|
11
|
-
* toolrank_suggest — Generate specific improvement suggestions
|
|
12
|
-
*/
|
|
13
|
-
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
14
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
15
|
-
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
16
|
-
function scoreTool(tool) {
|
|
17
|
-
const issues = [];
|
|
18
|
-
const name = tool.name || "unknown";
|
|
19
|
-
const desc = (tool.description || "").trim();
|
|
20
|
-
const schema = tool.inputSchema || tool.input_schema || {};
|
|
21
|
-
const props = schema.properties || {};
|
|
22
|
-
const required = schema.required || [];
|
|
23
|
-
const propKeys = Object.keys(props);
|
|
24
|
-
// --- Clarity (max 35) ---
|
|
25
|
-
let clarityScore = 0;
|
|
26
|
-
const clarityMax = 6;
|
|
27
|
-
let clarityPoints = 0;
|
|
28
|
-
if (!desc) {
|
|
29
|
-
issues.push({ dimension: "clarity", severity: "critical", message: "No description defined", fix: "Add a description explaining purpose, usage context, and return value", impact: 15 });
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
clarityPoints += 1;
|
|
33
|
-
if (desc.length < 20) {
|
|
34
|
-
issues.push({ dimension: "clarity", severity: "critical", message: `Description too short (${desc.length} chars)`, fix: "Expand to 80-200 chars with purpose and context", impact: 10 });
|
|
35
|
-
clarityPoints += 0.2;
|
|
36
|
-
}
|
|
37
|
-
else if (desc.length >= 80 && desc.length <= 250) {
|
|
38
|
-
clarityPoints += 1;
|
|
39
|
-
}
|
|
40
|
-
else if (desc.length >= 50) {
|
|
41
|
-
clarityPoints += 0.7;
|
|
42
|
-
if (desc.length < 80)
|
|
43
|
-
issues.push({ dimension: "clarity", severity: "warning", message: `Description short (${desc.length} chars)`, fix: "Expand to 80-200 chars for optimal agent understanding", impact: 5 });
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
clarityPoints += 0.3;
|
|
47
|
-
issues.push({ dimension: "clarity", severity: "warning", message: `Description short (${desc.length} chars)`, fix: "Expand to 80-200 chars", impact: 5 });
|
|
48
|
-
}
|
|
49
|
-
const descLower = desc.toLowerCase();
|
|
50
|
-
if (/^(get|set|create|update|delete|search|find|list|fetch|send|this tool|retriev|return|provid|allow|enabl|perform|analyz|generat|calculat|check|validat|convert|extract|scor|compar|suggest|optimiz)/.test(descLower)) {
|
|
51
|
-
clarityPoints += 1;
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
clarityPoints += 0.5;
|
|
55
|
-
issues.push({ dimension: "clarity", severity: "warning", message: "No clear purpose verb at start", fix: "Start with a verb: 'Analyzes...', 'Returns...', 'Generates...'", impact: 5 });
|
|
56
|
-
}
|
|
57
|
-
if (/use this|when|useful for|ideal for|designed for|helps|allows|enables|for example/.test(descLower)) {
|
|
58
|
-
clarityPoints += 1;
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
clarityPoints += 0.4;
|
|
62
|
-
issues.push({ dimension: "clarity", severity: "warning", message: "No usage context", fix: "Add 'Use this when...' to help agents decide when to select this tool", impact: 6 });
|
|
63
|
-
}
|
|
64
|
-
if (/returns?|outputs?|produces|yields|result|response|provides|generates/.test(descLower)) {
|
|
65
|
-
clarityPoints += 1;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
clarityPoints += 0.5;
|
|
69
|
-
issues.push({ dimension: "clarity", severity: "info", message: "No return value described", fix: "Add 'Returns...' to describe the output", impact: 3 });
|
|
70
|
-
}
|
|
71
|
-
const nameParts = name.toLowerCase().split(/[_\-]/).filter((p) => p.length > 2);
|
|
72
|
-
const matchCount = nameParts.filter((p) => descLower.includes(p)).length;
|
|
73
|
-
clarityPoints += nameParts.length > 0 && matchCount / nameParts.length >= 0.5 ? 1 : 0.5;
|
|
74
|
-
}
|
|
75
|
-
clarityScore = Math.round((clarityPoints / clarityMax) * 35 * 10) / 10;
|
|
76
|
-
// --- Precision (max 25) ---
|
|
77
|
-
let precisionPoints = 0;
|
|
78
|
-
const precisionMax = 5;
|
|
79
|
-
if (!schema.type) {
|
|
80
|
-
issues.push({ dimension: "precision", severity: "critical", message: "No input schema defined", fix: "Add inputSchema with type definitions for all parameters", impact: 12 });
|
|
81
|
-
}
|
|
82
|
-
else {
|
|
83
|
-
precisionPoints += 1;
|
|
84
|
-
if (propKeys.length > 0) {
|
|
85
|
-
const missingTypes = propKeys.filter(k => !props[k].type);
|
|
86
|
-
if (missingTypes.length === 0)
|
|
87
|
-
precisionPoints += 1;
|
|
88
|
-
else {
|
|
89
|
-
precisionPoints += Math.max(0, 1 - missingTypes.length / propKeys.length);
|
|
90
|
-
issues.push({ dimension: "precision", severity: "warning", message: `Missing types: ${missingTypes.join(", ")}`, fix: "Add 'type' to each parameter", impact: 4 });
|
|
91
|
-
}
|
|
92
|
-
const missingDesc = propKeys.filter(k => !props[k].description);
|
|
93
|
-
if (missingDesc.length === 0)
|
|
94
|
-
precisionPoints += 1;
|
|
95
|
-
else {
|
|
96
|
-
precisionPoints += Math.max(0, 1 - missingDesc.length / propKeys.length);
|
|
97
|
-
issues.push({ dimension: "precision", severity: "warning", message: `Missing param descriptions: ${missingDesc.join(", ")}`, fix: "Add 'description' to each parameter", impact: 5 });
|
|
98
|
-
}
|
|
99
|
-
precisionPoints += required.length > 0 ? 1 : 0.5;
|
|
100
|
-
if (required.length === 0 && propKeys.length > 0) {
|
|
101
|
-
issues.push({ dimension: "precision", severity: "info", message: "No required fields specified", fix: "Add 'required' array for mandatory parameters", impact: 3 });
|
|
102
|
-
}
|
|
103
|
-
precisionPoints += 1;
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
precisionPoints += 2;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const precisionScore = Math.round((precisionPoints / precisionMax) * 25 * 10) / 10;
|
|
110
|
-
// --- Efficiency (max 15) ---
|
|
111
|
-
const tokenEst = JSON.stringify(tool).length / 4;
|
|
112
|
-
let effRatio = tokenEst > 2000 ? 0.3 : tokenEst > 1000 ? 0.6 : tokenEst > 500 ? 0.8 : 1.0;
|
|
113
|
-
if (tokenEst > 1000)
|
|
114
|
-
issues.push({ dimension: "efficiency", severity: "warning", message: `~${Math.round(tokenEst)} tokens estimated`, fix: "Create a compact variant", impact: 3 });
|
|
115
|
-
if (!/^[a-z][a-zA-Z0-9_]*$/.test(name)) {
|
|
116
|
-
effRatio -= 0.15;
|
|
117
|
-
issues.push({ dimension: "efficiency", severity: "warning", message: `Name '${name}' not snake_case`, fix: "Use snake_case like 'search_users'", impact: 3 });
|
|
118
|
-
}
|
|
119
|
-
const genericNames = new Set(["run", "execute", "do", "action", "tool", "function", "process", "handle"]);
|
|
120
|
-
if (genericNames.has(name.toLowerCase())) {
|
|
121
|
-
effRatio -= 0.15;
|
|
122
|
-
issues.push({ dimension: "efficiency", severity: "warning", message: `Name '${name}' too generic`, fix: "Use specific name like 'create_pull_request'", impact: 5 });
|
|
123
|
-
}
|
|
124
|
-
const efficiencyScore = Math.round(Math.max(0, effRatio) * 15 * 10) / 10;
|
|
125
|
-
// --- Findability (max 25, limited without registry) ---
|
|
126
|
-
let findRatio = name.length < 4 ? 0.3 : 0.8;
|
|
127
|
-
if (name.length < 4)
|
|
128
|
-
issues.push({ dimension: "findability", severity: "warning", message: `Name '${name}' too short for discovery`, fix: "Use a longer, descriptive name", impact: 4 });
|
|
129
|
-
const findabilityScore = Math.round(findRatio * 25 * 10) / 10;
|
|
130
|
-
const total = Math.round((findabilityScore + clarityScore + precisionScore + efficiencyScore) * 10) / 10;
|
|
131
|
-
let level, levelName;
|
|
132
|
-
if (total >= 85) {
|
|
133
|
-
level = 4;
|
|
134
|
-
levelName = "Dominant";
|
|
135
|
-
}
|
|
136
|
-
else if (total >= 70) {
|
|
137
|
-
level = 3;
|
|
138
|
-
levelName = "Preferred";
|
|
139
|
-
}
|
|
140
|
-
else if (total >= 50) {
|
|
141
|
-
level = 2;
|
|
142
|
-
levelName = "Selectable";
|
|
143
|
-
}
|
|
144
|
-
else if (total >= 25) {
|
|
145
|
-
level = 1;
|
|
146
|
-
levelName = "Visible";
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
level = 0;
|
|
150
|
-
levelName = "Absent";
|
|
151
|
-
}
|
|
152
|
-
return {
|
|
153
|
-
name,
|
|
154
|
-
total,
|
|
155
|
-
level,
|
|
156
|
-
levelName,
|
|
157
|
-
dimensions: { findability: findabilityScore, clarity: clarityScore, precision: precisionScore, efficiency: efficiencyScore },
|
|
158
|
-
issues: issues.sort((a, b) => b.impact - a.impact),
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
// --- MCP Server Setup ---
|
|
162
|
-
const server = new Server({ name: "toolrank", version: "0.1.0" }, { capabilities: { tools: {} } });
|
|
163
|
-
// Tool definitions — these ARE the product. ATO Score 100/100 target.
|
|
164
|
-
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
165
|
-
tools: [
|
|
166
|
-
{
|
|
167
|
-
name: "toolrank_score",
|
|
168
|
-
description: "Analyzes MCP tool definitions and returns a ToolRank Score (0-100) measuring how likely AI agents are to discover and select each tool. Scores four dimensions: Findability (can agents find it?), Clarity (can agents understand it?), Precision (is the schema well-defined?), and Efficiency (is it token-efficient?). Use this when you want to evaluate the quality of your MCP server's tool definitions before publishing. Returns per-tool scores, maturity level (Absent/Visible/Selectable/Preferred/Dominant), specific issues found, and prioritized improvement suggestions with predicted score impact.",
|
|
169
|
-
inputSchema: {
|
|
170
|
-
type: "object",
|
|
171
|
-
properties: {
|
|
172
|
-
tools: {
|
|
173
|
-
type: "array",
|
|
174
|
-
description: "Array of MCP tool definition objects. Each object should have 'name' (string), 'description' (string), and optionally 'inputSchema' (JSON Schema object with properties, required, etc.)",
|
|
175
|
-
items: {
|
|
176
|
-
type: "object",
|
|
177
|
-
properties: {
|
|
178
|
-
name: { type: "string", description: "Tool name (e.g., 'create_issue')" },
|
|
179
|
-
description: { type: "string", description: "Tool description text" },
|
|
180
|
-
inputSchema: { type: "object", description: "JSON Schema for tool input parameters" },
|
|
181
|
-
},
|
|
182
|
-
required: ["name"],
|
|
183
|
-
},
|
|
184
|
-
},
|
|
185
|
-
server_name: {
|
|
186
|
-
type: "string",
|
|
187
|
-
description: "Optional name of the MCP server being scored. Used in the report header.",
|
|
188
|
-
default: "unnamed",
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
required: ["tools"],
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
{
|
|
195
|
-
name: "toolrank_compare",
|
|
196
|
-
description: "Compares a tool's ToolRank Score against ecosystem benchmarks. Use this after running toolrank_score to understand how your tools rank relative to the broader MCP ecosystem. Returns percentile ranking, dimension-by-dimension comparison against category averages, and specific areas where your tools underperform. Requires a prior toolrank_score result or raw tool definitions.",
|
|
197
|
-
inputSchema: {
|
|
198
|
-
type: "object",
|
|
199
|
-
properties: {
|
|
200
|
-
tools: {
|
|
201
|
-
type: "array",
|
|
202
|
-
description: "Array of MCP tool definition objects to compare",
|
|
203
|
-
items: { type: "object" },
|
|
204
|
-
},
|
|
205
|
-
category: {
|
|
206
|
-
type: "string",
|
|
207
|
-
description: "Tool category for comparison (e.g., 'crm', 'database', 'devtools'). If omitted, compares against the full ecosystem average.",
|
|
208
|
-
default: "all",
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
required: ["tools"],
|
|
212
|
-
},
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
name: "toolrank_suggest",
|
|
216
|
-
description: "Generates specific, actionable improvement suggestions for MCP tool definitions. Use this when you have a low ToolRank Score and want concrete text rewrites. Returns optimized versions of tool names, descriptions, and schema improvements ranked by expected score impact. Does not execute changes — returns suggestions for review.",
|
|
217
|
-
inputSchema: {
|
|
218
|
-
type: "object",
|
|
219
|
-
properties: {
|
|
220
|
-
tool: {
|
|
221
|
-
type: "object",
|
|
222
|
-
description: "Single MCP tool definition to improve. Must include 'name' and 'description'.",
|
|
223
|
-
properties: {
|
|
224
|
-
name: { type: "string", description: "Current tool name" },
|
|
225
|
-
description: { type: "string", description: "Current tool description" },
|
|
226
|
-
inputSchema: { type: "object", description: "Current input schema" },
|
|
227
|
-
},
|
|
228
|
-
required: ["name"],
|
|
229
|
-
},
|
|
230
|
-
focus: {
|
|
231
|
-
type: "string",
|
|
232
|
-
description: "Which dimension to prioritize improvements for",
|
|
233
|
-
enum: ["findability", "clarity", "precision", "efficiency", "all"],
|
|
234
|
-
default: "all",
|
|
235
|
-
},
|
|
236
|
-
},
|
|
237
|
-
required: ["tool"],
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
],
|
|
241
|
-
}));
|
|
242
|
-
// Tool execution handlers
|
|
243
|
-
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
244
|
-
const { name, arguments: args } = request.params;
|
|
245
|
-
switch (name) {
|
|
246
|
-
case "toolrank_score": {
|
|
247
|
-
const tools = args.tools || [];
|
|
248
|
-
const serverName = args.server_name || "unnamed";
|
|
249
|
-
const results = tools.map((t) => scoreTool(t));
|
|
250
|
-
const avgScore = results.length > 0
|
|
251
|
-
? Math.round((results.reduce((s, r) => s + r.total, 0) / results.length) * 10) / 10
|
|
252
|
-
: 0;
|
|
253
|
-
const allIssues = results.flatMap((r) => r.issues);
|
|
254
|
-
const topFixes = allIssues
|
|
255
|
-
.sort((a, b) => b.impact - a.impact)
|
|
256
|
-
.slice(0, 5);
|
|
257
|
-
// Server-level check: too many tools
|
|
258
|
-
if (tools.length > 20) {
|
|
259
|
-
topFixes.unshift({
|
|
260
|
-
dimension: "efficiency",
|
|
261
|
-
severity: "warning",
|
|
262
|
-
message: `Server has ${tools.length} tools. Agent accuracy degrades past 15-20 tools`,
|
|
263
|
-
fix: "Consolidate into 5-15 workflow-oriented tools",
|
|
264
|
-
impact: 8,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
return {
|
|
268
|
-
content: [{
|
|
269
|
-
type: "text",
|
|
270
|
-
text: JSON.stringify({
|
|
271
|
-
server_name: serverName,
|
|
272
|
-
average_score: avgScore,
|
|
273
|
-
tool_count: tools.length,
|
|
274
|
-
total_issues: allIssues.length,
|
|
275
|
-
critical_issues: allIssues.filter((i) => i.severity === "critical").length,
|
|
276
|
-
top_improvements: topFixes.slice(0, 3).map((i) => ({
|
|
277
|
-
message: i.message,
|
|
278
|
-
fix: i.fix,
|
|
279
|
-
impact: `+${i.impact}pt`,
|
|
280
|
-
})),
|
|
281
|
-
tools: results.map((r) => ({
|
|
282
|
-
name: r.name,
|
|
283
|
-
score: r.total,
|
|
284
|
-
level: `${r.level}: ${r.levelName}`,
|
|
285
|
-
dimensions: r.dimensions,
|
|
286
|
-
issues: r.issues.map((i) => ({
|
|
287
|
-
severity: i.severity,
|
|
288
|
-
message: i.message,
|
|
289
|
-
fix: i.fix,
|
|
290
|
-
impact: `+${i.impact}pt`,
|
|
291
|
-
})),
|
|
292
|
-
})),
|
|
293
|
-
}, null, 2),
|
|
294
|
-
}],
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
case "toolrank_compare": {
|
|
298
|
-
const tools = args.tools || [];
|
|
299
|
-
const category = args.category || "all";
|
|
300
|
-
const results = tools.map((t) => scoreTool(t));
|
|
301
|
-
const avgScore = results.length > 0
|
|
302
|
-
? Math.round((results.reduce((s, r) => s + r.total, 0) / results.length) * 10) / 10
|
|
303
|
-
: 0;
|
|
304
|
-
// Ecosystem benchmarks (from research data, will be replaced with live DB data)
|
|
305
|
-
const benchmarks = {
|
|
306
|
-
all: { avg: 42, median: 38, p75: 62, p90: 78 },
|
|
307
|
-
// Categories will be populated from scan DB
|
|
308
|
-
};
|
|
309
|
-
const bench = benchmarks[category] || benchmarks.all;
|
|
310
|
-
let percentile;
|
|
311
|
-
if (avgScore >= bench.p90)
|
|
312
|
-
percentile = 95;
|
|
313
|
-
else if (avgScore >= bench.p75)
|
|
314
|
-
percentile = 80;
|
|
315
|
-
else if (avgScore >= bench.median)
|
|
316
|
-
percentile = 55;
|
|
317
|
-
else if (avgScore >= bench.avg)
|
|
318
|
-
percentile = 40;
|
|
319
|
-
else
|
|
320
|
-
percentile = 20;
|
|
321
|
-
return {
|
|
322
|
-
content: [{
|
|
323
|
-
type: "text",
|
|
324
|
-
text: JSON.stringify({
|
|
325
|
-
your_score: avgScore,
|
|
326
|
-
category,
|
|
327
|
-
percentile: `Top ${100 - percentile}%`,
|
|
328
|
-
benchmark: bench,
|
|
329
|
-
comparison: {
|
|
330
|
-
vs_average: `${avgScore > bench.avg ? "+" : ""}${Math.round(avgScore - bench.avg)} points`,
|
|
331
|
-
vs_median: `${avgScore > bench.median ? "+" : ""}${Math.round(avgScore - bench.median)} points`,
|
|
332
|
-
},
|
|
333
|
-
note: "Benchmarks based on ecosystem scan data. Live rankings available at toolrank.dev/ranking",
|
|
334
|
-
}, null, 2),
|
|
335
|
-
}],
|
|
336
|
-
};
|
|
337
|
-
}
|
|
338
|
-
case "toolrank_suggest": {
|
|
339
|
-
const tool = args.tool || {};
|
|
340
|
-
const focus = args.focus || "all";
|
|
341
|
-
const result = scoreTool(tool);
|
|
342
|
-
const suggestions = [];
|
|
343
|
-
// Generate concrete suggestions based on issues
|
|
344
|
-
for (const issue of result.issues) {
|
|
345
|
-
if (focus !== "all" && issue.dimension !== focus)
|
|
346
|
-
continue;
|
|
347
|
-
const suggestion = {
|
|
348
|
-
dimension: issue.dimension,
|
|
349
|
-
current_problem: issue.message,
|
|
350
|
-
suggested_fix: issue.fix,
|
|
351
|
-
expected_impact: `+${issue.impact}pt`,
|
|
352
|
-
};
|
|
353
|
-
// Generate concrete rewrite for description issues
|
|
354
|
-
if (issue.dimension === "clarity" && tool.name) {
|
|
355
|
-
if (issue.message.includes("No description") || issue.message.includes("too short")) {
|
|
356
|
-
suggestion.example_rewrite = `${tool.name.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase())} — [describe what it does]. Use this when [describe use case]. Returns [describe output format].`;
|
|
357
|
-
}
|
|
358
|
-
if (issue.message.includes("No clear purpose verb")) {
|
|
359
|
-
suggestion.example_rewrite = `Retrieves/Creates/Searches for [object]. ${tool.description || ""}`;
|
|
360
|
-
}
|
|
361
|
-
if (issue.message.includes("No usage context")) {
|
|
362
|
-
suggestion.example_rewrite = `${tool.description || ""} Use this when you need to [specific scenario].`;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
suggestions.push(suggestion);
|
|
366
|
-
}
|
|
367
|
-
return {
|
|
368
|
-
content: [{
|
|
369
|
-
type: "text",
|
|
370
|
-
text: JSON.stringify({
|
|
371
|
-
tool_name: tool.name,
|
|
372
|
-
current_score: result.total,
|
|
373
|
-
current_level: `${result.level}: ${result.levelName}`,
|
|
374
|
-
suggestions: suggestions.sort((a, b) => {
|
|
375
|
-
const impA = parseInt(a.expected_impact) || 0;
|
|
376
|
-
const impB = parseInt(b.expected_impact) || 0;
|
|
377
|
-
return impB - impA;
|
|
378
|
-
}),
|
|
379
|
-
estimated_score_after_fixes: Math.min(100, result.total + suggestions.reduce((s, sg) => s + (parseInt(sg.expected_impact) || 0), 0)),
|
|
380
|
-
}, null, 2),
|
|
381
|
-
}],
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
default:
|
|
385
|
-
return {
|
|
386
|
-
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
387
|
-
isError: true,
|
|
388
|
-
};
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
// --- Start Server ---
|
|
392
|
-
async function main() {
|
|
393
|
-
const transport = new StdioServerTransport();
|
|
394
|
-
await server.connect(transport);
|
|
395
|
-
console.error("ToolRank MCP Server running on stdio");
|
|
396
|
-
}
|
|
397
|
-
main().catch(console.error);
|
package/smithery.yaml
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
name: toolrank
|
|
2
|
-
displayName: ToolRank
|
|
3
|
-
description: Score and optimize MCP tool definitions for AI agent discovery. Analyze your tools across Findability, Clarity, Precision, and Efficiency to maximize agent selection probability.
|
|
4
|
-
homepage: https://toolrank.dev
|
|
5
|
-
repository: https://github.com/imhiroki/toolrank
|
|
6
|
-
tags:
|
|
7
|
-
- developer-tools
|
|
8
|
-
- optimization
|
|
9
|
-
- scoring
|
|
10
|
-
- mcp
|
|
11
|
-
- ato
|
|
12
|
-
startCommand:
|
|
13
|
-
type: stdio
|
|
14
|
-
configSchema:
|
|
15
|
-
type: object
|
|
16
|
-
properties: {}
|
|
17
|
-
commandFunction: npx -y @toolrank/mcp-server
|
package/tsconfig.json
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"moduleResolution": "bundler",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"resolveJsonModule": true,
|
|
12
|
-
"declaration": true
|
|
13
|
-
},
|
|
14
|
-
"include": ["src/**/*"]
|
|
15
|
-
}
|