@mayamemo/mcp 0.1.0 → 0.2.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/bin.js +3 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/project.d.ts +12 -0
- package/dist/project.js +43 -0
- package/dist/tools.d.ts +3 -0
- package/dist/tools.js +49 -26
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHttpCallApi, createStdioRunner } from './stdio.js';
|
|
3
|
+
import { resolveDefaultProjectId } from './project.js';
|
|
3
4
|
const apiUrl = process.env.MAYAMEMO_API_URL ?? 'http://localhost:4000';
|
|
4
5
|
const token = process.env.MAYAMEMO_TOKEN;
|
|
5
6
|
if (!token) {
|
|
6
7
|
process.stderr.write('mayamemo-mcp: MAYAMEMO_TOKEN is required (workspace API key, mm_sk_...).\n');
|
|
7
8
|
process.exit(1);
|
|
8
9
|
}
|
|
9
|
-
|
|
10
|
+
const linked = resolveDefaultProjectId();
|
|
11
|
+
process.stderr.write(`mayamemo-mcp: serving on stdio, API at ${apiUrl}${linked ? `, linked project ${linked}` : ' — no project linked to this folder'}\n`);
|
|
10
12
|
createStdioRunner({
|
|
11
13
|
input: process.stdin,
|
|
12
14
|
output: process.stdout,
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface ProjectMapping {
|
|
2
|
+
projectId: string;
|
|
3
|
+
name?: string;
|
|
4
|
+
workspaceId?: string;
|
|
5
|
+
apiUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
/** True for placeholder ids agents invent when they don't know the project. */
|
|
8
|
+
export declare function isPlaceholderProjectId(value: string): boolean;
|
|
9
|
+
/** Walk up from `startDir` looking for .mayamemo/project.json. */
|
|
10
|
+
export declare function findMappedProjectId(startDir?: string): string | null;
|
|
11
|
+
/** env override first, then the linked mapping; null when nothing is linked. */
|
|
12
|
+
export declare function resolveDefaultProjectId(startDir?: string): string | null;
|
package/dist/project.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default project resolution for the MCP server. When a tool call omits
|
|
3
|
+
* project_id, resolve the project linked to the working directory:
|
|
4
|
+
* MAYAMEMO_PROJECT_ID env first, then the nearest .mayamemo/project.json
|
|
5
|
+
* (written by `mayamemo init` / `mayamemo install`), walking up.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { dirname, join, resolve } from 'node:path';
|
|
9
|
+
const ZERO_UUID = /^0{8}-0{4}-0{4}-0{4}-0{12}$/;
|
|
10
|
+
/** True for placeholder ids agents invent when they don't know the project. */
|
|
11
|
+
export function isPlaceholderProjectId(value) {
|
|
12
|
+
return ZERO_UUID.test(value.trim());
|
|
13
|
+
}
|
|
14
|
+
/** Walk up from `startDir` looking for .mayamemo/project.json. */
|
|
15
|
+
export function findMappedProjectId(startDir = process.cwd()) {
|
|
16
|
+
let dir = resolve(startDir);
|
|
17
|
+
for (;;) {
|
|
18
|
+
const candidate = join(dir, '.mayamemo', 'project.json');
|
|
19
|
+
if (existsSync(candidate)) {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = JSON.parse(readFileSync(candidate, 'utf8'));
|
|
22
|
+
if (typeof parsed.projectId === 'string' && parsed.projectId.trim().length > 0) {
|
|
23
|
+
return parsed.projectId.trim();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// Corrupt mapping — keep walking up.
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const parent = dirname(dir);
|
|
31
|
+
if (parent === dir)
|
|
32
|
+
return null;
|
|
33
|
+
dir = parent;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** env override first, then the linked mapping; null when nothing is linked. */
|
|
37
|
+
export function resolveDefaultProjectId(startDir) {
|
|
38
|
+
const env = process.env.MAYAMEMO_PROJECT_ID;
|
|
39
|
+
if (typeof env === 'string' && env.trim().length > 0 && !isPlaceholderProjectId(env)) {
|
|
40
|
+
return env.trim();
|
|
41
|
+
}
|
|
42
|
+
return findMappedProjectId(startDir);
|
|
43
|
+
}
|
package/dist/tools.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export interface McpDeps {
|
|
|
12
12
|
status: number;
|
|
13
13
|
body: unknown;
|
|
14
14
|
}>;
|
|
15
|
+
/** Hosted transports pin a default project (most recently active in the workspace)
|
|
16
|
+
* so remote agents can omit project_id entirely. */
|
|
17
|
+
fallbackProjectId?: string;
|
|
15
18
|
}
|
|
16
19
|
export interface McpToolResult {
|
|
17
20
|
text: string;
|
package/dist/tools.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* V1 tools per docs/MCP.md: context_get, context_search, memory_search,
|
|
5
5
|
* memory_save, project_state, checkpoint_create (unlocked at M06).
|
|
6
6
|
*/
|
|
7
|
+
import { isPlaceholderProjectId, resolveDefaultProjectId } from './project.js';
|
|
7
8
|
/** Thrown by handlers for invalid tool arguments → JSON-RPC -32602. */
|
|
8
9
|
export class ToolInputError extends Error {
|
|
9
10
|
}
|
|
@@ -60,6 +61,28 @@ function entityRefs(args, key, cap) {
|
|
|
60
61
|
}
|
|
61
62
|
return refs;
|
|
62
63
|
}
|
|
64
|
+
/** project_id is optional: an explicit value wins; otherwise fall back to the
|
|
65
|
+
* project linked to the working directory (MAYAMEMO_PROJECT_ID env or the
|
|
66
|
+
* nearest .mayamemo/project.json), then to the transport's fallback (hosted
|
|
67
|
+
* MCP pins the workspace's most recently active project). */
|
|
68
|
+
function resolveProjectId(args, deps) {
|
|
69
|
+
const raw = args.project_id;
|
|
70
|
+
if (raw !== undefined && raw !== null) {
|
|
71
|
+
if (typeof raw !== 'string')
|
|
72
|
+
throw new ToolInputError('Argument project_id must be a string');
|
|
73
|
+
const trimmed = raw.trim();
|
|
74
|
+
if (trimmed.length > 0 && !isPlaceholderProjectId(trimmed))
|
|
75
|
+
return trimmed;
|
|
76
|
+
}
|
|
77
|
+
// Hosted transports pin their default first: on a shared server the process
|
|
78
|
+
// cwd/env is meaningless (it belongs to the server, not the caller).
|
|
79
|
+
if (deps.fallbackProjectId)
|
|
80
|
+
return deps.fallbackProjectId;
|
|
81
|
+
const mapped = resolveDefaultProjectId();
|
|
82
|
+
if (mapped)
|
|
83
|
+
return mapped;
|
|
84
|
+
throw new ToolInputError('No MayaMemo project is linked to this folder. Run `npx -y @mayamemo/cli init` (or `mayamemo install opencode`) in the repo to link one, pass project_id explicitly, or use the hosted MCP at mcp.mayamemo.com where your most recently active project is used by default.');
|
|
85
|
+
}
|
|
63
86
|
function apiErrorText(status, body) {
|
|
64
87
|
const error = body.error;
|
|
65
88
|
const code = error?.code ?? 'API_ERROR';
|
|
@@ -76,7 +99,7 @@ export const TOOLS = [
|
|
|
76
99
|
inputSchema: {
|
|
77
100
|
type: 'object',
|
|
78
101
|
properties: {
|
|
79
|
-
project_id: { type: 'string', description: '
|
|
102
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder (.mayamemo/project.json or MAYAMEMO_PROJECT_ID)' },
|
|
80
103
|
statement: { type: 'string', description: 'The durable claim, present tense' },
|
|
81
104
|
type: { type: 'string', description: 'Memory type' },
|
|
82
105
|
confidence: { type: 'number', description: '0..1 how strongly the evidence supports it' },
|
|
@@ -101,11 +124,11 @@ export const TOOLS = [
|
|
|
101
124
|
description: 'Optional code entities this memory is about (linked into the project graph)',
|
|
102
125
|
},
|
|
103
126
|
},
|
|
104
|
-
required: ['
|
|
127
|
+
required: ['statement', 'type'],
|
|
105
128
|
},
|
|
106
129
|
async handler(args, deps) {
|
|
107
130
|
const payload = {
|
|
108
|
-
project_id:
|
|
131
|
+
project_id: resolveProjectId(args, deps),
|
|
109
132
|
statement: requireString(args, 'statement'),
|
|
110
133
|
type: requireString(args, 'type'),
|
|
111
134
|
};
|
|
@@ -141,7 +164,7 @@ export const TOOLS = [
|
|
|
141
164
|
inputSchema: {
|
|
142
165
|
type: 'object',
|
|
143
166
|
properties: {
|
|
144
|
-
project_id: { type: 'string' },
|
|
167
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' },
|
|
145
168
|
query: { type: 'string', description: 'Natural-language search query (may embed a time phrase: "last week", "past 3 days", "since 2026-09-01")' },
|
|
146
169
|
status: { type: 'string', description: 'Optional memory status filter (default active)' },
|
|
147
170
|
limit: { type: 'number', description: 'Max results (default 8, max 25)' },
|
|
@@ -153,11 +176,11 @@ export const TOOLS = [
|
|
|
153
176
|
since: { type: 'string', description: 'Optional ISO datetime lower bound (memories created or changed after it)' },
|
|
154
177
|
until: { type: 'string', description: 'Optional ISO datetime upper bound' },
|
|
155
178
|
},
|
|
156
|
-
required: ['
|
|
179
|
+
required: ['query'],
|
|
157
180
|
},
|
|
158
181
|
async handler(args, deps) {
|
|
159
182
|
const payload = {
|
|
160
|
-
project_id:
|
|
183
|
+
project_id: resolveProjectId(args, deps),
|
|
161
184
|
query: requireString(args, 'query'),
|
|
162
185
|
};
|
|
163
186
|
const status = optionalString(args, 'status');
|
|
@@ -193,16 +216,16 @@ export const TOOLS = [
|
|
|
193
216
|
inputSchema: {
|
|
194
217
|
type: 'object',
|
|
195
218
|
properties: {
|
|
196
|
-
project_id: { type: 'string' },
|
|
219
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' },
|
|
197
220
|
query: { type: 'string', description: 'Task title / intent to compile context for' },
|
|
198
221
|
token_budget: { type: 'number', description: 'Hard token budget (default 4000, max 50000)' },
|
|
199
222
|
branch: { type: 'string', description: 'Current git branch (branch-matched context ranks higher)' },
|
|
200
223
|
},
|
|
201
|
-
required: ['
|
|
224
|
+
required: ['query'],
|
|
202
225
|
},
|
|
203
226
|
async handler(args, deps) {
|
|
204
227
|
const payload = {
|
|
205
|
-
project_id:
|
|
228
|
+
project_id: resolveProjectId(args, deps),
|
|
206
229
|
title: requireString(args, 'query'),
|
|
207
230
|
token_budget: Math.min(Math.max(optionalNumber(args, 'token_budget') ?? 4000, 64), 50000),
|
|
208
231
|
max_items: MAX_CONTEXT_ITEMS,
|
|
@@ -258,11 +281,11 @@ export const TOOLS = [
|
|
|
258
281
|
description: 'Project state snapshot: counts, last session, last checkpoint, and recent ledger activity.',
|
|
259
282
|
inputSchema: {
|
|
260
283
|
type: 'object',
|
|
261
|
-
properties: { project_id: { type: 'string' } },
|
|
262
|
-
required: [
|
|
284
|
+
properties: { project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' } },
|
|
285
|
+
required: [],
|
|
263
286
|
},
|
|
264
287
|
async handler(args, deps) {
|
|
265
|
-
const projectId =
|
|
288
|
+
const projectId = resolveProjectId(args, deps);
|
|
266
289
|
const res = await deps.callApi('GET', `/v1/projects/${encodeURIComponent(projectId)}/state`);
|
|
267
290
|
if (res.status >= 400)
|
|
268
291
|
return apiErrorText(res.status, res.body);
|
|
@@ -286,7 +309,7 @@ export const TOOLS = [
|
|
|
286
309
|
inputSchema: {
|
|
287
310
|
type: 'object',
|
|
288
311
|
properties: {
|
|
289
|
-
project_id: { type: 'string' },
|
|
312
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' },
|
|
290
313
|
title: { type: 'string', description: 'Short checkpoint title' },
|
|
291
314
|
summary: { type: 'string', description: 'What was done and where things stand' },
|
|
292
315
|
next_steps: { type: 'array', items: { type: 'string' }, description: 'What the next agent should do' },
|
|
@@ -296,11 +319,11 @@ export const TOOLS = [
|
|
|
296
319
|
context_version_id: { type: 'string', description: 'Link an existing context version instead of compiling fresh' },
|
|
297
320
|
token_budget: { type: 'number', description: 'Budget for the fresh compile (default 6000)' },
|
|
298
321
|
},
|
|
299
|
-
required: ['
|
|
322
|
+
required: ['title', 'summary'],
|
|
300
323
|
},
|
|
301
324
|
async handler(args, deps) {
|
|
302
325
|
const payload = {
|
|
303
|
-
project_id:
|
|
326
|
+
project_id: resolveProjectId(args, deps),
|
|
304
327
|
title: requireString(args, 'title'),
|
|
305
328
|
summary: requireString(args, 'summary'),
|
|
306
329
|
};
|
|
@@ -337,7 +360,7 @@ export const TOOLS = [
|
|
|
337
360
|
inputSchema: {
|
|
338
361
|
type: 'object',
|
|
339
362
|
properties: {
|
|
340
|
-
project_id: { type: 'string', description: '
|
|
363
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' },
|
|
341
364
|
entities: {
|
|
342
365
|
type: 'array',
|
|
343
366
|
items: {
|
|
@@ -362,10 +385,10 @@ export const TOOLS = [
|
|
|
362
385
|
description: 'Relations between entities (max 100)',
|
|
363
386
|
},
|
|
364
387
|
},
|
|
365
|
-
required: [
|
|
388
|
+
required: [],
|
|
366
389
|
},
|
|
367
390
|
async handler(args, deps) {
|
|
368
|
-
const payload = { project_id:
|
|
391
|
+
const payload = { project_id: resolveProjectId(args, deps) };
|
|
369
392
|
const entities = entityRefs(args, 'entities', 50);
|
|
370
393
|
if (entities)
|
|
371
394
|
payload.entities = entities;
|
|
@@ -393,7 +416,7 @@ export const TOOLS = [
|
|
|
393
416
|
inputSchema: {
|
|
394
417
|
type: 'object',
|
|
395
418
|
properties: {
|
|
396
|
-
project_id: { type: 'string', description: '
|
|
419
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' },
|
|
397
420
|
q: { type: 'string', description: 'Name search (substring)' },
|
|
398
421
|
kind: { type: 'string', description: 'Filter by entity kind (file, module, symbol…)' },
|
|
399
422
|
entity_id: { type: 'string', description: 'Entity id — switches to neighbor mode' },
|
|
@@ -416,7 +439,7 @@ export const TOOLS = [
|
|
|
416
439
|
text: `Neighbors of ${graph.root.kind} ${graph.root.name} (${graph.nodes.length} nodes, ${graph.edges.length} edges${graph.truncated ? ', truncated' : ''}):\n${lines.join('\n')}`,
|
|
417
440
|
};
|
|
418
441
|
}
|
|
419
|
-
const projectId =
|
|
442
|
+
const projectId = resolveProjectId(args, deps);
|
|
420
443
|
const params = new URLSearchParams();
|
|
421
444
|
const q = optionalString(args, 'q');
|
|
422
445
|
if (q)
|
|
@@ -441,17 +464,17 @@ export const TOOLS = [
|
|
|
441
464
|
inputSchema: {
|
|
442
465
|
type: 'object',
|
|
443
466
|
properties: {
|
|
444
|
-
project_id: { type: 'string', description: '
|
|
467
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder' },
|
|
445
468
|
filename: { type: 'string', description: 'File name (drives text/code detection)' },
|
|
446
469
|
content_base64: { type: 'string', description: 'Raw file bytes, base64-encoded (max 10 MB decoded)' },
|
|
447
470
|
mime: { type: 'string', description: 'Optional MIME type (defaults to application/octet-stream)' },
|
|
448
471
|
memory_id: { type: 'string', description: 'Optional memory id to link the attachment to' },
|
|
449
472
|
},
|
|
450
|
-
required: ['
|
|
473
|
+
required: ['filename', 'content_base64'],
|
|
451
474
|
},
|
|
452
475
|
async handler(args, deps) {
|
|
453
476
|
const payload = {
|
|
454
|
-
project_id:
|
|
477
|
+
project_id: resolveProjectId(args, deps),
|
|
455
478
|
filename: requireString(args, 'filename'),
|
|
456
479
|
content_base64: requireString(args, 'content_base64'),
|
|
457
480
|
};
|
|
@@ -536,15 +559,15 @@ export const TOOLS = [
|
|
|
536
559
|
inputSchema: {
|
|
537
560
|
type: 'object',
|
|
538
561
|
properties: {
|
|
539
|
-
project_id: { type: 'string', description: '
|
|
562
|
+
project_id: { type: 'string', description: 'Optional — defaults to the project linked to this folder (.mayamemo/project.json or MAYAMEMO_PROJECT_ID)' },
|
|
540
563
|
agent: { type: 'string', description: 'Your agent name (e.g. opencode, claude-code, codex)' },
|
|
541
564
|
branch: { type: 'string', description: 'Optional git branch this session works on' },
|
|
542
565
|
summary: { type: 'string', description: 'Optional one-line intent for the session' },
|
|
543
566
|
},
|
|
544
|
-
required: [
|
|
567
|
+
required: [],
|
|
545
568
|
},
|
|
546
569
|
async handler(args, deps) {
|
|
547
|
-
const payload = { project_id:
|
|
570
|
+
const payload = { project_id: resolveProjectId(args, deps) };
|
|
548
571
|
const agent = optionalString(args, 'agent');
|
|
549
572
|
if (agent)
|
|
550
573
|
payload.agent = agent;
|