@recall_v3/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.
- package/dist/api/client.d.ts +111 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +244 -0
- package/dist/config/index.d.ts +89 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +256 -0
- package/dist/crypto/index.d.ts +56 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +224 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +189 -0
- package/dist/tools/getContext.d.ts +18 -0
- package/dist/tools/getContext.d.ts.map +1 -0
- package/dist/tools/getContext.js +87 -0
- package/dist/tools/getHistory.d.ts +18 -0
- package/dist/tools/getHistory.d.ts.map +1 -0
- package/dist/tools/getHistory.js +97 -0
- package/dist/tools/getTranscripts.d.ts +19 -0
- package/dist/tools/getTranscripts.d.ts.map +1 -0
- package/dist/tools/getTranscripts.js +129 -0
- package/dist/tools/index.d.ts +13 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +37 -0
- package/dist/tools/logDecision.d.ts +19 -0
- package/dist/tools/logDecision.d.ts.map +1 -0
- package/dist/tools/logDecision.js +92 -0
- package/dist/tools/saveSession.d.ts +26 -0
- package/dist/tools/saveSession.d.ts.map +1 -0
- package/dist/tools/saveSession.js +115 -0
- package/dist/tools/types.d.ts +32 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +33 -0
- package/dist/tools/utils.d.ts +52 -0
- package/dist/tools/utils.d.ts.map +1 -0
- package/dist/tools/utils.js +238 -0
- package/package.json +46 -0
- package/src/api/client.ts +295 -0
- package/src/config/index.ts +247 -0
- package/src/crypto/index.ts +207 -0
- package/src/index.ts +232 -0
- package/src/tools/getContext.ts +106 -0
- package/src/tools/getHistory.ts +118 -0
- package/src/tools/getTranscripts.ts +150 -0
- package/src/tools/index.ts +13 -0
- package/src/tools/logDecision.ts +118 -0
- package/src/tools/saveSession.ts +159 -0
- package/src/tools/types.ts +47 -0
- package/src/tools/utils.ts +226 -0
- package/tsconfig.json +14 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* logDecision Tool Implementation
|
|
4
|
+
*
|
|
5
|
+
* Logs an important decision made during coding.
|
|
6
|
+
* Quick way to capture why something was done.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.logDecision = logDecision;
|
|
10
|
+
const client_js_1 = require("../api/client.js");
|
|
11
|
+
const index_js_1 = require("../config/index.js");
|
|
12
|
+
const types_js_1 = require("./types.js");
|
|
13
|
+
const utils_js_1 = require("./utils.js");
|
|
14
|
+
/**
|
|
15
|
+
* Execute the logDecision tool
|
|
16
|
+
*
|
|
17
|
+
* @param args - Tool arguments with decision and reasoning
|
|
18
|
+
* @returns MCP tool response with confirmation
|
|
19
|
+
*/
|
|
20
|
+
async function logDecision(args) {
|
|
21
|
+
try {
|
|
22
|
+
// Validate required fields
|
|
23
|
+
if (!args.decision || args.decision.trim().length === 0) {
|
|
24
|
+
return (0, types_js_1.errorResponse)('Decision is required. Please describe what was decided.');
|
|
25
|
+
}
|
|
26
|
+
if (!args.reasoning || args.reasoning.trim().length === 0) {
|
|
27
|
+
return (0, types_js_1.errorResponse)('Reasoning is required. Please explain why this decision was made.');
|
|
28
|
+
}
|
|
29
|
+
// Get API token
|
|
30
|
+
const token = (0, index_js_1.getApiToken)();
|
|
31
|
+
if (!token) {
|
|
32
|
+
return (0, types_js_1.errorResponse)('Not authenticated. Run `recall auth` to connect your account, or set RECALL_API_TOKEN environment variable.');
|
|
33
|
+
}
|
|
34
|
+
// Resolve project path (use cwd)
|
|
35
|
+
const projectPath = (0, utils_js_1.resolveProjectPath)(undefined);
|
|
36
|
+
// Get repo info from git
|
|
37
|
+
const repoInfo = await (0, utils_js_1.getRepoInfo)(projectPath);
|
|
38
|
+
if (!repoInfo) {
|
|
39
|
+
return (0, types_js_1.errorResponse)(`Could not determine repository info for: ${projectPath}\n` +
|
|
40
|
+
'Make sure this is a git repository with a remote origin.');
|
|
41
|
+
}
|
|
42
|
+
// Create API client
|
|
43
|
+
const client = new client_js_1.RecallApiClient({
|
|
44
|
+
baseUrl: (0, index_js_1.getApiBaseUrl)(),
|
|
45
|
+
token,
|
|
46
|
+
});
|
|
47
|
+
// Resolve repo to get repoId and teamId
|
|
48
|
+
const { repoId, teamId } = await client.resolveRepo(repoInfo.fullName, repoInfo.defaultBranch);
|
|
49
|
+
// Get team key (fetch from API if not cached)
|
|
50
|
+
let teamKey = (0, index_js_1.getTeamKey)(teamId);
|
|
51
|
+
if (!teamKey) {
|
|
52
|
+
const keyResponse = await client.getTeamKey(teamId);
|
|
53
|
+
teamKey = keyResponse.encryptionKey;
|
|
54
|
+
(0, index_js_1.setTeamKey)(teamId, teamKey);
|
|
55
|
+
}
|
|
56
|
+
// Build the decision request
|
|
57
|
+
const decisionRequest = {
|
|
58
|
+
decision: args.decision.trim(),
|
|
59
|
+
reasoning: args.reasoning.trim(),
|
|
60
|
+
};
|
|
61
|
+
// Log decision via API
|
|
62
|
+
const response = await client.logDecision(repoId, decisionRequest);
|
|
63
|
+
// Build success message
|
|
64
|
+
const successMessage = [
|
|
65
|
+
`Decision logged successfully.`,
|
|
66
|
+
``,
|
|
67
|
+
`Decision ID: ${response.decisionId}`,
|
|
68
|
+
`Repository: ${repoInfo.fullName}`,
|
|
69
|
+
``,
|
|
70
|
+
`What: ${args.decision.trim()}`,
|
|
71
|
+
`Why: ${args.reasoning.trim()}`,
|
|
72
|
+
``,
|
|
73
|
+
`This decision has been added to the team memory.`,
|
|
74
|
+
].join('\n');
|
|
75
|
+
return (0, types_js_1.successResponse)(successMessage);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
if (error instanceof client_js_1.AuthenticationError) {
|
|
79
|
+
return (0, types_js_1.errorResponse)('Authentication failed. Your token may have expired.\n' +
|
|
80
|
+
'Run `recall auth` to reconnect your account.');
|
|
81
|
+
}
|
|
82
|
+
if (error instanceof client_js_1.RecallApiError) {
|
|
83
|
+
if (error.code === 'REPO_NOT_FOUND') {
|
|
84
|
+
return (0, types_js_1.errorResponse)('This repository is not connected to Recall.\n' +
|
|
85
|
+
'Run `recall init` to set up team memory for this repo.');
|
|
86
|
+
}
|
|
87
|
+
return (0, types_js_1.errorResponse)(`API Error: ${error.message}`);
|
|
88
|
+
}
|
|
89
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
90
|
+
return (0, types_js_1.errorResponse)(message);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* saveSession Tool Implementation
|
|
3
|
+
*
|
|
4
|
+
* Saves a summary of what was accomplished in this coding session.
|
|
5
|
+
* Updates the team memory files via the Recall API.
|
|
6
|
+
*/
|
|
7
|
+
import { type ToolResponse } from './types.js';
|
|
8
|
+
export interface SaveSessionArgs {
|
|
9
|
+
summary: string;
|
|
10
|
+
decisions?: Array<{
|
|
11
|
+
what: string;
|
|
12
|
+
why: string;
|
|
13
|
+
}>;
|
|
14
|
+
mistakes?: string[];
|
|
15
|
+
filesChanged?: string[];
|
|
16
|
+
nextSteps?: string;
|
|
17
|
+
blockers?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Execute the saveSession tool
|
|
21
|
+
*
|
|
22
|
+
* @param args - Tool arguments with session summary and metadata
|
|
23
|
+
* @returns MCP tool response with save confirmation
|
|
24
|
+
*/
|
|
25
|
+
export declare function saveSession(args: SaveSessionArgs): Promise<ToolResponse>;
|
|
26
|
+
//# sourceMappingURL=saveSession.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"saveSession.d.ts","sourceRoot":"","sources":["../../src/tools/saveSession.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAkC,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAI/E,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,CAAC,CA8H9E"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* saveSession Tool Implementation
|
|
4
|
+
*
|
|
5
|
+
* Saves a summary of what was accomplished in this coding session.
|
|
6
|
+
* Updates the team memory files via the Recall API.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.saveSession = saveSession;
|
|
10
|
+
const client_js_1 = require("../api/client.js");
|
|
11
|
+
const index_js_1 = require("../config/index.js");
|
|
12
|
+
const types_js_1 = require("./types.js");
|
|
13
|
+
const utils_js_1 = require("./utils.js");
|
|
14
|
+
/**
|
|
15
|
+
* Execute the saveSession tool
|
|
16
|
+
*
|
|
17
|
+
* @param args - Tool arguments with session summary and metadata
|
|
18
|
+
* @returns MCP tool response with save confirmation
|
|
19
|
+
*/
|
|
20
|
+
async function saveSession(args) {
|
|
21
|
+
try {
|
|
22
|
+
// Validate required fields
|
|
23
|
+
if (!args.summary || args.summary.trim().length === 0) {
|
|
24
|
+
return (0, types_js_1.errorResponse)('Summary is required. Please provide a description of what was accomplished.');
|
|
25
|
+
}
|
|
26
|
+
// Get API token
|
|
27
|
+
const token = (0, index_js_1.getApiToken)();
|
|
28
|
+
if (!token) {
|
|
29
|
+
return (0, types_js_1.errorResponse)('Not authenticated. Run `recall auth` to connect your account, or set RECALL_API_TOKEN environment variable.');
|
|
30
|
+
}
|
|
31
|
+
// Resolve project path (use cwd since projectPath is not in args)
|
|
32
|
+
const projectPath = (0, utils_js_1.resolveProjectPath)(undefined);
|
|
33
|
+
// Get repo info from git
|
|
34
|
+
const repoInfo = await (0, utils_js_1.getRepoInfo)(projectPath);
|
|
35
|
+
if (!repoInfo) {
|
|
36
|
+
return (0, types_js_1.errorResponse)(`Could not determine repository info for: ${projectPath}\n` +
|
|
37
|
+
'Make sure this is a git repository with a remote origin.');
|
|
38
|
+
}
|
|
39
|
+
// Create API client
|
|
40
|
+
const client = new client_js_1.RecallApiClient({
|
|
41
|
+
baseUrl: (0, index_js_1.getApiBaseUrl)(),
|
|
42
|
+
token,
|
|
43
|
+
});
|
|
44
|
+
// Resolve repo to get repoId and teamId
|
|
45
|
+
const { repoId, teamId } = await client.resolveRepo(repoInfo.fullName, repoInfo.defaultBranch);
|
|
46
|
+
// Get team key (fetch from API if not cached)
|
|
47
|
+
let teamKey = (0, index_js_1.getTeamKey)(teamId);
|
|
48
|
+
if (!teamKey) {
|
|
49
|
+
const keyResponse = await client.getTeamKey(teamId);
|
|
50
|
+
teamKey = keyResponse.encryptionKey;
|
|
51
|
+
(0, index_js_1.setTeamKey)(teamId, teamKey);
|
|
52
|
+
}
|
|
53
|
+
// Build the session request
|
|
54
|
+
const sessionRequest = {
|
|
55
|
+
summary: args.summary.trim(),
|
|
56
|
+
};
|
|
57
|
+
if (args.decisions && args.decisions.length > 0) {
|
|
58
|
+
sessionRequest.decisions = args.decisions;
|
|
59
|
+
}
|
|
60
|
+
if (args.mistakes && args.mistakes.length > 0) {
|
|
61
|
+
sessionRequest.mistakes = args.mistakes;
|
|
62
|
+
}
|
|
63
|
+
if (args.filesChanged && args.filesChanged.length > 0) {
|
|
64
|
+
sessionRequest.filesChanged = args.filesChanged;
|
|
65
|
+
}
|
|
66
|
+
if (args.nextSteps && args.nextSteps.trim().length > 0) {
|
|
67
|
+
sessionRequest.nextSteps = args.nextSteps.trim();
|
|
68
|
+
}
|
|
69
|
+
if (args.blockers && args.blockers.trim().length > 0) {
|
|
70
|
+
sessionRequest.blockers = args.blockers.trim();
|
|
71
|
+
}
|
|
72
|
+
// Save session via API
|
|
73
|
+
const response = await client.saveSession(repoId, sessionRequest);
|
|
74
|
+
// Build success message
|
|
75
|
+
const parts = [
|
|
76
|
+
`Session saved successfully.`,
|
|
77
|
+
``,
|
|
78
|
+
`Session ID: ${response.sessionId}`,
|
|
79
|
+
`Repository: ${repoInfo.fullName}`,
|
|
80
|
+
];
|
|
81
|
+
if (args.decisions && args.decisions.length > 0) {
|
|
82
|
+
parts.push(`Decisions logged: ${args.decisions.length}`);
|
|
83
|
+
}
|
|
84
|
+
if (args.mistakes && args.mistakes.length > 0) {
|
|
85
|
+
parts.push(`Mistakes documented: ${args.mistakes.length}`);
|
|
86
|
+
}
|
|
87
|
+
if (args.filesChanged && args.filesChanged.length > 0) {
|
|
88
|
+
parts.push(`Files tracked: ${args.filesChanged.length}`);
|
|
89
|
+
}
|
|
90
|
+
if (args.nextSteps) {
|
|
91
|
+
parts.push(`Next steps: ${args.nextSteps}`);
|
|
92
|
+
}
|
|
93
|
+
if (args.blockers) {
|
|
94
|
+
parts.push(`Blockers: ${args.blockers}`);
|
|
95
|
+
}
|
|
96
|
+
parts.push(``);
|
|
97
|
+
parts.push(`Team memory has been updated.`);
|
|
98
|
+
return (0, types_js_1.successResponse)(parts.join('\n'));
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
if (error instanceof client_js_1.AuthenticationError) {
|
|
102
|
+
return (0, types_js_1.errorResponse)('Authentication failed. Your token may have expired.\n' +
|
|
103
|
+
'Run `recall auth` to reconnect your account.');
|
|
104
|
+
}
|
|
105
|
+
if (error instanceof client_js_1.RecallApiError) {
|
|
106
|
+
if (error.code === 'REPO_NOT_FOUND') {
|
|
107
|
+
return (0, types_js_1.errorResponse)('This repository is not connected to Recall.\n' +
|
|
108
|
+
'Run `recall init` to set up team memory for this repo.');
|
|
109
|
+
}
|
|
110
|
+
return (0, types_js_1.errorResponse)(`API Error: ${error.message}`);
|
|
111
|
+
}
|
|
112
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
113
|
+
return (0, types_js_1.errorResponse)(message);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Tool Response Types
|
|
3
|
+
*
|
|
4
|
+
* Shared types for tool implementations.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Content item in MCP response
|
|
8
|
+
*/
|
|
9
|
+
export interface TextContent {
|
|
10
|
+
type: 'text';
|
|
11
|
+
text: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Standard MCP tool response format
|
|
15
|
+
*/
|
|
16
|
+
export interface ToolResponse {
|
|
17
|
+
content: TextContent[];
|
|
18
|
+
isError?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a success response
|
|
22
|
+
*/
|
|
23
|
+
export declare function successResponse(text: string): ToolResponse;
|
|
24
|
+
/**
|
|
25
|
+
* Create an error response
|
|
26
|
+
*/
|
|
27
|
+
export declare function errorResponse(message: string): ToolResponse;
|
|
28
|
+
/**
|
|
29
|
+
* Create a formatted response with header and content
|
|
30
|
+
*/
|
|
31
|
+
export declare function formattedResponse(header: string, content: string): ToolResponse;
|
|
32
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/tools/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,CAI1D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAK3D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,YAAY,CAE/E"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* MCP Tool Response Types
|
|
4
|
+
*
|
|
5
|
+
* Shared types for tool implementations.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.successResponse = successResponse;
|
|
9
|
+
exports.errorResponse = errorResponse;
|
|
10
|
+
exports.formattedResponse = formattedResponse;
|
|
11
|
+
/**
|
|
12
|
+
* Create a success response
|
|
13
|
+
*/
|
|
14
|
+
function successResponse(text) {
|
|
15
|
+
return {
|
|
16
|
+
content: [{ type: 'text', text }],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Create an error response
|
|
21
|
+
*/
|
|
22
|
+
function errorResponse(message) {
|
|
23
|
+
return {
|
|
24
|
+
content: [{ type: 'text', text: `Error: ${message}` }],
|
|
25
|
+
isError: true,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Create a formatted response with header and content
|
|
30
|
+
*/
|
|
31
|
+
function formattedResponse(header, content) {
|
|
32
|
+
return successResponse(`[${header}]\n\n${content}`);
|
|
33
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Utilities
|
|
3
|
+
*
|
|
4
|
+
* Shared utilities for tool implementations.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Repository information extracted from git
|
|
8
|
+
*/
|
|
9
|
+
export interface RepoInfo {
|
|
10
|
+
/** Full name like "owner/repo" */
|
|
11
|
+
fullName: string;
|
|
12
|
+
/** Remote URL */
|
|
13
|
+
remoteUrl: string;
|
|
14
|
+
/** Default branch name */
|
|
15
|
+
defaultBranch: string;
|
|
16
|
+
/** Local repo root path */
|
|
17
|
+
localPath: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resolve project path from argument or cwd
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveProjectPath(projectPath?: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Find the git root directory for a given path
|
|
25
|
+
*/
|
|
26
|
+
export declare function findGitRoot(startPath: string): string | null;
|
|
27
|
+
/**
|
|
28
|
+
* Get repository information from git
|
|
29
|
+
*/
|
|
30
|
+
export declare function getRepoInfo(projectPath: string): Promise<RepoInfo | null>;
|
|
31
|
+
/**
|
|
32
|
+
* Read a file if it exists
|
|
33
|
+
*/
|
|
34
|
+
export declare function readFileIfExists(filePath: string): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Get the Claude session directory for a project
|
|
37
|
+
* Claude stores sessions in ~/.claude/projects/<hash>/
|
|
38
|
+
*/
|
|
39
|
+
export declare function getClaudeSessionDir(projectPath: string): string | null;
|
|
40
|
+
/**
|
|
41
|
+
* Find the most recent Claude session JSONL file
|
|
42
|
+
*/
|
|
43
|
+
export declare function findLatestSessionFile(projectPath: string): string | null;
|
|
44
|
+
/**
|
|
45
|
+
* Format bytes to human-readable size
|
|
46
|
+
*/
|
|
47
|
+
export declare function formatBytes(bytes: number): string;
|
|
48
|
+
/**
|
|
49
|
+
* Format duration in seconds to human-readable
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatDuration(seconds: number): string;
|
|
52
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/tools/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0BAA0B;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAW5D;AAqCD;;GAEG;AACH,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAoC/E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMhE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoCtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAoBxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIjD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAMtD"}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool Utilities
|
|
4
|
+
*
|
|
5
|
+
* Shared utilities for tool implementations.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.resolveProjectPath = resolveProjectPath;
|
|
42
|
+
exports.findGitRoot = findGitRoot;
|
|
43
|
+
exports.getRepoInfo = getRepoInfo;
|
|
44
|
+
exports.readFileIfExists = readFileIfExists;
|
|
45
|
+
exports.getClaudeSessionDir = getClaudeSessionDir;
|
|
46
|
+
exports.findLatestSessionFile = findLatestSessionFile;
|
|
47
|
+
exports.formatBytes = formatBytes;
|
|
48
|
+
exports.formatDuration = formatDuration;
|
|
49
|
+
const fs = __importStar(require("node:fs"));
|
|
50
|
+
const path = __importStar(require("node:path"));
|
|
51
|
+
const node_child_process_1 = require("node:child_process");
|
|
52
|
+
/**
|
|
53
|
+
* Resolve project path from argument or cwd
|
|
54
|
+
*/
|
|
55
|
+
function resolveProjectPath(projectPath) {
|
|
56
|
+
if (projectPath) {
|
|
57
|
+
// Resolve to absolute path
|
|
58
|
+
return path.resolve(projectPath);
|
|
59
|
+
}
|
|
60
|
+
// Use current working directory
|
|
61
|
+
return process.cwd();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Find the git root directory for a given path
|
|
65
|
+
*/
|
|
66
|
+
function findGitRoot(startPath) {
|
|
67
|
+
let currentPath = path.resolve(startPath);
|
|
68
|
+
while (currentPath !== path.dirname(currentPath)) {
|
|
69
|
+
if (fs.existsSync(path.join(currentPath, '.git'))) {
|
|
70
|
+
return currentPath;
|
|
71
|
+
}
|
|
72
|
+
currentPath = path.dirname(currentPath);
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Execute a git command in the given directory
|
|
78
|
+
*/
|
|
79
|
+
function execGit(args, cwd) {
|
|
80
|
+
try {
|
|
81
|
+
const result = (0, node_child_process_1.execSync)(`git ${args.join(' ')}`, {
|
|
82
|
+
cwd,
|
|
83
|
+
encoding: 'utf-8',
|
|
84
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
85
|
+
});
|
|
86
|
+
return result.trim();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Parse GitHub/GitLab remote URL to get owner/repo
|
|
94
|
+
*/
|
|
95
|
+
function parseRemoteUrl(url) {
|
|
96
|
+
// SSH format: git@github.com:owner/repo.git
|
|
97
|
+
const sshMatch = url.match(/git@[\w.-]+:([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
|
|
98
|
+
if (sshMatch) {
|
|
99
|
+
return sshMatch[1];
|
|
100
|
+
}
|
|
101
|
+
// HTTPS format: https://github.com/owner/repo.git
|
|
102
|
+
const httpsMatch = url.match(/https?:\/\/[\w.-]+\/([\w.-]+\/[\w.-]+?)(?:\.git)?$/);
|
|
103
|
+
if (httpsMatch) {
|
|
104
|
+
return httpsMatch[1];
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get repository information from git
|
|
110
|
+
*/
|
|
111
|
+
async function getRepoInfo(projectPath) {
|
|
112
|
+
// Find git root
|
|
113
|
+
const gitRoot = findGitRoot(projectPath);
|
|
114
|
+
if (!gitRoot) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
// Get remote URL
|
|
118
|
+
const remoteUrl = execGit(['config', '--get', 'remote.origin.url'], gitRoot);
|
|
119
|
+
if (!remoteUrl) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
// Parse full name from URL
|
|
123
|
+
const fullName = parseRemoteUrl(remoteUrl);
|
|
124
|
+
if (!fullName) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
// Get default branch
|
|
128
|
+
// Try to get from remote HEAD first
|
|
129
|
+
let defaultBranch = execGit(['symbolic-ref', 'refs/remotes/origin/HEAD', '--short'], gitRoot);
|
|
130
|
+
if (defaultBranch) {
|
|
131
|
+
// Remove "origin/" prefix
|
|
132
|
+
defaultBranch = defaultBranch.replace(/^origin\//, '');
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Fallback to common defaults
|
|
136
|
+
defaultBranch = 'main';
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
fullName,
|
|
140
|
+
remoteUrl,
|
|
141
|
+
defaultBranch,
|
|
142
|
+
localPath: gitRoot,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Read a file if it exists
|
|
147
|
+
*/
|
|
148
|
+
function readFileIfExists(filePath) {
|
|
149
|
+
try {
|
|
150
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get the Claude session directory for a project
|
|
158
|
+
* Claude stores sessions in ~/.claude/projects/<hash>/
|
|
159
|
+
*/
|
|
160
|
+
function getClaudeSessionDir(projectPath) {
|
|
161
|
+
const claudeDir = path.join(process.env.HOME || '', '.claude', 'projects');
|
|
162
|
+
if (!fs.existsSync(claudeDir)) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
// Claude uses a hash of the project path
|
|
166
|
+
// We need to find the matching directory
|
|
167
|
+
try {
|
|
168
|
+
const dirs = fs.readdirSync(claudeDir);
|
|
169
|
+
for (const dir of dirs) {
|
|
170
|
+
const sessionPath = path.join(claudeDir, dir);
|
|
171
|
+
const stat = fs.statSync(sessionPath);
|
|
172
|
+
if (!stat.isDirectory())
|
|
173
|
+
continue;
|
|
174
|
+
// Check if this directory's config points to our project
|
|
175
|
+
const configPath = path.join(sessionPath, 'config.json');
|
|
176
|
+
if (fs.existsSync(configPath)) {
|
|
177
|
+
try {
|
|
178
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
179
|
+
if (config.projectPath === projectPath) {
|
|
180
|
+
return sessionPath;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// Skip invalid config files
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Find the most recent Claude session JSONL file
|
|
196
|
+
*/
|
|
197
|
+
function findLatestSessionFile(projectPath) {
|
|
198
|
+
const sessionDir = getClaudeSessionDir(projectPath);
|
|
199
|
+
if (!sessionDir) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
try {
|
|
203
|
+
const files = fs.readdirSync(sessionDir)
|
|
204
|
+
.filter(f => f.endsWith('.jsonl'))
|
|
205
|
+
.map(f => ({
|
|
206
|
+
name: f,
|
|
207
|
+
path: path.join(sessionDir, f),
|
|
208
|
+
mtime: fs.statSync(path.join(sessionDir, f)).mtime,
|
|
209
|
+
}))
|
|
210
|
+
.sort((a, b) => b.mtime.getTime() - a.mtime.getTime());
|
|
211
|
+
return files.length > 0 ? files[0].path : null;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Format bytes to human-readable size
|
|
219
|
+
*/
|
|
220
|
+
function formatBytes(bytes) {
|
|
221
|
+
if (bytes < 1024)
|
|
222
|
+
return `${bytes} B`;
|
|
223
|
+
if (bytes < 1024 * 1024)
|
|
224
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
225
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Format duration in seconds to human-readable
|
|
229
|
+
*/
|
|
230
|
+
function formatDuration(seconds) {
|
|
231
|
+
if (seconds < 60)
|
|
232
|
+
return `${seconds}s`;
|
|
233
|
+
if (seconds < 3600)
|
|
234
|
+
return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
235
|
+
const hours = Math.floor(seconds / 3600);
|
|
236
|
+
const mins = Math.floor((seconds % 3600) / 60);
|
|
237
|
+
return `${hours}h ${mins}m`;
|
|
238
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@recall_v3/mcp-server",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Recall MCP Server - Team memory for AI coding assistants",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"recall-mcp": "./dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"typecheck": "tsc --noEmit",
|
|
15
|
+
"clean": "rm -rf dist",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^0.6.0",
|
|
20
|
+
"@recall_v3/shared": "^0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/node": "^20.10.0",
|
|
24
|
+
"typescript": "^5.3.0"
|
|
25
|
+
},
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"mcp",
|
|
31
|
+
"claude",
|
|
32
|
+
"ai",
|
|
33
|
+
"memory",
|
|
34
|
+
"coding",
|
|
35
|
+
"recall"
|
|
36
|
+
],
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public"
|
|
39
|
+
},
|
|
40
|
+
"author": "Stoodio",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "https://github.com/stoodiohq/recall-v3"
|
|
45
|
+
}
|
|
46
|
+
}
|