@recall_v3/mcp-server 0.1.0 → 3.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/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @recall_v3/mcp-server
2
+
3
+ Team memory for AI coding assistants. Recall captures session context, summarizes with AI, and makes it available to your whole team.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npx @recall_v3/mcp-server install
9
+ ```
10
+
11
+ This will:
12
+ 1. Generate an API token
13
+ 2. Configure your Claude Code MCP settings
14
+ 3. Connect to your team's Recall workspace
15
+
16
+ ## Manual Configuration
17
+
18
+ Add to your Claude Code MCP config (`~/.config/claude/mcp.json`):
19
+
20
+ ```json
21
+ {
22
+ "mcpServers": {
23
+ "recall": {
24
+ "command": "npx",
25
+ "args": ["@recall_v3/mcp-server"],
26
+ "env": {
27
+ "RECALL_API_TOKEN": "your_token_here"
28
+ }
29
+ }
30
+ }
31
+ }
32
+ ```
33
+
34
+ ## Available Tools
35
+
36
+ Once installed, the following MCP tools are available in Claude Code:
37
+
38
+ | Tool | Description |
39
+ |------|-------------|
40
+ | `recall_get_context` | Load team context at session start |
41
+ | `recall_get_history` | Get recent session summaries |
42
+ | `recall_get_transcripts` | Deep dive into full transcripts (Pro) |
43
+ | `recall_save_session` | Save session summary at end |
44
+ | `recall_log_decision` | Log an important decision |
45
+
46
+ ## Environment Variables
47
+
48
+ | Variable | Description | Default |
49
+ |----------|-------------|---------|
50
+ | `RECALL_API_TOKEN` | Your API token | Required |
51
+ | `RECALL_API_URL` | API base URL | `https://api-v3.recall.team` |
52
+
53
+ ## Requirements
54
+
55
+ - Node.js 18+
56
+ - Claude Code or compatible MCP client
57
+
58
+ ## Links
59
+
60
+ - [Documentation](https://v3.recall.team/docs)
61
+ - [Dashboard](https://v3.recall.team/app)
62
+ - [Support](mailto:support@recall.team)
63
+
64
+ ## License
65
+
66
+ MIT
@@ -61,21 +61,123 @@ export declare class RecallApiClient {
61
61
  * Called by saveSession tool after reading JSONL from disk
62
62
  */
63
63
  summarize(request: SummarizeRequest): Promise<SummarizeResponse>;
64
+ /**
65
+ * Summarize a session transcript using the actual API format
66
+ * This bypasses the strict shared types which don't match the real API
67
+ * Uses 2 minute timeout since AI summarization can take time
68
+ */
69
+ summarizeRaw<TRequest, TResponse>(request: TRequest): Promise<TResponse>;
64
70
  /**
65
71
  * Save a session with manual input (not from transcript)
66
72
  * Used when user provides summary directly via MCP tool
73
+ * @deprecated Use saveSessionV3 for proper encrypted format
67
74
  */
68
75
  saveSession(repoId: string, data: SaveSessionRequest): Promise<SaveSessionResponse>;
76
+ /**
77
+ * Save a session with encrypted content (v3 format)
78
+ * This is the correct format for the v3 API
79
+ */
80
+ saveSessionV3(repoId: string, data: {
81
+ encrypted_content: string;
82
+ tldr: {
83
+ summary: string;
84
+ status: 'complete' | 'in-progress' | 'blocked';
85
+ decisions?: string[];
86
+ mistakes?: string[];
87
+ tags?: string[];
88
+ files_changed?: string[];
89
+ blockers?: string | null;
90
+ next_steps?: string | null;
91
+ };
92
+ started_at: string;
93
+ ended_at: string;
94
+ tool?: string;
95
+ }): Promise<{
96
+ id: string;
97
+ created_at: string;
98
+ warnings?: string[];
99
+ }>;
69
100
  /**
70
101
  * Get context for a repository (context.md)
71
102
  * Returns the distilled team brain for this repo
103
+ * @deprecated Use getContextV3 for proper v3 API format
72
104
  */
73
105
  getContext(repoId: string): Promise<GetContextResponse>;
106
+ /**
107
+ * Get context for a repository (v3 format)
108
+ * Returns sessions with encrypted content for client-side decryption
109
+ */
110
+ getContextV3(repoId: string): Promise<{
111
+ sessions: Array<{
112
+ id: string;
113
+ encrypted_content: string;
114
+ started_at: string;
115
+ ended_at: string;
116
+ status: string;
117
+ tldr_summary: string | null;
118
+ user: {
119
+ id: string;
120
+ name: string | null;
121
+ github_username: string | null;
122
+ avatar_url: string | null;
123
+ };
124
+ }>;
125
+ cached_context: string;
126
+ updated_at: string;
127
+ session_count: number;
128
+ tier: string;
129
+ }>;
74
130
  /**
75
131
  * Get history for a repository (context.md + history.md)
76
132
  * Returns more detail than getContext
133
+ * @deprecated Use getHistoryV3 for proper v3 API format
77
134
  */
78
135
  getHistory(repoId: string): Promise<GetHistoryResponse>;
136
+ /**
137
+ * Get history for a repository (v3 format)
138
+ * Returns sessions, decisions, and mistakes with encrypted content
139
+ */
140
+ getHistoryV3(repoId: string, days?: number): Promise<{
141
+ sessions: Array<{
142
+ id: string;
143
+ encrypted_content: string;
144
+ started_at: string;
145
+ ended_at: string;
146
+ status: string;
147
+ tldr_summary: string | null;
148
+ user: {
149
+ id: string;
150
+ name: string | null;
151
+ github_username: string | null;
152
+ avatar_url: string | null;
153
+ };
154
+ }>;
155
+ decisions: Array<{
156
+ id: string;
157
+ title: string;
158
+ encrypted_content: string;
159
+ created_at: string;
160
+ user: {
161
+ id: string;
162
+ name: string | null;
163
+ github_username: string | null;
164
+ avatar_url: string | null;
165
+ };
166
+ }>;
167
+ mistakes: Array<{
168
+ id: string;
169
+ title: string;
170
+ encrypted_content: string;
171
+ created_at: string;
172
+ user: {
173
+ id: string;
174
+ name: string | null;
175
+ github_username: string | null;
176
+ avatar_url: string | null;
177
+ };
178
+ }>;
179
+ token_warning?: string;
180
+ }>;
79
181
  /**
80
182
  * Get full transcripts for a repository
81
183
  * WARNING: Can be very large, uses many tokens
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,kBAAkB,EAClB,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,iBAAiB,EAElB,MAAM,mBAAmB,CAAC;AAG3B,qBAAa,cAAe,SAAQ,KAAK;aAGrB,IAAI,EAAE,MAAM;aACZ,UAAU,CAAC,EAAE,MAAM;aACnB,SAAS,EAAE,OAAO;gBAHlC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,UAAU,CAAC,EAAE,MAAM,YAAA,EACnB,SAAS,GAAE,OAAe;CAK7C;AAED,qBAAa,YAAa,SAAQ,cAAc;aACD,KAAK,CAAC,EAAE,KAAK;gBAA9C,OAAO,EAAE,MAAM,EAAkB,KAAK,CAAC,EAAE,KAAK,YAAA;CAI3D;AAED,qBAAa,mBAAoB,SAAQ,cAAc;gBACzC,OAAO,GAAE,MAAgC;CAItD;AAED,qBAAa,YAAa,SAAQ,cAAc;gBAClC,OAAO,GAAE,MAA4B;CAIlD;AAGD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,MAAM,EAAE,qBAAqB;IAQzC;;OAEG;YACW,OAAO;IAoGrB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAQxB;;OAEG;IACH,OAAO,CAAC,KAAK;IAQb;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAItE;;;OAGG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIzF;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI7D;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI7D;;;OAGG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAIrE;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIzF;;;OAGG;IACG,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAI1D;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAI7C;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAIhD;;;OAGG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAMzG"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
@@ -69,13 +69,14 @@ class RecallApiClient {
69
69
  /**
70
70
  * Internal fetch wrapper with timeout and retry logic
71
71
  */
72
- async request(method, path, body) {
72
+ async request(method, path, body, options) {
73
73
  const url = `${this.baseUrl}${path}`;
74
+ const timeout = options?.timeout ?? this.timeout;
74
75
  let lastError;
75
76
  for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
76
77
  try {
77
78
  const controller = new AbortController();
78
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
79
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
79
80
  try {
80
81
  const response = await fetch(url, {
81
82
  method,
@@ -104,11 +105,18 @@ class RecallApiClient {
104
105
  throw new RecallApiError(errorMessage, errorCode, response.status, false);
105
106
  }
106
107
  // Parse successful response
107
- const data = await response.json();
108
- if (!data.success && data.error) {
109
- throw new RecallApiError(data.error.message, data.error.code, response.status, false);
108
+ // API may return { success: true, data: T } or T directly
109
+ const rawData = await response.json();
110
+ // Check if it's a wrapped response
111
+ if (rawData && typeof rawData === 'object' && 'success' in rawData) {
112
+ const wrapped = rawData;
113
+ if (!wrapped.success && wrapped.error) {
114
+ throw new RecallApiError(wrapped.error.message, wrapped.error.code, response.status, false);
115
+ }
116
+ return wrapped.data;
110
117
  }
111
- return data.data;
118
+ // Direct response (not wrapped)
119
+ return rawData;
112
120
  }
113
121
  finally {
114
122
  clearTimeout(timeoutId);
@@ -175,67 +183,108 @@ class RecallApiClient {
175
183
  * Called by saveSession tool after reading JSONL from disk
176
184
  */
177
185
  async summarize(request) {
178
- return this.request('POST', '/api/v1/summarize', request);
186
+ return this.request('POST', '/v1/summarize', request);
187
+ }
188
+ /**
189
+ * Summarize a session transcript using the actual API format
190
+ * This bypasses the strict shared types which don't match the real API
191
+ * Uses 2 minute timeout since AI summarization can take time
192
+ */
193
+ async summarizeRaw(request) {
194
+ return this.request('POST', '/v1/summarize', request, { timeout: 120000 });
179
195
  }
180
196
  /**
181
197
  * Save a session with manual input (not from transcript)
182
198
  * Used when user provides summary directly via MCP tool
199
+ * @deprecated Use saveSessionV3 for proper encrypted format
183
200
  */
184
201
  async saveSession(repoId, data) {
185
- return this.request('POST', `/api/v1/repos/${repoId}/sessions`, data);
202
+ return this.request('POST', `/v1/repos/${repoId}/sessions`, data);
203
+ }
204
+ /**
205
+ * Save a session with encrypted content (v3 format)
206
+ * This is the correct format for the v3 API
207
+ */
208
+ async saveSessionV3(repoId, data) {
209
+ return this.request('POST', `/v1/repos/${repoId}/sessions`, data);
186
210
  }
187
211
  /**
188
212
  * Get context for a repository (context.md)
189
213
  * Returns the distilled team brain for this repo
214
+ * @deprecated Use getContextV3 for proper v3 API format
190
215
  */
191
216
  async getContext(repoId) {
192
- return this.request('GET', `/api/v1/repos/${repoId}/context`);
217
+ return this.request('GET', `/v1/repos/${repoId}/context`);
218
+ }
219
+ /**
220
+ * Get context for a repository (v3 format)
221
+ * Returns sessions with encrypted content for client-side decryption
222
+ */
223
+ async getContextV3(repoId) {
224
+ return this.request('GET', `/v1/repos/${repoId}/context`);
193
225
  }
194
226
  /**
195
227
  * Get history for a repository (context.md + history.md)
196
228
  * Returns more detail than getContext
229
+ * @deprecated Use getHistoryV3 for proper v3 API format
197
230
  */
198
231
  async getHistory(repoId) {
199
- return this.request('GET', `/api/v1/repos/${repoId}/history`);
232
+ return this.request('GET', `/v1/repos/${repoId}/history`);
233
+ }
234
+ /**
235
+ * Get history for a repository (v3 format)
236
+ * Returns sessions, decisions, and mistakes with encrypted content
237
+ */
238
+ async getHistoryV3(repoId, days = 30) {
239
+ return this.request('GET', `/v1/repos/${repoId}/history?days=${days}`);
200
240
  }
201
241
  /**
202
242
  * Get full transcripts for a repository
203
243
  * WARNING: Can be very large, uses many tokens
204
244
  */
205
245
  async getTranscripts(repoId) {
206
- return this.request('GET', `/api/v1/repos/${repoId}/transcripts`);
246
+ return this.request('GET', `/v1/repos/${repoId}/transcripts`);
207
247
  }
208
248
  /**
209
249
  * Log a decision for a repository
210
250
  */
211
251
  async logDecision(repoId, data) {
212
- return this.request('POST', `/api/v1/repos/${repoId}/decisions`, data);
252
+ return this.request('POST', `/v1/repos/${repoId}/decisions`, data);
213
253
  }
214
254
  /**
215
255
  * Get the encryption key for a team
216
256
  * Used to decrypt content locally
217
257
  */
218
258
  async getTeamKey(teamId) {
219
- return this.request('GET', `/api/v1/teams/${teamId}/key`);
259
+ // API returns { hasAccess, key, keyVersion, teamId, teamName, teamSlug, tier }
260
+ // Map to TeamKeyResponse type
261
+ const response = await this.request('GET', '/v1/keys/team');
262
+ return {
263
+ teamId: response.teamId,
264
+ encryptionKey: response.key,
265
+ keyVersion: response.keyVersion,
266
+ teamName: response.teamName,
267
+ tier: response.tier,
268
+ };
220
269
  }
221
270
  /**
222
271
  * List teams the user belongs to
223
272
  */
224
273
  async listTeams() {
225
- return this.request('GET', '/api/v1/teams');
274
+ return this.request('GET', '/v1/teams');
226
275
  }
227
276
  /**
228
277
  * Get authentication status
229
278
  */
230
279
  async getStatus() {
231
- return this.request('GET', '/api/v1/status');
280
+ return this.request('GET', '/v1/status');
232
281
  }
233
282
  /**
234
283
  * Lookup or create repo by GitHub repo info
235
284
  * Returns the repo ID for subsequent API calls
236
285
  */
237
286
  async resolveRepo(fullName, defaultBranch) {
238
- return this.request('POST', '/api/v1/repos/resolve', {
287
+ return this.request('POST', '/v1/repos/resolve', {
239
288
  fullName,
240
289
  defaultBranch,
241
290
  });
package/dist/cli.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Recall v3 CLI
4
+ *
5
+ * Command-line interface for Recall MCP operations.
6
+ * Used by Claude Code hooks and for manual operations.
7
+ *
8
+ * Commands:
9
+ * recall-mcp context Load and display team context
10
+ * recall-mcp save Save current session
11
+ * recall-mcp status Check authentication status
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
package/dist/cli.js ADDED
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * Recall v3 CLI
5
+ *
6
+ * Command-line interface for Recall MCP operations.
7
+ * Used by Claude Code hooks and for manual operations.
8
+ *
9
+ * Commands:
10
+ * recall-mcp context Load and display team context
11
+ * recall-mcp save Save current session
12
+ * recall-mcp status Check authentication status
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const getContext_js_1 = require("./tools/getContext.js");
16
+ const saveSession_js_1 = require("./tools/saveSession.js");
17
+ const index_js_1 = require("./config/index.js");
18
+ const client_js_1 = require("./api/client.js");
19
+ // Parse command line arguments
20
+ const args = process.argv.slice(2);
21
+ const command = args[0];
22
+ const flags = args.slice(1);
23
+ // Check for --quiet flag
24
+ const quiet = flags.includes('--quiet') || flags.includes('-q');
25
+ // Helper to log only if not quiet
26
+ function log(message) {
27
+ if (!quiet) {
28
+ console.log(message);
29
+ }
30
+ }
31
+ // Helper to log errors (always shown)
32
+ function logError(message) {
33
+ console.error(message);
34
+ }
35
+ /**
36
+ * Parse --project-path flag from args
37
+ */
38
+ function getProjectPathFromFlags(flagList) {
39
+ const projectPathIndex = flagList.findIndex(f => f === '--project-path' || f === '-p');
40
+ if (projectPathIndex !== -1 && flagList[projectPathIndex + 1]) {
41
+ return flagList[projectPathIndex + 1];
42
+ }
43
+ return undefined;
44
+ }
45
+ // Parse --project-path flag
46
+ const projectPath = getProjectPathFromFlags(flags);
47
+ /**
48
+ * Display usage information
49
+ */
50
+ function showHelp() {
51
+ console.log(`
52
+ Recall v3 CLI - Team memory for AI coding assistants
53
+
54
+ Usage:
55
+ recall-mcp <command> [options]
56
+
57
+ Commands:
58
+ context Load team context for current repository
59
+ save Save current session (with AI summarization)
60
+ status Check authentication and connection status
61
+ help Show this help message
62
+
63
+ Options:
64
+ --quiet, -q Suppress non-essential output
65
+ --summary, -s Provide a manual summary (skips AI summarization)
66
+ --project-path, -p PATH Specify the git repository path (defaults to cwd)
67
+
68
+ Save Command:
69
+ The save command supports three modes:
70
+
71
+ 1. AI Summarization (recommended):
72
+ echo "<conversation transcript>" | recall-mcp save
73
+ - Pipes conversation to API for AI-powered summarization
74
+ - Extracts decisions, mistakes, files changed automatically
75
+
76
+ 2. Manual Summary:
77
+ recall-mcp save --summary "Fixed authentication bug"
78
+ - Use when you want to write your own summary
79
+
80
+ 3. Placeholder (fallback):
81
+ recall-mcp save --quiet
82
+ - Saves minimal placeholder when no transcript available
83
+ - Used by Claude Code stop hooks as a backup
84
+
85
+ Examples:
86
+ recall-mcp context # Load context at session start
87
+ recall-mcp save --quiet # Save session quietly (for hooks)
88
+ recall-mcp save --summary "Fixed X" # Save with manual summary
89
+ cat transcript.txt | recall-mcp save # Save with AI summarization
90
+ recall-mcp save -p /path/to/repo --quiet # Save for specific repo (Claude Code hooks)
91
+ recall-mcp context --project-path /path/to/repo # Load context for specific repo
92
+ recall-mcp status # Check if properly configured
93
+
94
+ Environment Variables:
95
+ RECALL_API_URL API base URL (default: https://api-v3.recall.team)
96
+ RECALL_API_TOKEN Authentication token (required)
97
+ `);
98
+ }
99
+ /**
100
+ * Load and display team context
101
+ */
102
+ async function runContext() {
103
+ try {
104
+ const result = await (0, getContext_js_1.getContext)({ projectPath });
105
+ if (result.isError) {
106
+ logError(`Error: ${result.content[0]?.text || 'Unknown error'}`);
107
+ process.exit(1);
108
+ }
109
+ // Output the context (always, even in quiet mode - it's the primary output)
110
+ console.log(result.content[0]?.text || '');
111
+ }
112
+ catch (error) {
113
+ const message = error instanceof Error ? error.message : 'Unknown error';
114
+ logError(`Failed to load context: ${message}`);
115
+ process.exit(1);
116
+ }
117
+ }
118
+ /**
119
+ * Read from stdin if available (non-blocking)
120
+ * Handles large transcripts (Claude Code sessions can be 1MB+)
121
+ */
122
+ async function readStdin() {
123
+ return new Promise((resolve) => {
124
+ // Check if stdin has data (is a pipe, not TTY)
125
+ if (process.stdin.isTTY) {
126
+ resolve(null);
127
+ return;
128
+ }
129
+ const chunks = [];
130
+ let resolved = false;
131
+ // Initial timeout - wait up to 500ms for first data
132
+ let timeout = setTimeout(() => {
133
+ if (!resolved) {
134
+ resolved = true;
135
+ resolve(chunks.length > 0 ? chunks.join('') : null);
136
+ }
137
+ }, 500);
138
+ process.stdin.setEncoding('utf8');
139
+ process.stdin.on('data', (chunk) => {
140
+ // Clear and reset timeout on each chunk (allows large files)
141
+ clearTimeout(timeout);
142
+ chunks.push(chunk);
143
+ // Set a shorter timeout between chunks (100ms idle = end of data)
144
+ timeout = setTimeout(() => {
145
+ if (!resolved) {
146
+ resolved = true;
147
+ resolve(chunks.join(''));
148
+ }
149
+ }, 100);
150
+ });
151
+ process.stdin.on('end', () => {
152
+ clearTimeout(timeout);
153
+ if (!resolved) {
154
+ resolved = true;
155
+ resolve(chunks.length > 0 ? chunks.join('') : null);
156
+ }
157
+ });
158
+ process.stdin.on('error', () => {
159
+ clearTimeout(timeout);
160
+ if (!resolved) {
161
+ resolved = true;
162
+ resolve(null);
163
+ }
164
+ });
165
+ // Start reading
166
+ process.stdin.resume();
167
+ });
168
+ }
169
+ /**
170
+ * Save current session
171
+ *
172
+ * Supports two modes:
173
+ * 1. With transcript via stdin: `echo "conversation..." | recall-mcp save`
174
+ * - Sends transcript to API for AI summarization
175
+ * 2. Without transcript: `recall-mcp save`
176
+ * - Saves a minimal placeholder (fallback)
177
+ *
178
+ * Usage with --summary flag: `recall-mcp save --summary "What we did"`
179
+ */
180
+ async function runSave() {
181
+ try {
182
+ // Check for --summary flag
183
+ const summaryIndex = flags.findIndex(f => f === '--summary' || f === '-s');
184
+ let providedSummary;
185
+ if (summaryIndex !== -1 && flags[summaryIndex + 1]) {
186
+ providedSummary = flags[summaryIndex + 1];
187
+ }
188
+ // Try to read transcript from stdin
189
+ const transcript = await readStdin();
190
+ // Build saveSession args
191
+ const saveArgs = {};
192
+ // Add project path if specified
193
+ if (projectPath) {
194
+ saveArgs.projectPath = projectPath;
195
+ }
196
+ if (transcript && transcript.trim().length >= 50) {
197
+ // We have a transcript - send it for AI summarization
198
+ saveArgs.transcript = transcript;
199
+ log('Summarizing session with AI...');
200
+ }
201
+ else if (providedSummary) {
202
+ // Use provided summary
203
+ saveArgs.summary = providedSummary;
204
+ }
205
+ else {
206
+ // Fallback to placeholder - this is the minimal save case
207
+ saveArgs.summary = 'Session auto-saved by Recall hook';
208
+ }
209
+ const result = await (0, saveSession_js_1.saveSession)(saveArgs);
210
+ if (result.isError) {
211
+ logError(`Error: ${result.content[0]?.text || 'Unknown error'}`);
212
+ process.exit(1);
213
+ }
214
+ log(result.content[0]?.text || 'Session saved');
215
+ }
216
+ catch (error) {
217
+ const message = error instanceof Error ? error.message : 'Unknown error';
218
+ logError(`Failed to save session: ${message}`);
219
+ process.exit(1);
220
+ }
221
+ }
222
+ /**
223
+ * Check authentication and connection status
224
+ */
225
+ async function runStatus() {
226
+ try {
227
+ const token = (0, index_js_1.getApiToken)();
228
+ const baseUrl = (0, index_js_1.getApiBaseUrl)();
229
+ console.log('Recall v3 Status');
230
+ console.log('================');
231
+ console.log(`Config: ${(0, index_js_1.getConfigPath)()}`);
232
+ console.log(`API URL: ${baseUrl}`);
233
+ console.log(`Token: ${token ? `${token.substring(0, 10)}...` : 'Not set'}`);
234
+ if (!token) {
235
+ logError('\nError: RECALL_API_TOKEN not set');
236
+ logError(`Set the environment variable or configure ${(0, index_js_1.getConfigPath)()}`);
237
+ process.exit(1);
238
+ }
239
+ // Test API connection
240
+ const client = new client_js_1.RecallApiClient({
241
+ baseUrl,
242
+ token,
243
+ });
244
+ console.log('\nTesting API connection...');
245
+ try {
246
+ const teams = await client.listTeams();
247
+ console.log(`✓ Connected successfully`);
248
+ console.log(`✓ Teams: ${teams.teams?.length || 0}`);
249
+ if (teams.teams && teams.teams.length > 0) {
250
+ for (const team of teams.teams) {
251
+ console.log(` - ${team.name} (${team.role})`);
252
+ }
253
+ }
254
+ }
255
+ catch (apiError) {
256
+ const message = apiError instanceof Error ? apiError.message : 'Unknown error';
257
+ logError(`✗ API connection failed: ${message}`);
258
+ process.exit(1);
259
+ }
260
+ }
261
+ catch (error) {
262
+ const message = error instanceof Error ? error.message : 'Unknown error';
263
+ logError(`Status check failed: ${message}`);
264
+ process.exit(1);
265
+ }
266
+ }
267
+ /**
268
+ * Main entry point
269
+ */
270
+ async function main() {
271
+ switch (command) {
272
+ case 'context':
273
+ await runContext();
274
+ break;
275
+ case 'save':
276
+ await runSave();
277
+ break;
278
+ case 'status':
279
+ await runStatus();
280
+ break;
281
+ case 'help':
282
+ case '--help':
283
+ case '-h':
284
+ case undefined:
285
+ showHelp();
286
+ break;
287
+ default:
288
+ logError(`Unknown command: ${command}`);
289
+ logError('Run "recall-mcp help" for usage information');
290
+ process.exit(1);
291
+ }
292
+ }
293
+ // Run CLI
294
+ main().catch((error) => {
295
+ logError(`Fatal error: ${error instanceof Error ? error.message : 'Unknown error'}`);
296
+ process.exit(1);
297
+ });