docshark 0.1.5 → 0.1.7

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.
@@ -0,0 +1,35 @@
1
+ // src/tools/add-library.ts — Add new documentation source
2
+ import * as v from 'valibot';
3
+ import { tool } from 'tmcp/utils';
4
+ export function createAddLibraryTool(libraryService) {
5
+ return {
6
+ definition: {
7
+ name: 'add_library',
8
+ description: 'Add a new documentation library to be crawled and indexed for searching. ' +
9
+ 'Provide the documentation website URL and an optional name. ' +
10
+ 'The library will be crawled in the background. ' +
11
+ 'Use list_libraries to check crawl progress.',
12
+ schema: v.object({
13
+ url: v.pipe(v.string(), v.url(), v.description('The base URL of the documentation website to crawl.')),
14
+ name: v.optional(v.pipe(v.string(), v.description('A short identifier for the library (e.g., "svelte-5"). Auto-generated from URL if omitted.'))),
15
+ version: v.optional(v.pipe(v.string(), v.description('Version string (e.g., "5.0.0", "v4").'))),
16
+ max_depth: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(10), v.description('Maximum link depth to crawl. Default: 3.')), 3),
17
+ }),
18
+ },
19
+ handler: async ({ url, name, version, max_depth, }) => {
20
+ try {
21
+ const library = await libraryService.add({
22
+ url,
23
+ name,
24
+ version,
25
+ maxDepth: max_depth,
26
+ });
27
+ return tool.text(`✅ Library "${library.display_name}" added.\n` +
28
+ `Crawl job ${library.jobId} started. Use list_libraries to check progress.`);
29
+ }
30
+ catch (err) {
31
+ return tool.text(`❌ Failed to add library: ${err.message}`);
32
+ }
33
+ },
34
+ };
35
+ }
@@ -0,0 +1,25 @@
1
+ // src/tools/get-doc-page.ts — Full page retrieval
2
+ import * as v from 'valibot';
3
+ import { tool } from 'tmcp/utils';
4
+ export function createGetDocPageTool(db) {
5
+ return {
6
+ definition: {
7
+ name: 'get_doc_page',
8
+ description: 'Retrieve the complete content of a specific documentation page as markdown. ' +
9
+ 'Use this when search results reference a page and you need the full context, ' +
10
+ 'or when you know the exact page URL. Returns the entire page content.',
11
+ schema: v.object({
12
+ url: v.optional(v.pipe(v.string(), v.description('The full URL of the documentation page.'))),
13
+ library: v.optional(v.pipe(v.string(), v.description('Library name to search within.'))),
14
+ path: v.optional(v.pipe(v.string(), v.description('Relative path within the library (e.g., "/getting-started").'))),
15
+ }),
16
+ },
17
+ handler: async ({ url, library, path }) => {
18
+ const page = db.getPage({ url, library, path });
19
+ if (!page) {
20
+ return tool.text('Page not found. Use search_docs to find the correct page.');
21
+ }
22
+ return tool.text(`# ${page.title}\n**Source:** ${page.url}\n\n${page.content_markdown}`);
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,29 @@
1
+ // src/tools/list-libraries.ts — Discovery tool
2
+ import * as v from 'valibot';
3
+ import { tool } from 'tmcp/utils';
4
+ export function createListLibrariesTool(db) {
5
+ return {
6
+ definition: {
7
+ name: 'list_libraries',
8
+ description: 'List all documentation libraries currently indexed and available for searching. ' +
9
+ 'Use this to discover what documentation is available before running search_docs. ' +
10
+ 'Returns library names, URLs, page counts, and indexing status.',
11
+ schema: v.object({
12
+ status: v.optional(v.pipe(v.picklist(['indexed', 'crawling', 'error', 'all']), v.description('Filter by indexing status. Default: "all".')), 'all'),
13
+ }),
14
+ },
15
+ handler: async ({ status }) => {
16
+ const libraries = db.listLibraries(status);
17
+ if (libraries.length === 0) {
18
+ return tool.text('No libraries indexed yet. Use add_library to add a documentation website.');
19
+ }
20
+ let output = `## Indexed Libraries (${libraries.length} total)\n\n`;
21
+ output += '| Library | URL | Pages | Chunks | Status | Last Updated |\n';
22
+ output += '| ------- | --- | ----- | ------ | ------ | ------------ |\n';
23
+ for (const lib of libraries) {
24
+ output += `| ${lib.name} | ${lib.url} | ${lib.page_count} | ${lib.chunk_count} | ${lib.status} | ${lib.last_crawled_at || 'never'} |\n`;
25
+ }
26
+ return tool.text(output);
27
+ },
28
+ };
29
+ }
@@ -0,0 +1,25 @@
1
+ // src/tools/refresh-library.ts — Re-crawl existing library
2
+ import * as v from 'valibot';
3
+ import { tool } from 'tmcp/utils';
4
+ export function createRefreshLibraryTool(jobManager, db) {
5
+ return {
6
+ definition: {
7
+ name: 'refresh_library',
8
+ description: 'Re-crawl and re-index an existing documentation library to get the latest content. ' +
9
+ 'Use this when documentation may have been updated since it was last indexed. ' +
10
+ 'Only re-fetches pages that have changed (via HTTP ETags/Last-Modified).',
11
+ schema: v.object({
12
+ library: v.pipe(v.string(), v.description('The library name to refresh (e.g., "svelte-5").')),
13
+ }),
14
+ },
15
+ handler: async ({ library }) => {
16
+ const lib = db.getLibraryByName(library);
17
+ if (!lib) {
18
+ return tool.text(`Library "${library}" not found. Use list_libraries to see available libraries.`);
19
+ }
20
+ const job = jobManager.startCrawl(lib.id, { incremental: true });
21
+ return tool.text(`🔄 Refresh started for "${lib.display_name}".\n` +
22
+ `Job ${job.id}: checking for updated pages...`);
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,25 @@
1
+ // src/tools/remove-library.ts — Remove a library and all its data
2
+ import * as v from 'valibot';
3
+ import { tool } from 'tmcp/utils';
4
+ export function createRemoveLibraryTool(db) {
5
+ return {
6
+ definition: {
7
+ name: 'remove_library',
8
+ description: 'Remove a documentation library and all its indexed content. ' +
9
+ 'This permanently deletes the library, its pages, and search index. ' +
10
+ 'Use list_libraries first to confirm the library name.',
11
+ schema: v.object({
12
+ library: v.pipe(v.string(), v.description('The library name to remove (e.g., "svelte-5").')),
13
+ }),
14
+ },
15
+ handler: async ({ library }) => {
16
+ const lib = db.getLibraryByName(library);
17
+ if (!lib) {
18
+ return tool.text(`Library "${library}" not found.`);
19
+ }
20
+ db.removeLibrary(lib.id);
21
+ return tool.text(`🗑️ Library "${lib.display_name}" removed.\n` +
22
+ `Deleted ${lib.page_count} pages and ${lib.chunk_count} search chunks.`);
23
+ },
24
+ };
25
+ }
@@ -0,0 +1,35 @@
1
+ // src/tools/search-docs.ts — Primary search tool (80% of usage)
2
+ import * as v from 'valibot';
3
+ import { tool } from 'tmcp/utils';
4
+ export function createSearchDocsTool(searchEngine) {
5
+ return {
6
+ definition: {
7
+ name: 'search_docs',
8
+ description: 'Search through indexed documentation libraries for relevant information. ' +
9
+ 'Returns ranked documentation sections with code examples and source URLs. ' +
10
+ 'Use this when you need to find information about a library, framework, API, ' +
11
+ 'or any technical concept. You can optionally filter by a specific library name.',
12
+ schema: v.object({
13
+ query: v.pipe(v.string(), v.description('The search query. Use natural language or specific terms.')),
14
+ library: v.optional(v.pipe(v.string(), v.description('Filter results to a specific library name.'))),
15
+ limit: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1), v.maxValue(20), v.description('Max results to return. Default: 5.')), 5),
16
+ }),
17
+ },
18
+ handler: async ({ query, library, limit }) => {
19
+ const results = searchEngine.search(query, { library, limit });
20
+ if (results.length === 0) {
21
+ return tool.text(`No results found for "${query}".`);
22
+ }
23
+ const formatted = results
24
+ .map((r, i) => {
25
+ let block = `### ${i + 1}. ${r.page_title} — ${r.library_display_name}\n`;
26
+ block += `**Source:** ${r.page_url}\n`;
27
+ block += `**Section:** ${r.heading_context}\n\n`;
28
+ block += r.content;
29
+ return block;
30
+ })
31
+ .join('\n\n---\n\n');
32
+ return tool.text(`## Results for "${query}"\n\n${formatted}`);
33
+ },
34
+ };
35
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ // src/types.ts — Shared type definitions for DocShark
2
+ export {};
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.1.5";
1
+ export declare const VERSION = "0.1.7";
@@ -0,0 +1,2 @@
1
+ // This file is automatically updated by release-please
2
+ export const VERSION = '0.1.7'; // x-release-please-version
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docshark",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "🦈 Documentation MCP Server — scrape, index, and search any doc website",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -26,10 +26,14 @@
26
26
  "dev": "bun run --watch src/cli.ts start",
27
27
  "cli": "bun run src/cli.ts",
28
28
  "check": "tsc --noEmit",
29
- "build": "rm -rf dist && bun build ./src/cli.ts ./src/index.ts --outdir ./dist --target node --external '*' && tsc --emitDeclarationOnly",
29
+ "build": "rm -rf dist && tsc && chmod +x dist/cli.js",
30
30
  "prepublishOnly": "bun run build",
31
31
  "test:crawl": "bun run src/cli.ts add https://svelte.dev/docs/svelte/overview"
32
32
  },
33
+ "engines": {
34
+ "node": ">=20",
35
+ "bun": ">=1.1.0"
36
+ },
33
37
  "keywords": [
34
38
  "tmcp",
35
39
  "mcp",