@mantine/mcp-server 9.6.2 → 9.7.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.
package/esm/server.mjs CHANGED
@@ -1,243 +1,22 @@
1
1
  "use client";
2
2
  import { MantineMcpDataClient } from "./data-client.mjs";
3
- import readline from "node:readline";
3
+ import { registerTools } from "./tools.mjs";
4
+ import { McpServer } from "@modelcontextprotocol/server";
5
+ import { serveStdio } from "@modelcontextprotocol/server/stdio";
4
6
  //#region packages/@mantine/mcp-server/src/server.ts
5
- const TOOL_DEFINITIONS = [
6
- {
7
- name: "list_items",
8
- description: "List Mantine documentation items. Supports filtering by kind (component|hook), package and text query.",
9
- inputSchema: {
10
- type: "object",
11
- properties: {
12
- kind: {
13
- type: "string",
14
- enum: ["component", "hook"]
15
- },
16
- package: { type: "string" },
17
- query: { type: "string" },
18
- limit: { type: "number" }
19
- },
20
- additionalProperties: false
21
- }
22
- },
23
- {
24
- name: "get_item_doc",
25
- description: "Get the LLM-ready markdown documentation for a Mantine item by name or id.",
26
- inputSchema: {
27
- type: "object",
28
- properties: {
29
- name: { type: "string" },
30
- kind: {
31
- type: "string",
32
- enum: ["component", "hook"]
33
- }
34
- },
35
- required: ["name"],
36
- additionalProperties: false
37
- }
38
- },
39
- {
40
- name: "get_item_props",
41
- description: "Get normalized props metadata for a Mantine item by name or id.",
42
- inputSchema: {
43
- type: "object",
44
- properties: {
45
- name: { type: "string" },
46
- kind: {
47
- type: "string",
48
- enum: ["component", "hook"]
49
- }
50
- },
51
- required: ["name"],
52
- additionalProperties: false
53
- }
54
- },
55
- {
56
- name: "search_docs",
57
- description: "Search Mantine docs by free text query and return ranked matches.",
58
- inputSchema: {
59
- type: "object",
60
- properties: {
61
- kind: {
62
- type: "string",
63
- enum: ["component", "hook"]
64
- },
65
- query: { type: "string" },
66
- limit: { type: "number" }
67
- },
68
- required: ["query"],
69
- additionalProperties: false
70
- }
71
- }
72
- ];
73
- function createJsonRpcError(code, message) {
74
- return {
75
- code,
76
- message
77
- };
78
- }
79
- function toToolResult(content) {
80
- return { content: [{
81
- type: "text",
82
- text: typeof content === "string" ? content : JSON.stringify(content, null, 2)
83
- }] };
84
- }
85
- function toToolError(message) {
86
- return {
87
- isError: true,
88
- content: [{
89
- type: "text",
90
- text: message
91
- }]
92
- };
93
- }
94
- function parseListItemsArgs(args) {
95
- return {
96
- kind: args.kind === "component" || args.kind === "hook" ? args.kind : void 0,
97
- package: typeof args.package === "string" ? args.package : void 0,
98
- query: typeof args.query === "string" ? args.query : void 0,
99
- limit: typeof args.limit === "number" ? args.limit : void 0
100
- };
101
- }
102
- function parseGetItemArgs(args) {
103
- if (typeof args.name !== "string" || args.name.length === 0) return null;
104
- return {
105
- name: args.name,
106
- kind: args.kind === "component" || args.kind === "hook" ? args.kind : void 0
107
- };
108
- }
109
- function parseSearchDocsArgs(args) {
110
- if (typeof args.query !== "string" || args.query.length === 0) return null;
111
- return {
112
- query: args.query,
113
- kind: args.kind === "component" || args.kind === "hook" ? args.kind : void 0,
114
- limit: typeof args.limit === "number" ? args.limit : void 0
115
- };
116
- }
117
- function writeMessage(payload) {
118
- process.stdout.write(`${JSON.stringify(payload)}\n`);
119
- }
120
- function writeResponse(id, result) {
121
- writeMessage({
122
- jsonrpc: "2.0",
123
- id,
124
- result
125
- });
126
- }
127
- function writeError(id, code, message) {
128
- writeMessage({
129
- jsonrpc: "2.0",
130
- id,
131
- error: createJsonRpcError(code, message)
132
- });
133
- }
7
+ /** Starts the Mantine documentation MCP server over stdio */
134
8
  function startServer() {
135
9
  const timeoutMs = Number(process.env.MANTINE_MCP_TIMEOUT_MS || 1e4);
136
10
  const dataUrl = process.env.MANTINE_MCP_DATA_URL || "https://mantine.dev/mcp";
137
- const client = new MantineMcpDataClient(dataUrl, timeoutMs);
138
- readline.createInterface({
139
- input: process.stdin,
140
- terminal: false
141
- }).on("line", (line) => {
142
- const trimmed = line.trim();
143
- if (trimmed.length === 0) return;
144
- let request;
145
- try {
146
- request = JSON.parse(trimmed);
147
- } catch {
148
- writeError(null, -32700, "Parse error");
149
- return;
150
- }
151
- handleRequest(client, request);
11
+ serveStdio(() => {
12
+ const server = new McpServer({
13
+ name: "@mantine/mcp-server",
14
+ version: "9"
15
+ });
16
+ registerTools(server, new MantineMcpDataClient(dataUrl, timeoutMs));
17
+ return server;
152
18
  });
153
19
  }
154
- async function handleRequest(client, request) {
155
- const id = request.id ?? null;
156
- if (!request || request.jsonrpc !== "2.0" || typeof request.method !== "string") {
157
- writeError(id, -32600, "Invalid request");
158
- return;
159
- }
160
- try {
161
- switch (request.method) {
162
- case "initialize":
163
- writeResponse(id, {
164
- protocolVersion: "2024-11-05",
165
- capabilities: { tools: {} },
166
- serverInfo: {
167
- name: "@mantine/mcp-server",
168
- version: "9"
169
- }
170
- });
171
- break;
172
- case "notifications/initialized": return;
173
- case "tools/list":
174
- writeResponse(id, { tools: TOOL_DEFINITIONS });
175
- return;
176
- case "tools/call": {
177
- const params = request.params && typeof request.params === "object" ? request.params : {};
178
- const name = typeof params.name === "string" ? params.name : "";
179
- const args = params.arguments && typeof params.arguments === "object" ? params.arguments : {};
180
- if (!name) {
181
- writeResponse(id, toToolError("Tool name is required"));
182
- return;
183
- }
184
- if (name === "list_items") {
185
- writeResponse(id, toToolResult(await client.listItems(parseListItemsArgs(args))));
186
- return;
187
- }
188
- if (name === "get_item_doc") {
189
- const parsed = parseGetItemArgs(args);
190
- if (!parsed) {
191
- writeResponse(id, toToolError("Argument \"name\" is required"));
192
- return;
193
- }
194
- const result = await client.getItemDoc(parsed);
195
- if (!result) {
196
- writeResponse(id, toToolError(`Item not found: ${parsed.name}`));
197
- return;
198
- }
199
- writeResponse(id, toToolResult(result));
200
- return;
201
- }
202
- if (name === "get_item_props") {
203
- const parsed = parseGetItemArgs(args);
204
- if (!parsed) {
205
- writeResponse(id, toToolError("Argument \"name\" is required"));
206
- return;
207
- }
208
- const result = await client.getItemProps(parsed);
209
- if (!result) {
210
- writeResponse(id, toToolError(`Item not found: ${parsed.name}`));
211
- return;
212
- }
213
- writeResponse(id, toToolResult(result));
214
- return;
215
- }
216
- if (name === "search_docs") {
217
- const parsed = parseSearchDocsArgs(args);
218
- if (!parsed) {
219
- writeResponse(id, toToolError("Argument \"query\" is required"));
220
- return;
221
- }
222
- writeResponse(id, toToolResult(await client.searchDocs(parsed)));
223
- return;
224
- }
225
- writeResponse(id, toToolError(`Unknown tool: ${name}`));
226
- return;
227
- }
228
- default:
229
- if (id === null) return;
230
- writeError(id, -32601, `Method not found: ${request.method}`);
231
- }
232
- } catch (error) {
233
- const message = error instanceof Error ? error.message : "Unexpected error";
234
- if (request.method === "tools/call") {
235
- writeResponse(id, toToolError(message));
236
- return;
237
- }
238
- writeError(id, -32e3, message);
239
- }
240
- }
241
20
  //#endregion
242
21
  export { startServer };
243
22
 
@@ -1 +1 @@
1
- {"version":3,"file":"server.mjs","names":[],"sources":["../src/server.ts"],"sourcesContent":["import readline from 'node:readline';\nimport { MantineMcpDataClient } from './data-client';\nimport { GetItemArgs, ListItemsArgs, SearchDocsArgs } from './types';\n\ninterface JsonRpcRequest {\n jsonrpc: '2.0';\n id?: string | number | null;\n method: string;\n params?: unknown;\n}\n\nconst TOOL_DEFINITIONS = [\n {\n name: 'list_items',\n description:\n 'List Mantine documentation items. Supports filtering by kind (component|hook), package and text query.',\n inputSchema: {\n type: 'object',\n properties: {\n kind: { type: 'string', enum: ['component', 'hook'] },\n package: { type: 'string' },\n query: { type: 'string' },\n limit: { type: 'number' },\n },\n additionalProperties: false,\n },\n },\n {\n name: 'get_item_doc',\n description: 'Get the LLM-ready markdown documentation for a Mantine item by name or id.',\n inputSchema: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n kind: { type: 'string', enum: ['component', 'hook'] },\n },\n required: ['name'],\n additionalProperties: false,\n },\n },\n {\n name: 'get_item_props',\n description: 'Get normalized props metadata for a Mantine item by name or id.',\n inputSchema: {\n type: 'object',\n properties: {\n name: { type: 'string' },\n kind: { type: 'string', enum: ['component', 'hook'] },\n },\n required: ['name'],\n additionalProperties: false,\n },\n },\n {\n name: 'search_docs',\n description: 'Search Mantine docs by free text query and return ranked matches.',\n inputSchema: {\n type: 'object',\n properties: {\n kind: { type: 'string', enum: ['component', 'hook'] },\n query: { type: 'string' },\n limit: { type: 'number' },\n },\n required: ['query'],\n additionalProperties: false,\n },\n },\n];\n\nfunction createJsonRpcError(code: number, message: string) {\n return { code, message };\n}\n\nfunction toToolResult(content: unknown) {\n const text = typeof content === 'string' ? content : JSON.stringify(content, null, 2);\n\n return {\n content: [{ type: 'text', text }],\n };\n}\n\nfunction toToolError(message: string) {\n return {\n isError: true,\n content: [{ type: 'text', text: message }],\n };\n}\n\nfunction parseListItemsArgs(args: Record<string, unknown>): ListItemsArgs {\n return {\n kind: args.kind === 'component' || args.kind === 'hook' ? args.kind : undefined,\n package: typeof args.package === 'string' ? args.package : undefined,\n query: typeof args.query === 'string' ? args.query : undefined,\n limit: typeof args.limit === 'number' ? args.limit : undefined,\n };\n}\n\nfunction parseGetItemArgs(args: Record<string, unknown>): GetItemArgs | null {\n if (typeof args.name !== 'string' || args.name.length === 0) {\n return null;\n }\n\n return {\n name: args.name,\n kind: args.kind === 'component' || args.kind === 'hook' ? args.kind : undefined,\n };\n}\n\nfunction parseSearchDocsArgs(args: Record<string, unknown>): SearchDocsArgs | null {\n if (typeof args.query !== 'string' || args.query.length === 0) {\n return null;\n }\n\n return {\n query: args.query,\n kind: args.kind === 'component' || args.kind === 'hook' ? args.kind : undefined,\n limit: typeof args.limit === 'number' ? args.limit : undefined,\n };\n}\n\nfunction writeMessage(payload: unknown) {\n process.stdout.write(`${JSON.stringify(payload)}\\n`);\n}\n\nfunction writeResponse(id: string | number | null, result: unknown) {\n writeMessage({ jsonrpc: '2.0', id, result });\n}\n\nfunction writeError(id: string | number | null, code: number, message: string) {\n writeMessage({\n jsonrpc: '2.0',\n id,\n error: createJsonRpcError(code, message),\n });\n}\n\nexport function startServer() {\n const timeoutMs = Number(process.env.MANTINE_MCP_TIMEOUT_MS || 10000);\n const dataUrl = process.env.MANTINE_MCP_DATA_URL || 'https://mantine.dev/mcp';\n const client = new MantineMcpDataClient(dataUrl, timeoutMs);\n\n const rl = readline.createInterface({ input: process.stdin, terminal: false });\n\n rl.on('line', (line: string) => {\n const trimmed = line.trim();\n if (trimmed.length === 0) {\n return;\n }\n\n let request: JsonRpcRequest;\n try {\n request = JSON.parse(trimmed);\n } catch {\n writeError(null, -32700, 'Parse error');\n return;\n }\n\n void handleRequest(client, request);\n });\n}\n\nasync function handleRequest(client: MantineMcpDataClient, request: JsonRpcRequest) {\n const id = request.id ?? null;\n\n if (!request || request.jsonrpc !== '2.0' || typeof request.method !== 'string') {\n writeError(id, -32600, 'Invalid request');\n return;\n }\n\n try {\n switch (request.method) {\n case 'initialize': {\n writeResponse(id, {\n protocolVersion: '2024-11-05',\n capabilities: {\n tools: {},\n },\n serverInfo: {\n name: '@mantine/mcp-server',\n version: '9',\n },\n });\n break;\n }\n\n case 'notifications/initialized': {\n return;\n }\n\n case 'tools/list': {\n writeResponse(id, { tools: TOOL_DEFINITIONS });\n return;\n }\n\n case 'tools/call': {\n const params =\n request.params && typeof request.params === 'object'\n ? (request.params as Record<string, unknown>)\n : {};\n const name = typeof params.name === 'string' ? params.name : '';\n const args =\n params.arguments && typeof params.arguments === 'object'\n ? (params.arguments as Record<string, unknown>)\n : {};\n\n if (!name) {\n writeResponse(id, toToolError('Tool name is required'));\n return;\n }\n\n if (name === 'list_items') {\n const result = await client.listItems(parseListItemsArgs(args));\n writeResponse(id, toToolResult(result));\n return;\n }\n\n if (name === 'get_item_doc') {\n const parsed = parseGetItemArgs(args);\n if (!parsed) {\n writeResponse(id, toToolError('Argument \"name\" is required'));\n return;\n }\n\n const result = await client.getItemDoc(parsed);\n if (!result) {\n writeResponse(id, toToolError(`Item not found: ${parsed.name}`));\n return;\n }\n\n writeResponse(id, toToolResult(result));\n return;\n }\n\n if (name === 'get_item_props') {\n const parsed = parseGetItemArgs(args);\n if (!parsed) {\n writeResponse(id, toToolError('Argument \"name\" is required'));\n return;\n }\n\n const result = await client.getItemProps(parsed);\n if (!result) {\n writeResponse(id, toToolError(`Item not found: ${parsed.name}`));\n return;\n }\n\n writeResponse(id, toToolResult(result));\n return;\n }\n\n if (name === 'search_docs') {\n const parsed = parseSearchDocsArgs(args);\n if (!parsed) {\n writeResponse(id, toToolError('Argument \"query\" is required'));\n return;\n }\n\n const result = await client.searchDocs(parsed);\n writeResponse(id, toToolResult(result));\n return;\n }\n\n writeResponse(id, toToolError(`Unknown tool: ${name}`));\n return;\n }\n\n default: {\n if (id === null) {\n return;\n }\n\n writeError(id, -32601, `Method not found: ${request.method}`);\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unexpected error';\n\n if (request.method === 'tools/call') {\n writeResponse(id, toToolError(message));\n return;\n }\n\n writeError(id, -32000, message);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"server.mjs","names":[],"sources":["../src/server.ts"],"sourcesContent":["import { McpServer } from '@modelcontextprotocol/server';\nimport { serveStdio } from '@modelcontextprotocol/server/stdio';\nimport { MantineMcpDataClient } from './data-client';\nimport { registerTools } from './tools';\n\n/** Starts the Mantine documentation MCP server over stdio */\nexport function startServer(): void {\n const timeoutMs = Number(process.env.MANTINE_MCP_TIMEOUT_MS || 10000);\n const dataUrl = process.env.MANTINE_MCP_DATA_URL || 'https://mantine.dev/mcp';\n\n serveStdio(() => {\n const server = new McpServer({ name: '@mantine/mcp-server', version: '9' });\n registerTools(server, new MantineMcpDataClient(dataUrl, timeoutMs));\n return server;\n });\n}\n"],"mappings":";;;;;;;AAMA,SAAgB,cAAoB;CAClC,MAAM,YAAY,OAAO,QAAQ,IAAI,0BAA0B,GAAK;CACpE,MAAM,UAAU,QAAQ,IAAI,wBAAwB;CAEpD,iBAAiB;EACf,MAAM,SAAS,IAAI,UAAU;GAAE,MAAM;GAAuB,SAAS;EAAI,CAAC;EAC1E,cAAc,QAAQ,IAAI,qBAAqB,SAAS,SAAS,CAAC;EAClE,OAAO;CACT,CAAC;AACH"}
package/esm/tools.mjs ADDED
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import * as z from "zod";
3
+ //#region packages/@mantine/mcp-server/src/tools.ts
4
+ const kindSchema = z.enum([
5
+ "component",
6
+ "hook",
7
+ "guide",
8
+ "faq"
9
+ ]);
10
+ const groupSchema = z.enum([
11
+ "gettingStarted",
12
+ "theming",
13
+ "hooks",
14
+ "components",
15
+ "extensions",
16
+ "form"
17
+ ]);
18
+ const nameSchema = z.string().trim().min(1);
19
+ function text(payload) {
20
+ return { content: [{
21
+ type: "text",
22
+ text: typeof payload === "string" ? payload : JSON.stringify(payload, null, 2)
23
+ }] };
24
+ }
25
+ function error(message) {
26
+ return {
27
+ isError: true,
28
+ content: [{
29
+ type: "text",
30
+ text: message
31
+ }]
32
+ };
33
+ }
34
+ /** Registers every Mantine documentation tool on an MCP server instance */
35
+ function registerTools(server, client) {
36
+ server.registerTool("search_docs", {
37
+ title: "Search Mantine documentation",
38
+ description: "Search all Mantine documentation — components, hooks, @mantine/form, styling, theming, guides and FAQ. Returns ranked matches; a match may point at a specific section, which can then be fetched with get_item_doc.",
39
+ inputSchema: z.object({
40
+ query: z.string().describe("Free text query, e.g. \"validate a form with zod\""),
41
+ kind: kindSchema.optional(),
42
+ package: z.string().optional().describe("Filter by package, e.g. \"@mantine/form\""),
43
+ limit: z.number().optional().describe("Maximum number of results (default 20, max 100)")
44
+ })
45
+ }, async (args) => text(await client.search(args)));
46
+ server.registerTool("get_item_doc", {
47
+ title: "Get Mantine documentation",
48
+ description: "Get documentation for a Mantine component, hook, guide or FAQ page. Without a section it returns an outline (description plus section list) — pass a section to fetch just that part, or full:true for the whole page.",
49
+ inputSchema: z.object({
50
+ name: nameSchema.describe("Item name or id, e.g. \"useForm\", \"Button\", \"form-validation\""),
51
+ kind: kindSchema.optional(),
52
+ section: z.string().optional().describe("Section slug from the outline, e.g. \"zod\""),
53
+ full: z.boolean().optional().describe("Return the entire page; can be very large")
54
+ })
55
+ }, async (args) => {
56
+ const result = await client.getDoc(args);
57
+ if (!result) {
58
+ const suggestions = await client.suggestFor(args.name);
59
+ return error(suggestions.length > 0 ? `No exact match for "${args.name}". Did you mean: ${suggestions.join(", ")}?` : `No Mantine documentation found for "${args.name}". Try search_docs first.`);
60
+ }
61
+ return text(result);
62
+ });
63
+ server.registerTool("get_item_props", {
64
+ title: "Get Mantine props or signature",
65
+ description: "Get props for a Mantine component, or the TypeScript signature for a hook. Pages documenting an API in prose point at the relevant section instead.",
66
+ inputSchema: z.object({
67
+ name: nameSchema,
68
+ kind: kindSchema.optional()
69
+ })
70
+ }, async (args) => {
71
+ const result = await client.getProps(args);
72
+ if (!result) {
73
+ const suggestions = await client.suggestFor(args.name);
74
+ return error(suggestions.length > 0 ? `No exact match for "${args.name}". Did you mean: ${suggestions.join(", ")}?` : `No Mantine documentation found for "${args.name}".`);
75
+ }
76
+ return text(result);
77
+ });
78
+ server.registerTool("list_items", {
79
+ title: "List Mantine documentation items",
80
+ description: "List Mantine documentation items, filtered by kind, nav group, category, package or a text query.",
81
+ inputSchema: z.object({
82
+ kind: kindSchema.optional(),
83
+ group: groupSchema.optional(),
84
+ category: z.string().optional(),
85
+ package: z.string().optional(),
86
+ query: z.string().optional(),
87
+ limit: z.number().optional().describe("Maximum number of results (default 30, max 500)")
88
+ })
89
+ }, async (args) => text(await client.listItems(args)));
90
+ server.registerTool("get_api", {
91
+ title: "Resolve a Mantine symbol",
92
+ description: "Resolve a Mantine symbol (useForm, useDisclosure, UseFormReturnType) to its TypeScript signature and exported types, without knowing which page documents it.",
93
+ inputSchema: z.object({ symbol: nameSchema })
94
+ }, async ({ symbol }) => {
95
+ const result = await client.getApi(symbol);
96
+ if (!result) {
97
+ const suggestions = await client.suggestFor(symbol);
98
+ return error(suggestions.length > 0 ? `No API found for "${symbol}". Did you mean: ${suggestions.join(", ")}?` : `No API found for "${symbol}". Try search_docs.`);
99
+ }
100
+ return text(result);
101
+ });
102
+ }
103
+ //#endregion
104
+ export { registerTools };
105
+
106
+ //# sourceMappingURL=tools.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.mjs","names":[],"sources":["../src/tools.ts"],"sourcesContent":["import type { McpServer } from '@modelcontextprotocol/server';\nimport * as z from 'zod';\nimport { MantineMcpDataClient } from './data-client';\n\nconst kindSchema = z.enum(['component', 'hook', 'guide', 'faq']);\nconst groupSchema = z.enum([\n 'gettingStarted',\n 'theming',\n 'hooks',\n 'components',\n 'extensions',\n 'form',\n]);\nconst nameSchema = z.string().trim().min(1);\n\nfunction text(payload: unknown) {\n return {\n content: [\n {\n type: 'text' as const,\n text: typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2),\n },\n ],\n };\n}\n\nfunction error(message: string) {\n return { isError: true, content: [{ type: 'text' as const, text: message }] };\n}\n\n/** Registers every Mantine documentation tool on an MCP server instance */\nexport function registerTools(server: McpServer, client: MantineMcpDataClient): void {\n server.registerTool(\n 'search_docs',\n {\n title: 'Search Mantine documentation',\n description:\n 'Search all Mantine documentation — components, hooks, @mantine/form, styling, theming, guides and FAQ. Returns ranked matches; a match may point at a specific section, which can then be fetched with get_item_doc.',\n inputSchema: z.object({\n query: z.string().describe('Free text query, e.g. \"validate a form with zod\"'),\n kind: kindSchema.optional(),\n package: z.string().optional().describe('Filter by package, e.g. \"@mantine/form\"'),\n limit: z.number().optional().describe('Maximum number of results (default 20, max 100)'),\n }),\n },\n async (args) => text(await client.search(args))\n );\n\n server.registerTool(\n 'get_item_doc',\n {\n title: 'Get Mantine documentation',\n description:\n 'Get documentation for a Mantine component, hook, guide or FAQ page. Without a section it returns an outline (description plus section list) — pass a section to fetch just that part, or full:true for the whole page.',\n inputSchema: z.object({\n name: nameSchema.describe('Item name or id, e.g. \"useForm\", \"Button\", \"form-validation\"'),\n kind: kindSchema.optional(),\n section: z.string().optional().describe('Section slug from the outline, e.g. \"zod\"'),\n full: z.boolean().optional().describe('Return the entire page; can be very large'),\n }),\n },\n async (args) => {\n const result = await client.getDoc(args);\n\n if (!result) {\n const suggestions = await client.suggestFor(args.name);\n return error(\n suggestions.length > 0\n ? `No exact match for \"${args.name}\". Did you mean: ${suggestions.join(', ')}?`\n : `No Mantine documentation found for \"${args.name}\". Try search_docs first.`\n );\n }\n\n return text(result);\n }\n );\n\n server.registerTool(\n 'get_item_props',\n {\n title: 'Get Mantine props or signature',\n description:\n 'Get props for a Mantine component, or the TypeScript signature for a hook. Pages documenting an API in prose point at the relevant section instead.',\n inputSchema: z.object({ name: nameSchema, kind: kindSchema.optional() }),\n },\n async (args) => {\n const result = await client.getProps(args);\n\n if (!result) {\n const suggestions = await client.suggestFor(args.name);\n return error(\n suggestions.length > 0\n ? `No exact match for \"${args.name}\". Did you mean: ${suggestions.join(', ')}?`\n : `No Mantine documentation found for \"${args.name}\".`\n );\n }\n\n return text(result);\n }\n );\n\n server.registerTool(\n 'list_items',\n {\n title: 'List Mantine documentation items',\n description:\n 'List Mantine documentation items, filtered by kind, nav group, category, package or a text query.',\n inputSchema: z.object({\n kind: kindSchema.optional(),\n group: groupSchema.optional(),\n category: z.string().optional(),\n package: z.string().optional(),\n query: z.string().optional(),\n limit: z.number().optional().describe('Maximum number of results (default 30, max 500)'),\n }),\n },\n async (args) => text(await client.listItems(args))\n );\n\n server.registerTool(\n 'get_api',\n {\n title: 'Resolve a Mantine symbol',\n description:\n 'Resolve a Mantine symbol (useForm, useDisclosure, UseFormReturnType) to its TypeScript signature and exported types, without knowing which page documents it.',\n inputSchema: z.object({ symbol: nameSchema }),\n },\n async ({ symbol }) => {\n const result = await client.getApi(symbol);\n\n if (!result) {\n const suggestions = await client.suggestFor(symbol);\n return error(\n suggestions.length > 0\n ? `No API found for \"${symbol}\". Did you mean: ${suggestions.join(', ')}?`\n : `No API found for \"${symbol}\". Try search_docs.`\n );\n }\n\n return text(result);\n }\n );\n}\n"],"mappings":"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"}
@@ -1,53 +1,132 @@
1
- import { GetItemArgs, IndexItem, ItemKind, ListItemsArgs, SearchDocsArgs } from './types';
1
+ import { GetItemArgs, IndexItem, ListItemsArgs, SearchDocsArgs, SectionRecord } from './types';
2
+ /** Fetches and caches the static MCP data published alongside the docs site */
2
3
  export declare class MantineMcpDataClient {
3
4
  private readonly baseUrl;
4
5
  private readonly timeoutMs;
5
6
  private indexCache;
6
- private itemCache;
7
+ private indexEtag;
8
+ private indexCheckedAt;
9
+ private sectionsCache;
10
+ private docCache;
11
+ private scorer;
7
12
  constructor(baseUrl?: string, timeoutMs?: number);
8
13
  private fetchJson;
14
+ /**
15
+ * Re-fetches the index if the published copy changed since it was cached.
16
+ *
17
+ * Best-effort: a revalidation failure (network blip, non-ok response) is logged and
18
+ * swallowed so the still-good cached index keeps serving the in-flight request instead of
19
+ * failing it outright.
20
+ */
21
+ private revalidateIndex;
9
22
  getIndex(): Promise<IndexItem[]>;
23
+ getSections(): Promise<SectionRecord[]>;
24
+ private getScorer;
25
+ private getDocData;
10
26
  listItems(args?: ListItemsArgs): Promise<IndexItem[]>;
11
- private scoreMatch;
12
- searchDocs(args: SearchDocsArgs): Promise<{
27
+ findItem(args: GetItemArgs): Promise<IndexItem | null>;
28
+ /** Names close to a failed lookup, for a helpful error message */
29
+ suggestFor(name: string): Promise<string[]>;
30
+ search(args: SearchDocsArgs): Promise<{
13
31
  id: string;
14
32
  name: string;
15
- kind: ItemKind;
16
- package: string;
33
+ kind: import("./types").DocKind;
34
+ package: string | undefined;
17
35
  route: string;
36
+ docsUrl: string;
18
37
  description: string;
38
+ section: string | undefined;
39
+ heading: string | undefined;
40
+ snippet: string | undefined;
19
41
  score: number;
20
42
  }[]>;
21
- private getItemDataById;
22
- findItem(args: GetItemArgs): Promise<IndexItem | null>;
23
- getItemDoc(args: GetItemArgs): Promise<{
24
- item: {
25
- id: string;
26
- name: string;
27
- kind: ItemKind;
28
- package: string;
29
- route: string;
30
- description: string;
31
- llmUrl: string;
32
- };
43
+ getDoc(args: GetItemArgs): Promise<{
44
+ item: IndexItem;
45
+ mode: "full";
33
46
  markdown: string;
34
- } | null>;
35
- getItemProps(args: GetItemArgs): Promise<{
36
- item: {
37
- id: string;
38
- name: string;
39
- kind: ItemKind;
40
- package: string;
41
- route: string;
42
- description: string;
43
- };
44
- props: {
45
- name: string;
46
- description?: string;
47
- required?: boolean;
48
- defaultValue?: unknown;
49
- type?: unknown;
50
- sourceComponent: string;
47
+ availableSections?: undefined;
48
+ heading?: undefined;
49
+ intro?: undefined;
50
+ sections?: undefined;
51
+ source?: undefined;
52
+ docs?: undefined;
53
+ } | {
54
+ item: IndexItem;
55
+ mode: "section-not-found";
56
+ availableSections: string[];
57
+ markdown?: undefined;
58
+ heading?: undefined;
59
+ intro?: undefined;
60
+ sections?: undefined;
61
+ source?: undefined;
62
+ docs?: undefined;
63
+ } | {
64
+ item: IndexItem;
65
+ mode: "section";
66
+ heading: string;
67
+ markdown: string;
68
+ availableSections?: undefined;
69
+ intro?: undefined;
70
+ sections?: undefined;
71
+ source?: undefined;
72
+ docs?: undefined;
73
+ } | {
74
+ item: IndexItem;
75
+ mode: "outline";
76
+ intro: string;
77
+ sections: {
78
+ slug: string;
79
+ heading: string;
51
80
  }[];
81
+ source: string | undefined;
82
+ docs: string | undefined;
83
+ markdown?: undefined;
84
+ availableSections?: undefined;
85
+ heading?: undefined;
86
+ } | null>;
87
+ /** The section that most plausibly documents a page's API, when no structured props or signature exist */
88
+ private findApiSection;
89
+ getProps(args: GetItemArgs): Promise<{
90
+ item: IndexItem;
91
+ kind: "props";
92
+ props: import("./types").PropData[];
93
+ signature?: undefined;
94
+ exportedTypeNames?: undefined;
95
+ message?: undefined;
96
+ suggestedSection?: undefined;
97
+ availableSections?: undefined;
98
+ } | {
99
+ item: IndexItem;
100
+ kind: "signature";
101
+ signature: string;
102
+ exportedTypeNames: string[] | null;
103
+ props?: undefined;
104
+ message?: undefined;
105
+ suggestedSection?: undefined;
106
+ availableSections?: undefined;
107
+ } | {
108
+ item: IndexItem;
109
+ kind: "sections";
110
+ message: string;
111
+ suggestedSection: string | undefined;
112
+ availableSections: string[];
113
+ props?: undefined;
114
+ signature?: undefined;
115
+ exportedTypeNames?: undefined;
116
+ } | null>;
117
+ getApi(symbol: string): Promise<{
118
+ item: IndexItem;
119
+ signature: string | null;
120
+ exportedTypeNames: string[] | null;
121
+ propsCount: number;
122
+ suggestedSection: string | undefined;
123
+ availableSections: string[];
124
+ } | {
125
+ item: IndexItem;
126
+ signature: string | null;
127
+ exportedTypeNames: string[] | null;
128
+ propsCount: number;
129
+ suggestedSection?: undefined;
130
+ availableSections?: undefined;
52
131
  } | null>;
53
132
  }
@@ -0,0 +1,17 @@
1
+ import { IndexItem, SectionRecord } from './types';
2
+ /** A scoring function pair built against a fixed corpus's document-frequency statistics */
3
+ export interface SearchScorer {
4
+ scoreItem(item: IndexItem, query: string): number;
5
+ scoreSection(section: SectionRecord, query: string): number;
6
+ }
7
+ /**
8
+ * Builds an IDF-weighted scorer over a fixed corpus.
9
+ *
10
+ * Document frequency is computed lazily per token and cached for the lifetime of the
11
+ * returned scorer, so a common token like "a" contributes almost nothing to a match while
12
+ * a rare token like "zod" dominates it. Callers should rebuild the scorer whenever `index`
13
+ * or `sections` actually change.
14
+ */
15
+ export declare function createSearchScorer(index: IndexItem[], sections: SectionRecord[]): SearchScorer;
16
+ /** Returns names close enough to a failed lookup to be worth suggesting */
17
+ export declare function suggestNames(index: IndexItem[], query: string, limit?: number): string[];
package/lib/server.d.ts CHANGED
@@ -1 +1,2 @@
1
+ /** Starts the Mantine documentation MCP server over stdio */
1
2
  export declare function startServer(): void;
package/lib/tools.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
+ import { MantineMcpDataClient } from './data-client';
3
+ /** Registers every Mantine documentation tool on an MCP server instance */
4
+ export declare function registerTools(server: McpServer, client: MantineMcpDataClient): void;