Skip to content

Commit 31a4fe6

Browse files
committed
refactor: improve initialization and tools organization
- Add proper store initialization with await store.initialize() - Refactor tools into class-based implementations - Move CLI and server code into async main() functions - Add comprehensive error handling with proper cleanup - Group tools into a structured object for better organization - Make all async operations explicit with await - Remove legacy tool implementation files
1 parent 8a77206 commit 31a4fe6

File tree

10 files changed

+560
-471
lines changed

10 files changed

+560
-471
lines changed

src/cli.ts

Lines changed: 153 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,133 +4,171 @@ import { Command } from "commander";
44
import path from "node:path";
55
import os from "node:os";
66
import { VectorStoreService } from "./store/VectorStoreService.js";
7-
import { findVersion, listLibraries } from "./tools/library.js";
8-
import { search } from "./tools/search.js";
9-
import { scrape } from "./tools/scrape.js";
7+
import {
8+
FindVersionTool,
9+
ListLibrariesTool,
10+
ScrapeTool,
11+
SearchTool,
12+
} from "./tools";
1013

1114
const formatOutput = (data: unknown) => JSON.stringify(data, null, 2);
1215

13-
const program = new Command();
16+
async function main() {
17+
const store = new VectorStoreService();
1418

15-
// Initialize the store manager
16-
const store = new VectorStoreService();
19+
try {
20+
await store.initialize();
1721

18-
// Function to ensure store is shutdown before exiting
19-
const cleanupAndExit = async (error?: unknown) => {
20-
await store.shutdown();
21-
if (error) {
22-
console.error(
23-
"Error:",
24-
error instanceof Error ? error.message : String(error)
25-
);
26-
process.exit(1);
27-
}
28-
process.exit(0);
29-
};
22+
const tools = {
23+
listLibraries: new ListLibrariesTool(store),
24+
findVersion: new FindVersionTool(store),
25+
scrape: new ScrapeTool(store),
26+
search: new SearchTool(store),
27+
};
3028

31-
// Handle cleanup on SIGINT
32-
process.on("SIGINT", () => {
33-
cleanupAndExit();
34-
});
29+
const program = new Command();
3530

36-
program
37-
.name("docs-mcp")
38-
.description("CLI for managing documentation vector store")
39-
.version("1.0.0");
31+
// Handle cleanup on SIGINT
32+
process.on("SIGINT", async () => {
33+
await store.shutdown();
34+
process.exit(0);
35+
});
4036

41-
program
42-
.command("scrape <library> <version> <url>")
43-
.description("Scrape and index documentation from a URL")
44-
.option("-p, --max-pages <number>", "Maximum pages to scrape", "100")
45-
.option("-d, --max-depth <number>", "Maximum navigation depth", "3")
46-
.option(
47-
"--subpages-only",
48-
"Allow scraping pages outside the initial URL path",
49-
true
50-
)
51-
.action(async (library, version, url, options) => {
52-
try {
53-
const result = await scrape({
54-
storeService: store,
55-
url,
56-
library,
57-
version,
58-
options: {
59-
maxPages: Number.parseInt(options.maxPages),
60-
maxDepth: Number.parseInt(options.maxDepth),
61-
},
37+
program
38+
.name("docs-mcp")
39+
.description("CLI for managing documentation vector store")
40+
.version("1.0.0");
41+
42+
program
43+
.command("scrape <library> <version> <url>")
44+
.description("Scrape and index documentation from a URL")
45+
.option("-p, --max-pages <number>", "Maximum pages to scrape", "100")
46+
.option("-d, --max-depth <number>", "Maximum navigation depth", "3")
47+
.option(
48+
"--subpages-only",
49+
"Allow scraping pages outside the initial URL path",
50+
true
51+
)
52+
.action(async (library, version, url, options) => {
53+
try {
54+
const result = await tools.scrape.execute({
55+
url,
56+
library,
57+
version,
58+
options: {
59+
maxPages: Number.parseInt(options.maxPages),
60+
maxDepth: Number.parseInt(options.maxDepth),
61+
},
62+
});
63+
console.log(`✅ Successfully scraped ${result.pagesScraped} pages`);
64+
await store.shutdown();
65+
process.exit(0);
66+
} catch (error) {
67+
await store.shutdown();
68+
console.error(
69+
"Error:",
70+
error instanceof Error ? error.message : String(error)
71+
);
72+
process.exit(1);
73+
}
6274
});
63-
console.log(`✅ Successfully scraped ${result.pagesScraped} pages`);
64-
await cleanupAndExit();
65-
} catch (error) {
66-
await cleanupAndExit(error);
67-
}
68-
});
6975

70-
program
71-
.command("search <library> <version> <query>")
72-
.description(
73-
"Search documents in a library. Version matching examples:\n" +
74-
" - search react 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\n" +
75-
" - search react 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\n" +
76-
" - search typescript 5.x 'types' -> matches any TypeScript 5.x.x version\n" +
77-
" - search typescript 5.2.x 'types' -> matches any TypeScript 5.2.x version"
78-
)
79-
.option("-l, --limit <number>", "Maximum number of results", "5")
80-
.option(
81-
"-e, --exact-match",
82-
"Only use exact version match (e.g., '18.0.0' matches only 18.0.0, not 17.x.x) (default: false)",
83-
false
84-
)
85-
.action(async (library, version, query, options) => {
86-
try {
87-
const result = await search({
88-
library,
89-
version,
90-
query,
91-
limit: Number.parseInt(options.limit),
92-
exactMatch: options.exactMatch,
93-
storeService: store,
76+
program
77+
.command("search <library> <version> <query>")
78+
.description(
79+
"Search documents in a library. Version matching examples:\n" +
80+
" - search react 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\n" +
81+
" - search react 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\n" +
82+
" - search typescript 5.x 'types' -> matches any TypeScript 5.x.x version\n" +
83+
" - search typescript 5.2.x 'types' -> matches any TypeScript 5.2.x version"
84+
)
85+
.option("-l, --limit <number>", "Maximum number of results", "5")
86+
.option(
87+
"-e, --exact-match",
88+
"Only use exact version match (e.g., '18.0.0' matches only 18.0.0, not 17.x.x) (default: false)",
89+
false
90+
)
91+
.action(async (library, version, query, options) => {
92+
try {
93+
const result = await tools.search.execute({
94+
library,
95+
version,
96+
query,
97+
limit: Number.parseInt(options.limit),
98+
exactMatch: options.exactMatch,
99+
});
100+
console.log(formatOutput(result.results));
101+
await store.shutdown();
102+
process.exit(0);
103+
} catch (error) {
104+
await store.shutdown();
105+
console.error(
106+
"Error:",
107+
error instanceof Error ? error.message : String(error)
108+
);
109+
process.exit(1);
110+
}
94111
});
95-
console.log(formatOutput(result.results));
96-
await cleanupAndExit();
97-
} catch (error) {
98-
await cleanupAndExit(error);
99-
}
100-
});
101112

102-
program
103-
.command("list-libraries")
104-
.description("List all available libraries and their versions")
105-
.action(async () => {
106-
try {
107-
const result = await listLibraries({ storeService: store });
108-
console.log(formatOutput(result.libraries));
109-
await cleanupAndExit();
110-
} catch (error) {
111-
await cleanupAndExit(error);
112-
}
113-
});
113+
program
114+
.command("list-libraries")
115+
.description("List all available libraries and their versions")
116+
.action(async () => {
117+
try {
118+
const result = await tools.listLibraries.execute();
119+
console.log(formatOutput(result.libraries));
120+
await store.shutdown();
121+
process.exit(0);
122+
} catch (error) {
123+
await store.shutdown();
124+
console.error(
125+
"Error:",
126+
error instanceof Error ? error.message : String(error)
127+
);
128+
process.exit(1);
129+
}
130+
});
114131

115-
program
116-
.command("find-version <library> [targetVersion]")
117-
.description("Find the best matching version for a library")
118-
.action(async (library, targetVersion) => {
119-
try {
120-
const version = await findVersion({
121-
storeService: store,
122-
library,
123-
targetVersion,
132+
program
133+
.command("find-version <library> [targetVersion]")
134+
.description("Find the best matching version for a library")
135+
.action(async (library, targetVersion) => {
136+
try {
137+
const version = await tools.findVersion.execute({
138+
library,
139+
targetVersion,
140+
});
141+
if (version) {
142+
console.log(version);
143+
await store.shutdown();
144+
process.exit(0);
145+
} else {
146+
await store.shutdown();
147+
console.error("Error: No matching version found");
148+
process.exit(1);
149+
}
150+
} catch (error) {
151+
await store.shutdown();
152+
console.error(
153+
"Error:",
154+
error instanceof Error ? error.message : String(error)
155+
);
156+
process.exit(1);
157+
}
124158
});
125-
if (version) {
126-
console.log(version);
127-
await cleanupAndExit();
128-
} else {
129-
await cleanupAndExit("No matching version found");
130-
}
131-
} catch (error) {
132-
await cleanupAndExit(error);
133-
}
134-
});
135159

136-
program.parse();
160+
await program.parseAsync();
161+
} catch (error) {
162+
await store.shutdown();
163+
console.error(
164+
"Error:",
165+
error instanceof Error ? error.message : String(error)
166+
);
167+
process.exit(1);
168+
}
169+
}
170+
171+
main().catch((error) => {
172+
console.error("Fatal error:", error);
173+
process.exit(1);
174+
});

0 commit comments

Comments
 (0)