@splicr/mcp-server 0.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.
Files changed (53) hide show
  1. package/dist/auth.d.ts +18 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +115 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/cli.d.ts +11 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +80 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.d.ts +10 -0
  10. package/dist/config.d.ts.map +1 -0
  11. package/dist/config.js +34 -0
  12. package/dist/config.js.map +1 -0
  13. package/dist/index.d.ts +3 -0
  14. package/dist/index.d.ts.map +1 -0
  15. package/dist/index.js +93 -0
  16. package/dist/index.js.map +1 -0
  17. package/dist/lib/auto-register.d.ts +7 -0
  18. package/dist/lib/auto-register.d.ts.map +1 -0
  19. package/dist/lib/auto-register.js +204 -0
  20. package/dist/lib/auto-register.js.map +1 -0
  21. package/dist/lib/project-detector.d.ts +12 -0
  22. package/dist/lib/project-detector.d.ts.map +1 -0
  23. package/dist/lib/project-detector.js +68 -0
  24. package/dist/lib/project-detector.js.map +1 -0
  25. package/dist/lib/supabase.d.ts +14 -0
  26. package/dist/lib/supabase.d.ts.map +1 -0
  27. package/dist/lib/supabase.js +50 -0
  28. package/dist/lib/supabase.js.map +1 -0
  29. package/dist/login-cli.d.ts +3 -0
  30. package/dist/login-cli.d.ts.map +1 -0
  31. package/dist/login-cli.js +7 -0
  32. package/dist/login-cli.js.map +1 -0
  33. package/dist/tools/get-project-context.d.ts +20 -0
  34. package/dist/tools/get-project-context.d.ts.map +1 -0
  35. package/dist/tools/get-project-context.js +59 -0
  36. package/dist/tools/get-project-context.js.map +1 -0
  37. package/dist/tools/get-recent-insights.d.ts +19 -0
  38. package/dist/tools/get-recent-insights.d.ts.map +1 -0
  39. package/dist/tools/get-recent-insights.js +55 -0
  40. package/dist/tools/get-recent-insights.js.map +1 -0
  41. package/dist/tools/retry-failed.d.ts +10 -0
  42. package/dist/tools/retry-failed.d.ts.map +1 -0
  43. package/dist/tools/retry-failed.js +53 -0
  44. package/dist/tools/retry-failed.js.map +1 -0
  45. package/dist/tools/save-from-agent.d.ts +31 -0
  46. package/dist/tools/save-from-agent.d.ts.map +1 -0
  47. package/dist/tools/save-from-agent.js +84 -0
  48. package/dist/tools/save-from-agent.js.map +1 -0
  49. package/dist/tools/search-knowledge.d.ts +24 -0
  50. package/dist/tools/search-knowledge.d.ts.map +1 -0
  51. package/dist/tools/search-knowledge.js +160 -0
  52. package/dist/tools/search-knowledge.js.map +1 -0
  53. package/package.json +29 -0
package/dist/auth.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Load auth credentials, auto-refreshing if within 5 minutes of expiry.
3
+ * Returns access token and user ID.
4
+ */
5
+ export declare function loadAuth(): Promise<{
6
+ accessToken: string;
7
+ refreshToken: string;
8
+ userId: string;
9
+ }>;
10
+ /**
11
+ * Interactive login flow — prompts for email + password.
12
+ */
13
+ export declare function login(): Promise<void>;
14
+ /**
15
+ * Check if auth.json exists (user has logged in before).
16
+ */
17
+ export declare function hasAuth(): boolean;
18
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AA8DA;;;GAGG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBvG;AAED;;GAEG;AACH,wBAAsB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAuC3C;AAED;;GAEG;AACH,wBAAgB,OAAO,IAAI,OAAO,CAEjC"}
package/dist/auth.js ADDED
@@ -0,0 +1,115 @@
1
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { createClient } from '@supabase/supabase-js';
5
+ import { loadConfig } from './config.js';
6
+ // Check ~/.splicr first, fall back to ~/.cortex for backward compat
7
+ const SPLICR_AUTH = join(homedir(), '.splicr', 'auth.json');
8
+ const CORTEX_AUTH = join(homedir(), '.cortex', 'auth.json');
9
+ const AUTH_FILE = existsSync(SPLICR_AUTH) ? SPLICR_AUTH : existsSync(CORTEX_AUTH) ? CORTEX_AUTH : SPLICR_AUTH;
10
+ // Mutex to prevent concurrent refresh races
11
+ let refreshPromise = null;
12
+ function readAuthFile() {
13
+ if (!existsSync(AUTH_FILE))
14
+ return null;
15
+ try {
16
+ return JSON.parse(readFileSync(AUTH_FILE, 'utf-8'));
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ function writeAuthFile(auth) {
23
+ const dir = join(homedir(), '.splicr');
24
+ if (!existsSync(dir))
25
+ mkdirSync(dir, { recursive: true });
26
+ writeFileSync(AUTH_FILE, JSON.stringify(auth, null, 2));
27
+ }
28
+ async function refreshToken(auth) {
29
+ const config = loadConfig();
30
+ const anonKey = config.supabaseAnonKey;
31
+ if (!anonKey) {
32
+ throw new Error('SUPABASE_ANON_KEY required for token refresh. Add it to ~/.splicr/config.json or env.');
33
+ }
34
+ const client = createClient(config.supabaseUrl, anonKey);
35
+ const { data, error } = await client.auth.refreshSession({
36
+ refresh_token: auth.refresh_token,
37
+ });
38
+ if (error || !data.session) {
39
+ throw new Error('Session expired. Run `splicr login` to re-authenticate.');
40
+ }
41
+ const newAuth = {
42
+ access_token: data.session.access_token,
43
+ refresh_token: data.session.refresh_token,
44
+ expires_at: data.session.expires_at ?? Math.floor(Date.now() / 1000) + 3600,
45
+ user_id: data.user?.id ?? auth.user_id,
46
+ };
47
+ writeAuthFile(newAuth);
48
+ return newAuth;
49
+ }
50
+ /**
51
+ * Load auth credentials, auto-refreshing if within 5 minutes of expiry.
52
+ * Returns access token and user ID.
53
+ */
54
+ export async function loadAuth() {
55
+ const auth = readAuthFile();
56
+ if (!auth) {
57
+ throw new Error('Not authenticated. Run `splicr login` to sign in.');
58
+ }
59
+ const now = Math.floor(Date.now() / 1000);
60
+ const fiveMinutes = 300;
61
+ if (auth.expires_at - now < fiveMinutes) {
62
+ // Token is expiring soon — refresh with mutex to prevent races
63
+ if (!refreshPromise) {
64
+ refreshPromise = refreshToken(auth).finally(() => {
65
+ refreshPromise = null;
66
+ });
67
+ }
68
+ const refreshed = await refreshPromise;
69
+ return { accessToken: refreshed.access_token, refreshToken: refreshed.refresh_token, userId: refreshed.user_id };
70
+ }
71
+ return { accessToken: auth.access_token, refreshToken: auth.refresh_token, userId: auth.user_id };
72
+ }
73
+ /**
74
+ * Interactive login flow — prompts for email + password.
75
+ */
76
+ export async function login() {
77
+ const readline = await import('readline');
78
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
79
+ const ask = (q) => new Promise(resolve => rl.question(q, resolve));
80
+ try {
81
+ const config = loadConfig();
82
+ const anonKey = config.supabaseAnonKey;
83
+ if (!anonKey) {
84
+ console.error('Error: SUPABASE_ANON_KEY not found. Add it to ~/.splicr/config.json or env.');
85
+ process.exit(1);
86
+ }
87
+ const email = await ask('Email: ');
88
+ const password = await ask('Password: ');
89
+ const client = createClient(config.supabaseUrl, anonKey);
90
+ const { data, error } = await client.auth.signInWithPassword({ email, password });
91
+ if (error || !data.session) {
92
+ console.error(`Login failed: ${error?.message ?? 'Unknown error'}`);
93
+ process.exit(1);
94
+ }
95
+ const auth = {
96
+ access_token: data.session.access_token,
97
+ refresh_token: data.session.refresh_token,
98
+ expires_at: data.session.expires_at ?? Math.floor(Date.now() / 1000) + 3600,
99
+ user_id: data.user.id,
100
+ };
101
+ writeAuthFile(auth);
102
+ console.error(`Authenticated as ${email} (${data.user.id})`);
103
+ console.error(`Credentials saved to ${AUTH_FILE}`);
104
+ }
105
+ finally {
106
+ rl.close();
107
+ }
108
+ }
109
+ /**
110
+ * Check if auth.json exists (user has logged in before).
111
+ */
112
+ export function hasAuth() {
113
+ return readAuthFile() !== null;
114
+ }
115
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Unified entry point for the Splicr MCP server.
4
+ *
5
+ * - No args → starts the MCP stdio server (Claude Code runs this)
6
+ * - "setup" → one-time setup: login + configure Claude Code
7
+ * - "login" → re-authenticate
8
+ * - "help" → print usage
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG"}
package/dist/cli.js ADDED
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Unified entry point for the Splicr MCP server.
4
+ *
5
+ * - No args → starts the MCP stdio server (Claude Code runs this)
6
+ * - "setup" → one-time setup: login + configure Claude Code
7
+ * - "login" → re-authenticate
8
+ * - "help" → print usage
9
+ */
10
+ const command = process.argv[2];
11
+ async function main() {
12
+ switch (command) {
13
+ case 'setup':
14
+ await setup();
15
+ break;
16
+ case 'login': {
17
+ const { login } = await import('./auth.js');
18
+ await login();
19
+ break;
20
+ }
21
+ case 'help':
22
+ case '--help':
23
+ case '-h':
24
+ printHelp();
25
+ break;
26
+ case undefined:
27
+ // No args → start MCP server (this is what Claude Code calls)
28
+ await import('./index.js');
29
+ break;
30
+ default:
31
+ console.error(`Unknown command: ${command}\n`);
32
+ printHelp();
33
+ process.exit(1);
34
+ }
35
+ }
36
+ async function setup() {
37
+ const { login } = await import('./auth.js');
38
+ const { execSync } = await import('child_process');
39
+ console.error('\n Splicr — route what you read to what you\'re building\n');
40
+ // Step 1: Login
41
+ console.error(' Step 1/2: Authenticate\n');
42
+ await login();
43
+ // Step 2: Configure Claude Code
44
+ console.error('\n Step 2/2: Configure Claude Code\n');
45
+ try {
46
+ execSync('claude mcp add splicr -- npx -y @splicr/mcp-server', {
47
+ stdio: 'inherit',
48
+ });
49
+ console.error('\n Done! Splicr is ready. Start a new Claude Code session to use it.');
50
+ console.error(' Save articles from your phone or browser — they\'ll show up when you code.\n');
51
+ }
52
+ catch {
53
+ console.error(' Could not auto-configure Claude Code. Add this manually:\n');
54
+ console.error(' claude mcp add splicr -- npx -y @splicr/mcp-server\n');
55
+ console.error(' Or add to your MCP settings JSON:\n');
56
+ console.error(' "splicr": { "command": "npx", "args": ["-y", "@splicr/mcp-server"] }\n');
57
+ }
58
+ }
59
+ function printHelp() {
60
+ console.error(`
61
+ Splicr MCP Server — route what you read to what you're building
62
+
63
+ Usage:
64
+ npx @splicr/mcp-server setup One-time setup: login + configure Claude Code
65
+ npx @splicr/mcp-server login Authenticate with your Splicr account
66
+ npx @splicr/mcp-server Start MCP server (used by Claude Code)
67
+
68
+ Quick start:
69
+ 1. Sign up at splicr.dev
70
+ 2. Run: npx @splicr/mcp-server setup
71
+ 3. Start saving articles from your phone or browser
72
+ 4. Open Claude Code — your saves show up when relevant
73
+ `);
74
+ }
75
+ main().catch(err => {
76
+ console.error(`Error: ${err.message}`);
77
+ process.exit(1);
78
+ });
79
+ export {};
80
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;GAOG;AAEH,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,KAAK,UAAU,IAAI;IACjB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,OAAO;YACV,MAAM,KAAK,EAAE,CAAC;YACd,MAAM;QAER,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;YAC5C,MAAM,KAAK,EAAE,CAAC;YACd,MAAM;QACR,CAAC;QAED,KAAK,MAAM,CAAC;QACZ,KAAK,QAAQ,CAAC;QACd,KAAK,IAAI;YACP,SAAS,EAAE,CAAC;YACZ,MAAM;QAER,KAAK,SAAS;YACZ,8DAA8D;YAC9D,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;YAC3B,MAAM;QAER;YACE,OAAO,CAAC,KAAK,CAAC,oBAAoB,OAAO,IAAI,CAAC,CAAC;YAC/C,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,KAAK;IAClB,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IAC5C,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,eAAe,CAAC,CAAC;IAEnD,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;IAE7E,gBAAgB;IAChB,OAAO,CAAC,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAC5C,MAAM,KAAK,EAAE,CAAC;IAEd,gCAAgC;IAChC,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;IACvD,IAAI,CAAC;QACH,QAAQ,CAAC,oDAAoD,EAAE;YAC7D,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;QACvF,OAAO,CAAC,KAAK,CAAC,gFAAgF,CAAC,CAAC;IAClG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAC;QAC9E,OAAO,CAAC,KAAK,CAAC,0DAA0D,CAAC,CAAC;QAC1E,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QACvD,OAAO,CAAC,KAAK,CAAC,4EAA4E,CAAC,CAAC;IAC9F,CAAC;AACH,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,KAAK,CAAC;;;;;;;;;;;;;CAaf,CAAC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface SplicrConfig {
2
+ supabaseUrl: string;
3
+ supabaseServiceRoleKey: string;
4
+ supabaseAnonKey: string;
5
+ geminiApiKey: string;
6
+ userId: string;
7
+ apiServerUrl: string;
8
+ }
9
+ export declare function loadConfig(): SplicrConfig;
10
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,UAAU,IAAI,YAAY,CA+BzC"}
package/dist/config.js ADDED
@@ -0,0 +1,34 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ export function loadConfig() {
5
+ // Priority: env vars > ~/.splicr/config.json
6
+ if (process.env.SUPABASE_URL && process.env.SUPABASE_SERVICE_ROLE_KEY) {
7
+ return {
8
+ supabaseUrl: process.env.SUPABASE_URL,
9
+ supabaseServiceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY,
10
+ supabaseAnonKey: process.env.SUPABASE_ANON_KEY || '',
11
+ geminiApiKey: process.env.GEMINI_API_KEY || '',
12
+ userId: process.env.SPLICR_USER_ID || process.env.CORTEX_USER_ID || '',
13
+ apiServerUrl: process.env.SPLICR_API_URL || process.env.CORTEX_API_URL || 'https://api-production-d889.up.railway.app',
14
+ };
15
+ }
16
+ // Check ~/.splicr first, fall back to ~/.cortex for backward compat
17
+ let configPath = join(homedir(), '.splicr', 'config.json');
18
+ if (!existsSync(configPath)) {
19
+ configPath = join(homedir(), '.cortex', 'config.json');
20
+ }
21
+ if (!existsSync(configPath)) {
22
+ throw new Error('No config found. Set SUPABASE_URL env var or create ~/.splicr/config.json');
23
+ }
24
+ const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
25
+ return {
26
+ supabaseUrl: raw.supabase_url || raw.api_url,
27
+ supabaseServiceRoleKey: raw.supabase_service_role_key || raw.api_key,
28
+ supabaseAnonKey: raw.supabase_anon_key || '',
29
+ geminiApiKey: raw.gemini_api_key || '',
30
+ userId: raw.user_id,
31
+ apiServerUrl: raw.api_server_url || 'http://localhost:3000',
32
+ };
33
+ }
34
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,MAAM,UAAU,UAAU;IACxB,6CAA6C;IAC7C,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,CAAC;QACtE,OAAO;YACL,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;YACrC,sBAAsB,EAAE,OAAO,CAAC,GAAG,CAAC,yBAAyB;YAC7D,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,EAAE;YACpD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;YAC9C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,EAAE;YACtE,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,4CAA4C;SACvH,CAAC;IACJ,CAAC;IAED,oEAAoE;IACpE,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IAC3D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IAC/F,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,WAAW,EAAE,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,OAAO;QAC5C,sBAAsB,EAAE,GAAG,CAAC,yBAAyB,IAAI,GAAG,CAAC,OAAO;QACpE,eAAe,EAAE,GAAG,CAAC,iBAAiB,IAAI,EAAE;QAC5C,YAAY,EAAE,GAAG,CAAC,cAAc,IAAI,EAAE;QACtC,MAAM,EAAE,GAAG,CAAC,OAAO;QACnB,YAAY,EAAE,GAAG,CAAC,cAAc,IAAI,uBAAuB;KAC5D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,93 @@
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 { searchKnowledgeSchema, handleSearchKnowledge } from './tools/search-knowledge.js';
6
+ import { getProjectContextSchema, handleGetProjectContext } from './tools/get-project-context.js';
7
+ import { getRecentInsightsSchema, handleGetRecentInsights } from './tools/get-recent-insights.js';
8
+ import { saveFromAgentSchema, handleSaveFromAgent } from './tools/save-from-agent.js';
9
+ import { retryFailedSchema, handleRetryFailed } from './tools/retry-failed.js';
10
+ // Prevent unhandled errors from crashing the MCP server
11
+ process.on('uncaughtException', (err) => {
12
+ console.error('[Splicr MCP] Uncaught exception:', err.message);
13
+ });
14
+ process.on('unhandledRejection', (err) => {
15
+ console.error('[Splicr MCP] Unhandled rejection:', err);
16
+ });
17
+ const server = new Server({ name: 'splicr', version: '0.1.0' }, {
18
+ capabilities: { tools: {} },
19
+ instructions: `Splicr is the user's personal knowledge base — their second brain. They actively save articles, tweets, code snippets, technical insights, and opinions from across the web into Splicr.
20
+
21
+ CRITICAL REQUIREMENT: You MUST call search_knowledge BEFORE answering ANY question about technologies, libraries, frameworks, tools, design patterns, architecture decisions, best practices, opinions, or technical concepts. This is non-negotiable — the user expects their personally curated knowledge to ground your answers. Do NOT rely solely on your training data when Splicr may have relevant saves.
22
+
23
+ When to call search_knowledge:
24
+ - User asks "how should I implement X?" → search first
25
+ - User asks about a technology, library, or pattern → search first
26
+ - User asks for opinions or best practices → search first
27
+ - User is debugging or troubleshooting → search first
28
+ - User is making an architectural decision → search first
29
+
30
+ When NOT needed:
31
+ - Pure code edits ("rename this variable", "fix this typo")
32
+ - Git operations ("commit this", "create a PR")
33
+ - File operations ("read this file", "create a new file")
34
+
35
+ HOW TO USE SEARCH RESULTS — each result includes metadata to help you judge quality:
36
+ - **source**: where it came from (tweet, article, docs, agent). Official docs > articles > tweets for factual claims.
37
+ - **saved**: how old the save is. Recent saves are more likely to reflect current best practices. Saves older than 6 months about fast-moving tech (frameworks, APIs) may be outdated.
38
+ - **match**: relevance score. High match (>60%) = strong semantic fit. Low match (<30%) = tangential, use cautiously.
39
+
40
+ CONFLICT RESOLUTION — when Splicr results and your training data disagree:
41
+ - If the save is recent + high-match + from docs/articles → trust Splicr, it likely reflects the user's researched opinion
42
+ - If the save is old + low-match + from a tweet → prefer your training data, but mention what the user saved
43
+ - When genuinely uncertain, present both: "Your Splicr save suggests X, but current best practice is Y — here's the tradeoff..."
44
+ - NEVER silently ignore Splicr results. Always acknowledge what the user saved, even if you disagree.
45
+
46
+ The user chose Splicr so their curated context informs your answers. But curated ≠ infallible. Use the metadata to make intelligent judgments.`,
47
+ });
48
+ // List tools
49
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
50
+ tools: [
51
+ searchKnowledgeSchema,
52
+ getProjectContextSchema,
53
+ getRecentInsightsSchema,
54
+ saveFromAgentSchema,
55
+ retryFailedSchema,
56
+ ],
57
+ }));
58
+ // Handle tool calls with per-tool timeout
59
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
60
+ const { name, arguments: args } = request.params;
61
+ try {
62
+ const handler = {
63
+ search_knowledge: handleSearchKnowledge,
64
+ get_project_context: handleGetProjectContext,
65
+ get_recent_insights: handleGetRecentInsights,
66
+ save_from_agent: handleSaveFromAgent,
67
+ retry_failed: handleRetryFailed,
68
+ }[name];
69
+ if (!handler) {
70
+ return { content: [{ type: 'text', text: `Unknown tool: ${name}` }], isError: true };
71
+ }
72
+ // 30-second timeout per tool call
73
+ const result = await Promise.race([
74
+ handler(args || {}),
75
+ new Promise((_, reject) => setTimeout(() => reject(new Error('Tool call timed out after 30s')), 30000)),
76
+ ]);
77
+ return { content: [{ type: 'text', text: result }] };
78
+ }
79
+ catch (error) {
80
+ const msg = error instanceof Error ? error.message : 'Unknown error';
81
+ console.error(`[Splicr MCP] Tool "${name}" failed:`, msg);
82
+ return { content: [{ type: 'text', text: `Splicr error (${name}): ${msg}` }], isError: true };
83
+ }
84
+ });
85
+ // Start stdio transport
86
+ const transport = new StdioServerTransport();
87
+ server.connect(transport).then(() => {
88
+ console.error('[Splicr MCP] Server running on stdio');
89
+ }).catch((err) => {
90
+ console.error('[Splicr MCP] Failed to start:', err);
91
+ process.exit(1);
92
+ });
93
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ interface RegisteredProject {
2
+ id: string;
3
+ name: string;
4
+ }
5
+ export declare function autoRegisterProject(cwd: string): Promise<RegisteredProject | null>;
6
+ export {};
7
+ //# sourceMappingURL=auto-register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-register.d.ts","sourceRoot":"","sources":["../../src/lib/auto-register.ts"],"names":[],"mappings":"AAOA,UAAU,iBAAiB;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA6DxF"}
@@ -0,0 +1,204 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { getSupabase } from './supabase.js';
4
+ import { getGitRemoteUrl, normalizeGitUrl, normalizePath } from './project-detector.js';
5
+ import { loadConfig } from '../config.js';
6
+ import { loadAuth, hasAuth } from '../auth.js';
7
+ export async function autoRegisterProject(cwd) {
8
+ const { supabase, userId } = await getSupabase();
9
+ const projectName = detectProjectName(cwd);
10
+ const description = detectDescription(cwd);
11
+ const techStack = detectTechStack(cwd);
12
+ const gitRemote = getGitRemoteUrl(cwd);
13
+ const normalizedCwd = normalizePath(cwd);
14
+ const insertData = {
15
+ user_id: userId,
16
+ name: projectName,
17
+ description,
18
+ tech_stack: techStack,
19
+ local_path: normalizedCwd,
20
+ status: 'active',
21
+ };
22
+ if (gitRemote) {
23
+ insertData.github_repo_url = normalizeGitUrl(gitRemote);
24
+ }
25
+ const { data, error } = await supabase
26
+ .from('projects')
27
+ .insert(insertData)
28
+ .select('id, name')
29
+ .single();
30
+ if (error) {
31
+ // Unique constraint violation — project already registered (race condition or different local path)
32
+ if (error.code === '23505') {
33
+ // Try local_path first, then git remote URL
34
+ const { data: byPath } = await supabase
35
+ .from('projects')
36
+ .select('id, name')
37
+ .eq('user_id', userId)
38
+ .eq('local_path', normalizedCwd)
39
+ .limit(1);
40
+ if (byPath?.[0])
41
+ return byPath[0];
42
+ if (gitRemote) {
43
+ const { data: byRepo } = await supabase
44
+ .from('projects')
45
+ .select('id, name')
46
+ .eq('user_id', userId)
47
+ .eq('github_repo_url', normalizeGitUrl(gitRemote))
48
+ .limit(1);
49
+ if (byRepo?.[0])
50
+ return byRepo[0];
51
+ }
52
+ return null;
53
+ }
54
+ console.error('Auto-register failed:', error.message);
55
+ return null;
56
+ }
57
+ console.error(`Auto-registered project "${data.name}" from ${cwd}`);
58
+ // Fire-and-forget: retroactively route old captures to this new project
59
+ triggerRetroactiveRouting(data.id).catch(() => { });
60
+ return data;
61
+ }
62
+ /** Call the API server to retroactively route existing captures to a newly registered project */
63
+ async function triggerRetroactiveRouting(projectId) {
64
+ if (!hasAuth())
65
+ return;
66
+ try {
67
+ const config = loadConfig();
68
+ const auth = await loadAuth();
69
+ const url = `${config.apiServerUrl}/projects/${projectId}/retroactive-route`;
70
+ const res = await fetch(url, {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Authorization': `Bearer ${auth.accessToken}`,
74
+ 'Content-Type': 'application/json',
75
+ },
76
+ signal: AbortSignal.timeout(10000),
77
+ });
78
+ if (res.ok) {
79
+ console.error(`[Splicr] Retroactive routing triggered for project ${projectId}`);
80
+ }
81
+ }
82
+ catch {
83
+ // Best-effort — don't block project detection if API is unreachable
84
+ }
85
+ }
86
+ function detectProjectName(cwd) {
87
+ // Try package.json name first
88
+ const pkgPath = join(cwd, 'package.json');
89
+ if (existsSync(pkgPath)) {
90
+ try {
91
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
92
+ if (pkg.name) {
93
+ // Strip @scope/ prefix
94
+ return pkg.name.replace(/^@[^/]+\//, '');
95
+ }
96
+ }
97
+ catch { /* ignore parse errors */ }
98
+ }
99
+ // Fall back to directory name
100
+ return cwd.replace(/\\/g, '/').split('/').pop() || 'unnamed-project';
101
+ }
102
+ function detectDescription(cwd) {
103
+ // Try CLAUDE.md first — richest source
104
+ const claudePath = join(cwd, 'CLAUDE.md');
105
+ if (existsSync(claudePath)) {
106
+ try {
107
+ const content = readFileSync(claudePath, 'utf-8');
108
+ const desc = extractFirstParagraph(content);
109
+ if (desc)
110
+ return desc;
111
+ }
112
+ catch { /* ignore */ }
113
+ }
114
+ // Try README.md
115
+ const readmePath = join(cwd, 'README.md');
116
+ if (existsSync(readmePath)) {
117
+ try {
118
+ const content = readFileSync(readmePath, 'utf-8');
119
+ const desc = extractFirstParagraph(content);
120
+ if (desc)
121
+ return desc;
122
+ }
123
+ catch { /* ignore */ }
124
+ }
125
+ // Try package.json description
126
+ const pkgPath = join(cwd, 'package.json');
127
+ if (existsSync(pkgPath)) {
128
+ try {
129
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
130
+ if (pkg.description)
131
+ return pkg.description;
132
+ }
133
+ catch { /* ignore */ }
134
+ }
135
+ return `Local project at ${cwd}`;
136
+ }
137
+ function extractFirstParagraph(markdown) {
138
+ const lines = markdown.split('\n');
139
+ let paragraph = '';
140
+ let foundContent = false;
141
+ for (const line of lines) {
142
+ const trimmed = line.trim();
143
+ // Skip headings, empty lines, frontmatter, and code blocks
144
+ if (trimmed.startsWith('#') || trimmed.startsWith('---') || trimmed.startsWith('```')) {
145
+ if (foundContent)
146
+ break; // End of paragraph
147
+ continue;
148
+ }
149
+ if (trimmed === '') {
150
+ if (foundContent)
151
+ break; // End of paragraph
152
+ continue;
153
+ }
154
+ // Skip lines that look like metadata (tables, lists starting with |)
155
+ if (trimmed.startsWith('|'))
156
+ continue;
157
+ foundContent = true;
158
+ paragraph += (paragraph ? ' ' : '') + trimmed;
159
+ }
160
+ if (!paragraph)
161
+ return null;
162
+ // Cap at 500 chars
163
+ return paragraph.length > 500 ? paragraph.substring(0, 497) + '...' : paragraph;
164
+ }
165
+ const KNOWN_TECH = [
166
+ 'react', 'next', 'express', 'fastify', 'vue', 'angular', 'svelte',
167
+ 'tailwindcss', 'prisma', '@supabase/supabase-js', 'typescript',
168
+ 'react-native', 'expo', 'electron', '@modelcontextprotocol/sdk',
169
+ 'zod', 'drizzle-orm', 'mongoose', 'graphql', 'trpc',
170
+ ];
171
+ function detectTechStack(cwd) {
172
+ const techStack = [];
173
+ const pkgPath = join(cwd, 'package.json');
174
+ if (existsSync(pkgPath)) {
175
+ try {
176
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
177
+ const allDeps = { ...pkg.dependencies, ...pkg.devDependencies };
178
+ for (const tech of KNOWN_TECH) {
179
+ if (allDeps[tech]) {
180
+ techStack.push(tech.replace('@supabase/supabase-js', 'supabase').replace('@modelcontextprotocol/sdk', 'mcp'));
181
+ }
182
+ }
183
+ // Check for TypeScript via tsconfig
184
+ if (!allDeps['typescript'] && existsSync(join(cwd, 'tsconfig.json'))) {
185
+ techStack.push('typescript');
186
+ }
187
+ }
188
+ catch { /* ignore */ }
189
+ }
190
+ // Check for Python
191
+ if (existsSync(join(cwd, 'requirements.txt')) || existsSync(join(cwd, 'pyproject.toml'))) {
192
+ techStack.push('python');
193
+ }
194
+ // Check for Go
195
+ if (existsSync(join(cwd, 'go.mod'))) {
196
+ techStack.push('go');
197
+ }
198
+ // Check for Rust
199
+ if (existsSync(join(cwd, 'Cargo.toml'))) {
200
+ techStack.push('rust');
201
+ }
202
+ return techStack;
203
+ }
204
+ //# sourceMappingURL=auto-register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-register.js","sourceRoot":"","sources":["../../src/lib/auto-register.ts"],"names":[],"mappings":"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"}