@splicr/mcp-server 0.1.0 → 0.2.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/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +30 -36
- package/dist/auth.js.map +1 -1
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +162 -20
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +0 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +14 -22
- package/dist/config.js.map +1 -1
- package/dist/lib/api-client.d.ts +54 -0
- package/dist/lib/api-client.d.ts.map +1 -0
- package/dist/lib/api-client.js +56 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/embedding.d.ts +3 -0
- package/dist/lib/embedding.d.ts.map +1 -0
- package/dist/lib/embedding.js +44 -0
- package/dist/lib/embedding.js.map +1 -0
- package/dist/lib/project-detector.d.ts.map +1 -1
- package/dist/lib/project-detector.js +122 -38
- package/dist/lib/project-detector.js.map +1 -1
- package/dist/tools/get-project-context.d.ts.map +1 -1
- package/dist/tools/get-project-context.js +13 -26
- package/dist/tools/get-project-context.js.map +1 -1
- package/dist/tools/get-recent-insights.d.ts.map +1 -1
- package/dist/tools/get-recent-insights.js +7 -28
- package/dist/tools/get-recent-insights.js.map +1 -1
- package/dist/tools/retry-failed.d.ts.map +1 -1
- package/dist/tools/retry-failed.js +5 -41
- package/dist/tools/retry-failed.js.map +1 -1
- package/dist/tools/save-from-agent.d.ts +1 -1
- package/dist/tools/save-from-agent.d.ts.map +1 -1
- package/dist/tools/save-from-agent.js +16 -58
- package/dist/tools/save-from-agent.js.map +1 -1
- package/dist/tools/search-knowledge.d.ts.map +1 -1
- package/dist/tools/search-knowledge.js +10 -96
- package/dist/tools/search-knowledge.js.map +1 -1
- package/package.json +1 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { searchKnowledge } from '../lib/api-client.js';
|
|
2
2
|
import { detectProject } from '../lib/project-detector.js';
|
|
3
3
|
export const searchKnowledgeSchema = {
|
|
4
4
|
name: 'search_knowledge',
|
|
@@ -23,47 +23,6 @@ The search is semantic — describe what you're looking for in natural language.
|
|
|
23
23
|
required: ['query'],
|
|
24
24
|
},
|
|
25
25
|
};
|
|
26
|
-
async function generateEmbedding(text) {
|
|
27
|
-
if (!geminiApiKey) {
|
|
28
|
-
throw new Error('GEMINI_API_KEY is not configured. Set it in ~/.splicr/config.json or environment.');
|
|
29
|
-
}
|
|
30
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
31
|
-
try {
|
|
32
|
-
const url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-embedding-001:embedContent';
|
|
33
|
-
const response = await fetch(url, {
|
|
34
|
-
method: 'POST',
|
|
35
|
-
headers: {
|
|
36
|
-
'Content-Type': 'application/json',
|
|
37
|
-
'x-goog-api-key': geminiApiKey,
|
|
38
|
-
},
|
|
39
|
-
signal: AbortSignal.timeout(10000),
|
|
40
|
-
body: JSON.stringify({
|
|
41
|
-
model: 'models/gemini-embedding-001',
|
|
42
|
-
content: { parts: [{ text }] },
|
|
43
|
-
outputDimensionality: 768,
|
|
44
|
-
}),
|
|
45
|
-
});
|
|
46
|
-
if (!response.ok) {
|
|
47
|
-
const errText = await response.text();
|
|
48
|
-
if (attempt === 0 && (response.status === 429 || response.status >= 500)) {
|
|
49
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
throw new Error(`Gemini embedding failed (${response.status}): ${errText}`);
|
|
53
|
-
}
|
|
54
|
-
const result = await response.json();
|
|
55
|
-
return result.embedding.values;
|
|
56
|
-
}
|
|
57
|
-
catch (err) {
|
|
58
|
-
if (attempt === 0) {
|
|
59
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
throw err;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
throw new Error('Gemini embedding failed after retries');
|
|
66
|
-
}
|
|
67
26
|
function formatAge(dateStr) {
|
|
68
27
|
const date = new Date(dateStr);
|
|
69
28
|
if (isNaN(date.getTime()))
|
|
@@ -84,7 +43,6 @@ function formatAge(dateStr) {
|
|
|
84
43
|
return `${Math.floor(days / 365)}y ago`;
|
|
85
44
|
}
|
|
86
45
|
export async function handleSearchKnowledge(args) {
|
|
87
|
-
const { supabase, userId } = await getSupabase();
|
|
88
46
|
const query = String(args.query || '').trim();
|
|
89
47
|
if (!query)
|
|
90
48
|
return 'Please provide a search query.';
|
|
@@ -92,69 +50,25 @@ export async function handleSearchKnowledge(args) {
|
|
|
92
50
|
return 'Query too long (max 1000 chars).';
|
|
93
51
|
const limit = Math.min(Math.max(1, Number(args.limit) || 5), 50);
|
|
94
52
|
const projectArg = args.project;
|
|
95
|
-
// Resolve project
|
|
96
|
-
let
|
|
53
|
+
// Resolve project name
|
|
54
|
+
let projectName;
|
|
97
55
|
if (projectArg === 'auto') {
|
|
98
56
|
const detected = await detectProject(process.cwd());
|
|
99
|
-
|
|
57
|
+
projectName = detected?.name;
|
|
100
58
|
}
|
|
101
59
|
else if (projectArg) {
|
|
102
|
-
|
|
103
|
-
.from('projects')
|
|
104
|
-
.select('id')
|
|
105
|
-
.eq('user_id', userId)
|
|
106
|
-
.ilike('name', projectArg)
|
|
107
|
-
.limit(1);
|
|
108
|
-
if (lookupError) {
|
|
109
|
-
return `Error looking up project: ${lookupError.message}`;
|
|
110
|
-
}
|
|
111
|
-
projectId = data?.[0]?.id;
|
|
112
|
-
if (!projectId) {
|
|
113
|
-
return `Project "${projectArg}" not found. Check the name and try again.`;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// Generate embedding for query
|
|
117
|
-
const queryEmbedding = await generateEmbedding(query);
|
|
118
|
-
// Hybrid search: vector + full-text + RRF + boost signals
|
|
119
|
-
const { data, error } = await supabase.rpc('hybrid_search_captures', {
|
|
120
|
-
query_text: query,
|
|
121
|
-
query_embedding: queryEmbedding,
|
|
122
|
-
match_user_id: userId,
|
|
123
|
-
match_project_id: projectId || null,
|
|
124
|
-
match_limit: limit,
|
|
125
|
-
match_threshold: 0.2,
|
|
126
|
-
});
|
|
127
|
-
if (error) {
|
|
128
|
-
// Fallback to legacy vector-only search if hybrid isn't available yet
|
|
129
|
-
const { data: fallbackData, error: fallbackError } = await supabase.rpc('search_captures', {
|
|
130
|
-
query_embedding: queryEmbedding,
|
|
131
|
-
match_user_id: userId,
|
|
132
|
-
match_project_id: projectId || null,
|
|
133
|
-
match_limit: limit,
|
|
134
|
-
match_threshold: 0.3,
|
|
135
|
-
});
|
|
136
|
-
if (fallbackError)
|
|
137
|
-
return `Error searching: ${fallbackError.message}`;
|
|
138
|
-
if (!fallbackData || fallbackData.length === 0)
|
|
139
|
-
return 'No relevant knowledge found in Splicr.';
|
|
140
|
-
const results = fallbackData.map((r, i) => {
|
|
141
|
-
const age = formatAge(r.created_at);
|
|
142
|
-
const score = r.similarity ? `${(r.similarity * 100).toFixed(0)}%` : 'n/a';
|
|
143
|
-
return `${i + 1}. **${r.title || 'Untitled'}**\n` +
|
|
144
|
-
` ${r.insight}\n` +
|
|
145
|
-
` source: ${r.source_type || 'unknown'} | saved: ${age} | match: ${score} | tags: ${r.tags?.join(', ') || 'none'}${r.source_url ? `\n url: ${r.source_url}` : ''}`;
|
|
146
|
-
}).join('\n\n');
|
|
147
|
-
return `*From Splicr — ${fallbackData.length} result(s):*\n\n${results}`;
|
|
60
|
+
projectName = projectArg;
|
|
148
61
|
}
|
|
149
|
-
|
|
62
|
+
const results = await searchKnowledge({ query, project_name: projectName, limit });
|
|
63
|
+
if (!results || results.length === 0)
|
|
150
64
|
return 'No relevant knowledge found in Splicr.';
|
|
151
|
-
const
|
|
65
|
+
const formatted = results.map((r, i) => {
|
|
152
66
|
const age = formatAge(r.created_at);
|
|
153
|
-
const score = r.rrf_score ? `${(r.rrf_score * 100).toFixed(0)}%` : 'n/a';
|
|
67
|
+
const score = r.rrf_score ? `${(r.rrf_score * 100).toFixed(0)}%` : r.similarity ? `${(r.similarity * 100).toFixed(0)}%` : 'n/a';
|
|
154
68
|
return `${i + 1}. **${r.title || 'Untitled'}**\n` +
|
|
155
69
|
` ${r.insight}\n` +
|
|
156
70
|
` source: ${r.source_type || 'unknown'} | saved: ${age} | match: ${score} | tags: ${r.tags?.join(', ') || 'none'}${r.source_url ? `\n url: ${r.source_url}` : ''}`;
|
|
157
71
|
}).join('\n\n');
|
|
158
|
-
return `*From Splicr — ${
|
|
72
|
+
return `*From Splicr — ${results.length} result(s):*\n\n${formatted}`;
|
|
159
73
|
}
|
|
160
74
|
//# sourceMappingURL=search-knowledge.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-knowledge.js","sourceRoot":"","sources":["../../src/tools/search-knowledge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"search-knowledge.js","sourceRoot":"","sources":["../../src/tools/search-knowledge.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splicr/mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Splicr MCP server — route what you read to what you're building",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./dist/cli.js",
|
|
@@ -15,10 +15,7 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@google/generative-ai": "^0.24.1",
|
|
19
18
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
20
|
-
"@supabase/supabase-js": "^2.45.0",
|
|
21
|
-
"openai": "^4.55.0",
|
|
22
19
|
"zod": "^3.23.0"
|
|
23
20
|
},
|
|
24
21
|
"devDependencies": {
|