hippocamp 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,196 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js");
4
+ const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js");
5
+ const nodePath = require("node:path");
6
+ const { z } = require("zod");
7
+ const memory = require("./hippocamp-memory.cjs");
8
+
9
+ function toTextResult(payload) {
10
+ return {
11
+ content: [
12
+ {
13
+ type: "text",
14
+ text: typeof payload === "string" ? payload : JSON.stringify(payload, null, 2),
15
+ },
16
+ ],
17
+ };
18
+ }
19
+
20
+ function printHelp() {
21
+ console.log(`Hippocamp MCP server
22
+
23
+ Runs a local stdio MCP server for Hippocamp memory.
24
+
25
+ Environment:
26
+ HIPPOCAMP_GLOBAL_ROOT Local path to the Lagoon clone. Default: ~/.lagoon
27
+ HIPPOCAMP_PROJECT_ROOT Project root used to infer the current project slug. Default: current working directory
28
+
29
+ Tools:
30
+ wake_up
31
+ read_memory_file
32
+ write_memory_file
33
+ append_event
34
+ list_memory_files
35
+ search_memory
36
+ sync_memory
37
+ `);
38
+ }
39
+
40
+ async function runSmoke() {
41
+ const wake = await memory.wakeUp({});
42
+ console.log(JSON.stringify(wake, null, 2));
43
+ }
44
+
45
+ async function main() {
46
+ if (process.argv.includes("--help")) {
47
+ printHelp();
48
+ return;
49
+ }
50
+
51
+ if (process.argv.includes("--smoke")) {
52
+ await runSmoke();
53
+ return;
54
+ }
55
+
56
+ const server = new McpServer({
57
+ name: "hippocamp",
58
+ version: "0.1.0",
59
+ });
60
+
61
+ server.registerTool(
62
+ "wake_up",
63
+ {
64
+ description:
65
+ "Load the default global and project wake-up files. Use this at the start of a top-level task thread.",
66
+ inputSchema: {
67
+ projectRoot: z.string().optional(),
68
+ },
69
+ },
70
+ async ({ projectRoot }) => toTextResult(await memory.wakeUp({ projectRoot })),
71
+ );
72
+
73
+ server.registerTool(
74
+ "read_memory_file",
75
+ {
76
+ description: "Read one memory file from either global memory or the current project's memory folder.",
77
+ inputSchema: {
78
+ scope: z.enum(["global", "project"]),
79
+ path: z.string(),
80
+ projectRoot: z.string().optional(),
81
+ },
82
+ },
83
+ async ({ scope, path, projectRoot }) =>
84
+ toTextResult(await memory.readMemoryFile({ scope, path, projectRoot })),
85
+ );
86
+
87
+ server.registerTool(
88
+ "write_memory_file",
89
+ {
90
+ description:
91
+ "Create or overwrite one memory file. Use this for curated files like identity.md, current_state.md, or open_threads.md.",
92
+ inputSchema: {
93
+ scope: z.enum(["global", "project"]),
94
+ path: z.string(),
95
+ content: z.string(),
96
+ sync: z.boolean().optional(),
97
+ projectRoot: z.string().optional(),
98
+ },
99
+ },
100
+ async ({ scope, path, content, sync, projectRoot }) =>
101
+ toTextResult(await memory.writeMemoryFile({ scope, path, content, projectRoot, sync })),
102
+ );
103
+
104
+ server.registerTool(
105
+ "append_event",
106
+ {
107
+ description:
108
+ "Append a dated event entry under events/YYYY-MM-DD.md and update the sibling cue index. Store references to GitHub artifacts instead of copying their content.",
109
+ inputSchema: {
110
+ scope: z.enum(["global", "project"]).default("project"),
111
+ title: z.string().optional(),
112
+ cues: z.array(z.string()).optional(),
113
+ content: z.string(),
114
+ sync: z.boolean().optional(),
115
+ projectRoot: z.string().optional(),
116
+ },
117
+ },
118
+ async ({ scope, title, cues, content, sync, projectRoot }) =>
119
+ toTextResult(await memory.appendEvent({ scope, title, cues, content, projectRoot, sync })),
120
+ );
121
+
122
+ server.registerTool(
123
+ "list_memory_files",
124
+ {
125
+ description: "List files under global memory or the project memory folder.",
126
+ inputSchema: {
127
+ scope: z.enum(["global", "project"]),
128
+ path: z.string().optional(),
129
+ projectRoot: z.string().optional(),
130
+ },
131
+ },
132
+ async ({ scope, path, projectRoot }) =>
133
+ toTextResult({
134
+ scope,
135
+ root: memory.getScopeRoot(scope, projectRoot),
136
+ items: await memory.listDirectoryEntries(scope, path || ".", projectRoot),
137
+ }),
138
+ );
139
+
140
+ server.registerTool(
141
+ "search_memory",
142
+ {
143
+ description:
144
+ "Fuzzy-search memory files. Event search ranks indexed cues and headings first. Use this only when the wake-up files are insufficient, not as the default startup path.",
145
+ inputSchema: {
146
+ query: z.string(),
147
+ scope: z.enum(["global", "project", "both"]).default("both"),
148
+ maxResults: z.number().int().min(1).max(20).optional(),
149
+ projectRoot: z.string().optional(),
150
+ },
151
+ },
152
+ async ({ query, scope, maxResults, projectRoot }) =>
153
+ toTextResult(await memory.searchMemory({ query, scope, maxResults, projectRoot })),
154
+ );
155
+
156
+ server.registerTool(
157
+ "sync_memory",
158
+ {
159
+ description:
160
+ "Commit and push memory changes through the dedicated Lagoon clone. Omitting path syncs the default memory paths.",
161
+ inputSchema: {
162
+ scope: z.enum(["global", "project"]),
163
+ path: z.string().optional(),
164
+ projectRoot: z.string().optional(),
165
+ message: z.string().optional(),
166
+ },
167
+ },
168
+ async ({ scope, path, projectRoot, message }) => {
169
+ const scopeRoot = memory.getScopeRoot(scope, projectRoot);
170
+ const targetPaths = path
171
+ ? [nodePath.resolve(scopeRoot, path)]
172
+ : await memory.getDefaultSyncPaths(scope, projectRoot);
173
+
174
+ if (!targetPaths.length) {
175
+ throw new Error("no memory paths found to sync.");
176
+ }
177
+
178
+ return toTextResult(
179
+ await memory.syncMemory({
180
+ scope,
181
+ projectRoot,
182
+ paths: targetPaths,
183
+ message: message || `hippocamp: sync ${scope} memory`,
184
+ }),
185
+ );
186
+ },
187
+ );
188
+
189
+ const transport = new StdioServerTransport();
190
+ await server.connect(transport);
191
+ }
192
+
193
+ main().catch((error) => {
194
+ console.error("Hippocamp MCP server error:", error);
195
+ process.exit(1);
196
+ });