@assistant-ui/mcp-docs-server 0.1.17 → 0.1.18
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/.docs/organized/code-examples/with-ag-ui.md +146 -152
- package/.docs/organized/code-examples/with-ai-sdk-v5.md +96 -101
- package/.docs/organized/code-examples/with-assistant-transport.md +132 -220
- package/.docs/organized/code-examples/with-cloud.md +124 -131
- package/.docs/organized/code-examples/with-custom-thread-list.md +26 -46
- package/.docs/organized/code-examples/with-external-store.md +146 -151
- package/.docs/organized/code-examples/with-ffmpeg.md +129 -139
- package/.docs/organized/code-examples/with-langgraph.md +231 -225
- package/.docs/organized/code-examples/with-parent-id-grouping.md +146 -151
- package/.docs/organized/code-examples/with-react-hook-form.md +146 -152
- package/.docs/organized/code-examples/{store-example.md → with-store.md} +16 -20
- package/.docs/organized/code-examples/with-tanstack.md +23 -41
- package/.docs/raw/docs/runtimes/custom/custom-thread-list.mdx +36 -0
- package/.docs/raw/docs/runtimes/custom/local.mdx +31 -8
- package/.docs/raw/docs/ui/Scrollbar.mdx +0 -6
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -1
- package/dist/index.js.map +1 -0
- package/dist/prepare-docs/code-examples.d.ts +2 -0
- package/dist/prepare-docs/code-examples.d.ts.map +1 -0
- package/dist/prepare-docs/code-examples.js +129 -0
- package/dist/prepare-docs/code-examples.js.map +1 -0
- package/dist/prepare-docs/copy-raw.d.ts +2 -0
- package/dist/prepare-docs/copy-raw.d.ts.map +1 -0
- package/dist/prepare-docs/copy-raw.js +50 -0
- package/dist/prepare-docs/copy-raw.js.map +1 -0
- package/dist/prepare-docs/prepare.d.ts +2 -0
- package/dist/prepare-docs/prepare.d.ts.map +1 -0
- package/dist/prepare-docs/prepare.js +18 -195
- package/dist/prepare-docs/prepare.js.map +1 -0
- package/dist/stdio.d.ts +3 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +4 -5
- package/dist/stdio.js.map +1 -0
- package/dist/tools/docs.d.ts +23 -0
- package/dist/tools/docs.d.ts.map +1 -0
- package/dist/tools/docs.js +168 -0
- package/dist/tools/docs.js.map +1 -0
- package/dist/tools/examples.d.ts +23 -0
- package/dist/tools/examples.d.ts.map +1 -0
- package/dist/tools/examples.js +95 -0
- package/dist/tools/examples.js.map +1 -0
- package/dist/tools/tests/test-setup.d.ts +4 -0
- package/dist/tools/tests/test-setup.d.ts.map +1 -0
- package/dist/tools/tests/test-setup.js +36 -0
- package/dist/tools/tests/test-setup.js.map +1 -0
- package/dist/utils/logger.d.ts +7 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +20 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/mcp-format.d.ts +7 -0
- package/dist/utils/mcp-format.d.ts.map +1 -0
- package/dist/utils/mcp-format.js +11 -0
- package/dist/utils/mcp-format.js.map +1 -0
- package/dist/utils/mdx.d.ts +9 -0
- package/dist/utils/mdx.d.ts.map +1 -0
- package/dist/utils/mdx.js +27 -0
- package/dist/utils/mdx.js.map +1 -0
- package/dist/utils/paths.d.ts +8 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +84 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/security.d.ts +2 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +43 -0
- package/dist/utils/security.js.map +1 -0
- package/package.json +37 -19
- package/src/constants.ts +22 -0
- package/src/index.ts +51 -0
- package/src/prepare-docs/code-examples.ts +158 -0
- package/src/prepare-docs/copy-raw.ts +55 -0
- package/src/prepare-docs/prepare.ts +24 -0
- package/src/stdio.ts +7 -0
- package/src/tools/docs.ts +207 -0
- package/src/tools/examples.ts +107 -0
- package/src/tools/tests/docs.test.ts +122 -0
- package/src/tools/tests/examples.test.ts +94 -0
- package/src/tools/tests/integration.test.ts +46 -0
- package/src/tools/tests/json-parsing.test.ts +23 -0
- package/src/tools/tests/mcp-protocol.test.ts +133 -0
- package/src/tools/tests/path-traversal.test.ts +81 -0
- package/src/tools/tests/test-setup.ts +40 -0
- package/src/utils/logger.ts +20 -0
- package/src/utils/mcp-format.ts +12 -0
- package/src/utils/mdx.ts +39 -0
- package/src/utils/paths.ts +114 -0
- package/src/utils/security.ts +52 -0
- package/src/utils/tests/security.test.ts +119 -0
- package/dist/chunk-M2RKUM66.js +0 -38
- package/dist/chunk-NVNFQ5ZO.js +0 -423
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IS_PREPARE_MODE } from "../constants.js";
|
|
2
|
+
export const logger = {
|
|
3
|
+
debug: (message, ...args) => {
|
|
4
|
+
if (process.env["DEBUG"]) {
|
|
5
|
+
console.debug(`[DEBUG] ${message}`, ...args);
|
|
6
|
+
}
|
|
7
|
+
},
|
|
8
|
+
info: (message, ...args) => {
|
|
9
|
+
if (IS_PREPARE_MODE) {
|
|
10
|
+
console.log(`[INFO] ${message}`, ...args);
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
error: (message, ...args) => {
|
|
14
|
+
console.error(`[ERROR] ${message}`, ...args);
|
|
15
|
+
},
|
|
16
|
+
warn: (message, ...args) => {
|
|
17
|
+
console.warn(`[WARN] ${message}`, ...args);
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE;QACzC,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,IAAI,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE;QACxC,IAAI,eAAe,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,KAAK,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE;QACzC,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,EAAE,CAAC,OAAe,EAAE,GAAG,IAAW,EAAE,EAAE;QACxC,OAAO,CAAC,IAAI,CAAC,UAAU,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;IAC7C,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-format.d.ts","sourceRoot":"","sources":["../../src/utils/mcp-format.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,GAAG;IAC5C,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD,CASA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-format.js","sourceRoot":"","sources":["../../src/utils/mcp-format.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,iBAAiB,CAAC,IAAS;IAGzC,OAAO;QACL,OAAO,EAAE;YACP;gBACE,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;aACtE;SACF;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface MDXContent {
|
|
2
|
+
content: string;
|
|
3
|
+
frontmatter: Record<string, any>;
|
|
4
|
+
excerpt?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function readMDXFile(filePath: string): Promise<MDXContent | null>;
|
|
7
|
+
export declare function formatMDXContent(mdxContent: MDXContent): string;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=mdx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdx.d.ts","sourceRoot":"","sources":["../../src/utils/mdx.ts"],"names":[],"mappings":"AAIA,UAAU,UAAU;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAoB5B;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,UAAU,GAAG,MAAM,CAI/D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import matter from "gray-matter";
|
|
3
|
+
import { logger } from "./logger.js";
|
|
4
|
+
export async function readMDXFile(filePath) {
|
|
5
|
+
try {
|
|
6
|
+
const fileContent = await readFile(filePath, "utf-8");
|
|
7
|
+
const { content, data } = matter(fileContent);
|
|
8
|
+
const excerptMatch = content.match(/^(.+?)(?:\n\n|$)/);
|
|
9
|
+
const excerpt = excerptMatch?.[1] !== undefined
|
|
10
|
+
? excerptMatch[1].replace(/^#+ /, "")
|
|
11
|
+
: undefined;
|
|
12
|
+
return {
|
|
13
|
+
content,
|
|
14
|
+
frontmatter: data,
|
|
15
|
+
...(excerpt !== undefined && { excerpt }),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
logger.error(`Failed to read MDX file: ${filePath}`, error);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function formatMDXContent(mdxContent) {
|
|
24
|
+
const { content, frontmatter } = mdxContent;
|
|
25
|
+
return matter.stringify(content, frontmatter);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=mdx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mdx.js","sourceRoot":"","sources":["../../src/utils/mdx.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAQrC,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACtD,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;QAE9C,MAAM,YAAY,GAAG,OAAO,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QACvD,MAAM,OAAO,GACX,YAAY,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS;YAC7B,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YACrC,CAAC,CAAC,SAAS,CAAC;QAEhB,OAAO;YACL,OAAO;YACP,WAAW,EAAE,IAAI;YACjB,GAAG,CAAC,OAAO,KAAK,SAAS,IAAI,EAAE,OAAO,EAAE,CAAC;SAC1C,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,4BAA4B,QAAQ,EAAE,EAAE,KAAK,CAAC,CAAC;QAC5D,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAsB;IACrD,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,UAAU,CAAC;IAE5C,OAAO,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare function listDirContents(dirPath: string): Promise<{
|
|
2
|
+
directories: string[];
|
|
3
|
+
files: string[];
|
|
4
|
+
}>;
|
|
5
|
+
export declare function getAvailablePaths(): Promise<string[]>;
|
|
6
|
+
export declare function findNearestPaths(requestedPath: string, availablePaths: string[]): string[];
|
|
7
|
+
export declare function pathExists(path: string): Promise<boolean>;
|
|
8
|
+
//# sourceMappingURL=paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAYA,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9D,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC,CAyBD;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAwB3D;AAED,wBAAgB,gBAAgB,CAC9B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,EAAE,GACvB,MAAM,EAAE,CAiCV;AAED,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join, extname } from "node:path";
|
|
3
|
+
import { DOCS_PATH, MDX_EXTENSION, MD_EXTENSION } from "../constants.js";
|
|
4
|
+
import { logger } from "./logger.js";
|
|
5
|
+
const SIMILARITY_THRESHOLDS = {
|
|
6
|
+
EXACT_MATCH: 1,
|
|
7
|
+
CONTAINS_MATCH: 0.8,
|
|
8
|
+
PARTIAL_MATCH: 0.5,
|
|
9
|
+
MIN_SUGGESTION: 0.3,
|
|
10
|
+
};
|
|
11
|
+
export async function listDirContents(dirPath) {
|
|
12
|
+
try {
|
|
13
|
+
const items = await readdir(dirPath, { withFileTypes: true });
|
|
14
|
+
const directories = items
|
|
15
|
+
.filter((item) => item.isDirectory())
|
|
16
|
+
.map((item) => item.name)
|
|
17
|
+
.filter((name) => !name.startsWith("."))
|
|
18
|
+
.sort();
|
|
19
|
+
const files = items
|
|
20
|
+
.filter((item) => item.isFile() &&
|
|
21
|
+
(extname(item.name) === MDX_EXTENSION ||
|
|
22
|
+
extname(item.name) === MD_EXTENSION))
|
|
23
|
+
.map((item) => item.name)
|
|
24
|
+
.sort();
|
|
25
|
+
return { directories, files };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
logger.error(`Failed to list directory contents: ${dirPath}`, error);
|
|
29
|
+
return { directories: [], files: [] };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function getAvailablePaths() {
|
|
33
|
+
const paths = [];
|
|
34
|
+
async function scanDirectory(dir, prefix = "") {
|
|
35
|
+
const { directories, files } = await listDirContents(dir);
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const name = file.replace(MDX_EXTENSION, "").replace(MD_EXTENSION, "");
|
|
38
|
+
paths.push(prefix ? `${prefix}/${name}` : name);
|
|
39
|
+
}
|
|
40
|
+
for (const subdir of directories) {
|
|
41
|
+
const subdirPath = join(dir, subdir);
|
|
42
|
+
const newPrefix = prefix ? `${prefix}/${subdir}` : subdir;
|
|
43
|
+
paths.push(newPrefix);
|
|
44
|
+
await scanDirectory(subdirPath, newPrefix);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
await scanDirectory(DOCS_PATH);
|
|
48
|
+
return paths.sort();
|
|
49
|
+
}
|
|
50
|
+
export function findNearestPaths(requestedPath, availablePaths) {
|
|
51
|
+
const normalizedRequest = requestedPath
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.replace(/[^a-z0-9]/g, "");
|
|
54
|
+
const scored = availablePaths.map((path) => {
|
|
55
|
+
const normalizedPath = path.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
56
|
+
if (normalizedPath.includes(normalizedRequest)) {
|
|
57
|
+
return { path, score: SIMILARITY_THRESHOLDS.EXACT_MATCH };
|
|
58
|
+
}
|
|
59
|
+
if (normalizedRequest.includes(normalizedPath)) {
|
|
60
|
+
return { path, score: SIMILARITY_THRESHOLDS.CONTAINS_MATCH };
|
|
61
|
+
}
|
|
62
|
+
const overlap = [...normalizedRequest].filter((char) => normalizedPath.includes(char)).length;
|
|
63
|
+
return {
|
|
64
|
+
path,
|
|
65
|
+
score: (overlap / normalizedRequest.length) *
|
|
66
|
+
SIMILARITY_THRESHOLDS.PARTIAL_MATCH,
|
|
67
|
+
};
|
|
68
|
+
});
|
|
69
|
+
return scored
|
|
70
|
+
.filter((item) => item.score > SIMILARITY_THRESHOLDS.MIN_SUGGESTION)
|
|
71
|
+
.sort((a, b) => b.score - a.score)
|
|
72
|
+
.slice(0, 3)
|
|
73
|
+
.map((item) => item.path);
|
|
74
|
+
}
|
|
75
|
+
export async function pathExists(path) {
|
|
76
|
+
try {
|
|
77
|
+
await stat(path);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=paths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.d.ts","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAiDrD"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
export function sanitizePath(userPath) {
|
|
3
|
+
if (!userPath || typeof userPath !== "string") {
|
|
4
|
+
throw new Error("Invalid path: Path must be a non-empty string");
|
|
5
|
+
}
|
|
6
|
+
// Check for traversal patterns before normalization - defense in depth
|
|
7
|
+
if (userPath.includes("../") || userPath.includes("..\\")) {
|
|
8
|
+
throw new Error("Invalid path: Directory traversal attempt detected");
|
|
9
|
+
}
|
|
10
|
+
const normalized = path.normalize(userPath);
|
|
11
|
+
if (path.isAbsolute(normalized)) {
|
|
12
|
+
throw new Error("Invalid path: Absolute paths are not allowed");
|
|
13
|
+
}
|
|
14
|
+
const relativePath = path.relative("", normalized);
|
|
15
|
+
if (relativePath.startsWith("..")) {
|
|
16
|
+
throw new Error("Invalid path: Directory traversal attempt detected");
|
|
17
|
+
}
|
|
18
|
+
if (relativePath.includes("..")) {
|
|
19
|
+
throw new Error("Invalid path: Path contains invalid traversal sequences");
|
|
20
|
+
}
|
|
21
|
+
if (relativePath.includes("\0")) {
|
|
22
|
+
throw new Error("Invalid path: Path contains null bytes");
|
|
23
|
+
}
|
|
24
|
+
if (process.platform !== "win32") {
|
|
25
|
+
if (normalized.includes("\\")) {
|
|
26
|
+
throw new Error("Invalid path: Backslashes not allowed");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
if (normalized.includes(":") || normalized.startsWith("\\\\")) {
|
|
31
|
+
throw new Error("Invalid path: Path contains invalid Windows characters");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const segments = relativePath.split(path.sep);
|
|
35
|
+
for (const segment of segments) {
|
|
36
|
+
if (segment.startsWith(".") && segment !== ".") {
|
|
37
|
+
throw new Error("Invalid path: Hidden files are not allowed");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// Always return Unix-style paths for consistency across platforms
|
|
41
|
+
return relativePath.replace(/\\/g, "/");
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../../src/utils/security.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACnE,CAAC;IAED,uEAAuE;IACvE,IAAI,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAE5C,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IAEnD,IAAI,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IACxE,CAAC;IAED,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;IAED,IAAI,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,GAAG,EAAE,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,OAAO,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,46 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@assistant-ui/mcp-docs-server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "MCP server for assistant-ui documentation and examples",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"mcp",
|
|
7
|
+
"model-context-protocol",
|
|
8
|
+
"assistant-ui",
|
|
9
|
+
"documentation",
|
|
10
|
+
"ai",
|
|
11
|
+
"claude"
|
|
12
|
+
],
|
|
13
|
+
"author": "AgentbaseAI Inc.",
|
|
14
|
+
"license": "MIT",
|
|
5
15
|
"type": "module",
|
|
6
|
-
"
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"aui-source": "./src/index.ts",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"main": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
7
25
|
"bin": {
|
|
8
26
|
"assistant-ui-mcp": "./dist/stdio.js"
|
|
9
27
|
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"src",
|
|
31
|
+
".docs",
|
|
32
|
+
"README.md"
|
|
33
|
+
],
|
|
34
|
+
"sideEffects": false,
|
|
10
35
|
"dependencies": {
|
|
11
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
12
|
-
"zod": "^4.1.13",
|
|
36
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
13
37
|
"gray-matter": "^4.0.3",
|
|
14
|
-
"
|
|
38
|
+
"zod": "^4.2.1"
|
|
15
39
|
},
|
|
16
40
|
"devDependencies": {
|
|
17
|
-
"@types/node": "^25.0.
|
|
18
|
-
"tsup": "^8.5.1",
|
|
41
|
+
"@types/node": "^25.0.3",
|
|
19
42
|
"tsx": "^4.21.0",
|
|
20
|
-
"
|
|
21
|
-
"
|
|
43
|
+
"vitest": "^4.0.16",
|
|
44
|
+
"@assistant-ui/x-buildutils": "0.0.1"
|
|
22
45
|
},
|
|
23
|
-
"files": [
|
|
24
|
-
"dist",
|
|
25
|
-
".docs",
|
|
26
|
-
"README.md"
|
|
27
|
-
],
|
|
28
46
|
"publishConfig": {
|
|
29
47
|
"access": "public",
|
|
30
48
|
"provenance": true
|
|
31
49
|
},
|
|
50
|
+
"homepage": "https://www.assistant-ui.com/",
|
|
32
51
|
"repository": {
|
|
33
52
|
"type": "git",
|
|
34
|
-
"url": "https://github.com/assistant-ui/assistant-ui
|
|
53
|
+
"url": "git+https://github.com/assistant-ui/assistant-ui.git",
|
|
54
|
+
"directory": "packages/mcp-docs-server"
|
|
35
55
|
},
|
|
36
56
|
"bugs": {
|
|
37
57
|
"url": "https://github.com/assistant-ui/assistant-ui/issues"
|
|
38
58
|
},
|
|
39
59
|
"scripts": {
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"build:cli": "tsup src/stdio.ts src/prepare-docs/prepare.ts src/index.ts --format esm --treeshake=smallest --splitting",
|
|
43
|
-
"build": "pnpm clean && pnpm build:cli && pnpm prepare-docs",
|
|
60
|
+
"prepare-docs": "node ./dist/prepare-docs/prepare.js",
|
|
61
|
+
"build": "aui-build && pnpm prepare-docs",
|
|
44
62
|
"dev": "tsx src/stdio.ts",
|
|
45
63
|
"pretest": "pnpm build",
|
|
46
64
|
"test": "vitest run",
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fileURLToPath } from "node:url";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
|
|
4
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
|
+
|
|
6
|
+
export const ROOT_DIR = join(__dirname, "../../../");
|
|
7
|
+
export const PACKAGE_DIR = join(__dirname, "../");
|
|
8
|
+
|
|
9
|
+
export const EXAMPLES_PATH = join(ROOT_DIR, "examples");
|
|
10
|
+
|
|
11
|
+
const DOCS_BASE = join(PACKAGE_DIR, ".docs");
|
|
12
|
+
export const DOCS_PATH = join(DOCS_BASE, "raw/docs");
|
|
13
|
+
export const CODE_EXAMPLES_PATH = join(DOCS_BASE, "organized/code-examples");
|
|
14
|
+
|
|
15
|
+
export const MDX_EXTENSION = ".mdx";
|
|
16
|
+
export const MD_EXTENSION = ".md";
|
|
17
|
+
|
|
18
|
+
export const MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
19
|
+
|
|
20
|
+
export const IS_PREPARE_MODE = process.argv[1]?.includes(
|
|
21
|
+
"prepare-docs/prepare",
|
|
22
|
+
);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { docsTools } from "./tools/docs.js";
|
|
4
|
+
import { examplesTools } from "./tools/examples.js";
|
|
5
|
+
import { logger } from "./utils/logger.js";
|
|
6
|
+
import { PACKAGE_DIR } from "./constants.js";
|
|
7
|
+
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { join } from "node:path";
|
|
10
|
+
|
|
11
|
+
const packageJson = JSON.parse(
|
|
12
|
+
readFileSync(join(PACKAGE_DIR, "package.json"), "utf-8"),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export const server = new McpServer({
|
|
16
|
+
name: "assistant-ui-docs",
|
|
17
|
+
version: packageJson.version,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
server.tool(
|
|
21
|
+
docsTools.name,
|
|
22
|
+
docsTools.description,
|
|
23
|
+
docsTools.parameters,
|
|
24
|
+
docsTools.execute,
|
|
25
|
+
);
|
|
26
|
+
server.tool(
|
|
27
|
+
examplesTools.name,
|
|
28
|
+
examplesTools.description,
|
|
29
|
+
examplesTools.parameters,
|
|
30
|
+
examplesTools.execute,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
export async function runServer() {
|
|
34
|
+
try {
|
|
35
|
+
logger.info(
|
|
36
|
+
`Starting assistant-ui MCP docs server v${packageJson.version}`,
|
|
37
|
+
);
|
|
38
|
+
const transport = new StdioServerTransport();
|
|
39
|
+
await server.connect(transport);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error("Failed to start MCP server", error);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
47
|
+
void runServer().catch((error) => {
|
|
48
|
+
console.error("Failed to start server:", error);
|
|
49
|
+
process.exit(1);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { rm, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join, relative, extname } from "node:path";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { ROOT_DIR, EXAMPLES_PATH } from "../constants.js";
|
|
5
|
+
|
|
6
|
+
const OUTPUT_DIR = join(
|
|
7
|
+
ROOT_DIR,
|
|
8
|
+
"packages/mcp-docs-server/.docs/organized/code-examples",
|
|
9
|
+
);
|
|
10
|
+
const MAX_LINES = 10000;
|
|
11
|
+
|
|
12
|
+
interface FileContent {
|
|
13
|
+
path: string;
|
|
14
|
+
content: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function scanDirectory(
|
|
18
|
+
dir: string,
|
|
19
|
+
baseDir: string,
|
|
20
|
+
): Promise<FileContent[]> {
|
|
21
|
+
const files: FileContent[] = [];
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
25
|
+
|
|
26
|
+
for (const entry of entries) {
|
|
27
|
+
const fullPath = join(dir, entry.name);
|
|
28
|
+
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
const skipDirs = [
|
|
31
|
+
"node_modules",
|
|
32
|
+
"dist",
|
|
33
|
+
"build",
|
|
34
|
+
".next",
|
|
35
|
+
".git",
|
|
36
|
+
".turbo",
|
|
37
|
+
];
|
|
38
|
+
if (!skipDirs.includes(entry.name)) {
|
|
39
|
+
const subFiles = await scanDirectory(fullPath, baseDir);
|
|
40
|
+
files.push(...subFiles);
|
|
41
|
+
}
|
|
42
|
+
} else if (entry.isFile()) {
|
|
43
|
+
const includeExts = [
|
|
44
|
+
".ts",
|
|
45
|
+
".tsx",
|
|
46
|
+
".js",
|
|
47
|
+
".jsx",
|
|
48
|
+
".json",
|
|
49
|
+
".css",
|
|
50
|
+
".md",
|
|
51
|
+
".mdx",
|
|
52
|
+
];
|
|
53
|
+
const ext = extname(entry.name).toLowerCase();
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
includeExts.includes(ext) ||
|
|
57
|
+
entry.name === "package.json" ||
|
|
58
|
+
entry.name === "tsconfig.json"
|
|
59
|
+
) {
|
|
60
|
+
try {
|
|
61
|
+
const content = await readFile(fullPath, "utf-8");
|
|
62
|
+
const relativePath = relative(baseDir, fullPath);
|
|
63
|
+
files.push({ path: relativePath, content });
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.warn(`Failed to read file: ${fullPath}`, error);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
logger.error(`Failed to scan directory: ${dir}`, error);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return files;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getFileType(filename: string): string {
|
|
78
|
+
const ext = extname(filename).toLowerCase();
|
|
79
|
+
const extMap: Record<string, string> = {
|
|
80
|
+
".ts": "typescript",
|
|
81
|
+
".tsx": "tsx",
|
|
82
|
+
".js": "javascript",
|
|
83
|
+
".jsx": "jsx",
|
|
84
|
+
".json": "json",
|
|
85
|
+
".css": "css",
|
|
86
|
+
".md": "markdown",
|
|
87
|
+
".mdx": "mdx",
|
|
88
|
+
};
|
|
89
|
+
return extMap[ext] || "text";
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export async function prepareCodeExamples(): Promise<void> {
|
|
93
|
+
logger.info("Preparing code examples...");
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
await rm(OUTPUT_DIR, { recursive: true, force: true });
|
|
97
|
+
await mkdir(OUTPUT_DIR, { recursive: true });
|
|
98
|
+
|
|
99
|
+
const exampleDirs = await readdir(EXAMPLES_PATH, { withFileTypes: true });
|
|
100
|
+
|
|
101
|
+
for (const dir of exampleDirs) {
|
|
102
|
+
if (dir.isDirectory() && !dir.name.startsWith(".")) {
|
|
103
|
+
const examplePath = join(EXAMPLES_PATH, dir.name);
|
|
104
|
+
logger.info(`Processing example: ${dir.name}`);
|
|
105
|
+
|
|
106
|
+
let description = "";
|
|
107
|
+
try {
|
|
108
|
+
const packageJsonPath = join(examplePath, "package.json");
|
|
109
|
+
const packageJson = JSON.parse(
|
|
110
|
+
await readFile(packageJsonPath, "utf-8"),
|
|
111
|
+
);
|
|
112
|
+
description = packageJson.description || "";
|
|
113
|
+
} catch (error: any) {
|
|
114
|
+
if (error?.code !== "ENOENT") {
|
|
115
|
+
logger.warn(`Failed to read package.json for ${dir.name}:`, error);
|
|
116
|
+
} else {
|
|
117
|
+
logger.debug(`No package.json found for example: ${dir.name}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const files = await scanDirectory(examplePath, examplePath);
|
|
122
|
+
|
|
123
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
124
|
+
|
|
125
|
+
let markdown = `# Example: ${dir.name}\n\n`;
|
|
126
|
+
if (description) {
|
|
127
|
+
markdown += `${description}\n\n`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let totalLines = 0;
|
|
131
|
+
for (const file of files) {
|
|
132
|
+
const lines = file.content.split("\n").length;
|
|
133
|
+
if (totalLines + lines > MAX_LINES) {
|
|
134
|
+
markdown += `\n_Note: Additional files truncated due to size limits_\n`;
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Normalize Windows backslashes to forward slashes for consistent markdown output
|
|
139
|
+
markdown += `## ${file.path.replace(/\\/g, "/")}\n\n`;
|
|
140
|
+
markdown += `\`\`\`${getFileType(file.path)}\n`;
|
|
141
|
+
markdown += file.content;
|
|
142
|
+
markdown += `\n\`\`\`\n\n`;
|
|
143
|
+
|
|
144
|
+
totalLines += lines;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const outputPath = join(OUTPUT_DIR, `${dir.name}.md`);
|
|
148
|
+
await writeFile(outputPath, markdown, "utf-8");
|
|
149
|
+
logger.debug(`Created example: ${outputPath}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logger.info("Code examples preparation complete");
|
|
154
|
+
} catch (error) {
|
|
155
|
+
logger.error("Failed to prepare code examples", error);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { rm, mkdir, readdir, copyFile } from "node:fs/promises";
|
|
2
|
+
import { join, extname } from "node:path";
|
|
3
|
+
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { ROOT_DIR } from "../constants.js";
|
|
5
|
+
|
|
6
|
+
const DOCS_SOURCE = join(ROOT_DIR, "apps/docs/content/docs");
|
|
7
|
+
const BLOG_SOURCE = join(ROOT_DIR, "apps/docs/content/blog");
|
|
8
|
+
const DOCS_DEST = join(ROOT_DIR, "packages/mcp-docs-server/.docs/raw");
|
|
9
|
+
|
|
10
|
+
async function copyDir(src: string, dest: string): Promise<void> {
|
|
11
|
+
try {
|
|
12
|
+
await mkdir(dest, { recursive: true });
|
|
13
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
14
|
+
|
|
15
|
+
for (const entry of entries) {
|
|
16
|
+
const srcPath = join(src, entry.name);
|
|
17
|
+
const destPath = join(dest, entry.name);
|
|
18
|
+
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
await copyDir(srcPath, destPath);
|
|
21
|
+
} else if (entry.isFile()) {
|
|
22
|
+
const ext = extname(entry.name).toLowerCase();
|
|
23
|
+
if (ext === ".mdx" || ext === ".md") {
|
|
24
|
+
await copyFile(srcPath, destPath);
|
|
25
|
+
logger.debug(`Copied: ${srcPath} -> ${destPath}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logger.error(`Failed to copy directory: ${src}`, error);
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export async function copyRaw(): Promise<void> {
|
|
36
|
+
logger.info("Copying raw documentation files...");
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await rm(DOCS_DEST, { recursive: true, force: true });
|
|
40
|
+
await mkdir(DOCS_DEST, { recursive: true });
|
|
41
|
+
|
|
42
|
+
const docsPath = join(DOCS_DEST, "docs");
|
|
43
|
+
await copyDir(DOCS_SOURCE, docsPath);
|
|
44
|
+
logger.info(`Copied documentation to ${docsPath}`);
|
|
45
|
+
|
|
46
|
+
const blogPath = join(DOCS_DEST, "blog");
|
|
47
|
+
await copyDir(BLOG_SOURCE, blogPath);
|
|
48
|
+
logger.info(`Copied blog posts to ${blogPath}`);
|
|
49
|
+
|
|
50
|
+
logger.info("Raw documentation copy complete");
|
|
51
|
+
} catch (error) {
|
|
52
|
+
logger.error("Failed to copy raw documentation", error);
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.js";
|
|
2
|
+
import { copyRaw } from "./copy-raw.js";
|
|
3
|
+
import { prepareCodeExamples } from "./code-examples.js";
|
|
4
|
+
|
|
5
|
+
async function prepare(): Promise<void> {
|
|
6
|
+
logger.info("Starting documentation preparation...");
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
await copyRaw();
|
|
10
|
+
await prepareCodeExamples();
|
|
11
|
+
|
|
12
|
+
logger.info("Documentation preparation complete");
|
|
13
|
+
} catch (error) {
|
|
14
|
+
logger.error("Documentation preparation failed", error);
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
20
|
+
prepare().catch((error) => {
|
|
21
|
+
logger.error("Preparation failed", error);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|
|
24
|
+
}
|