memoclaw-mcp 1.0.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/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # MemoClaw MCP Server
2
+
3
+ MCP (Model Context Protocol) server for MemoClaw semantic memory API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g memoclaw-mcp
9
+ ```
10
+
11
+ ## Configuration
12
+
13
+ Set your private key:
14
+ ```bash
15
+ export MEMOCLAW_PRIVATE_KEY=0x...
16
+ ```
17
+
18
+ ### Claude Desktop
19
+
20
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
21
+
22
+ ```json
23
+ {
24
+ "mcpServers": {
25
+ "memoclaw": {
26
+ "command": "memoclaw-mcp",
27
+ "env": {
28
+ "MEMOCLAW_PRIVATE_KEY": "0x..."
29
+ }
30
+ }
31
+ }
32
+ }
33
+ ```
34
+
35
+ ### Cursor
36
+
37
+ Add to MCP settings in Cursor preferences.
38
+
39
+ ## Tools
40
+
41
+ | Tool | Description |
42
+ |------|-------------|
43
+ | `memoclaw_store` | Store a memory with semantic embeddings |
44
+ | `memoclaw_recall` | Recall memories via semantic search |
45
+ | `memoclaw_list` | List stored memories |
46
+ | `memoclaw_delete` | Delete a memory by ID |
47
+
48
+ ## Example Usage
49
+
50
+ Once configured, Claude can use commands like:
51
+
52
+ - "Remember that the meeting is at 3pm tomorrow"
53
+ - "What did I say about the project deadline?"
54
+ - "List my recent memories"
55
+
56
+ ## Pricing
57
+
58
+ - Store: $0.001 per memory
59
+ - Recall: $0.001 per query
60
+ - List: $0.0005
61
+ - Delete: $0.0001
62
+
63
+ Paid with USDC on Base via x402 protocol.
64
+
65
+ ## Links
66
+
67
+ - [MemoClaw Website](https://memoclaw.dev)
68
+ - [MemoClaw Docs](https://docs.memoclaw.com)
69
+ - [MCP Specification](https://modelcontextprotocol.io)
70
+
71
+ ## License
72
+
73
+ MIT
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
5
+ import { x402Client } from '@x402/core/client';
6
+ import { x402HTTPClient } from '@x402/core/http';
7
+ import { ExactEvmScheme } from '@x402/evm/exact/client';
8
+ import { toClientEvmSigner } from '@x402/evm';
9
+ import { privateKeyToAccount } from 'viem/accounts';
10
+ const API_URL = process.env.MEMOCLAW_URL || 'https://api.memoclaw.dev';
11
+ const PRIVATE_KEY = process.env.MEMOCLAW_PRIVATE_KEY;
12
+ if (!PRIVATE_KEY) {
13
+ console.error('MEMOCLAW_PRIVATE_KEY environment variable required');
14
+ process.exit(1);
15
+ }
16
+ // x402 payment client setup
17
+ const account = privateKeyToAccount(PRIVATE_KEY);
18
+ const signer = toClientEvmSigner(account);
19
+ const coreClient = new x402Client().register('eip155:*', new ExactEvmScheme(signer));
20
+ const client = new x402HTTPClient(coreClient);
21
+ async function makeRequest(method, path, body) {
22
+ const url = `${API_URL}${path}`;
23
+ const headers = { 'Content-Type': 'application/json' };
24
+ const options = { method, headers };
25
+ if (body)
26
+ options.body = JSON.stringify(body);
27
+ let res = await fetch(url, options);
28
+ // Handle 402 Payment Required
29
+ if (res.status === 402) {
30
+ const errorBody = await res.json();
31
+ const paymentRequired = client.getPaymentRequiredResponse((name) => res.headers.get(name), errorBody);
32
+ const paymentPayload = await client.createPaymentPayload(paymentRequired);
33
+ const paymentHeaders = client.encodePaymentSignatureHeader(paymentPayload);
34
+ res = await fetch(url, {
35
+ method,
36
+ headers: { ...headers, ...paymentHeaders },
37
+ body: body ? JSON.stringify(body) : undefined,
38
+ });
39
+ }
40
+ if (!res.ok) {
41
+ const err = await res.text();
42
+ throw new Error(`HTTP ${res.status}: ${err}`);
43
+ }
44
+ return res.json();
45
+ }
46
+ const server = new Server({ name: 'memoclaw', version: '1.0.0' }, { capabilities: { tools: {} } });
47
+ // List available tools
48
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
49
+ tools: [
50
+ {
51
+ name: 'memoclaw_store',
52
+ description: 'Store a memory with semantic embeddings for later recall',
53
+ inputSchema: {
54
+ type: 'object',
55
+ properties: {
56
+ content: { type: 'string', description: 'Memory content to store' },
57
+ importance: { type: 'number', description: 'Importance score 0-1 (default 0.5)' },
58
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags for categorization' },
59
+ namespace: { type: 'string', description: 'Namespace for organization' },
60
+ },
61
+ required: ['content'],
62
+ },
63
+ },
64
+ {
65
+ name: 'memoclaw_recall',
66
+ description: 'Recall memories via semantic search',
67
+ inputSchema: {
68
+ type: 'object',
69
+ properties: {
70
+ query: { type: 'string', description: 'Search query' },
71
+ limit: { type: 'number', description: 'Max results (default 5)' },
72
+ min_similarity: { type: 'number', description: 'Min similarity threshold 0-1' },
73
+ tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
74
+ namespace: { type: 'string', description: 'Filter by namespace' },
75
+ },
76
+ required: ['query'],
77
+ },
78
+ },
79
+ {
80
+ name: 'memoclaw_list',
81
+ description: 'List stored memories',
82
+ inputSchema: {
83
+ type: 'object',
84
+ properties: {
85
+ limit: { type: 'number', description: 'Max results (default 20)' },
86
+ offset: { type: 'number', description: 'Pagination offset' },
87
+ tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
88
+ namespace: { type: 'string', description: 'Filter by namespace' },
89
+ },
90
+ },
91
+ },
92
+ {
93
+ name: 'memoclaw_delete',
94
+ description: 'Delete a memory by ID',
95
+ inputSchema: {
96
+ type: 'object',
97
+ properties: {
98
+ id: { type: 'string', description: 'Memory ID to delete' },
99
+ },
100
+ required: ['id'],
101
+ },
102
+ },
103
+ ],
104
+ }));
105
+ // Handle tool calls
106
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
107
+ const { name, arguments: args } = request.params;
108
+ try {
109
+ switch (name) {
110
+ case 'memoclaw_store': {
111
+ const { content, importance, tags, namespace } = args;
112
+ const result = await makeRequest('POST', '/v1/store', {
113
+ content,
114
+ importance,
115
+ metadata: tags ? { tags } : undefined,
116
+ namespace,
117
+ });
118
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
119
+ }
120
+ case 'memoclaw_recall': {
121
+ const { query, limit, min_similarity, tags, namespace } = args;
122
+ const result = await makeRequest('POST', '/v1/recall', {
123
+ query,
124
+ limit,
125
+ min_similarity,
126
+ filters: tags ? { tags } : undefined,
127
+ namespace,
128
+ });
129
+ // Format results nicely
130
+ const memories = result.memories || [];
131
+ const formatted = memories.map((m) => `[${m.score?.toFixed(3) || '?'}] ${m.content}\n tags: ${m.metadata?.tags?.join(', ') || 'none'}`).join('\n\n');
132
+ return { content: [{ type: 'text', text: formatted || 'No memories found' }] };
133
+ }
134
+ case 'memoclaw_list': {
135
+ const { limit, offset, tags, namespace } = args;
136
+ const params = new URLSearchParams();
137
+ if (limit)
138
+ params.set('limit', String(limit));
139
+ if (offset)
140
+ params.set('offset', String(offset));
141
+ if (namespace)
142
+ params.set('namespace', namespace);
143
+ const result = await makeRequest('GET', `/v1/memories?${params}`);
144
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
145
+ }
146
+ case 'memoclaw_delete': {
147
+ const { id } = args;
148
+ const result = await makeRequest('DELETE', `/v1/memories/${id}`);
149
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
150
+ }
151
+ default:
152
+ throw new Error(`Unknown tool: ${name}`);
153
+ }
154
+ }
155
+ catch (error) {
156
+ return {
157
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
158
+ isError: true,
159
+ };
160
+ }
161
+ });
162
+ // Start server
163
+ async function main() {
164
+ const transport = new StdioServerTransport();
165
+ await server.connect(transport);
166
+ console.error('MemoClaw MCP server running');
167
+ }
168
+ main().catch(console.error);
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "memoclaw-mcp",
3
+ "version": "1.0.0",
4
+ "description": "MCP server for MemoClaw semantic memory API",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "memoclaw-mcp": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "start": "node dist/index.js"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.0.0",
16
+ "@x402/core": "^2.3.0",
17
+ "@x402/evm": "^2.3.0",
18
+ "viem": "^2.0.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^22.0.0",
22
+ "typescript": "^5.7.0"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "memory",
27
+ "ai-agent",
28
+ "semantic-search"
29
+ ],
30
+ "license": "MIT"
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import {
5
+ CallToolRequestSchema,
6
+ ListToolsRequestSchema,
7
+ } from '@modelcontextprotocol/sdk/types.js';
8
+ import { x402Client } from '@x402/core/client';
9
+ import { x402HTTPClient } from '@x402/core/http';
10
+ import { ExactEvmScheme } from '@x402/evm/exact/client';
11
+ import { toClientEvmSigner } from '@x402/evm';
12
+ import { privateKeyToAccount } from 'viem/accounts';
13
+
14
+ const API_URL = process.env.MEMOCLAW_URL || 'https://api.memoclaw.dev';
15
+ const PRIVATE_KEY = process.env.MEMOCLAW_PRIVATE_KEY;
16
+
17
+ if (!PRIVATE_KEY) {
18
+ console.error('MEMOCLAW_PRIVATE_KEY environment variable required');
19
+ process.exit(1);
20
+ }
21
+
22
+ // x402 payment client setup
23
+ const account = privateKeyToAccount(PRIVATE_KEY as `0x${string}`);
24
+ const signer = toClientEvmSigner(account);
25
+ const coreClient = new x402Client().register('eip155:*', new ExactEvmScheme(signer));
26
+ const client = new x402HTTPClient(coreClient);
27
+
28
+ async function makeRequest(method: string, path: string, body?: any) {
29
+ const url = `${API_URL}${path}`;
30
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
31
+ const options: RequestInit = { method, headers };
32
+ if (body) options.body = JSON.stringify(body);
33
+
34
+ let res = await fetch(url, options);
35
+
36
+ // Handle 402 Payment Required
37
+ if (res.status === 402) {
38
+ const errorBody = await res.json();
39
+ const paymentRequired = client.getPaymentRequiredResponse(
40
+ (name: string) => res.headers.get(name),
41
+ errorBody
42
+ );
43
+
44
+ const paymentPayload = await client.createPaymentPayload(paymentRequired);
45
+ const paymentHeaders = client.encodePaymentSignatureHeader(paymentPayload);
46
+
47
+ res = await fetch(url, {
48
+ method,
49
+ headers: { ...headers, ...paymentHeaders },
50
+ body: body ? JSON.stringify(body) : undefined,
51
+ });
52
+ }
53
+
54
+ if (!res.ok) {
55
+ const err = await res.text();
56
+ throw new Error(`HTTP ${res.status}: ${err}`);
57
+ }
58
+
59
+ return res.json();
60
+ }
61
+
62
+ const server = new Server(
63
+ { name: 'memoclaw', version: '1.0.0' },
64
+ { capabilities: { tools: {} } }
65
+ );
66
+
67
+ // List available tools
68
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
69
+ tools: [
70
+ {
71
+ name: 'memoclaw_store',
72
+ description: 'Store a memory with semantic embeddings for later recall',
73
+ inputSchema: {
74
+ type: 'object',
75
+ properties: {
76
+ content: { type: 'string', description: 'Memory content to store' },
77
+ importance: { type: 'number', description: 'Importance score 0-1 (default 0.5)' },
78
+ tags: { type: 'array', items: { type: 'string' }, description: 'Tags for categorization' },
79
+ namespace: { type: 'string', description: 'Namespace for organization' },
80
+ },
81
+ required: ['content'],
82
+ },
83
+ },
84
+ {
85
+ name: 'memoclaw_recall',
86
+ description: 'Recall memories via semantic search',
87
+ inputSchema: {
88
+ type: 'object',
89
+ properties: {
90
+ query: { type: 'string', description: 'Search query' },
91
+ limit: { type: 'number', description: 'Max results (default 5)' },
92
+ min_similarity: { type: 'number', description: 'Min similarity threshold 0-1' },
93
+ tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
94
+ namespace: { type: 'string', description: 'Filter by namespace' },
95
+ },
96
+ required: ['query'],
97
+ },
98
+ },
99
+ {
100
+ name: 'memoclaw_list',
101
+ description: 'List stored memories',
102
+ inputSchema: {
103
+ type: 'object',
104
+ properties: {
105
+ limit: { type: 'number', description: 'Max results (default 20)' },
106
+ offset: { type: 'number', description: 'Pagination offset' },
107
+ tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
108
+ namespace: { type: 'string', description: 'Filter by namespace' },
109
+ },
110
+ },
111
+ },
112
+ {
113
+ name: 'memoclaw_delete',
114
+ description: 'Delete a memory by ID',
115
+ inputSchema: {
116
+ type: 'object',
117
+ properties: {
118
+ id: { type: 'string', description: 'Memory ID to delete' },
119
+ },
120
+ required: ['id'],
121
+ },
122
+ },
123
+ ],
124
+ }));
125
+
126
+ // Handle tool calls
127
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
128
+ const { name, arguments: args } = request.params;
129
+
130
+ try {
131
+ switch (name) {
132
+ case 'memoclaw_store': {
133
+ const { content, importance, tags, namespace } = args as any;
134
+ const result = await makeRequest('POST', '/v1/store', {
135
+ content,
136
+ importance,
137
+ metadata: tags ? { tags } : undefined,
138
+ namespace,
139
+ });
140
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
141
+ }
142
+
143
+ case 'memoclaw_recall': {
144
+ const { query, limit, min_similarity, tags, namespace } = args as any;
145
+ const result = await makeRequest('POST', '/v1/recall', {
146
+ query,
147
+ limit,
148
+ min_similarity,
149
+ filters: tags ? { tags } : undefined,
150
+ namespace,
151
+ });
152
+
153
+ // Format results nicely
154
+ const memories = result.memories || [];
155
+ const formatted = memories.map((m: any) =>
156
+ `[${m.score?.toFixed(3) || '?'}] ${m.content}\n tags: ${m.metadata?.tags?.join(', ') || 'none'}`
157
+ ).join('\n\n');
158
+
159
+ return { content: [{ type: 'text', text: formatted || 'No memories found' }] };
160
+ }
161
+
162
+ case 'memoclaw_list': {
163
+ const { limit, offset, tags, namespace } = args as any;
164
+ const params = new URLSearchParams();
165
+ if (limit) params.set('limit', String(limit));
166
+ if (offset) params.set('offset', String(offset));
167
+ if (namespace) params.set('namespace', namespace);
168
+
169
+ const result = await makeRequest('GET', `/v1/memories?${params}`);
170
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
171
+ }
172
+
173
+ case 'memoclaw_delete': {
174
+ const { id } = args as any;
175
+ const result = await makeRequest('DELETE', `/v1/memories/${id}`);
176
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
177
+ }
178
+
179
+ default:
180
+ throw new Error(`Unknown tool: ${name}`);
181
+ }
182
+ } catch (error) {
183
+ return {
184
+ content: [{ type: 'text', text: `Error: ${error instanceof Error ? error.message : String(error)}` }],
185
+ isError: true,
186
+ };
187
+ }
188
+ });
189
+
190
+ // Start server
191
+ async function main() {
192
+ const transport = new StdioServerTransport();
193
+ await server.connect(transport);
194
+ console.error('MemoClaw MCP server running');
195
+ }
196
+
197
+ main().catch(console.error);
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "outDir": "dist",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "declaration": true
11
+ },
12
+ "include": ["src/**/*"]
13
+ }