@telvok/librarian-mcp 1.5.4 → 2.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/dist/library/errors.d.ts +48 -0
- package/dist/library/errors.js +80 -0
- package/dist/library/schemas.d.ts +6 -6
- package/dist/library/storage.d.ts +2 -2
- package/dist/library/storage.js +2 -2
- package/dist/library 2/embeddings.d.ts +21 -0
- package/dist/library 2/embeddings.js +86 -0
- package/dist/library 2/manager.d.ts +42 -0
- package/dist/library 2/manager.js +218 -0
- package/dist/library 2/parsers/cursor.d.ts +15 -0
- package/dist/library 2/parsers/cursor.js +168 -0
- package/dist/library 2/parsers/index.d.ts +6 -0
- package/dist/library 2/parsers/index.js +5 -0
- package/dist/library 2/parsers/json.d.ts +11 -0
- package/dist/library 2/parsers/json.js +95 -0
- package/dist/library 2/parsers/jsonl.d.ts +14 -0
- package/dist/library 2/parsers/jsonl.js +85 -0
- package/dist/library 2/parsers/markdown.d.ts +15 -0
- package/dist/library 2/parsers/markdown.js +77 -0
- package/dist/library 2/parsers/sqlite.d.ts +8 -0
- package/dist/library 2/parsers/sqlite.js +123 -0
- package/dist/library 2/parsers/types.d.ts +21 -0
- package/dist/library 2/parsers/types.js +4 -0
- package/dist/library 2/query.d.ts +26 -0
- package/dist/library 2/query.js +104 -0
- package/dist/library 2/schemas.d.ts +324 -0
- package/dist/library 2/schemas.js +79 -0
- package/dist/library 2/storage.d.ts +22 -0
- package/dist/library 2/storage.js +36 -0
- package/dist/library 2/vector-index.d.ts +55 -0
- package/dist/library 2/vector-index.js +160 -0
- package/dist/server 2.js +199 -0
- package/dist/server.d 2.ts +2 -0
- package/dist/server.js +102 -54
- package/dist/tools/adopt.d.ts +1 -0
- package/dist/tools/adopt.js +37 -10
- package/dist/tools/auth.d.ts +69 -0
- package/dist/tools/auth.js +379 -0
- package/dist/tools/bounty-claim.d.ts +28 -0
- package/dist/tools/bounty-claim.js +92 -0
- package/dist/tools/bounty-create.d.ts +47 -0
- package/dist/tools/bounty-create.js +118 -0
- package/dist/tools/bounty-list.d.ts +50 -0
- package/dist/tools/bounty-list.js +116 -0
- package/dist/tools/bounty-submit.d.ts +34 -0
- package/dist/tools/bounty-submit.js +94 -0
- package/dist/tools/brief.d.ts +94 -0
- package/dist/tools/brief.js +234 -15
- package/dist/tools/delete.d.ts +87 -0
- package/dist/tools/delete.js +266 -0
- package/dist/tools/feedback.d.ts +27 -0
- package/dist/tools/feedback.js +98 -0
- package/dist/tools/help.d.ts +22 -0
- package/dist/tools/help.js +482 -0
- package/dist/tools/import-memories.d.ts +1 -0
- package/dist/tools/import-memories.js +18 -13
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +12 -0
- package/dist/tools/library-buy.d.ts +31 -0
- package/dist/tools/library-buy.js +104 -0
- package/dist/tools/library-download.d.ts +27 -0
- package/dist/tools/library-download.js +177 -0
- package/dist/tools/library-publish.d.ts +112 -0
- package/dist/tools/library-publish.js +387 -0
- package/dist/tools/library-search.d.ts +110 -0
- package/dist/tools/library-search.js +132 -0
- package/dist/tools/mark-hit.d.ts +1 -0
- package/dist/tools/mark-hit.js +83 -5
- package/dist/tools/my-books.d.ts +51 -0
- package/dist/tools/my-books.js +115 -0
- package/dist/tools/my-bounties.d.ts +43 -0
- package/dist/tools/my-bounties.js +126 -0
- package/dist/tools/rate-book.d.ts +40 -0
- package/dist/tools/rate-book.js +147 -0
- package/dist/tools/rebuild-index.d.ts +1 -0
- package/dist/tools/rebuild-index.js +40 -8
- package/dist/tools/record.d.ts +18 -0
- package/dist/tools/record.js +30 -26
- package/dist/tools/seller-analytics.d.ts +53 -0
- package/dist/tools/seller-analytics.js +180 -0
- package/dist/tools/sync.d.ts +55 -0
- package/dist/tools/sync.js +304 -0
- package/dist/tools/unsubscribe.d.ts +48 -0
- package/dist/tools/unsubscribe.js +120 -0
- package/dist/tools 2/adopt.d.ts +24 -0
- package/dist/tools 2/adopt.js +154 -0
- package/dist/tools 2/auth.d.ts +35 -0
- package/dist/tools 2/auth.js +229 -0
- package/dist/tools 2/brief.d.ts +56 -0
- package/dist/tools 2/brief.js +414 -0
- package/dist/tools 2/help.d.ts +21 -0
- package/dist/tools 2/help.js +267 -0
- package/dist/tools 2/import-memories.d.ts +32 -0
- package/dist/tools 2/import-memories.js +231 -0
- package/dist/tools 2/index.d.ts +12 -0
- package/dist/tools 2/index.js +12 -0
- package/dist/tools 2/mark-hit.d.ts +20 -0
- package/dist/tools 2/mark-hit.js +71 -0
- package/dist/tools 2/marketplace-buy.d.ts +30 -0
- package/dist/tools 2/marketplace-buy.js +97 -0
- package/dist/tools 2/marketplace-download.d.ts +26 -0
- package/dist/tools 2/marketplace-download.js +160 -0
- package/dist/tools 2/marketplace-publish.d.ts +111 -0
- package/dist/tools 2/marketplace-publish.js +377 -0
- package/dist/tools 2/marketplace-search.d.ts +57 -0
- package/dist/tools 2/marketplace-search.js +96 -0
- package/dist/tools 2/my-books.d.ts +50 -0
- package/dist/tools 2/my-books.js +107 -0
- package/dist/tools 2/rate-book.d.ts +39 -0
- package/dist/tools 2/rate-book.js +139 -0
- package/dist/tools 2/rebuild-index.d.ts +23 -0
- package/dist/tools 2/rebuild-index.js +107 -0
- package/dist/tools 2/record.d.ts +40 -0
- package/dist/tools 2/record.js +205 -0
- package/dist/tools 2/seller-analytics.d.ts +35 -0
- package/dist/tools 2/seller-analytics.js +102 -0
- package/dist/tools 2/sync.d.ts +54 -0
- package/dist/tools 2/sync.js +298 -0
- package/package.json +1 -1
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { getLibraryPath } from './storage.js';
|
|
4
|
+
import { getEmbedding, chunkText, cosineSimilarity, EMBEDDING_MODEL_ID } from './embeddings.js';
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Constants
|
|
7
|
+
// ============================================================================
|
|
8
|
+
const INDEX_FILENAME = 'index.json';
|
|
9
|
+
const CURRENT_VERSION = 1;
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Index File Operations
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Get path to the index file.
|
|
15
|
+
*/
|
|
16
|
+
function getIndexPath() {
|
|
17
|
+
return path.join(getLibraryPath(), INDEX_FILENAME);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Load the vector index from disk.
|
|
21
|
+
* Returns empty index if file doesn't exist or is invalid.
|
|
22
|
+
*/
|
|
23
|
+
export async function loadIndex() {
|
|
24
|
+
const indexPath = getIndexPath();
|
|
25
|
+
try {
|
|
26
|
+
const data = await fs.readFile(indexPath, 'utf-8');
|
|
27
|
+
const index = JSON.parse(data);
|
|
28
|
+
// Validate structure
|
|
29
|
+
if (!index.version || !Array.isArray(index.entries)) {
|
|
30
|
+
return createEmptyIndex();
|
|
31
|
+
}
|
|
32
|
+
return index;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// File doesn't exist or is invalid
|
|
36
|
+
return createEmptyIndex();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Save the vector index to disk.
|
|
41
|
+
*/
|
|
42
|
+
export async function saveIndex(index) {
|
|
43
|
+
const indexPath = getIndexPath();
|
|
44
|
+
// Update metadata
|
|
45
|
+
index.rebuilt = new Date().toISOString();
|
|
46
|
+
index.modelId = EMBEDDING_MODEL_ID;
|
|
47
|
+
// Ensure directory exists
|
|
48
|
+
await fs.mkdir(path.dirname(indexPath), { recursive: true });
|
|
49
|
+
// Write atomically by writing to temp file first
|
|
50
|
+
const tempPath = indexPath + '.tmp';
|
|
51
|
+
await fs.writeFile(tempPath, JSON.stringify(index, null, 2), 'utf-8');
|
|
52
|
+
await fs.rename(tempPath, indexPath);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create an empty index.
|
|
56
|
+
*/
|
|
57
|
+
function createEmptyIndex() {
|
|
58
|
+
return {
|
|
59
|
+
version: CURRENT_VERSION,
|
|
60
|
+
rebuilt: '',
|
|
61
|
+
modelId: EMBEDDING_MODEL_ID,
|
|
62
|
+
entries: [],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// Index Operations
|
|
67
|
+
// ============================================================================
|
|
68
|
+
/**
|
|
69
|
+
* Add or update an entry in the index.
|
|
70
|
+
* Chunks the content and generates embeddings for each chunk.
|
|
71
|
+
*/
|
|
72
|
+
export async function addToIndex(index, entryPath, title, content) {
|
|
73
|
+
// Remove any existing entries for this path
|
|
74
|
+
index.entries = index.entries.filter(e => e.path !== entryPath);
|
|
75
|
+
// Chunk the content
|
|
76
|
+
const chunks = chunkText(content);
|
|
77
|
+
// Generate embeddings for each chunk
|
|
78
|
+
for (let i = 0; i < chunks.length; i++) {
|
|
79
|
+
const chunk = chunks[i];
|
|
80
|
+
try {
|
|
81
|
+
const embedding = await getEmbedding(chunk);
|
|
82
|
+
index.entries.push({
|
|
83
|
+
path: entryPath,
|
|
84
|
+
title,
|
|
85
|
+
embedding,
|
|
86
|
+
chunk: i,
|
|
87
|
+
preview: chunk.slice(0, 100) + (chunk.length > 100 ? '...' : ''),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
// Log but don't fail - entry will still be searchable via keywords
|
|
92
|
+
console.error(`Failed to embed chunk ${i} for ${entryPath}:`, error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Remove an entry from the index.
|
|
98
|
+
*/
|
|
99
|
+
export function removeFromIndex(index, entryPath) {
|
|
100
|
+
index.entries = index.entries.filter(e => e.path !== entryPath);
|
|
101
|
+
}
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Semantic Search
|
|
104
|
+
// ============================================================================
|
|
105
|
+
/**
|
|
106
|
+
* Search the index for entries semantically similar to the query.
|
|
107
|
+
* Returns paths ranked by similarity, deduped to best chunk per entry.
|
|
108
|
+
*/
|
|
109
|
+
export async function semanticSearch(index, query, limit = 5) {
|
|
110
|
+
if (index.entries.length === 0) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
// Get query embedding
|
|
114
|
+
const queryEmbedding = await getEmbedding(query);
|
|
115
|
+
// Score all entries
|
|
116
|
+
const scored = index.entries.map(entry => ({
|
|
117
|
+
...entry,
|
|
118
|
+
similarity: cosineSimilarity(queryEmbedding, entry.embedding),
|
|
119
|
+
}));
|
|
120
|
+
// Dedupe by path - keep the chunk with highest similarity
|
|
121
|
+
const byPath = new Map();
|
|
122
|
+
for (const entry of scored) {
|
|
123
|
+
const existing = byPath.get(entry.path);
|
|
124
|
+
if (!existing || entry.similarity > existing.similarity) {
|
|
125
|
+
byPath.set(entry.path, entry);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Sort by similarity descending and apply limit
|
|
129
|
+
const results = [...byPath.values()]
|
|
130
|
+
.sort((a, b) => b.similarity - a.similarity)
|
|
131
|
+
.slice(0, limit)
|
|
132
|
+
.map(entry => ({
|
|
133
|
+
path: entry.path,
|
|
134
|
+
title: entry.title,
|
|
135
|
+
similarity: entry.similarity,
|
|
136
|
+
preview: entry.preview,
|
|
137
|
+
}));
|
|
138
|
+
return results;
|
|
139
|
+
}
|
|
140
|
+
// ============================================================================
|
|
141
|
+
// Index Health
|
|
142
|
+
// ============================================================================
|
|
143
|
+
/**
|
|
144
|
+
* Check if the index might be stale (model changed).
|
|
145
|
+
*/
|
|
146
|
+
export function isIndexStale(index) {
|
|
147
|
+
return index.modelId !== EMBEDDING_MODEL_ID;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Get index statistics.
|
|
151
|
+
*/
|
|
152
|
+
export function getIndexStats(index) {
|
|
153
|
+
const uniquePaths = new Set(index.entries.map(e => e.path));
|
|
154
|
+
return {
|
|
155
|
+
entryCount: uniquePaths.size,
|
|
156
|
+
chunkCount: index.entries.length,
|
|
157
|
+
modelId: index.modelId,
|
|
158
|
+
rebuilt: index.rebuilt,
|
|
159
|
+
};
|
|
160
|
+
}
|
package/dist/server 2.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { briefTool } from './tools/brief.js';
|
|
6
|
+
import { recordTool } from './tools/record.js';
|
|
7
|
+
import { adoptTool } from './tools/adopt.js';
|
|
8
|
+
import { markHitTool } from './tools/mark-hit.js';
|
|
9
|
+
import { importMemoriesTool } from './tools/import-memories.js';
|
|
10
|
+
import { rebuildIndexTool } from './tools/rebuild-index.js';
|
|
11
|
+
import { authTool } from './tools/auth.js';
|
|
12
|
+
import { marketplaceSearchTool } from './tools/marketplace-search.js';
|
|
13
|
+
import { marketplaceBuyTool } from './tools/marketplace-buy.js';
|
|
14
|
+
import { marketplaceDownloadTool } from './tools/marketplace-download.js';
|
|
15
|
+
import { marketplacePublishTool } from './tools/marketplace-publish.js';
|
|
16
|
+
import { myBooksTool } from './tools/my-books.js';
|
|
17
|
+
import { syncTool } from './tools/sync.js';
|
|
18
|
+
import { sellerAnalyticsTool } from './tools/seller-analytics.js';
|
|
19
|
+
import { rateBookTool } from './tools/rate-book.js';
|
|
20
|
+
import { helpTool } from './tools/help.js';
|
|
21
|
+
const server = new Server({
|
|
22
|
+
name: 'librarian',
|
|
23
|
+
version: '1.0.0',
|
|
24
|
+
}, {
|
|
25
|
+
capabilities: {
|
|
26
|
+
tools: {},
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
// List available tools
|
|
30
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
31
|
+
return {
|
|
32
|
+
tools: [
|
|
33
|
+
{
|
|
34
|
+
name: briefTool.name,
|
|
35
|
+
description: briefTool.description,
|
|
36
|
+
inputSchema: briefTool.inputSchema,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: recordTool.name,
|
|
40
|
+
description: recordTool.description,
|
|
41
|
+
inputSchema: recordTool.inputSchema,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: adoptTool.name,
|
|
45
|
+
description: adoptTool.description,
|
|
46
|
+
inputSchema: adoptTool.inputSchema,
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: markHitTool.name,
|
|
50
|
+
description: markHitTool.description,
|
|
51
|
+
inputSchema: markHitTool.inputSchema,
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: importMemoriesTool.name,
|
|
55
|
+
description: importMemoriesTool.description,
|
|
56
|
+
inputSchema: importMemoriesTool.inputSchema,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: rebuildIndexTool.name,
|
|
60
|
+
description: rebuildIndexTool.description,
|
|
61
|
+
inputSchema: rebuildIndexTool.inputSchema,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: authTool.name,
|
|
65
|
+
description: authTool.description,
|
|
66
|
+
inputSchema: authTool.inputSchema,
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: marketplaceSearchTool.name,
|
|
70
|
+
description: marketplaceSearchTool.description,
|
|
71
|
+
inputSchema: marketplaceSearchTool.inputSchema,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: marketplaceBuyTool.name,
|
|
75
|
+
description: marketplaceBuyTool.description,
|
|
76
|
+
inputSchema: marketplaceBuyTool.inputSchema,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
name: marketplaceDownloadTool.name,
|
|
80
|
+
description: marketplaceDownloadTool.description,
|
|
81
|
+
inputSchema: marketplaceDownloadTool.inputSchema,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: marketplacePublishTool.name,
|
|
85
|
+
description: marketplacePublishTool.description,
|
|
86
|
+
inputSchema: marketplacePublishTool.inputSchema,
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: myBooksTool.name,
|
|
90
|
+
description: myBooksTool.description,
|
|
91
|
+
inputSchema: myBooksTool.inputSchema,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: syncTool.name,
|
|
95
|
+
description: syncTool.description,
|
|
96
|
+
inputSchema: syncTool.inputSchema,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: sellerAnalyticsTool.name,
|
|
100
|
+
description: sellerAnalyticsTool.description,
|
|
101
|
+
inputSchema: sellerAnalyticsTool.inputSchema,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: rateBookTool.name,
|
|
105
|
+
description: rateBookTool.description,
|
|
106
|
+
inputSchema: rateBookTool.inputSchema,
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
name: helpTool.name,
|
|
110
|
+
description: helpTool.description,
|
|
111
|
+
inputSchema: helpTool.inputSchema,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
// Handle tool calls
|
|
117
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
118
|
+
const { name, arguments: args } = request.params;
|
|
119
|
+
try {
|
|
120
|
+
let result;
|
|
121
|
+
switch (name) {
|
|
122
|
+
case 'brief':
|
|
123
|
+
result = await briefTool.handler(args);
|
|
124
|
+
break;
|
|
125
|
+
case 'record':
|
|
126
|
+
result = await recordTool.handler(args);
|
|
127
|
+
break;
|
|
128
|
+
case 'adopt':
|
|
129
|
+
result = await adoptTool.handler(args);
|
|
130
|
+
break;
|
|
131
|
+
case 'mark_hit':
|
|
132
|
+
result = await markHitTool.handler(args);
|
|
133
|
+
break;
|
|
134
|
+
case 'import_memories':
|
|
135
|
+
result = await importMemoriesTool.handler(args);
|
|
136
|
+
break;
|
|
137
|
+
case 'rebuild_index':
|
|
138
|
+
result = await rebuildIndexTool.handler(args);
|
|
139
|
+
break;
|
|
140
|
+
case 'auth':
|
|
141
|
+
result = await authTool.handler(args);
|
|
142
|
+
break;
|
|
143
|
+
case 'marketplace_search':
|
|
144
|
+
result = await marketplaceSearchTool.handler(args);
|
|
145
|
+
break;
|
|
146
|
+
case 'marketplace_buy':
|
|
147
|
+
result = await marketplaceBuyTool.handler(args);
|
|
148
|
+
break;
|
|
149
|
+
case 'marketplace_download':
|
|
150
|
+
result = await marketplaceDownloadTool.handler(args);
|
|
151
|
+
break;
|
|
152
|
+
case 'marketplace_publish':
|
|
153
|
+
result = await marketplacePublishTool.handler(args);
|
|
154
|
+
break;
|
|
155
|
+
case 'my_books':
|
|
156
|
+
result = await myBooksTool.handler(args);
|
|
157
|
+
break;
|
|
158
|
+
case 'sync':
|
|
159
|
+
result = await syncTool.handler(args);
|
|
160
|
+
break;
|
|
161
|
+
case 'seller_analytics':
|
|
162
|
+
result = await sellerAnalyticsTool.handler();
|
|
163
|
+
break;
|
|
164
|
+
case 'rate_book':
|
|
165
|
+
result = await rateBookTool.handler(args);
|
|
166
|
+
break;
|
|
167
|
+
case 'help':
|
|
168
|
+
result = await helpTool.handler(args);
|
|
169
|
+
break;
|
|
170
|
+
default:
|
|
171
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
type: 'text',
|
|
177
|
+
text: JSON.stringify(result, null, 2),
|
|
178
|
+
},
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
184
|
+
return {
|
|
185
|
+
content: [
|
|
186
|
+
{
|
|
187
|
+
type: 'text',
|
|
188
|
+
text: JSON.stringify({ error: message }),
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
isError: true,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
async function main() {
|
|
196
|
+
const transport = new StdioServerTransport();
|
|
197
|
+
await server.connect(transport);
|
|
198
|
+
}
|
|
199
|
+
main().catch(console.error);
|
package/dist/server.js
CHANGED
|
@@ -2,12 +2,85 @@
|
|
|
2
2
|
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { LibrarianError } from './library/errors.js';
|
|
5
6
|
import { briefTool } from './tools/brief.js';
|
|
6
7
|
import { recordTool } from './tools/record.js';
|
|
7
8
|
import { adoptTool } from './tools/adopt.js';
|
|
8
9
|
import { markHitTool } from './tools/mark-hit.js';
|
|
9
10
|
import { importMemoriesTool } from './tools/import-memories.js';
|
|
10
11
|
import { rebuildIndexTool } from './tools/rebuild-index.js';
|
|
12
|
+
import { authTool } from './tools/auth.js';
|
|
13
|
+
import { librarySearchTool } from './tools/library-search.js';
|
|
14
|
+
import { libraryBuyTool } from './tools/library-buy.js';
|
|
15
|
+
import { libraryDownloadTool } from './tools/library-download.js';
|
|
16
|
+
import { libraryPublishTool } from './tools/library-publish.js';
|
|
17
|
+
import { myBooksTool } from './tools/my-books.js';
|
|
18
|
+
import { syncTool } from './tools/sync.js';
|
|
19
|
+
import { sellerAnalyticsTool } from './tools/seller-analytics.js';
|
|
20
|
+
import { rateBookTool } from './tools/rate-book.js';
|
|
21
|
+
import { helpTool } from './tools/help.js';
|
|
22
|
+
import { feedbackTool } from './tools/feedback.js';
|
|
23
|
+
import { bountyCreateTool } from './tools/bounty-create.js';
|
|
24
|
+
import { bountyListTool } from './tools/bounty-list.js';
|
|
25
|
+
import { bountyClaimTool } from './tools/bounty-claim.js';
|
|
26
|
+
import { bountySubmitTool } from './tools/bounty-submit.js';
|
|
27
|
+
import { myBountiesTool } from './tools/my-bounties.js';
|
|
28
|
+
import { deleteTool } from './tools/delete.js';
|
|
29
|
+
import { unsubscribeTool } from './tools/unsubscribe.js';
|
|
30
|
+
const allTools = [
|
|
31
|
+
// Core tools — local knowledge management
|
|
32
|
+
{ tool: briefTool, group: 'core' },
|
|
33
|
+
{ tool: recordTool, group: 'core' },
|
|
34
|
+
{ tool: adoptTool, group: 'core' },
|
|
35
|
+
{ tool: markHitTool, group: 'core' },
|
|
36
|
+
{ tool: importMemoriesTool, group: 'core' },
|
|
37
|
+
{ tool: rebuildIndexTool, group: 'core' },
|
|
38
|
+
{ tool: deleteTool, group: 'core' },
|
|
39
|
+
// Marketplace tools — cloud features
|
|
40
|
+
{ tool: librarySearchTool, group: 'marketplace' },
|
|
41
|
+
{ tool: libraryBuyTool, group: 'marketplace' },
|
|
42
|
+
{ tool: libraryDownloadTool, group: 'marketplace' },
|
|
43
|
+
{ tool: libraryPublishTool, group: 'marketplace' },
|
|
44
|
+
{ tool: myBooksTool, group: 'marketplace' },
|
|
45
|
+
{ tool: syncTool, group: 'marketplace' },
|
|
46
|
+
{ tool: sellerAnalyticsTool, group: 'marketplace' },
|
|
47
|
+
{ tool: rateBookTool, group: 'marketplace' },
|
|
48
|
+
{ tool: bountyCreateTool, group: 'marketplace' },
|
|
49
|
+
{ tool: bountyListTool, group: 'marketplace' },
|
|
50
|
+
{ tool: bountyClaimTool, group: 'marketplace' },
|
|
51
|
+
{ tool: bountySubmitTool, group: 'marketplace' },
|
|
52
|
+
{ tool: myBountiesTool, group: 'marketplace' },
|
|
53
|
+
{ tool: unsubscribeTool, group: 'marketplace' },
|
|
54
|
+
// Both — shared across core and marketplace
|
|
55
|
+
{ tool: authTool, group: 'both' },
|
|
56
|
+
{ tool: helpTool, group: 'both' },
|
|
57
|
+
{ tool: feedbackTool, group: 'both' },
|
|
58
|
+
];
|
|
59
|
+
function parseServerMode() {
|
|
60
|
+
const serverArg = process.argv.find(a => a.startsWith('--server='));
|
|
61
|
+
if (!serverArg)
|
|
62
|
+
return 'all';
|
|
63
|
+
const mode = serverArg.split('=')[1];
|
|
64
|
+
if (mode === 'core' || mode === 'marketplace')
|
|
65
|
+
return mode;
|
|
66
|
+
console.error(`Unknown --server mode: "${mode}". Valid: core, marketplace. Defaulting to all.`);
|
|
67
|
+
return 'all';
|
|
68
|
+
}
|
|
69
|
+
const serverMode = parseServerMode();
|
|
70
|
+
function isToolIncluded(entry) {
|
|
71
|
+
if (serverMode === 'all')
|
|
72
|
+
return true;
|
|
73
|
+
return entry.group === serverMode || entry.group === 'both';
|
|
74
|
+
}
|
|
75
|
+
const enabledTools = allTools.filter(isToolIncluded);
|
|
76
|
+
// Build lookup map for call handler
|
|
77
|
+
const toolMap = new Map();
|
|
78
|
+
for (const entry of enabledTools) {
|
|
79
|
+
toolMap.set(entry.tool.name, entry.tool);
|
|
80
|
+
}
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
// MCP Server
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
11
84
|
const server = new Server({
|
|
12
85
|
name: 'librarian',
|
|
13
86
|
version: '1.0.0',
|
|
@@ -19,67 +92,29 @@ const server = new Server({
|
|
|
19
92
|
// List available tools
|
|
20
93
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
21
94
|
return {
|
|
22
|
-
tools:
|
|
23
|
-
{
|
|
24
|
-
name:
|
|
25
|
-
description:
|
|
26
|
-
inputSchema:
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
name: adoptTool.name,
|
|
35
|
-
description: adoptTool.description,
|
|
36
|
-
inputSchema: adoptTool.inputSchema,
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: markHitTool.name,
|
|
40
|
-
description: markHitTool.description,
|
|
41
|
-
inputSchema: markHitTool.inputSchema,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
name: importMemoriesTool.name,
|
|
45
|
-
description: importMemoriesTool.description,
|
|
46
|
-
inputSchema: importMemoriesTool.inputSchema,
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
name: rebuildIndexTool.name,
|
|
50
|
-
description: rebuildIndexTool.description,
|
|
51
|
-
inputSchema: rebuildIndexTool.inputSchema,
|
|
52
|
-
},
|
|
53
|
-
],
|
|
95
|
+
tools: enabledTools.map(({ tool }) => {
|
|
96
|
+
const entry = {
|
|
97
|
+
name: tool.name,
|
|
98
|
+
description: tool.description,
|
|
99
|
+
inputSchema: tool.inputSchema,
|
|
100
|
+
};
|
|
101
|
+
if (tool.title)
|
|
102
|
+
entry.title = tool.title;
|
|
103
|
+
if (tool.outputSchema)
|
|
104
|
+
entry.outputSchema = tool.outputSchema;
|
|
105
|
+
return entry;
|
|
106
|
+
}),
|
|
54
107
|
};
|
|
55
108
|
});
|
|
56
109
|
// Handle tool calls
|
|
57
110
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
58
111
|
const { name, arguments: args } = request.params;
|
|
59
112
|
try {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
result = await briefTool.handler(args);
|
|
64
|
-
break;
|
|
65
|
-
case 'record':
|
|
66
|
-
result = await recordTool.handler(args);
|
|
67
|
-
break;
|
|
68
|
-
case 'adopt':
|
|
69
|
-
result = await adoptTool.handler(args);
|
|
70
|
-
break;
|
|
71
|
-
case 'mark_hit':
|
|
72
|
-
result = await markHitTool.handler(args);
|
|
73
|
-
break;
|
|
74
|
-
case 'import_memories':
|
|
75
|
-
result = await importMemoriesTool.handler(args);
|
|
76
|
-
break;
|
|
77
|
-
case 'rebuild_index':
|
|
78
|
-
result = await rebuildIndexTool.handler(args);
|
|
79
|
-
break;
|
|
80
|
-
default:
|
|
81
|
-
throw new Error(`Unknown tool: ${name}`);
|
|
113
|
+
const tool = toolMap.get(name);
|
|
114
|
+
if (!tool) {
|
|
115
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
82
116
|
}
|
|
117
|
+
const result = await tool.handler(args);
|
|
83
118
|
return {
|
|
84
119
|
content: [
|
|
85
120
|
{
|
|
@@ -90,6 +125,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
90
125
|
};
|
|
91
126
|
}
|
|
92
127
|
catch (error) {
|
|
128
|
+
// Handle LibrarianError with error codes
|
|
129
|
+
if (error instanceof LibrarianError) {
|
|
130
|
+
return {
|
|
131
|
+
content: [
|
|
132
|
+
{
|
|
133
|
+
type: 'text',
|
|
134
|
+
text: JSON.stringify(error.toJSON()),
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
isError: true,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// Handle generic errors
|
|
93
141
|
const message = error instanceof Error ? error.message : String(error);
|
|
94
142
|
return {
|
|
95
143
|
content: [
|
package/dist/tools/adopt.d.ts
CHANGED
package/dist/tools/adopt.js
CHANGED
|
@@ -1,20 +1,29 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import matter from 'gray-matter';
|
|
4
|
-
import { getLibraryPath, getImportedPath, getLocalPath } from '../library/storage.js';
|
|
4
|
+
import { getLibraryPath, getImportedPath, getLocalPath, getPackagesPath } from '../library/storage.js';
|
|
5
5
|
// ============================================================================
|
|
6
6
|
// Tool Definition
|
|
7
7
|
// ============================================================================
|
|
8
8
|
export const adoptTool = {
|
|
9
9
|
name: 'adopt',
|
|
10
|
-
|
|
10
|
+
title: 'Adopt Imported Entry',
|
|
11
|
+
description: `Move an imported/packages entry to local library for editing.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
USE THIS TOOL WHEN:
|
|
14
|
+
- An imported entry is useful but needs customization
|
|
15
|
+
- Want to evolve third-party knowledge with our learnings
|
|
16
|
+
- Entry from packages/ or imported/ should become our own
|
|
17
|
+
|
|
18
|
+
Copies to local/ where we can edit it. Original stays in place.
|
|
19
|
+
|
|
20
|
+
TRIGGER PATTERNS:
|
|
21
|
+
- Entry from brief() needs edits → adopt({ path: "..." })
|
|
22
|
+
- "Make that entry ours" → adopt({ path: "...", title: "Our version" })
|
|
23
|
+
- Want to customize purchased content → adopt({ path: "packages/..." })
|
|
15
24
|
|
|
16
25
|
Examples:
|
|
17
|
-
- adopt({ path: "
|
|
26
|
+
- adopt({ path: "packages/stripe-patterns/webhook-idempotency" })
|
|
18
27
|
- adopt({ path: "imported/auth-patterns/token-refresh", title: "Our token refresh" })`,
|
|
19
28
|
inputSchema: {
|
|
20
29
|
type: 'object',
|
|
@@ -37,22 +46,40 @@ Examples:
|
|
|
37
46
|
}
|
|
38
47
|
const libraryPath = getLibraryPath();
|
|
39
48
|
const importedPath = getImportedPath(libraryPath);
|
|
49
|
+
const packagesPath = getPackagesPath(libraryPath);
|
|
40
50
|
const localPath = getLocalPath(libraryPath);
|
|
41
|
-
// Normalize path: strip "imported/" prefix if present, add .md if missing
|
|
51
|
+
// Normalize path: strip "imported/" or "packages/" prefix if present, add .md if missing
|
|
42
52
|
let normalizedPath = entryPath;
|
|
43
53
|
if (normalizedPath.startsWith('imported/')) {
|
|
44
54
|
normalizedPath = normalizedPath.slice('imported/'.length);
|
|
45
55
|
}
|
|
56
|
+
else if (normalizedPath.startsWith('packages/')) {
|
|
57
|
+
normalizedPath = normalizedPath.slice('packages/'.length);
|
|
58
|
+
}
|
|
46
59
|
if (!normalizedPath.endsWith('.md')) {
|
|
47
60
|
normalizedPath += '.md';
|
|
48
61
|
}
|
|
49
|
-
|
|
50
|
-
|
|
62
|
+
// Try both imported/ and packages/ paths
|
|
63
|
+
let sourcePath = path.resolve(importedPath, normalizedPath);
|
|
64
|
+
// Prevent path traversal
|
|
65
|
+
if (!sourcePath.startsWith(path.resolve(importedPath)) && !sourcePath.startsWith(path.resolve(packagesPath))) {
|
|
66
|
+
throw new Error('Invalid path: must be within imported/ or packages/');
|
|
67
|
+
}
|
|
51
68
|
try {
|
|
52
69
|
await fs.access(sourcePath);
|
|
53
70
|
}
|
|
54
71
|
catch {
|
|
55
|
-
|
|
72
|
+
// Try packages path (for library downloads)
|
|
73
|
+
sourcePath = path.resolve(packagesPath, normalizedPath);
|
|
74
|
+
if (!sourcePath.startsWith(path.resolve(packagesPath))) {
|
|
75
|
+
throw new Error('Invalid path: must be within imported/ or packages/');
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
await fs.access(sourcePath);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
throw new Error(`Entry not found: ${entryPath}`);
|
|
82
|
+
}
|
|
56
83
|
}
|
|
57
84
|
// Read source file
|
|
58
85
|
const content = await fs.readFile(sourcePath, 'utf-8');
|