@memoraone/mcp 0.1.3 → 0.1.4
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/client/memoraClient.js +24 -10
- package/dist/config.js +1 -3
- package/dist/index.js +36 -1
- package/dist/runContext.js +7 -0
- package/dist/tools/handlers/askWithMemory.js +6 -1
- package/dist/tools/handlers/listProjects.js +4 -0
- package/dist/tools/handlers/logChangeSummary.js +6 -2
- package/dist/tools/handlers/logCommand.js +6 -2
- package/dist/tools/handlers/logIntent.js +6 -2
- package/dist/tools/handlers/logToolResult.js +6 -2
- package/dist/tools/handlers/postEvent.js +6 -1
- package/dist/tools/handlers/setProject.js +10 -0
- package/dist/tools/listProjects.js +1 -0
- package/dist/tools/setProject.js +4 -0
- package/package.json +1 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
async function
|
|
1
|
+
async function requestJson(url, method, headers, body) {
|
|
2
2
|
const { default: fetch } = await import('node-fetch');
|
|
3
3
|
const res = await fetch(url, {
|
|
4
|
-
method
|
|
4
|
+
method,
|
|
5
5
|
headers,
|
|
6
|
-
body: JSON.stringify(body ?? {}),
|
|
6
|
+
body: body === undefined ? undefined : JSON.stringify(body ?? {}),
|
|
7
7
|
});
|
|
8
8
|
const text = await res.text();
|
|
9
9
|
return {
|
|
@@ -24,17 +24,31 @@ export class MemoraOneHttpError extends Error {
|
|
|
24
24
|
export class MemoraClient {
|
|
25
25
|
constructor(cfg) {
|
|
26
26
|
this.baseUrl = cfg.apiUrl;
|
|
27
|
-
this.projectId = cfg.projectId;
|
|
28
27
|
this.apiKey = cfg.apiKey;
|
|
29
28
|
}
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
process.stderr.write(`[memoraone-mcp] http url=${url} projectId=${this.projectId} apiKeyPrefix=${String(this.apiKey).slice(0, 8)} apiKeyLen=${String(this.apiKey).length}\n`);
|
|
33
|
-
const res = await postJson(url, {
|
|
29
|
+
buildHeaders(options) {
|
|
30
|
+
const headers = {
|
|
34
31
|
'content-type': 'application/json',
|
|
35
32
|
'x-api-key': this.apiKey,
|
|
36
|
-
|
|
37
|
-
}
|
|
33
|
+
...(options?.headers ?? {}),
|
|
34
|
+
};
|
|
35
|
+
if (options?.projectId) {
|
|
36
|
+
headers['x-memora-project-id'] = options.projectId;
|
|
37
|
+
}
|
|
38
|
+
return headers;
|
|
39
|
+
}
|
|
40
|
+
async post(path, body, options) {
|
|
41
|
+
const url = `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;
|
|
42
|
+
process.stderr.write(`[memoraone-mcp] http url=${url} apiKeyPrefix=${String(this.apiKey).slice(0, 8)} apiKeyLen=${String(this.apiKey).length}\n`);
|
|
43
|
+
const res = await requestJson(url, 'POST', this.buildHeaders(options), body);
|
|
44
|
+
if (!res.ok) {
|
|
45
|
+
throw new MemoraOneHttpError(res.status, res.statusText, res.text);
|
|
46
|
+
}
|
|
47
|
+
return res.text ? JSON.parse(res.text) : null;
|
|
48
|
+
}
|
|
49
|
+
async get(path, options) {
|
|
50
|
+
const url = `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;
|
|
51
|
+
const res = await requestJson(url, 'GET', this.buildHeaders(options));
|
|
38
52
|
if (!res.ok) {
|
|
39
53
|
throw new MemoraOneHttpError(res.status, res.statusText, res.text);
|
|
40
54
|
}
|
package/dist/config.js
CHANGED
|
@@ -16,7 +16,6 @@ if (fs.existsSync(dotenvPath)) {
|
|
|
16
16
|
}
|
|
17
17
|
const EnvSchema = z.object({
|
|
18
18
|
MEMORAONE_API_URL: z.string().url().optional(),
|
|
19
|
-
MEMORAONE_PROJECT_ID: z.string().min(1),
|
|
20
19
|
MEMORAONE_API_KEY: z.string().min(1),
|
|
21
20
|
MEMORAONE_DEV_MODE: z.string().min(1).optional(),
|
|
22
21
|
MEMORAONE_AGENT_NAME: z.string().min(1).optional(),
|
|
@@ -26,7 +25,7 @@ const EnvSchema = z.object({
|
|
|
26
25
|
MEMORAONE_HEARTBEAT: z.string().min(1).optional(),
|
|
27
26
|
MEMORAONE_HEARTBEAT_INTERVAL_MS: z.string().min(1).optional(),
|
|
28
27
|
});
|
|
29
|
-
const requiredEnvVars = ['MEMORAONE_API_KEY'
|
|
28
|
+
const requiredEnvVars = ['MEMORAONE_API_KEY'];
|
|
30
29
|
const missingEnvVars = requiredEnvVars.filter((key) => {
|
|
31
30
|
const value = process.env[key];
|
|
32
31
|
return value === undefined || value.trim() === '';
|
|
@@ -59,7 +58,6 @@ const parseBooleanFlag = (value, defaultValue) => {
|
|
|
59
58
|
};
|
|
60
59
|
export const config = {
|
|
61
60
|
apiUrl: resolvedApiUrl.replace(/\/+$/, ''),
|
|
62
|
-
projectId: parsed.data.MEMORAONE_PROJECT_ID,
|
|
63
61
|
apiKey: parsed.data.MEMORAONE_API_KEY,
|
|
64
62
|
agentName: parsed.data.MEMORAONE_AGENT_NAME ?? 'cursor',
|
|
65
63
|
agentType: parsed.data.MEMORAONE_AGENT_TYPE ?? 'agent',
|
package/dist/index.js
CHANGED
|
@@ -12,12 +12,17 @@ import { logIntentShape } from './tools/logIntent.js';
|
|
|
12
12
|
import { logChangeSummaryShape } from './tools/logChangeSummary.js';
|
|
13
13
|
import { logToolResultShape } from './tools/logToolResult.js';
|
|
14
14
|
import { logCommandShape } from './tools/logCommand.js';
|
|
15
|
+
import { listProjectsShape } from './tools/listProjects.js';
|
|
16
|
+
import { setProjectShape } from './tools/setProject.js';
|
|
15
17
|
import { handlePostEvent } from './tools/handlers/postEvent.js';
|
|
16
18
|
import { handleAskWithMemory } from './tools/handlers/askWithMemory.js';
|
|
17
19
|
import { handleLogIntent } from './tools/handlers/logIntent.js';
|
|
18
20
|
import { handleLogChangeSummary } from './tools/handlers/logChangeSummary.js';
|
|
19
21
|
import { handleLogToolResult } from './tools/handlers/logToolResult.js';
|
|
20
22
|
import { handleLogCommand } from './tools/handlers/logCommand.js';
|
|
23
|
+
import { handleListProjects } from './tools/handlers/listProjects.js';
|
|
24
|
+
import { handleSetProject } from './tools/handlers/setProject.js';
|
|
25
|
+
import { getCurrentProjectId } from './runContext.js';
|
|
21
26
|
async function sendHeartbeat(client) {
|
|
22
27
|
try {
|
|
23
28
|
await client.post('/admin/api-keys/heartbeat', {});
|
|
@@ -63,6 +68,10 @@ function sanitizeArgsSummary(args) {
|
|
|
63
68
|
}
|
|
64
69
|
async function postWorklogEvent(client, message) {
|
|
65
70
|
try {
|
|
71
|
+
const projectId = getCurrentProjectId();
|
|
72
|
+
if (!projectId) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
66
75
|
const body = {
|
|
67
76
|
kind: 'note',
|
|
68
77
|
concept: 'concept:worklog',
|
|
@@ -73,7 +82,7 @@ async function postWorklogEvent(client, message) {
|
|
|
73
82
|
purpose: 'worklog',
|
|
74
83
|
},
|
|
75
84
|
};
|
|
76
|
-
await client.post('/timeline/events', body);
|
|
85
|
+
await client.post('/timeline/events', body, { projectId });
|
|
77
86
|
}
|
|
78
87
|
catch (err) {
|
|
79
88
|
console.error('[memoraone-mcp] worklog error (silent)', err);
|
|
@@ -126,6 +135,32 @@ async function main() {
|
|
|
126
135
|
],
|
|
127
136
|
};
|
|
128
137
|
});
|
|
138
|
+
// ---- memora_list_projects ----
|
|
139
|
+
registeredToolNames.push('memora_list_projects');
|
|
140
|
+
server.tool('memora_list_projects', 'List projects available to the current API key', listProjectsShape, async () => {
|
|
141
|
+
const result = await handleListProjects(client);
|
|
142
|
+
return {
|
|
143
|
+
content: [
|
|
144
|
+
{
|
|
145
|
+
type: 'text',
|
|
146
|
+
text: JSON.stringify(result),
|
|
147
|
+
},
|
|
148
|
+
],
|
|
149
|
+
};
|
|
150
|
+
});
|
|
151
|
+
// ---- memora_set_project ----
|
|
152
|
+
registeredToolNames.push('memora_set_project');
|
|
153
|
+
server.tool('memora_set_project', 'Set the current project id for subsequent tool calls', setProjectShape, async (args) => {
|
|
154
|
+
const result = await handleSetProject(args);
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: 'text',
|
|
159
|
+
text: JSON.stringify(result),
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
});
|
|
129
164
|
// ---- memora_ask_with_memory ----
|
|
130
165
|
registeredToolNames.push('memora_ask_with_memory');
|
|
131
166
|
registerToolWithWorklog(server, client, 'memora_ask_with_memory', 'Ask MemoraOne with project memory context', askWithMemoryShape, async (args) => {
|
package/dist/runContext.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
// packages/mcp/src/runContext.ts
|
|
2
2
|
import * as crypto from 'crypto';
|
|
3
3
|
let currentRunId = null;
|
|
4
|
+
let currentProjectId = null;
|
|
4
5
|
export function getCurrentRunId() {
|
|
5
6
|
return currentRunId;
|
|
6
7
|
}
|
|
7
8
|
export function setCurrentRunId(id) {
|
|
8
9
|
currentRunId = id;
|
|
9
10
|
}
|
|
11
|
+
export function getCurrentProjectId() {
|
|
12
|
+
return currentProjectId;
|
|
13
|
+
}
|
|
14
|
+
export function setCurrentProjectId(id) {
|
|
15
|
+
currentProjectId = id;
|
|
16
|
+
}
|
|
10
17
|
export function resolveRunId(passed) {
|
|
11
18
|
if (passed) {
|
|
12
19
|
return passed;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
//memoraone/adapters/vscode-mcp/src/tools/handlers/askWithMemory.ts
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
|
+
import { getCurrentProjectId } from '../../runContext.js';
|
|
3
4
|
const askWithMemoryInputSchema = z.object({
|
|
4
5
|
question: z.string().min(1),
|
|
5
6
|
code_context: z
|
|
@@ -18,13 +19,17 @@ function isAskWithMemoryResponse(value) {
|
|
|
18
19
|
}
|
|
19
20
|
export async function handleAskWithMemory(client, args) {
|
|
20
21
|
const parsed = askWithMemoryInputSchema.parse(args ?? {});
|
|
22
|
+
const projectId = getCurrentProjectId();
|
|
23
|
+
if (!projectId) {
|
|
24
|
+
throw new Error('No project selected. Use memora_list_projects and memora_set_project to select a project.');
|
|
25
|
+
}
|
|
21
26
|
const payload = {
|
|
22
27
|
question: parsed.question,
|
|
23
28
|
};
|
|
24
29
|
if (parsed.code_context) {
|
|
25
30
|
payload.code_context = parsed.code_context;
|
|
26
31
|
}
|
|
27
|
-
const res = await client.post('/agent/ask-with-memory', payload);
|
|
32
|
+
const res = await client.post('/agent/ask-with-memory', payload, { projectId });
|
|
28
33
|
if (!isAskWithMemoryResponse(res)) {
|
|
29
34
|
const err = new Error('Unexpected response from MemoraOne');
|
|
30
35
|
err.status = 502;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//packages/mcp/src/tools/handlers/logChangeSummary.ts
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
|
-
import { resolveRunId } from '../../runContext.js';
|
|
3
|
+
import { getCurrentProjectId, resolveRunId } from '../../runContext.js';
|
|
4
4
|
import { config } from '../../config.js';
|
|
5
5
|
const logChangeSummaryInputSchema = z.object({
|
|
6
6
|
summary: z.string().min(1),
|
|
@@ -18,6 +18,10 @@ const logChangeSummaryInputSchema = z.object({
|
|
|
18
18
|
});
|
|
19
19
|
export async function handleLogChangeSummary(client, args) {
|
|
20
20
|
const parsed = logChangeSummaryInputSchema.parse(args ?? {});
|
|
21
|
+
const projectId = getCurrentProjectId();
|
|
22
|
+
if (!projectId) {
|
|
23
|
+
throw new Error('No project selected. Use memora_list_projects and memora_set_project to select a project.');
|
|
24
|
+
}
|
|
21
25
|
const { summary, scope, files, stats, commit } = parsed;
|
|
22
26
|
const message = summary.startsWith('CHANGE:')
|
|
23
27
|
? summary
|
|
@@ -39,6 +43,6 @@ export async function handleLogChangeSummary(client, args) {
|
|
|
39
43
|
...(run_id ? { run_id } : {}),
|
|
40
44
|
},
|
|
41
45
|
};
|
|
42
|
-
await client.post('/timeline/events', body);
|
|
46
|
+
await client.post('/timeline/events', body, { projectId });
|
|
43
47
|
return { ok: true };
|
|
44
48
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//packages/mcp/src/tools/handlers/logCommand.ts
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
|
-
import { resolveRunId } from '../../runContext.js';
|
|
3
|
+
import { getCurrentProjectId, resolveRunId } from '../../runContext.js';
|
|
4
4
|
import { config } from '../../config.js';
|
|
5
5
|
const logCommandInputSchema = z.object({
|
|
6
6
|
cmd: z.string().min(1),
|
|
@@ -13,6 +13,10 @@ const logCommandInputSchema = z.object({
|
|
|
13
13
|
});
|
|
14
14
|
export async function handleLogCommand(client, args) {
|
|
15
15
|
const parsed = logCommandInputSchema.parse(args ?? {});
|
|
16
|
+
const projectId = getCurrentProjectId();
|
|
17
|
+
if (!projectId) {
|
|
18
|
+
throw new Error('No project selected. Use memora_list_projects and memora_set_project to select a project.');
|
|
19
|
+
}
|
|
16
20
|
const { cmd, summary, cwd, exit_code, duration_ms, stats } = parsed;
|
|
17
21
|
const message = summary.startsWith('COMMAND:')
|
|
18
22
|
? summary
|
|
@@ -35,6 +39,6 @@ export async function handleLogCommand(client, args) {
|
|
|
35
39
|
...(stats ? { stats } : {}),
|
|
36
40
|
},
|
|
37
41
|
};
|
|
38
|
-
await client.post('/timeline/events', body);
|
|
42
|
+
await client.post('/timeline/events', body, { projectId });
|
|
39
43
|
return { ok: true };
|
|
40
44
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//packages/mcp/src/tools/handlers/logIntent.ts
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
|
-
import { generateRunId, setCurrentRunId } from '../../runContext.js';
|
|
3
|
+
import { generateRunId, getCurrentProjectId, setCurrentRunId } from '../../runContext.js';
|
|
4
4
|
import { config } from '../../config.js';
|
|
5
5
|
const logIntentInputSchema = z.object({
|
|
6
6
|
intent: z.enum(['task', 'decision']),
|
|
@@ -11,6 +11,10 @@ const logIntentInputSchema = z.object({
|
|
|
11
11
|
});
|
|
12
12
|
export async function handleLogIntent(client, args) {
|
|
13
13
|
const parsed = logIntentInputSchema.parse(args ?? {});
|
|
14
|
+
const projectId = getCurrentProjectId();
|
|
15
|
+
if (!projectId) {
|
|
16
|
+
throw new Error('No project selected. Use memora_list_projects and memora_set_project to select a project.');
|
|
17
|
+
}
|
|
14
18
|
const intent = parsed.intent;
|
|
15
19
|
const message = parsed.message.trim();
|
|
16
20
|
if (!message) {
|
|
@@ -37,6 +41,6 @@ export async function handleLogIntent(client, args) {
|
|
|
37
41
|
...(context ? { context } : {}),
|
|
38
42
|
},
|
|
39
43
|
};
|
|
40
|
-
await client.post('/timeline/events', body);
|
|
44
|
+
await client.post('/timeline/events', body, { projectId });
|
|
41
45
|
return { ok: true, run_id };
|
|
42
46
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//packages/mcp/src/tools/handlers/logToolResult.ts
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
|
-
import { resolveRunId } from '../../runContext.js';
|
|
3
|
+
import { getCurrentProjectId, resolveRunId } from '../../runContext.js';
|
|
4
4
|
import { config } from '../../config.js';
|
|
5
5
|
const logToolResultInputSchema = z.object({
|
|
6
6
|
tool: z.string().min(1),
|
|
@@ -15,6 +15,10 @@ const logToolResultInputSchema = z.object({
|
|
|
15
15
|
});
|
|
16
16
|
export async function handleLogToolResult(client, args) {
|
|
17
17
|
const parsed = logToolResultInputSchema.parse(args ?? {});
|
|
18
|
+
const projectId = getCurrentProjectId();
|
|
19
|
+
if (!projectId) {
|
|
20
|
+
throw new Error('No project selected. Use memora_list_projects and memora_set_project to select a project.');
|
|
21
|
+
}
|
|
18
22
|
const { tool, status, summary, duration_ms, error_code, error_message, error_kind, stats } = parsed;
|
|
19
23
|
const message = summary.startsWith('RESULT:')
|
|
20
24
|
? summary
|
|
@@ -39,6 +43,6 @@ export async function handleLogToolResult(client, args) {
|
|
|
39
43
|
...(stats ? { stats } : {}),
|
|
40
44
|
},
|
|
41
45
|
};
|
|
42
|
-
await client.post('/timeline/events', body);
|
|
46
|
+
await client.post('/timeline/events', body, { projectId });
|
|
43
47
|
return { ok: true };
|
|
44
48
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
//packages/mcp/src/tools/handlers/postEvent.ts
|
|
2
2
|
import { z } from 'zod/v4';
|
|
3
3
|
import { config } from '../../config.js';
|
|
4
|
+
import { getCurrentProjectId } from '../../runContext.js';
|
|
4
5
|
const postEventInputSchema = z.object({
|
|
5
6
|
kind: z.string().min(1),
|
|
6
7
|
actor: z.object({ identifier: z.string().min(1) }),
|
|
@@ -9,6 +10,10 @@ const postEventInputSchema = z.object({
|
|
|
9
10
|
});
|
|
10
11
|
export async function handlePostEvent(client, args) {
|
|
11
12
|
const parsed = postEventInputSchema.parse(args ?? {});
|
|
13
|
+
const projectId = getCurrentProjectId();
|
|
14
|
+
if (!projectId) {
|
|
15
|
+
throw new Error('No project selected. Use memora_list_projects and memora_set_project to select a project.');
|
|
16
|
+
}
|
|
12
17
|
const body = {
|
|
13
18
|
kind: parsed.kind,
|
|
14
19
|
actor: { type: config.agentType, identifier: parsed.actor.identifier },
|
|
@@ -18,6 +23,6 @@ export async function handlePostEvent(client, args) {
|
|
|
18
23
|
...parsed.metadata,
|
|
19
24
|
},
|
|
20
25
|
};
|
|
21
|
-
await client.post('/timeline/events', body);
|
|
26
|
+
await client.post('/timeline/events', body, { projectId });
|
|
22
27
|
return { ok: true, forwarded: true };
|
|
23
28
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import { setCurrentProjectId } from '../../runContext.js';
|
|
3
|
+
const setProjectInputSchema = z.object({
|
|
4
|
+
projectId: z.string().min(1),
|
|
5
|
+
});
|
|
6
|
+
export async function handleSetProject(args) {
|
|
7
|
+
const parsed = setProjectInputSchema.parse(args ?? {});
|
|
8
|
+
setCurrentProjectId(parsed.projectId);
|
|
9
|
+
return { ok: true, projectId: parsed.projectId };
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const listProjectsShape = {};
|