@ceed/docs-mcp 1.0.0-next.1
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/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/rag/build-index.d.ts +2 -0
- package/dist/rag/build-index.js +278 -0
- package/dist/rag/build-index.js.map +1 -0
- package/dist/rag/constants.d.ts +7 -0
- package/dist/rag/constants.js +14 -0
- package/dist/rag/constants.js.map +1 -0
- package/dist/rag/embeddings.d.ts +8 -0
- package/dist/rag/embeddings.js +61 -0
- package/dist/rag/embeddings.js.map +1 -0
- package/dist/rag/search.d.ts +12 -0
- package/dist/rag/search.js +158 -0
- package/dist/rag/search.js.map +1 -0
- package/dist/rag/storage.d.ts +3 -0
- package/dist/rag/storage.js +15 -0
- package/dist/rag/storage.js.map +1 -0
- package/dist/rag/types.d.ts +28 -0
- package/dist/rag/types.js +2 -0
- package/dist/rag/types.js.map +1 -0
- package/dist/server.d.ts +4 -0
- package/dist/server.js +140 -0
- package/dist/server.js.map +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/package.json +55 -0
- package/rag-index.json +485021 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { INDEX_PATH } from './constants.js';
|
|
3
|
+
export const loadEmbeddingIndex = async (indexPath = INDEX_PATH) => {
|
|
4
|
+
const raw = await readFile(indexPath, 'utf-8');
|
|
5
|
+
const parsed = JSON.parse(raw);
|
|
6
|
+
if (!Array.isArray(parsed.chunks)) {
|
|
7
|
+
throw new Error('Invalid embedding index: missing chunks array');
|
|
8
|
+
}
|
|
9
|
+
return parsed;
|
|
10
|
+
};
|
|
11
|
+
export const saveEmbeddingIndex = async (index, indexPath = INDEX_PATH) => {
|
|
12
|
+
const payload = JSON.stringify(index, null, 2);
|
|
13
|
+
await writeFile(indexPath, payload, 'utf-8');
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/rag/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG5C,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,YAAoB,UAAU,EAA2B,EAAE;IAChG,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;IAEjD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,KAAqB,EAAE,YAAoB,UAAU,EAAE,EAAE;IAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,MAAM,SAAS,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AACjD,CAAC,CAAC","sourcesContent":["import { readFile, writeFile } from 'node:fs/promises';\n\nimport { INDEX_PATH } from './constants.js';\nimport type { EmbeddingIndex } from './types.js';\n\nexport const loadEmbeddingIndex = async (indexPath: string = INDEX_PATH): Promise<EmbeddingIndex> => {\n const raw = await readFile(indexPath, 'utf-8');\n const parsed = JSON.parse(raw) as EmbeddingIndex;\n\n if (!Array.isArray(parsed.chunks)) {\n throw new Error('Invalid embedding index: missing chunks array');\n }\n\n return parsed;\n};\n\nexport const saveEmbeddingIndex = async (index: EmbeddingIndex, indexPath: string = INDEX_PATH) => {\n const payload = JSON.stringify(index, null, 2);\n await writeFile(indexPath, payload, 'utf-8');\n};\n"]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface DocumentMetadata {
|
|
2
|
+
source: string;
|
|
3
|
+
heading?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface DocumentChunk {
|
|
6
|
+
id: string;
|
|
7
|
+
text: string;
|
|
8
|
+
metadata: DocumentMetadata;
|
|
9
|
+
}
|
|
10
|
+
export interface EmbeddingChunk extends DocumentChunk {
|
|
11
|
+
embedding: number[];
|
|
12
|
+
}
|
|
13
|
+
export interface EmbeddingIndex {
|
|
14
|
+
model: string;
|
|
15
|
+
dimension: number;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
chunks: EmbeddingChunk[];
|
|
18
|
+
}
|
|
19
|
+
export interface SearchResult extends DocumentChunk {
|
|
20
|
+
score: number;
|
|
21
|
+
}
|
|
22
|
+
export interface SearchResultsPage {
|
|
23
|
+
results: SearchResult[];
|
|
24
|
+
total: number;
|
|
25
|
+
page: number;
|
|
26
|
+
pageSize: number;
|
|
27
|
+
hasMore: boolean;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/rag/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface DocumentMetadata {\n source: string;\n heading?: string;\n}\n\nexport interface DocumentChunk {\n id: string;\n text: string;\n metadata: DocumentMetadata;\n}\n\nexport interface EmbeddingChunk extends DocumentChunk {\n embedding: number[];\n}\n\nexport interface EmbeddingIndex {\n model: string;\n dimension: number;\n createdAt: string;\n chunks: EmbeddingChunk[];\n}\n\nexport interface SearchResult extends DocumentChunk {\n score: number;\n}\n\nexport interface SearchResultsPage {\n results: SearchResult[];\n total: number;\n page: number;\n pageSize: number;\n hasMore: boolean;\n}\n"]}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
4
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { createRagSearcher } from './rag/search.js';
|
|
7
|
+
import { DOCS_MCP_VERSION } from './version.js';
|
|
8
|
+
const pingSchema = z.object({
|
|
9
|
+
message: z.string().optional(),
|
|
10
|
+
});
|
|
11
|
+
const pingInputSchema = pingSchema.shape;
|
|
12
|
+
const searchSchema = z.object({
|
|
13
|
+
packageName: z.enum(['@ceed/ads', '@ceed/cds']).describe('Package to search (@ceed/ads or @ceed/cds).'),
|
|
14
|
+
query: z.string().min(1, 'Query text is required.'),
|
|
15
|
+
topK: z
|
|
16
|
+
.number()
|
|
17
|
+
.int()
|
|
18
|
+
.min(1)
|
|
19
|
+
.max(100)
|
|
20
|
+
.optional()
|
|
21
|
+
.describe('Number of matches to return per page (default: 5, max: 100).'),
|
|
22
|
+
page: z.number().int().min(1).optional().describe('Results page to fetch (default: 1).'),
|
|
23
|
+
});
|
|
24
|
+
const searchInputSchema = searchSchema.shape;
|
|
25
|
+
const formatSearchResult = (rank, result) => {
|
|
26
|
+
const lines = [`#${rank + 1} (score: ${result.score.toFixed(3)})`, `source: ${result.metadata.source}`];
|
|
27
|
+
if (result.metadata.heading) {
|
|
28
|
+
lines.splice(1, 0, `heading: ${result.metadata.heading}`);
|
|
29
|
+
}
|
|
30
|
+
lines.push('', result.text.trim());
|
|
31
|
+
return lines.join('\n');
|
|
32
|
+
};
|
|
33
|
+
export const createDocsMcpServer = () => {
|
|
34
|
+
const server = new McpServer({
|
|
35
|
+
name: '@ceed/docs-mcp',
|
|
36
|
+
version: DOCS_MCP_VERSION,
|
|
37
|
+
});
|
|
38
|
+
const ragSearcher = createRagSearcher();
|
|
39
|
+
server.registerTool('ping', {
|
|
40
|
+
title: 'Ping',
|
|
41
|
+
description: 'Return a simple response to verify the MCP server is healthy.',
|
|
42
|
+
inputSchema: pingInputSchema,
|
|
43
|
+
}, async (rawInput) => {
|
|
44
|
+
const { message } = pingSchema.parse(rawInput);
|
|
45
|
+
return {
|
|
46
|
+
content: [
|
|
47
|
+
{
|
|
48
|
+
type: 'text',
|
|
49
|
+
text: message ?? 'pong',
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
});
|
|
54
|
+
server.registerTool('search', {
|
|
55
|
+
title: 'Search documentation',
|
|
56
|
+
description: 'Look up documentation chunks that best match the given query.',
|
|
57
|
+
inputSchema: searchInputSchema,
|
|
58
|
+
}, async (rawInput) => {
|
|
59
|
+
const { packageName, query, topK, page } = searchSchema.parse(rawInput);
|
|
60
|
+
const packageKey = packageName.split('/').pop() ?? undefined;
|
|
61
|
+
try {
|
|
62
|
+
const pageData = await ragSearcher.search(query, topK, packageKey, page);
|
|
63
|
+
const { results, total, page: currentPage, pageSize, hasMore } = pageData;
|
|
64
|
+
if (results.length === 0) {
|
|
65
|
+
if (total === 0) {
|
|
66
|
+
return {
|
|
67
|
+
content: [
|
|
68
|
+
{
|
|
69
|
+
type: 'text',
|
|
70
|
+
text: 'No matching documentation found.',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
76
|
+
return {
|
|
77
|
+
content: [
|
|
78
|
+
{
|
|
79
|
+
type: 'text',
|
|
80
|
+
text: `No results on page ${currentPage}. Try a page value between 1 and ${totalPages}.`,
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
86
|
+
const startIndex = (currentPage - 1) * pageSize;
|
|
87
|
+
const headerLines = [
|
|
88
|
+
`Total matches: ${total}`,
|
|
89
|
+
`Page ${currentPage} of ${totalPages} (page size ${pageSize})`,
|
|
90
|
+
];
|
|
91
|
+
if (hasMore) {
|
|
92
|
+
headerLines.push('More results available. Increase the page value to continue.');
|
|
93
|
+
}
|
|
94
|
+
const formatted = results
|
|
95
|
+
.map((result, index) => formatSearchResult(startIndex + index, result))
|
|
96
|
+
.join('\n\n---\n\n');
|
|
97
|
+
const text = `${headerLines.join('\n')}\n\n${formatted}`;
|
|
98
|
+
return {
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: 'text',
|
|
102
|
+
text,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
const message = error instanceof Error ? error.message : 'Unknown search error';
|
|
109
|
+
return {
|
|
110
|
+
isError: true,
|
|
111
|
+
content: [
|
|
112
|
+
{
|
|
113
|
+
type: 'text',
|
|
114
|
+
text: `Failed to search documentation index: ${message}`,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
return server;
|
|
121
|
+
};
|
|
122
|
+
export const startDocsMcpServer = async () => {
|
|
123
|
+
const server = createDocsMcpServer();
|
|
124
|
+
const transport = new StdioServerTransport();
|
|
125
|
+
await server.connect(transport);
|
|
126
|
+
};
|
|
127
|
+
const invokedFromCli = () => {
|
|
128
|
+
const entry = process.argv[1];
|
|
129
|
+
if (!entry) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return import.meta.url === pathToFileURL(entry).href;
|
|
133
|
+
};
|
|
134
|
+
if (invokedFromCli()) {
|
|
135
|
+
startDocsMcpServer().catch((error) => {
|
|
136
|
+
console.error('Failed to start @ceed/docs-mcp server:', error);
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { pathToFileURL } from 'node:url';\n\nimport { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\n\nimport { createRagSearcher } from './rag/search.js';\nimport type { SearchResult } from './rag/types.js';\nimport { DOCS_MCP_VERSION } from './version.js';\n\nconst pingSchema = z.object({\n message: z.string().optional(),\n});\n\nconst pingInputSchema = pingSchema.shape;\n\nconst searchSchema = z.object({\n packageName: z.enum(['@ceed/ads', '@ceed/cds']).describe('Package to search (@ceed/ads or @ceed/cds).'),\n query: z.string().min(1, 'Query text is required.'),\n topK: z\n .number()\n .int()\n .min(1)\n .max(100)\n .optional()\n .describe('Number of matches to return per page (default: 5, max: 100).'),\n page: z.number().int().min(1).optional().describe('Results page to fetch (default: 1).'),\n});\n\nconst searchInputSchema = searchSchema.shape;\n\nconst formatSearchResult = (rank: number, result: SearchResult) => {\n const lines = [`#${rank + 1} (score: ${result.score.toFixed(3)})`, `source: ${result.metadata.source}`];\n\n if (result.metadata.heading) {\n lines.splice(1, 0, `heading: ${result.metadata.heading}`);\n }\n\n lines.push('', result.text.trim());\n\n return lines.join('\\n');\n};\n\nexport const createDocsMcpServer = () => {\n const server = new McpServer({\n name: '@ceed/docs-mcp',\n version: DOCS_MCP_VERSION,\n });\n\n const ragSearcher = createRagSearcher();\n\n server.registerTool(\n 'ping',\n {\n title: 'Ping',\n description: 'Return a simple response to verify the MCP server is healthy.',\n inputSchema: pingInputSchema,\n },\n async (rawInput) => {\n const { message } = pingSchema.parse(rawInput);\n\n return {\n content: [\n {\n type: 'text',\n text: message ?? 'pong',\n },\n ],\n };\n },\n );\n\n server.registerTool(\n 'search',\n {\n title: 'Search documentation',\n description: 'Look up documentation chunks that best match the given query.',\n inputSchema: searchInputSchema,\n },\n async (rawInput) => {\n const { packageName, query, topK, page } = searchSchema.parse(rawInput);\n const packageKey = packageName.split('/').pop() ?? undefined;\n\n try {\n const pageData = await ragSearcher.search(query, topK, packageKey, page);\n const { results, total, page: currentPage, pageSize, hasMore } = pageData;\n\n if (results.length === 0) {\n if (total === 0) {\n return {\n content: [\n {\n type: 'text',\n text: 'No matching documentation found.',\n },\n ],\n };\n }\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n\n return {\n content: [\n {\n type: 'text',\n text: `No results on page ${currentPage}. Try a page value between 1 and ${totalPages}.`,\n },\n ],\n };\n }\n\n const totalPages = Math.max(1, Math.ceil(total / pageSize));\n const startIndex = (currentPage - 1) * pageSize;\n const headerLines = [\n `Total matches: ${total}`,\n `Page ${currentPage} of ${totalPages} (page size ${pageSize})`,\n ];\n\n if (hasMore) {\n headerLines.push('More results available. Increase the page value to continue.');\n }\n\n const formatted = results\n .map((result, index) => formatSearchResult(startIndex + index, result))\n .join('\\n\\n---\\n\\n');\n\n const text = `${headerLines.join('\\n')}\\n\\n${formatted}`;\n\n return {\n content: [\n {\n type: 'text',\n text,\n },\n ],\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown search error';\n\n return {\n isError: true,\n content: [\n {\n type: 'text',\n text: `Failed to search documentation index: ${message}`,\n },\n ],\n };\n }\n },\n );\n\n return server;\n};\n\nexport const startDocsMcpServer = async () => {\n const server = createDocsMcpServer();\n const transport = new StdioServerTransport();\n\n await server.connect(transport);\n};\n\nconst invokedFromCli = () => {\n const entry = process.argv[1];\n\n if (!entry) {\n return false;\n }\n\n return import.meta.url === pathToFileURL(entry).href;\n};\n\nif (invokedFromCli()) {\n startDocsMcpServer().catch((error) => {\n console.error('Failed to start @ceed/docs-mcp server:', error);\n process.exitCode = 1;\n });\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const DOCS_MCP_VERSION: string;
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
const packageJsonUrl = new URL('../package.json', import.meta.url);
|
|
3
|
+
const packageJson = JSON.parse(readFileSync(packageJsonUrl, 'utf8'));
|
|
4
|
+
const version = typeof packageJson.version === 'string' ? packageJson.version : undefined;
|
|
5
|
+
if (!version) {
|
|
6
|
+
throw new Error('Package version is missing from package.json');
|
|
7
|
+
}
|
|
8
|
+
export const DOCS_MCP_VERSION = version;
|
|
9
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnE,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAElE,CAAC;AAEF,MAAM,OAAO,GAAG,OAAO,WAAW,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAE1F,IAAI,CAAC,OAAO,EAAE,CAAC;IACX,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC","sourcesContent":["import { readFileSync } from 'node:fs';\n\nconst packageJsonUrl = new URL('../package.json', import.meta.url);\nconst packageJson = JSON.parse(readFileSync(packageJsonUrl, 'utf8')) as {\n version?: unknown;\n};\n\nconst version = typeof packageJson.version === 'string' ? packageJson.version : undefined;\n\nif (!version) {\n throw new Error('Package version is missing from package.json');\n}\n\nexport const DOCS_MCP_VERSION = version;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ceed/docs-mcp",
|
|
3
|
+
"version": "1.0.0-next.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"description": "Documentation tooling for the MCP project",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"extract:docs": "rm -rf ./rag-source && node ../../scripts/generate-llms-txt.mjs --input ../ads/stories --output ./rag-source/ads --project-name \"@ceed/ads Component Guide\" && node ../../scripts/generate-llms-txt.mjs --input ../cds/stories --output ./rag-source/cds --project-name \"@ceed/cds Component Guide\"",
|
|
10
|
+
"build:index": "node dist/rag/build-index.js",
|
|
11
|
+
"build": "rm -rf ./dist && tsc -p tsconfig.build.json",
|
|
12
|
+
"dev": "yarn build && mcp-inspector node dist/server.js",
|
|
13
|
+
"lint": "oxlint && eslint \"./src/**/*.{ts,tsx}\"",
|
|
14
|
+
"lint:fix": "oxlint --fix && eslint --fix \"./src/**/*.{ts,tsx}\""
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"rag-index.json"
|
|
19
|
+
],
|
|
20
|
+
"bin": "dist/server.js",
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"import": "./dist/index.js",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"author": "Ecube Labs",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@ecubelabs/tsconfig": "^1.0.0",
|
|
32
|
+
"@modelcontextprotocol/inspector": "^0.16.7",
|
|
33
|
+
"@types/node": "^22.0.0",
|
|
34
|
+
"eslint": "^8.46.0",
|
|
35
|
+
"eslint-config-prettier": "^10.1.5",
|
|
36
|
+
"eslint-plugin-oxlint": "^1.9.0",
|
|
37
|
+
"eslint-plugin-prettier": "^5.5.1",
|
|
38
|
+
"oxlint": "^1.9.0",
|
|
39
|
+
"prettier": "^3.6.2",
|
|
40
|
+
"semantic-release": "^24.2.1",
|
|
41
|
+
"semantic-release-yarn": "^3.0.2",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
46
|
+
"@xenova/transformers": "^2.17.2",
|
|
47
|
+
"zod": "^3.0.0"
|
|
48
|
+
},
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git+ssh://git@github.com/Ecube-Labs/hds.git"
|
|
52
|
+
},
|
|
53
|
+
"packageManager": "yarn@4.1.0",
|
|
54
|
+
"stableVersion": "0.1.0"
|
|
55
|
+
}
|