mcp-rustdoc 2.0.0 → 3.0.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/README.md +42 -2
- package/dist/index.js +155 -32
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# mcp-rustdoc
|
|
2
2
|
|
|
3
|
-
An MCP server that gives AI assistants deep access to the Rust ecosystem. It scrapes docs.rs with surgical DOM extraction (cheerio) and queries the crates.io API, exposing
|
|
3
|
+
An MCP server that gives AI assistants deep access to the Rust ecosystem. It scrapes docs.rs (and `doc.rust-lang.org` for `std`/`core`/`alloc`) with surgical DOM extraction (cheerio) and queries the crates.io API, exposing seven tools that cover everything from high-level crate overviews to individual method signatures, feature gates, trait impls, and code examples. Responses are cached in memory (5-minute TTL) to avoid redundant fetches.
|
|
4
4
|
|
|
5
5
|
## Tools
|
|
6
6
|
|
|
@@ -12,9 +12,22 @@ An MCP server that gives AI assistants deep access to the Rust ecosystem. It scr
|
|
|
12
12
|
| `get_crate_items` | Items in a module with types, feature gates, and descriptions |
|
|
13
13
|
| `lookup_crate_item` | Item detail: signature, docs, methods, variants, optionally trait impls + examples |
|
|
14
14
|
| `search_crate` | Ranked symbol search (exact > prefix > substring) with canonical paths |
|
|
15
|
+
| `search_crates` | Search crates.io by keyword — returns name, description, downloads, version |
|
|
15
16
|
|
|
16
17
|
Every tool accepts an optional `version` parameter to pin a specific crate version instead of `latest`.
|
|
17
18
|
|
|
19
|
+
### Standard library support
|
|
20
|
+
|
|
21
|
+
All documentation tools work with `std`, `core`, and `alloc` — the Rust standard library crates hosted at `doc.rust-lang.org`. Use them exactly like any other crate:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
> lookup_crate_docs({ crateName: "std" })
|
|
25
|
+
> get_crate_items({ crateName: "std", modulePath: "collections" })
|
|
26
|
+
> search_crate({ crateName: "core", query: "Option" })
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`get_crate_metadata` returns a helpful message for std crates since they aren't published on crates.io.
|
|
30
|
+
|
|
18
31
|
## Install
|
|
19
32
|
|
|
20
33
|
No clone needed. Just configure your AI coding assistant with `npx`:
|
|
@@ -335,6 +348,29 @@ Searches all items in a crate by name. Results are ranked: exact match on the ba
|
|
|
335
348
|
|
|
336
349
|
---
|
|
337
350
|
|
|
351
|
+
### `search_crates`
|
|
352
|
+
|
|
353
|
+
Search for Rust crates on crates.io by keyword.
|
|
354
|
+
|
|
355
|
+
| Parameter | Type | Required | Description |
|
|
356
|
+
|---|---|---|---|
|
|
357
|
+
| `query` | string | yes | Search keywords |
|
|
358
|
+
| `page` | number | no | Page number (default 1) |
|
|
359
|
+
| `perPage` | number | no | Results per page (default 10, max 50) |
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
> search_crates({ query: "http" })
|
|
363
|
+
|
|
364
|
+
# Crate search: "http" — 1234 results (page 1)
|
|
365
|
+
|
|
366
|
+
http v1.2.0 (50,000,000 downloads) — A set of types for representing HTTP requests and responses.
|
|
367
|
+
hyper v1.5.2 (120,000,000 downloads) — A fast and correct HTTP library.
|
|
368
|
+
reqwest v0.12.12 (100,000,000 downloads) — higher level HTTP client library
|
|
369
|
+
...
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
338
374
|
## Recommended workflows
|
|
339
375
|
|
|
340
376
|
### Exploring a new crate
|
|
@@ -362,6 +398,7 @@ Searches all items in a crate by name. Results are ranked: exact match on the ba
|
|
|
362
398
|
src/
|
|
363
399
|
index.ts Entry point — registers tools + prompt, starts stdio server
|
|
364
400
|
lib.ts Shared: URL builders, HTTP/DOM helpers, crates.io API, extractors
|
|
401
|
+
cache.ts In-memory TTL cache (5-minute default)
|
|
365
402
|
types/
|
|
366
403
|
html-to-text.d.ts Type declarations for html-to-text
|
|
367
404
|
tools/
|
|
@@ -370,6 +407,7 @@ src/
|
|
|
370
407
|
get-items.ts get_crate_items
|
|
371
408
|
lookup-item.ts lookup_crate_item
|
|
372
409
|
search.ts search_crate
|
|
410
|
+
search-crates.ts search_crates
|
|
373
411
|
crate-metadata.ts get_crate_metadata
|
|
374
412
|
crate-brief.ts get_crate_brief
|
|
375
413
|
```
|
|
@@ -377,7 +415,8 @@ src/
|
|
|
377
415
|
### Data sources
|
|
378
416
|
|
|
379
417
|
- **docs.rs** — HTML pages parsed with cheerio for surgical DOM extraction (only the elements needed, not full-page conversion)
|
|
380
|
-
- **
|
|
418
|
+
- **doc.rust-lang.org** — Same rustdoc HTML format, used for `std`, `core`, and `alloc`
|
|
419
|
+
- **crates.io API** — JSON endpoints for metadata, features, dependencies, and search
|
|
381
420
|
|
|
382
421
|
### Design decisions
|
|
383
422
|
|
|
@@ -385,6 +424,7 @@ src/
|
|
|
385
424
|
- **Ranked search** — `all.html` contains every public item; scoring by exact/prefix/substring gives better results than flat substring matching
|
|
386
425
|
- **Version parameter everywhere** — Agents working on projects with pinned dependencies need to read docs for specific versions
|
|
387
426
|
- **Optional sections** — `includeImpls` and `includeExamples` default to off so the base response stays compact; agents opt in when they need more detail
|
|
427
|
+
- **In-memory cache** — All HTTP responses are cached for 5 minutes, avoiding redundant fetches when agents issue multiple related tool calls
|
|
388
428
|
|
|
389
429
|
## License
|
|
390
430
|
|
package/dist/index.js
CHANGED
|
@@ -5,9 +5,23 @@ import { z } from "zod";
|
|
|
5
5
|
import axios from "axios";
|
|
6
6
|
import { load } from "cheerio";
|
|
7
7
|
import { convert } from "html-to-text";
|
|
8
|
+
const DEFAULT_TTL = 5 * 60 * 1e3;
|
|
9
|
+
const store = /* @__PURE__ */ new Map();
|
|
10
|
+
function cacheGet(key) {
|
|
11
|
+
const entry = store.get(key);
|
|
12
|
+
if (!entry) return null;
|
|
13
|
+
if (Date.now() > entry.expiry) {
|
|
14
|
+
store.delete(key);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return entry.data;
|
|
18
|
+
}
|
|
19
|
+
function cacheSet(key, data, ttl = DEFAULT_TTL) {
|
|
20
|
+
store.set(key, { data, expiry: Date.now() + ttl });
|
|
21
|
+
}
|
|
8
22
|
const DOCS_BASE = "https://docs.rs";
|
|
9
23
|
const CRATES_IO = "https://crates.io/api/v1";
|
|
10
|
-
const USER_AGENT = "mcp-rust-docs/
|
|
24
|
+
const USER_AGENT = "mcp-rust-docs/3.0.0";
|
|
11
25
|
const MAX_DOC_LENGTH = 6e3;
|
|
12
26
|
const MAX_SEARCH_RESULTS = 100;
|
|
13
27
|
const SECTION_TO_TYPE = {
|
|
@@ -38,10 +52,18 @@ const TYPE_FILE_PREFIX = {
|
|
|
38
52
|
attr: "attr.",
|
|
39
53
|
derive: "derive."
|
|
40
54
|
};
|
|
55
|
+
const STD_CRATES = /* @__PURE__ */ new Set(["std", "core", "alloc"]);
|
|
56
|
+
function isStdCrate(name) {
|
|
57
|
+
return STD_CRATES.has(name);
|
|
58
|
+
}
|
|
59
|
+
function stdDocsUrl(crate, path) {
|
|
60
|
+
return `https://doc.rust-lang.org/stable/${crate}/${path}`;
|
|
61
|
+
}
|
|
41
62
|
function crateSlug(name) {
|
|
42
63
|
return name.replace(/-/g, "_");
|
|
43
64
|
}
|
|
44
65
|
function docsUrl(crate, path = "index.html", version = "latest") {
|
|
66
|
+
if (isStdCrate(crate)) return stdDocsUrl(crate, path);
|
|
45
67
|
return `${DOCS_BASE}/${crate}/${version}/${crateSlug(crate)}/${path}`;
|
|
46
68
|
}
|
|
47
69
|
function modToUrlPrefix(modulePath) {
|
|
@@ -51,7 +73,13 @@ function modToRustPrefix(modulePath) {
|
|
|
51
73
|
return modulePath ? modulePath.replace(/\./g, "::") + "::" : "";
|
|
52
74
|
}
|
|
53
75
|
async function fetchDom(url) {
|
|
76
|
+
const cached = cacheGet(`dom:${url}`);
|
|
77
|
+
if (cached) {
|
|
78
|
+
console.log(`[cache hit] ${url}`);
|
|
79
|
+
return load(cached);
|
|
80
|
+
}
|
|
54
81
|
const { data } = await axios.get(url, { timeout: 15e3 });
|
|
82
|
+
cacheSet(`dom:${url}`, data);
|
|
55
83
|
return load(data);
|
|
56
84
|
}
|
|
57
85
|
function cleanHtml(html) {
|
|
@@ -74,12 +102,18 @@ function errorResult(msg) {
|
|
|
74
102
|
}
|
|
75
103
|
const cratesIoHeaders = { "User-Agent": USER_AGENT };
|
|
76
104
|
async function fetchCrateInfo(name) {
|
|
105
|
+
const cacheKey = `crate-info:${name}`;
|
|
106
|
+
const cached = cacheGet(cacheKey);
|
|
107
|
+
if (cached) {
|
|
108
|
+
console.log(`[cache hit] crate-info ${name}`);
|
|
109
|
+
return cached;
|
|
110
|
+
}
|
|
77
111
|
const { data } = await axios.get(`${CRATES_IO}/crates/${name}`, {
|
|
78
112
|
headers: cratesIoHeaders,
|
|
79
113
|
timeout: 1e4
|
|
80
114
|
});
|
|
81
115
|
const c = data.crate;
|
|
82
|
-
|
|
116
|
+
const info = {
|
|
83
117
|
name: c.name,
|
|
84
118
|
version: c.max_stable_version || c.max_version,
|
|
85
119
|
description: c.description,
|
|
@@ -87,34 +121,52 @@ async function fetchCrateInfo(name) {
|
|
|
87
121
|
repository: c.repository,
|
|
88
122
|
downloads: c.downloads
|
|
89
123
|
};
|
|
124
|
+
cacheSet(cacheKey, info);
|
|
125
|
+
return info;
|
|
90
126
|
}
|
|
91
127
|
async function fetchCrateVersionInfo(name, version) {
|
|
128
|
+
const cacheKey = `crate-version:${name}@${version}`;
|
|
129
|
+
const cached = cacheGet(cacheKey);
|
|
130
|
+
if (cached) {
|
|
131
|
+
console.log(`[cache hit] crate-version ${name}@${version}`);
|
|
132
|
+
return cached;
|
|
133
|
+
}
|
|
92
134
|
const { data } = await axios.get(`${CRATES_IO}/crates/${name}/${version}`, {
|
|
93
135
|
headers: cratesIoHeaders,
|
|
94
136
|
timeout: 1e4
|
|
95
137
|
});
|
|
96
138
|
const v = data.version;
|
|
97
139
|
const features = v.features ?? {};
|
|
98
|
-
|
|
140
|
+
const info = {
|
|
99
141
|
num: v.num,
|
|
100
142
|
features,
|
|
101
143
|
defaultFeatures: features["default"] ?? [],
|
|
102
144
|
yanked: v.yanked,
|
|
103
145
|
license: v.license
|
|
104
146
|
};
|
|
147
|
+
cacheSet(cacheKey, info);
|
|
148
|
+
return info;
|
|
105
149
|
}
|
|
106
150
|
async function fetchCrateDeps(name, version) {
|
|
151
|
+
const cacheKey = `crate-deps:${name}@${version}`;
|
|
152
|
+
const cached = cacheGet(cacheKey);
|
|
153
|
+
if (cached) {
|
|
154
|
+
console.log(`[cache hit] crate-deps ${name}@${version}`);
|
|
155
|
+
return cached;
|
|
156
|
+
}
|
|
107
157
|
const { data } = await axios.get(`${CRATES_IO}/crates/${name}/${version}/dependencies`, {
|
|
108
158
|
headers: cratesIoHeaders,
|
|
109
159
|
timeout: 1e4
|
|
110
160
|
});
|
|
111
|
-
|
|
161
|
+
const deps = (data.dependencies ?? []).map((d) => ({
|
|
112
162
|
name: d.crate_id,
|
|
113
163
|
req: d.req,
|
|
114
164
|
optional: d.optional,
|
|
115
165
|
kind: d.kind,
|
|
116
166
|
features: d.features ?? []
|
|
117
167
|
}));
|
|
168
|
+
cacheSet(cacheKey, deps);
|
|
169
|
+
return deps;
|
|
118
170
|
}
|
|
119
171
|
function extractItemFeatureGate($) {
|
|
120
172
|
const gate = $(".item-info .stab.portability").first().text().trim();
|
|
@@ -157,7 +209,7 @@ const itemTypeEnum = z.enum([
|
|
|
157
209
|
"derive"
|
|
158
210
|
]);
|
|
159
211
|
const versionParam = z.string().optional().describe('Crate version (e.g. "1.49.0"). Defaults to latest.');
|
|
160
|
-
function register$
|
|
212
|
+
function register$6(server2) {
|
|
161
213
|
server2.tool(
|
|
162
214
|
"lookup_crate_docs",
|
|
163
215
|
"Fetch the main documentation for a Rust crate. Returns overview, version, sections, and re-exports.",
|
|
@@ -165,7 +217,6 @@ function register$5(server2) {
|
|
|
165
217
|
crateName: z.string().describe('Crate name (e.g. "tokio", "serde-json")'),
|
|
166
218
|
version: versionParam
|
|
167
219
|
},
|
|
168
|
-
// @ts-expect-error — MCP SDK deep type instantiation with Zod schemas
|
|
169
220
|
async ({ crateName, version }) => {
|
|
170
221
|
try {
|
|
171
222
|
const ver = version ?? "latest";
|
|
@@ -195,7 +246,7 @@ function register$5(server2) {
|
|
|
195
246
|
}
|
|
196
247
|
);
|
|
197
248
|
}
|
|
198
|
-
function register$
|
|
249
|
+
function register$5(server2) {
|
|
199
250
|
server2.tool(
|
|
200
251
|
"get_crate_items",
|
|
201
252
|
"List public items in a crate root or module. Returns names, types, feature gates, and short descriptions.",
|
|
@@ -240,7 +291,7 @@ function register$4(server2) {
|
|
|
240
291
|
}
|
|
241
292
|
);
|
|
242
293
|
}
|
|
243
|
-
function register$
|
|
294
|
+
function register$4(server2) {
|
|
244
295
|
server2.tool(
|
|
245
296
|
"lookup_crate_item",
|
|
246
297
|
"Get detailed documentation for a specific item. Returns signature, docs, feature gate, methods, trait impls, and optionally examples.",
|
|
@@ -253,7 +304,6 @@ function register$3(server2) {
|
|
|
253
304
|
includeExamples: z.boolean().optional().describe("Include code examples from the docs. Default: false."),
|
|
254
305
|
includeImpls: z.boolean().optional().describe("Include trait implementation list. Default: false.")
|
|
255
306
|
},
|
|
256
|
-
// @ts-expect-error — MCP SDK deep type instantiation with Zod schemas
|
|
257
307
|
async ({ crateName, itemType, itemName, modulePath, version, includeExamples, includeImpls }) => {
|
|
258
308
|
try {
|
|
259
309
|
const ver = version ?? "latest";
|
|
@@ -339,7 +389,7 @@ function scoreMatch(name, query) {
|
|
|
339
389
|
if (lower.includes(q)) return 20;
|
|
340
390
|
return 0;
|
|
341
391
|
}
|
|
342
|
-
function register$
|
|
392
|
+
function register$3(server2) {
|
|
343
393
|
server2.tool(
|
|
344
394
|
"search_crate",
|
|
345
395
|
"Search for items by name within a Rust crate. Returns ranked results with canonical paths and item types.",
|
|
@@ -383,7 +433,7 @@ function register$2(server2) {
|
|
|
383
433
|
}
|
|
384
434
|
);
|
|
385
435
|
}
|
|
386
|
-
function register$
|
|
436
|
+
function register$2(server2) {
|
|
387
437
|
server2.tool(
|
|
388
438
|
"get_crate_metadata",
|
|
389
439
|
"Get crate metadata from crates.io: version, features, default features, optional dependencies, and links.",
|
|
@@ -393,6 +443,12 @@ function register$1(server2) {
|
|
|
393
443
|
},
|
|
394
444
|
async ({ crateName, version }) => {
|
|
395
445
|
try {
|
|
446
|
+
if (isStdCrate(crateName)) {
|
|
447
|
+
return textResult(
|
|
448
|
+
`"${crateName}" is part of the Rust standard library and is not published on crates.io.
|
|
449
|
+
Use lookup_crate_docs, get_crate_items, lookup_crate_item, or search_crate to browse its documentation.`
|
|
450
|
+
);
|
|
451
|
+
}
|
|
396
452
|
const info = await fetchCrateInfo(crateName);
|
|
397
453
|
const ver = version ?? info.version;
|
|
398
454
|
const [versionInfo, deps] = await Promise.all([
|
|
@@ -444,7 +500,7 @@ function register$1(server2) {
|
|
|
444
500
|
}
|
|
445
501
|
);
|
|
446
502
|
}
|
|
447
|
-
function register(server2) {
|
|
503
|
+
function register$1(server2) {
|
|
448
504
|
server2.tool(
|
|
449
505
|
"get_crate_brief",
|
|
450
506
|
"Bundle call: fetches crate metadata, overview docs, module list, re-exports, and optionally items from focused modules — all in one shot.",
|
|
@@ -455,9 +511,16 @@ function register(server2) {
|
|
|
455
511
|
},
|
|
456
512
|
async ({ crateName, version, focusModules }) => {
|
|
457
513
|
try {
|
|
458
|
-
const
|
|
459
|
-
|
|
460
|
-
|
|
514
|
+
const isStd = isStdCrate(crateName);
|
|
515
|
+
let info = null;
|
|
516
|
+
let versionInfo = null;
|
|
517
|
+
if (!isStd) {
|
|
518
|
+
const crateInfo = await fetchCrateInfo(crateName);
|
|
519
|
+
const ver2 = version ?? crateInfo.version;
|
|
520
|
+
versionInfo = await fetchCrateVersionInfo(crateName, ver2);
|
|
521
|
+
info = crateInfo;
|
|
522
|
+
}
|
|
523
|
+
const ver = isStd ? "latest" : version ?? info.version;
|
|
461
524
|
const rootUrl = docsUrl(crateName, "index.html", ver);
|
|
462
525
|
const $ = await fetchDom(rootUrl);
|
|
463
526
|
const doc = truncate(cleanHtml($("details.toggle.top-doc").html() ?? ""), 3e3);
|
|
@@ -477,22 +540,33 @@ function register(server2) {
|
|
|
477
540
|
});
|
|
478
541
|
if (items.length) itemsBySection[type] = items;
|
|
479
542
|
});
|
|
480
|
-
const parts = [
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
543
|
+
const parts = [];
|
|
544
|
+
if (isStd) {
|
|
545
|
+
const pageVersion = $(".sidebar-crate .version").text().trim() || "stable";
|
|
546
|
+
parts.push(
|
|
547
|
+
`# ${crateName} (Rust standard library) v${pageVersion}`,
|
|
548
|
+
rootUrl,
|
|
549
|
+
"",
|
|
550
|
+
`The "${crateName}" crate is part of the Rust standard library.`
|
|
551
|
+
);
|
|
552
|
+
} else {
|
|
553
|
+
parts.push(
|
|
554
|
+
`# ${info.name} v${versionInfo.num}`,
|
|
555
|
+
rootUrl,
|
|
556
|
+
"",
|
|
557
|
+
info.description,
|
|
558
|
+
"",
|
|
559
|
+
`license: ${versionInfo.license} | downloads: ${info.downloads.toLocaleString()}`
|
|
560
|
+
);
|
|
561
|
+
if (info.repository) parts.push(`repo: ${info.repository}`);
|
|
562
|
+
const { defaultFeatures, features } = versionInfo;
|
|
563
|
+
parts.push(
|
|
564
|
+
"",
|
|
565
|
+
"## Features",
|
|
566
|
+
` default = [${defaultFeatures.join(", ")}]`,
|
|
567
|
+
` all: ${Object.keys(features).filter((f) => f !== "default").sort().join(", ")}`
|
|
568
|
+
);
|
|
569
|
+
}
|
|
496
570
|
if (doc) parts.push("", "## Overview", doc);
|
|
497
571
|
if (reexports.length) {
|
|
498
572
|
parts.push("", "## Re-exports", ...reexports.map((r) => ` ${r}`));
|
|
@@ -536,8 +610,57 @@ function register(server2) {
|
|
|
536
610
|
}
|
|
537
611
|
);
|
|
538
612
|
}
|
|
613
|
+
function register(server2) {
|
|
614
|
+
server2.tool(
|
|
615
|
+
"search_crates",
|
|
616
|
+
"Search for Rust crates on crates.io by keyword. Returns name, description, downloads, and version.",
|
|
617
|
+
{
|
|
618
|
+
query: z.string().describe("Search keywords"),
|
|
619
|
+
page: z.number().min(1).optional().describe("Page number (default 1)"),
|
|
620
|
+
perPage: z.number().min(1).max(50).optional().describe("Results per page (default 10, max 50)")
|
|
621
|
+
},
|
|
622
|
+
async ({ query, page: rawPage, perPage: rawPerPage }) => {
|
|
623
|
+
const page = rawPage ?? 1;
|
|
624
|
+
const perPage = rawPerPage ?? 10;
|
|
625
|
+
try {
|
|
626
|
+
const cacheKey = `search-crates:${query}:${page}:${perPage}`;
|
|
627
|
+
const cached = cacheGet(cacheKey);
|
|
628
|
+
if (cached) {
|
|
629
|
+
console.log(`[cache hit] search-crates "${query}" page=${page}`);
|
|
630
|
+
return textResult(cached);
|
|
631
|
+
}
|
|
632
|
+
const { data } = await axios.get(`${CRATES_IO}/crates`, {
|
|
633
|
+
params: { q: query, per_page: perPage, page },
|
|
634
|
+
headers: { "User-Agent": USER_AGENT },
|
|
635
|
+
timeout: 1e4
|
|
636
|
+
});
|
|
637
|
+
const crates = data.crates ?? [];
|
|
638
|
+
if (!crates.length) {
|
|
639
|
+
return textResult(`No crates found for "${query}".`);
|
|
640
|
+
}
|
|
641
|
+
const total = data.meta?.total ?? crates.length;
|
|
642
|
+
const lines = crates.map((c) => {
|
|
643
|
+
const ver = c.max_stable_version || c.max_version;
|
|
644
|
+
const dl = c.downloads.toLocaleString();
|
|
645
|
+
const desc = c.description ? ` — ${c.description.trim()}` : "";
|
|
646
|
+
return ` ${c.name} v${ver} (${dl} downloads)${desc}`;
|
|
647
|
+
});
|
|
648
|
+
const result = [
|
|
649
|
+
`# Crate search: "${query}" — ${total} results (page ${page})`,
|
|
650
|
+
"",
|
|
651
|
+
...lines
|
|
652
|
+
].join("\n");
|
|
653
|
+
cacheSet(cacheKey, result);
|
|
654
|
+
return textResult(result);
|
|
655
|
+
} catch (e) {
|
|
656
|
+
return errorResult(`Could not search crates.io for "${query}". ${e.message}`);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
);
|
|
660
|
+
}
|
|
539
661
|
console.log = (...args) => console.error(...args);
|
|
540
|
-
const server = new McpServer({ name: "rust-docs", version: "
|
|
662
|
+
const server = new McpServer({ name: "rust-docs", version: "3.0.0" });
|
|
663
|
+
register$6(server);
|
|
541
664
|
register$5(server);
|
|
542
665
|
register$4(server);
|
|
543
666
|
register$3(server);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-rustdoc",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP server for fetching and browsing Rust crate documentation from docs.rs and crates.io",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,18 +39,18 @@
|
|
|
39
39
|
},
|
|
40
40
|
"homepage": "https://github.com/kieled/mcp-rustdoc#readme",
|
|
41
41
|
"engines": {
|
|
42
|
-
"node": ">=
|
|
42
|
+
"node": ">=20"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@modelcontextprotocol/sdk": "
|
|
46
|
-
"axios": "
|
|
47
|
-
"cheerio": "
|
|
48
|
-
"html-to-text": "
|
|
49
|
-
"zod": "
|
|
45
|
+
"@modelcontextprotocol/sdk": "1.26.0",
|
|
46
|
+
"axios": "1.13.4",
|
|
47
|
+
"cheerio": "1.2.0",
|
|
48
|
+
"html-to-text": "9.0.5",
|
|
49
|
+
"zod": "4.3.6"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
|
-
"@types/node": "
|
|
53
|
-
"typescript": "
|
|
54
|
-
"vite": "
|
|
52
|
+
"@types/node": "25.2.1",
|
|
53
|
+
"typescript": "5.9.3",
|
|
54
|
+
"vite": "7.3.1"
|
|
55
55
|
}
|
|
56
56
|
}
|