fast-context-mcp 1.0.0 → 1.1.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/package.json +3 -3
- package/src/core.mjs +11 -11
- package/src/extract-key.mjs +39 -28
- package/src/server.mjs +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fast-context-mcp",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "AI-driven semantic code search
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "AI-driven semantic code search MCP server (Node.js)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/server.mjs",
|
|
7
7
|
"bin": {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
23
23
|
"@vscode/ripgrep": "^1.15.9",
|
|
24
|
-
"
|
|
24
|
+
"sql.js": "^1.14.0",
|
|
25
25
|
"tree-node-cli": "^1.6.0",
|
|
26
26
|
"zod": "^3.23.0"
|
|
27
27
|
},
|
package/src/core.mjs
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { readdirSync, existsSync, statSync } from "node:fs";
|
|
15
|
-
import { resolve, join, relative } from "node:path";
|
|
15
|
+
import { resolve, join, relative, sep } from "node:path";
|
|
16
16
|
import { gzipSync } from "node:zlib";
|
|
17
17
|
import { randomUUID } from "node:crypto";
|
|
18
18
|
import { platform, arch, release, version as osVersion, hostname, cpus, totalmem } from "node:os";
|
|
@@ -361,11 +361,11 @@ function getToolDefinitions(maxCommands = 8) {
|
|
|
361
361
|
|
|
362
362
|
/**
|
|
363
363
|
* Auto-discover Windsurf API key from local installation.
|
|
364
|
-
* @returns {string|null}
|
|
364
|
+
* @returns {Promise<string|null>}
|
|
365
365
|
*/
|
|
366
|
-
function autoDiscoverApiKey() {
|
|
366
|
+
async function autoDiscoverApiKey() {
|
|
367
367
|
try {
|
|
368
|
-
const result = extractKey();
|
|
368
|
+
const result = await extractKey();
|
|
369
369
|
if (result.api_key && result.api_key.startsWith("sk-")) {
|
|
370
370
|
return result.api_key;
|
|
371
371
|
}
|
|
@@ -377,12 +377,12 @@ function autoDiscoverApiKey() {
|
|
|
377
377
|
|
|
378
378
|
/**
|
|
379
379
|
* Get API key from env var or auto-discovery.
|
|
380
|
-
* @returns {string}
|
|
380
|
+
* @returns {Promise<string>}
|
|
381
381
|
*/
|
|
382
|
-
function getApiKey() {
|
|
382
|
+
async function getApiKey() {
|
|
383
383
|
const key = process.env.WINDSURF_API_KEY;
|
|
384
384
|
if (key) return key;
|
|
385
|
-
const discovered = autoDiscoverApiKey();
|
|
385
|
+
const discovered = await autoDiscoverApiKey();
|
|
386
386
|
if (discovered) return discovered;
|
|
387
387
|
throw new Error(
|
|
388
388
|
"Windsurf API Key not found. Set WINDSURF_API_KEY env var or ensure Windsurf is logged in. " +
|
|
@@ -933,7 +933,7 @@ function _parseAnswer(xmlText, projectRoot) {
|
|
|
933
933
|
|
|
934
934
|
// Path safety: reject traversal attempts (../) and paths outside project root
|
|
935
935
|
const fullPath = resolve(projectRoot, rel);
|
|
936
|
-
if (!fullPath.startsWith(resolvedRoot +
|
|
936
|
+
if (!fullPath.startsWith(resolvedRoot + sep) && fullPath !== resolvedRoot) {
|
|
937
937
|
continue;
|
|
938
938
|
}
|
|
939
939
|
|
|
@@ -984,7 +984,7 @@ export async function search({
|
|
|
984
984
|
|
|
985
985
|
// Get credentials
|
|
986
986
|
if (!apiKey) {
|
|
987
|
-
apiKey = getApiKey();
|
|
987
|
+
apiKey = await getApiKey();
|
|
988
988
|
}
|
|
989
989
|
if (!jwt) {
|
|
990
990
|
log("Fetching JWT...");
|
|
@@ -1199,8 +1199,8 @@ export async function searchWithContent({
|
|
|
1199
1199
|
|
|
1200
1200
|
/**
|
|
1201
1201
|
* Extract Windsurf API Key info (for MCP tool use).
|
|
1202
|
-
* @returns {Object}
|
|
1202
|
+
* @returns {Promise<Object>}
|
|
1203
1203
|
*/
|
|
1204
|
-
export function extractKeyInfo() {
|
|
1204
|
+
export async function extractKeyInfo() {
|
|
1205
1205
|
return extractKey();
|
|
1206
1206
|
}
|
package/src/extract-key.mjs
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
* Windsurf API Key extraction from local installation.
|
|
3
3
|
*
|
|
4
4
|
* Cross-platform: macOS / Windows / Linux.
|
|
5
|
-
* Uses
|
|
5
|
+
* Uses sql.js (pure JS/WASM) to read state.vscdb — no native compilation needed.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync } from "node:fs";
|
|
8
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { homedir, platform } from "node:os";
|
|
11
|
-
import
|
|
11
|
+
import initSqlJs from "sql.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Get the platform-specific path to Windsurf's state.vscdb.
|
|
@@ -34,9 +34,9 @@ export function getDbPath() {
|
|
|
34
34
|
/**
|
|
35
35
|
* Extract API Key from Windsurf state.vscdb.
|
|
36
36
|
* @param {string} [dbPath]
|
|
37
|
-
* @returns {{ api_key?: string, db_path: string, error?: string, hint?: string }}
|
|
37
|
+
* @returns {Promise<{ api_key?: string, db_path: string, error?: string, hint?: string }>}
|
|
38
38
|
*/
|
|
39
|
-
export function extractKey(dbPath) {
|
|
39
|
+
export async function extractKey(dbPath) {
|
|
40
40
|
if (!dbPath) {
|
|
41
41
|
dbPath = getDbPath();
|
|
42
42
|
}
|
|
@@ -49,34 +49,45 @@ export function extractKey(dbPath) {
|
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
let
|
|
52
|
+
let db;
|
|
53
53
|
try {
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
db.
|
|
54
|
+
const SQL = await initSqlJs();
|
|
55
|
+
const buf = readFileSync(dbPath);
|
|
56
|
+
db = new SQL.Database(buf);
|
|
57
57
|
} catch (e) {
|
|
58
|
-
return { error: `Failed to
|
|
58
|
+
return { error: `Failed to open database: ${e.message}`, db_path: dbPath };
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
if (!row) {
|
|
62
|
-
return {
|
|
63
|
-
error: "windsurfAuthStatus record not found",
|
|
64
|
-
hint: "Ensure Windsurf is logged in.",
|
|
65
|
-
db_path: dbPath,
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let data;
|
|
70
61
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
62
|
+
const stmt = db.prepare("SELECT value FROM ItemTable WHERE key = 'windsurfAuthStatus'");
|
|
63
|
+
if (!stmt.step()) {
|
|
64
|
+
stmt.free();
|
|
65
|
+
return {
|
|
66
|
+
error: "windsurfAuthStatus record not found",
|
|
67
|
+
hint: "Ensure Windsurf is logged in.",
|
|
68
|
+
db_path: dbPath,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
const row = stmt.getAsObject();
|
|
73
|
+
stmt.free();
|
|
74
|
+
|
|
75
|
+
let data;
|
|
76
|
+
try {
|
|
77
|
+
data = JSON.parse(row.value);
|
|
78
|
+
} catch {
|
|
79
|
+
return { error: "windsurfAuthStatus data parse failed", db_path: dbPath };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const apiKey = data.apiKey || "";
|
|
83
|
+
if (!apiKey) {
|
|
84
|
+
return { error: "apiKey field is empty", db_path: dbPath };
|
|
85
|
+
}
|
|
80
86
|
|
|
81
|
-
|
|
87
|
+
return { api_key: apiKey, db_path: dbPath };
|
|
88
|
+
} catch (e) {
|
|
89
|
+
return { error: `Extraction failed: ${e.message}`, db_path: dbPath };
|
|
90
|
+
} finally {
|
|
91
|
+
db.close();
|
|
92
|
+
}
|
|
82
93
|
}
|
package/src/server.mjs
CHANGED
|
@@ -174,7 +174,7 @@ server.tool(
|
|
|
174
174
|
"Windsurf's local database. Set the result as WINDSURF_API_KEY env var.",
|
|
175
175
|
{},
|
|
176
176
|
async () => {
|
|
177
|
-
const result = extractKeyInfo();
|
|
177
|
+
const result = await extractKeyInfo();
|
|
178
178
|
|
|
179
179
|
if (result.error) {
|
|
180
180
|
const text = `Error: ${result.error}\n${result.hint || ""}\nDB path: ${result.db_path || "N/A"}`;
|