@engramx/mcp 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.
@@ -0,0 +1,89 @@
1
+ // `npx @engramx/mcp connect` — print ready-to-use config for connecting any MCP client to an
2
+ // engram's remote MCP endpoint, and optionally write the Claude Desktop config file. Uses the
3
+ // shared connect-config module (connect-config.ts) so every consumer emits identical config.
4
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'node:fs';
5
+ import { dirname, join } from 'node:path';
6
+ import { homedir, platform } from 'node:os';
7
+ import { connectInfo, claudeDesktopConfig } from './connect-config.js';
8
+ function parseFlags(argv) {
9
+ const f = {};
10
+ for (let i = 0; i < argv.length; i++) {
11
+ const arg = argv[i];
12
+ if (arg && arg.startsWith('--')) {
13
+ const k = arg.slice(2);
14
+ const next = argv[i + 1];
15
+ if (next !== undefined && !next.startsWith('--')) {
16
+ f[k] = next;
17
+ i++;
18
+ }
19
+ else {
20
+ f[k] = true;
21
+ }
22
+ }
23
+ }
24
+ return f;
25
+ }
26
+ /** Default claude_desktop_config.json path per OS. */
27
+ function defaultClaudeDesktopPath() {
28
+ const home = homedir();
29
+ switch (platform()) {
30
+ case 'darwin':
31
+ return join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
32
+ case 'win32':
33
+ return join(process.env['APPDATA'] || join(home, 'AppData', 'Roaming'), 'Claude', 'claude_desktop_config.json');
34
+ default:
35
+ return join(home, '.config', 'Claude', 'claude_desktop_config.json');
36
+ }
37
+ }
38
+ /** Merge the engramx server into an existing claude_desktop_config.json without clobbering others. */
39
+ function writeClaudeDesktopConfig(path, p) {
40
+ let existing = {};
41
+ if (existsSync(path)) {
42
+ try {
43
+ existing = JSON.parse(readFileSync(path, 'utf-8'));
44
+ }
45
+ catch {
46
+ throw new Error(`${path} exists but is not valid JSON — fix or remove it, then retry.`);
47
+ }
48
+ }
49
+ existing.mcpServers = existing.mcpServers || {};
50
+ existing.mcpServers.engramx = claudeDesktopConfig(p).mcpServers.engramx;
51
+ mkdirSync(dirname(path), { recursive: true });
52
+ writeFileSync(path, JSON.stringify(existing, null, 2) + '\n');
53
+ }
54
+ export async function runConnect(argv) {
55
+ const f = parseFlags(argv);
56
+ if (f['help'] || f['h']) {
57
+ console.log('Usage: npx @engramx/mcp connect --canister <id> [--host <url>] [--write [path]]');
58
+ return;
59
+ }
60
+ const canisterId = f['canister'] ||
61
+ process.env['ENGRAM_CANISTER_ID'] ||
62
+ process.env['VAULT_CANISTER_ID'];
63
+ const host = f['host'] || process.env['ENGRAM_HOST'] || 'https://icp0.io';
64
+ if (!canisterId) {
65
+ console.error('Error: --canister <id> (or ENGRAM_CANISTER_ID) is required.');
66
+ console.error('Usage: npx @engramx/mcp connect --canister <id> [--host <url>] [--write [path]]');
67
+ process.exit(2);
68
+ }
69
+ const info = connectInfo({ canisterId, host });
70
+ console.log('\nConnect Claude (and other MCP clients) to your engram\n');
71
+ console.log(`Remote MCP URL:\n ${info.mcpUrl}\n`);
72
+ for (const g of info.guides) {
73
+ console.log(`• ${g.name} [${g.transport}]`);
74
+ for (const s of g.steps)
75
+ console.log(` - ${s}`);
76
+ if (g.snippet)
77
+ for (const line of g.snippet.split('\n'))
78
+ console.log(` ${line}`);
79
+ }
80
+ if (f['write']) {
81
+ const path = typeof f['write'] === 'string' ? f['write'] : defaultClaudeDesktopPath();
82
+ writeClaudeDesktopConfig(path, { canisterId, host });
83
+ console.log(`\n✓ Wrote the engramx MCP server to ${path}. Restart Claude Desktop to load it.`);
84
+ }
85
+ else {
86
+ console.log('\n(Re-run with --write to merge the stdio config into claude_desktop_config.json.)');
87
+ }
88
+ }
89
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA,6FAA6F;AAC7F,8FAA8F;AAC9F,6FAA6F;AAE7F,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAsB,MAAM,qBAAqB,CAAC;AAE3F,SAAS,UAAU,CAAC,IAAc;IAChC,MAAM,CAAC,GAAqC,EAAE,CAAC;IAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAChC,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;YACvB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YACzB,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACjD,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;gBACZ,CAAC,EAAE,CAAC;YACN,CAAC;iBAAM,CAAC;gBACN,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;YACd,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,sDAAsD;AACtD,SAAS,wBAAwB;IAC/B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,QAAQ,QAAQ,EAAE,EAAE,CAAC;QACnB,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;QAC9F,KAAK,OAAO;YACV,OAAO,IAAI,CACT,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,EAC1D,QAAQ,EACR,4BAA4B,CAC7B,CAAC;QACJ;YACE,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,4BAA4B,CAAC,CAAC;IACzE,CAAC;AACH,CAAC;AAED,sGAAsG;AACtG,SAAS,wBAAwB,CAAC,IAAY,EAAE,CAAgB;IAC9D,IAAI,QAAQ,GAAQ,EAAE,CAAC;IACvB,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;QACrD,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,GAAG,IAAI,+DAA+D,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IACD,QAAQ,CAAC,UAAU,GAAG,QAAQ,CAAC,UAAU,IAAI,EAAE,CAAC;IAChD,QAAQ,CAAC,UAAU,CAAC,OAAO,GAAG,mBAAmB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC;IACxE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAc;IAC7C,MAAM,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,iFAAiF,CAAC,CAAC;QAC/F,OAAO;IACT,CAAC;IACD,MAAM,UAAU,GACb,CAAC,CAAC,UAAU,CAAY;QACzB,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IACnC,MAAM,IAAI,GAAI,CAAC,CAAC,MAAM,CAAY,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,iBAAiB,CAAC;IACtF,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QAC7E,OAAO,CAAC,KAAK,CACX,iFAAiF,CAClF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,OAAO,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC;IACnD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC;QAC7C,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACnD,IAAI,CAAC,CAAC,OAAO;YAAE,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC;IACxF,CAAC;IAED,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,wBAAwB,EAAE,CAAC;QACtF,wBAAwB,CAAC,IAAI,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,OAAO,CAAC,GAAG,CAAC,uCAAuC,IAAI,sCAAsC,CAAC,CAAC;IACjG,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CACT,oFAAoF,CACrF,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ // Entry router. With no subcommand, start the stdio MCP server (the default). The `connect`
3
+ // subcommand only generates client config, so it's handled BEFORE importing ./server.js — which
4
+ // statically pulls in @engramx/client and requires a session key.
5
+ import { runConnect } from './connect.js';
6
+ if (process.argv[2] === 'connect') {
7
+ await runConnect(process.argv.slice(3));
8
+ process.exit(0);
9
+ }
10
+ await import('./server.js');
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,4FAA4F;AAC5F,gGAAgG;AAChG,kEAAkE;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;IAClC,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { ProfileManager, SessionManager } from '@engramx/client';
3
+ export declare function registerProfileTools(server: McpServer, profile: ProfileManager, sessions: SessionManager): void;
@@ -0,0 +1,144 @@
1
+ import { z } from 'zod';
2
+ import { toSerializable } from './utils.js';
3
+ function jsonText(value) {
4
+ return JSON.stringify(toSerializable(value), null, 2);
5
+ }
6
+ export function registerProfileTools(server, profile, sessions) {
7
+ // 1. engram_get_profile
8
+ server.tool('engram_get_profile', 'Load the user identity (soul, user, preferences) and accumulated lessons in one call. Use at session start to learn who the user is.', async () => {
9
+ try {
10
+ const p = await profile.getProfile();
11
+ return { content: [{ type: 'text', text: jsonText(p) }] };
12
+ }
13
+ catch (err) {
14
+ return {
15
+ content: [
16
+ { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
17
+ ],
18
+ isError: true,
19
+ };
20
+ }
21
+ });
22
+ // 2. engram_save_preference
23
+ server.tool('engram_save_preference', 'Save a user preference (e.g. "prefers terse responses", "uses dark mode"). Persists across platforms.', { preference: z.string().describe('The preference to save') }, async ({ preference }) => {
24
+ try {
25
+ const version = await profile.savePreference(preference);
26
+ return { content: [{ type: 'text', text: jsonText({ saved: true, version }) }] };
27
+ }
28
+ catch (err) {
29
+ return {
30
+ content: [
31
+ { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
32
+ ],
33
+ isError: true,
34
+ };
35
+ }
36
+ });
37
+ // 3. engram_save_insight
38
+ server.tool('engram_save_insight', 'Save a lesson learned or insight. Optionally provide a topic for domain-specific knowledge.', {
39
+ content: z.string().describe('The insight or lesson to save'),
40
+ topic: z
41
+ .string()
42
+ .optional()
43
+ .describe('Optional domain topic (e.g. "typescript", "devops"). Lowercase, alphanumeric.'),
44
+ }, async ({ content, topic }) => {
45
+ try {
46
+ const version = await profile.saveInsight({ content, topic });
47
+ return { content: [{ type: 'text', text: jsonText({ saved: true, version, topic }) }] };
48
+ }
49
+ catch (err) {
50
+ return {
51
+ content: [
52
+ { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
53
+ ],
54
+ isError: true,
55
+ };
56
+ }
57
+ });
58
+ // 4. engram_save_decision
59
+ server.tool('engram_save_decision', 'Record a decision and its rationale for future reference across platforms.', {
60
+ decision: z.string().describe('What was decided'),
61
+ rationale: z.string().describe('Why this decision was made'),
62
+ }, async ({ decision, rationale }) => {
63
+ try {
64
+ const version = await profile.saveDecision(decision, rationale);
65
+ return { content: [{ type: 'text', text: jsonText({ saved: true, version }) }] };
66
+ }
67
+ catch (err) {
68
+ return {
69
+ content: [
70
+ { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
71
+ ],
72
+ isError: true,
73
+ };
74
+ }
75
+ });
76
+ // 5. engram_session_summary
77
+ server.tool('engram_session_summary', 'Write a session summary for today. Other platforms can read this to maintain continuity.', {
78
+ platform: z.string().describe('Platform name (e.g. "claude", "openclaw")'),
79
+ summary: z.string().describe('Summary of what was discussed or accomplished'),
80
+ date: z
81
+ .string()
82
+ .regex(/^\d{4}-\d{2}-\d{2}$/)
83
+ .optional()
84
+ .describe('Date in YYYY-MM-DD format (defaults to today)'),
85
+ }, async ({ platform, summary, date }) => {
86
+ try {
87
+ const version = await sessions.writeSummary(platform, summary, date);
88
+ return { content: [{ type: 'text', text: jsonText({ saved: true, platform, version }) }] };
89
+ }
90
+ catch (err) {
91
+ return {
92
+ content: [
93
+ { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
94
+ ],
95
+ isError: true,
96
+ };
97
+ }
98
+ });
99
+ // 6. engram_read_sessions
100
+ server.tool('engram_read_sessions', 'Read recent session summaries. Optionally filter by platform, or exclude a platform to see cross-platform activity.', {
101
+ platform: z.string().optional().describe('Filter to a specific platform'),
102
+ exclude_platform: z
103
+ .string()
104
+ .optional()
105
+ .describe('Exclude a platform (for cross-platform reads, e.g. exclude "claude" to see what OpenClaw did)'),
106
+ limit: z.number().min(1).max(20).optional().default(5).describe('Max sessions to return'),
107
+ }, async ({ platform, exclude_platform, limit }) => {
108
+ try {
109
+ let result;
110
+ if (exclude_platform) {
111
+ result = await sessions.readCrossPlatform(exclude_platform, limit);
112
+ }
113
+ else if (platform) {
114
+ const entries = await sessions.listSessions(platform);
115
+ const recent = entries.slice(0, limit);
116
+ const paths = recent.map((e) => e.path);
117
+ if (paths.length === 0) {
118
+ return { content: [{ type: 'text', text: '[]' }] };
119
+ }
120
+ // Read the actual content — use readSummary for each
121
+ result = [];
122
+ for (const entry of recent) {
123
+ const content = await sessions.readSummary(entry.platform, entry.date);
124
+ if (content) {
125
+ result.push({ platform: entry.platform, date: entry.date, content });
126
+ }
127
+ }
128
+ }
129
+ else {
130
+ result = await sessions.readRecent(limit);
131
+ }
132
+ return { content: [{ type: 'text', text: jsonText(result) }] };
133
+ }
134
+ catch (err) {
135
+ return {
136
+ content: [
137
+ { type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` },
138
+ ],
139
+ isError: true,
140
+ };
141
+ }
142
+ });
143
+ }
144
+ //# sourceMappingURL=profile-tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile-tools.js","sourceRoot":"","sources":["../src/profile-tools.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { EngramClient } from '@engramx/client';
3
+ export declare function registerResources(server: McpServer, engram: EngramClient): void;
@@ -0,0 +1,22 @@
1
+ import { toSerializable } from './utils.js';
2
+ export function registerResources(server, engram) {
3
+ server.resource('engram-status', 'engram://status', { description: 'Engram canister status — memory file count, operators, cycles, version' }, async (uri) => {
4
+ try {
5
+ const status = await engram.status();
6
+ return {
7
+ contents: [
8
+ {
9
+ uri: uri.href,
10
+ mimeType: 'application/json',
11
+ text: JSON.stringify(toSerializable(status), null, 2),
12
+ },
13
+ ],
14
+ };
15
+ }
16
+ catch (error) {
17
+ const message = error instanceof Error ? error.message : String(error);
18
+ return { contents: [{ uri: uri.href, mimeType: 'text/plain', text: `Error: ${message}` }] };
19
+ }
20
+ });
21
+ }
22
+ //# sourceMappingURL=resources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,UAAU,iBAAiB,CAAC,MAAiB,EAAE,MAAoB;IACvE,MAAM,CAAC,QAAQ,CACb,eAAe,EACf,iBAAiB,EACjB,EAAE,WAAW,EAAE,wEAAwE,EAAE,EACzF,KAAK,EAAE,GAAG,EAAE,EAAE;QACZ,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;YACrC,OAAO;gBACL,QAAQ,EAAE;oBACR;wBACE,GAAG,EAAE,GAAG,CAAC,IAAI;wBACb,QAAQ,EAAE,kBAAkB;wBAC5B,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;qBACtD;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACvE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,UAAU,OAAO,EAAE,EAAE,CAAC,EAAE,CAAC;QAC9F,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
package/dist/server.js ADDED
@@ -0,0 +1,52 @@
1
+ // The stdio MCP server (default invocation). Split out of index.ts so the lightweight `connect`
2
+ // subcommand router doesn't statically pull in @engramx/client or require a session key.
3
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
4
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
+ import { join } from 'node:path';
6
+ import { existsSync } from 'node:fs';
7
+ import { homedir } from 'node:os';
8
+ import { EngramClient, ProfileManager, SessionManager } from '@engramx/client';
9
+ import { registerTools } from './tools.js';
10
+ import { registerProfileTools } from './profile-tools.js';
11
+ import { registerResources } from './resources.js';
12
+ // --- Environment variables (same names as agent-skill.ts) ---
13
+ const canisterId = process.env['ENGRAM_CANISTER_ID'] || process.env['VAULT_CANISTER_ID'];
14
+ if (!canisterId) {
15
+ console.error('Error: ENGRAM_CANISTER_ID environment variable is required.\n' +
16
+ 'Set it to your engram canister ID (e.g. "xxxxx-xxxxx-xxxxx-xxxxx-xxx").\n' +
17
+ 'To connect a client instead, run: npx @engramx/mcp connect --canister <id>');
18
+ process.exit(1);
19
+ }
20
+ const sessionKeyPath = process.env['ENGRAM_SESSION_KEY_PATH'] ||
21
+ process.env['ENGRAMX_SESSION_KEY_PATH'] ||
22
+ process.env['VAULT_SESSION_KEY_PATH'] ||
23
+ join(homedir(), '.engramx', 'session.key');
24
+ if (!existsSync(sessionKeyPath)) {
25
+ console.error(`Error: Session key not found at ${sessionKeyPath}.\n` +
26
+ 'Generate one with: npx @engramx/client pair <invite-code> --engram <canister-id>');
27
+ process.exit(1);
28
+ }
29
+ const host = process.env['ENGRAM_HOST'] || 'https://ic0.app';
30
+ // --- Client ---
31
+ const engram = new EngramClient({
32
+ canisterId,
33
+ sessionKeyPath,
34
+ host,
35
+ cacheMaxAge: 60_000,
36
+ });
37
+ // --- MCP Server ---
38
+ const server = new McpServer({
39
+ name: 'engramx',
40
+ version: '0.1.0',
41
+ });
42
+ const profile = new ProfileManager(engram);
43
+ const sessions = new SessionManager(engram);
44
+ registerTools(server, engram);
45
+ registerProfileTools(server, profile, sessions);
46
+ registerResources(server, engram);
47
+ // --- Connect via stdio ---
48
+ const transport = new StdioServerTransport();
49
+ await server.connect(transport);
50
+ // Log to stderr — stdout is the MCP protocol channel
51
+ console.error(`engramx MCP server started (canister: ${canisterId}, host: ${host})`);
52
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,yFAAyF;AAEzF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC/E,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,+DAA+D;AAE/D,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;AACzF,IAAI,CAAC,UAAU,EAAE,CAAC;IAChB,OAAO,CAAC,KAAK,CACX,+DAA+D;QAC7D,2EAA2E;QAC3E,4EAA4E,CAC/E,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,cAAc,GAClB,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC;IACvC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC;IACrC,IAAI,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;AAE7C,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,EAAE,CAAC;IAChC,OAAO,CAAC,KAAK,CACX,mCAAmC,cAAc,KAAK;QACpD,kFAAkF,CACrF,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,iBAAiB,CAAC;AAE7D,iBAAiB;AAEjB,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC;IAC9B,UAAU;IACV,cAAc;IACd,IAAI;IACJ,WAAW,EAAE,MAAM;CACpB,CAAC,CAAC;AAEH,qBAAqB;AAErB,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AAC3C,MAAM,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,CAAC,CAAC;AAE5C,aAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAC9B,oBAAoB,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;AAChD,iBAAiB,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAElC,4BAA4B;AAE5B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAEhC,qDAAqD;AACrD,OAAO,CAAC,KAAK,CAAC,yCAAyC,UAAU,WAAW,IAAI,GAAG,CAAC,CAAC"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Canonical schema source for MCP tools — used by both surfaces.
3
+ *
4
+ * Stdio MCP imports `TOOL_SCHEMAS` and registers tools at runtime.
5
+ *
6
+ * HTTP MCP (canister) gets a generated Motoko file `src/engram/McpTools.mo`
7
+ * from `scripts/gen-mcp-tools.mjs` reading `tool-schemas.json` (which this
8
+ * file re-exports). Run as part of `compile-wasm.sh`.
9
+ *
10
+ * To add a tool:
11
+ * 1. Add an entry to `tool-schemas.json` with surfaces ['stdio', 'http'] (or just one).
12
+ * 2. For stdio: add a handler in `tools.ts` keyed by tool name.
13
+ * 3. For HTTP MCP: add a `case (#X) { ... }` in `dispatchTool` (main.mo),
14
+ * then run `node scripts/gen-mcp-tools.mjs` to refresh the catalog.
15
+ */
16
+ import { z } from 'zod';
17
+ export type Surface = 'stdio' | 'http';
18
+ export interface JsonSchemaProperty {
19
+ type: 'string' | 'integer' | 'number' | 'boolean' | 'array' | 'object';
20
+ description?: string;
21
+ default?: unknown;
22
+ items?: {
23
+ type: string;
24
+ };
25
+ maxItems?: number;
26
+ }
27
+ export interface ToolSchema {
28
+ name: string;
29
+ description: string;
30
+ inputSchema: {
31
+ type: 'object';
32
+ required?: string[];
33
+ properties: Record<string, JsonSchemaProperty>;
34
+ };
35
+ requiredScope?: 'memory.read' | 'memory.append' | 'wallet.read' | 'wallet.spend';
36
+ surfaces: Surface[];
37
+ }
38
+ export declare const TOOL_SCHEMAS: ToolSchema[];
39
+ /** Convert a JSON Schema property shape to a Zod type for runtime validation. */
40
+ export declare function jsonSchemaPropertyToZod(prop: JsonSchemaProperty, required: boolean): z.ZodTypeAny;
41
+ /** Build a Zod shape (record of property → ZodType) from a tool's inputSchema. */
42
+ export declare function buildZodShape(tool: ToolSchema): Record<string, z.ZodTypeAny>;
43
+ /** Find a tool schema by name. Returns undefined if not found. */
44
+ export declare function findToolSchema(name: string): ToolSchema | undefined;
45
+ /** Tools available on a given surface. */
46
+ export declare function toolsForSurface(surface: Surface): ToolSchema[];
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Canonical schema source for MCP tools — used by both surfaces.
3
+ *
4
+ * Stdio MCP imports `TOOL_SCHEMAS` and registers tools at runtime.
5
+ *
6
+ * HTTP MCP (canister) gets a generated Motoko file `src/engram/McpTools.mo`
7
+ * from `scripts/gen-mcp-tools.mjs` reading `tool-schemas.json` (which this
8
+ * file re-exports). Run as part of `compile-wasm.sh`.
9
+ *
10
+ * To add a tool:
11
+ * 1. Add an entry to `tool-schemas.json` with surfaces ['stdio', 'http'] (or just one).
12
+ * 2. For stdio: add a handler in `tools.ts` keyed by tool name.
13
+ * 3. For HTTP MCP: add a `case (#X) { ... }` in `dispatchTool` (main.mo),
14
+ * then run `node scripts/gen-mcp-tools.mjs` to refresh the catalog.
15
+ */
16
+ import { z } from 'zod';
17
+ import schemasJson from './tool-schemas.json' with { type: 'json' };
18
+ export const TOOL_SCHEMAS = schemasJson.tools;
19
+ // ---------------------------------------------------------------------------
20
+ // Helpers
21
+ // ---------------------------------------------------------------------------
22
+ /** Convert a JSON Schema property shape to a Zod type for runtime validation. */
23
+ export function jsonSchemaPropertyToZod(prop, required) {
24
+ let zodType;
25
+ switch (prop.type) {
26
+ case 'string':
27
+ zodType = z.string();
28
+ break;
29
+ case 'integer':
30
+ case 'number':
31
+ zodType = z.number();
32
+ break;
33
+ case 'boolean':
34
+ zodType = z.boolean();
35
+ break;
36
+ case 'array':
37
+ zodType = z.array(prop.items?.type === 'string' ? z.string() : z.any());
38
+ break;
39
+ case 'object':
40
+ zodType = z.record(z.string(), z.any());
41
+ break;
42
+ default:
43
+ zodType = z.any();
44
+ }
45
+ if (prop.description)
46
+ zodType = zodType.describe(prop.description);
47
+ if (prop.default !== undefined)
48
+ zodType = zodType.default(prop.default);
49
+ if (!required)
50
+ zodType = zodType.optional();
51
+ return zodType;
52
+ }
53
+ /** Build a Zod shape (record of property → ZodType) from a tool's inputSchema. */
54
+ export function buildZodShape(tool) {
55
+ const shape = {};
56
+ const required = new Set(tool.inputSchema.required ?? []);
57
+ for (const [name, prop] of Object.entries(tool.inputSchema.properties)) {
58
+ shape[name] = jsonSchemaPropertyToZod(prop, required.has(name));
59
+ }
60
+ return shape;
61
+ }
62
+ /** Find a tool schema by name. Returns undefined if not found. */
63
+ export function findToolSchema(name) {
64
+ return TOOL_SCHEMAS.find((t) => t.name === name);
65
+ }
66
+ /** Tools available on a given surface. */
67
+ export function toolsForSurface(surface) {
68
+ return TOOL_SCHEMAS.filter((t) => t.surfaces.includes(surface));
69
+ }
70
+ //# sourceMappingURL=tool-schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-schemas.js","sourceRoot":"","sources":["../src/tool-schemas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,WAAW,MAAM,qBAAqB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAwBpE,MAAM,CAAC,MAAM,YAAY,GAAiB,WAAW,CAAC,KAAqB,CAAC;AAE5E,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,iFAAiF;AACjF,MAAM,UAAU,uBAAuB,CAAC,IAAwB,EAAE,QAAiB;IACjF,IAAI,OAAqB,CAAC;IAC1B,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM;QACR,KAAK,SAAS,CAAC;QACf,KAAK,QAAQ;YACX,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM;QACR,KAAK,SAAS;YACZ,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;YACtB,MAAM;QACR,KAAK,OAAO;YACV,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACxE,MAAM;QACR,KAAK,QAAQ;YACX,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;YACxC,MAAM;QACR;YACE,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC;IACtB,CAAC;IACD,IAAI,IAAI,CAAC,WAAW;QAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnE,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAC5B,OAAO,GAAI,OAAsC,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1E,IAAI,CAAC,QAAQ;QAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAC5C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,aAAa,CAAC,IAAgB;IAC5C,MAAM,KAAK,GAAiC,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC1D,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,CAAC;QACvE,KAAK,CAAC,IAAI,CAAC,GAAG,uBAAuB,CAAC,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,kEAAkE;AAClE,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACnD,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,136 @@
1
+ {
2
+ "$comment": "Canonical MCP tool catalog. Single source of truth for stdio MCP and HTTP MCP. Stdio loads at runtime via tool-schemas.ts; HTTP MCP gets a generated Motoko file (src/engram/McpTools.mo) from scripts/gen-mcp-tools.mjs reading this JSON.",
3
+ "tools": [
4
+ {
5
+ "name": "engram_read_memory",
6
+ "description": "Read a memory file from the engram by path. Returns {path, content, version, lastModifiedAt}.",
7
+ "inputSchema": {
8
+ "type": "object",
9
+ "required": ["path"],
10
+ "properties": {
11
+ "path": {
12
+ "type": "string",
13
+ "description": "Path within the engram (e.g. identity/preferences.md)"
14
+ }
15
+ }
16
+ },
17
+ "requiredScope": "memory.read",
18
+ "surfaces": ["stdio", "http"]
19
+ },
20
+ {
21
+ "name": "engram_append_memory",
22
+ "description": "Append content to a memory file. Creates the file if it does not exist. Cannot write to owner-only protected paths.",
23
+ "inputSchema": {
24
+ "type": "object",
25
+ "required": ["path", "content"],
26
+ "properties": {
27
+ "path": { "type": "string", "description": "File path" },
28
+ "content": { "type": "string", "description": "Content to append" }
29
+ }
30
+ },
31
+ "requiredScope": "memory.append",
32
+ "surfaces": ["stdio", "http"]
33
+ },
34
+ {
35
+ "name": "engram_list_memory_files",
36
+ "description": "List all memory file paths in the engram with version and lastModifiedAt.",
37
+ "inputSchema": { "type": "object", "properties": {} },
38
+ "requiredScope": "memory.read",
39
+ "surfaces": ["stdio", "http"]
40
+ },
41
+ {
42
+ "name": "engram_read_memory_batch",
43
+ "description": "Read multiple memory files in a single call. Returns content for each path or an error.",
44
+ "inputSchema": {
45
+ "type": "object",
46
+ "required": ["paths"],
47
+ "properties": {
48
+ "paths": { "type": "array", "items": { "type": "string" }, "maxItems": 50 }
49
+ }
50
+ },
51
+ "requiredScope": "memory.read",
52
+ "surfaces": ["stdio", "http"]
53
+ },
54
+ {
55
+ "name": "engram_memory_history",
56
+ "description": "Get version history of a memory file. Returns each prior version with timestamp and modifying principal.",
57
+ "inputSchema": {
58
+ "type": "object",
59
+ "required": ["path"],
60
+ "properties": { "path": { "type": "string" } }
61
+ },
62
+ "requiredScope": "memory.read",
63
+ "surfaces": ["stdio", "http"]
64
+ },
65
+ {
66
+ "name": "engram_read_audit_log",
67
+ "description": "Read the engram audit log entries.",
68
+ "inputSchema": {
69
+ "type": "object",
70
+ "properties": {
71
+ "fromIndex": { "type": "integer", "default": 0 },
72
+ "limit": { "type": "integer", "default": 50 }
73
+ }
74
+ },
75
+ "requiredScope": "memory.read",
76
+ "surfaces": ["stdio", "http"]
77
+ },
78
+ {
79
+ "name": "engram_status",
80
+ "description": "Get engram operational status: version, file count, audit log size, frozen/paused state.",
81
+ "inputSchema": { "type": "object", "properties": {} },
82
+ "surfaces": ["stdio", "http"]
83
+ },
84
+ {
85
+ "name": "engram_wallet_balance",
86
+ "description": "Get the engram canister cycles balance (canister fuel).",
87
+ "inputSchema": { "type": "object", "properties": {} },
88
+ "requiredScope": "wallet.read",
89
+ "surfaces": ["stdio", "http"]
90
+ },
91
+ {
92
+ "name": "engram_list_services",
93
+ "description": "List paid services this engram brokers via ic402.",
94
+ "inputSchema": { "type": "object", "properties": {} },
95
+ "surfaces": ["stdio", "http"]
96
+ },
97
+ {
98
+ "name": "engram_get_job_result",
99
+ "description": "Retrieve the result of a previously-submitted ic402 service job by job ID.",
100
+ "inputSchema": {
101
+ "type": "object",
102
+ "required": ["jobId"],
103
+ "properties": { "jobId": { "type": "string" } }
104
+ },
105
+ "requiredScope": "memory.read",
106
+ "surfaces": ["stdio", "http"]
107
+ },
108
+ {
109
+ "name": "engram_submit_service_request",
110
+ "description": "Submit a paid request to an ic402 service registered on this engram.",
111
+ "inputSchema": {
112
+ "type": "object",
113
+ "required": ["serviceId", "params"],
114
+ "properties": {
115
+ "serviceId": { "type": "string" },
116
+ "params": { "type": "string", "description": "JSON-encoded service-specific parameters" }
117
+ }
118
+ },
119
+ "requiredScope": "wallet.spend",
120
+ "surfaces": ["stdio"]
121
+ },
122
+ {
123
+ "name": "engram_dispute_job",
124
+ "description": "Dispute a job result (operator role).",
125
+ "inputSchema": {
126
+ "type": "object",
127
+ "required": ["jobId", "reason"],
128
+ "properties": {
129
+ "jobId": { "type": "string" },
130
+ "reason": { "type": "string" }
131
+ }
132
+ },
133
+ "surfaces": ["stdio"]
134
+ }
135
+ ]
136
+ }
@@ -0,0 +1,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { EngramClient } from '@engramx/client';
3
+ export declare function registerTools(server: McpServer, engram: EngramClient): void;