smritea-mcp 0.1.1
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/.eslintrc.json +19 -0
- package/.pre-commit-config.yaml +30 -0
- package/Makefile +20 -0
- package/README.md +214 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +40 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +17 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +63 -0
- package/dist/tools/app.d.ts +15 -0
- package/dist/tools/app.d.ts.map +1 -0
- package/dist/tools/app.js +51 -0
- package/dist/tools/memory.d.ts +12 -0
- package/dist/tools/memory.d.ts.map +1 -0
- package/dist/tools/memory.js +77 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/package.json +32 -0
- package/src/config.ts +67 -0
- package/src/errors.ts +15 -0
- package/src/index.ts +62 -0
- package/src/server.ts +119 -0
- package/src/tools/app.ts +59 -0
- package/src/tools/memory.ts +102 -0
- package/src/types.ts +35 -0
- package/tsconfig.json +17 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"parserOptions": {
|
|
4
|
+
"project": "./tsconfig.json",
|
|
5
|
+
"ecmaVersion": 2022
|
|
6
|
+
},
|
|
7
|
+
"plugins": ["@typescript-eslint"],
|
|
8
|
+
"extends": [
|
|
9
|
+
"eslint:recommended",
|
|
10
|
+
"plugin:@typescript-eslint/recommended"
|
|
11
|
+
],
|
|
12
|
+
"ignorePatterns": ["dist/**", "*.js"],
|
|
13
|
+
"rules": {
|
|
14
|
+
"@typescript-eslint/no-explicit-any": "warn",
|
|
15
|
+
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
16
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
17
|
+
"@typescript-eslint/no-non-null-assertion": "warn"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Pre-commit hooks for smritea-mcp
|
|
2
|
+
#
|
|
3
|
+
# Setup:
|
|
4
|
+
# pip install pre-commit && pre-commit install
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# pre-commit run --all-files # Run all hooks
|
|
8
|
+
# pre-commit run <hook-id> # Run a specific hook
|
|
9
|
+
#
|
|
10
|
+
repos:
|
|
11
|
+
- repo: https://github.com/gitleaks/gitleaks
|
|
12
|
+
rev: v8.18.4
|
|
13
|
+
hooks:
|
|
14
|
+
- id: gitleaks
|
|
15
|
+
|
|
16
|
+
- repo: local
|
|
17
|
+
hooks:
|
|
18
|
+
- id: mcp-lint
|
|
19
|
+
name: Lint
|
|
20
|
+
entry: make lint
|
|
21
|
+
language: system
|
|
22
|
+
files: ^src/.*\.ts$
|
|
23
|
+
pass_filenames: false
|
|
24
|
+
|
|
25
|
+
- id: mcp-build
|
|
26
|
+
name: Build
|
|
27
|
+
entry: make build
|
|
28
|
+
language: system
|
|
29
|
+
files: ^src/.*\.ts$
|
|
30
|
+
pass_filenames: false
|
package/Makefile
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
.PHONY: lint typecheck build publish help
|
|
2
|
+
|
|
3
|
+
help: ## Show available targets
|
|
4
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " %-20s %s\n", $$1, $$2}'
|
|
5
|
+
|
|
6
|
+
lint: ## Type-check and lint smritea-mcp TypeScript
|
|
7
|
+
npm install --silent
|
|
8
|
+
npm run typecheck
|
|
9
|
+
npm run lint
|
|
10
|
+
|
|
11
|
+
build: ## Compile TypeScript
|
|
12
|
+
rm -rf dist
|
|
13
|
+
npm install --silent
|
|
14
|
+
npm run build
|
|
15
|
+
|
|
16
|
+
publish: build ## Build and publish smritea-mcp to npm (requires NPM_TOKEN env var)
|
|
17
|
+
@if [ -z "$$NPM_TOKEN" ]; then \
|
|
18
|
+
echo "ERROR: NPM_TOKEN environment variable is not set"; exit 1; \
|
|
19
|
+
fi
|
|
20
|
+
npm set "//registry.npmjs.org/:_authToken=$$NPM_TOKEN" && npm publish --access public
|
package/README.md
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
# smritea-mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for [smritea](https://smritea.ai) — gives AI assistants (Claude Code, Cursor, etc.) direct access to your smritea memory store.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
### Step 1 — Run init
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx -y smritea-mcp init
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
This interactive wizard prompts for three values:
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
API base URL [https://api.smritea.ai]:
|
|
19
|
+
```
|
|
20
|
+
Press Enter to use the default (`https://api.smritea.ai`). If you are self-hosting smritea, enter your custom URL here.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
API key:
|
|
24
|
+
```
|
|
25
|
+
Paste your smritea API key (required). You can find it in the smritea dashboard under API Keys.
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Your name or user ID (used when you say "I prefer…", "I like…") [optional]:
|
|
29
|
+
```
|
|
30
|
+
Optional. If set, this value is automatically used as the `user_id` when you refer to yourself in conversation ("I prefer dark mode", "I like Python"), so memories about you are stored under a consistent ID without having to pass `user_id` explicitly every time.
|
|
31
|
+
|
|
32
|
+
On success, credentials are saved to `~/.smritea/mcp-config.json`.
|
|
33
|
+
|
|
34
|
+
### Step 2 — Register the server with your AI client
|
|
35
|
+
|
|
36
|
+
**Claude Code** — add to `~/.claude.json` under the `mcpServers` key:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"mcpServers": {
|
|
41
|
+
"smritea": {
|
|
42
|
+
"command": "npx",
|
|
43
|
+
"args": ["-y", "smritea-mcp", "serve"]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Step 3 — Set the active app (once per project)
|
|
50
|
+
|
|
51
|
+
In a conversation with Claude Code, run:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
Use the select_app tool with app_id "<your-app-id>"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
This writes `.smritea/config.json` in your project directory (automatically gitignored) so all memory operations in that project are scoped to the correct app.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
smritea-mcp uses a two-tier config system.
|
|
64
|
+
|
|
65
|
+
### User-scoped (`~/.smritea/mcp-config.json`)
|
|
66
|
+
|
|
67
|
+
Created once by `npx smritea-mcp init`. Stores credentials shared across all projects.
|
|
68
|
+
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"api_key": "sk-...",
|
|
72
|
+
"base_url": "https://api.smritea.ai",
|
|
73
|
+
"first_person_user_id": "alice"
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`first_person_user_id` is optional. When set, it is automatically used as `user_id` for memory operations where the user refers to themselves ("I prefer…", "I like…") without explicitly passing a `user_id`.
|
|
78
|
+
|
|
79
|
+
### Project-scoped (`.smritea/config.json`)
|
|
80
|
+
|
|
81
|
+
Created per-project by the `select_app` tool. Determines which smritea app receives memory operations in this project. Automatically gitignored.
|
|
82
|
+
|
|
83
|
+
```json
|
|
84
|
+
{
|
|
85
|
+
"app_id": "app_abc123",
|
|
86
|
+
"app_name": "My App"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Environment variable overrides
|
|
91
|
+
|
|
92
|
+
| Variable | Overrides |
|
|
93
|
+
|----------|-----------|
|
|
94
|
+
| `SMRITEA_API_KEY` | `api_key` in user config |
|
|
95
|
+
| `SMRITEA_BASE_URL` | `base_url` in user config |
|
|
96
|
+
| `SMRITEA_APP_ID` | `app_id` in project config |
|
|
97
|
+
| `SMRITEA_FIRST_PERSON_USER_ID` | `first_person_user_id` in user config |
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Tools
|
|
102
|
+
|
|
103
|
+
### `select_app`
|
|
104
|
+
|
|
105
|
+
Set the active smritea app for the current project. All subsequent memory operations in this project will use the specified app.
|
|
106
|
+
|
|
107
|
+
Writes `.smritea/config.json` in the current working directory and creates `.smritea/.gitignore` so the config is never accidentally committed.
|
|
108
|
+
|
|
109
|
+
**Parameters**
|
|
110
|
+
|
|
111
|
+
| Name | Type | Required | Description |
|
|
112
|
+
|------|------|----------|-------------|
|
|
113
|
+
| `app_id` | string | Yes | The smritea app ID (e.g. `app_abc123`) |
|
|
114
|
+
| `app_name` | string | No | Optional display name for the app |
|
|
115
|
+
|
|
116
|
+
**Example**
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
Use the select_app tool with app_id "app_abc123" and app_name "My Project"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### `list_apps`
|
|
125
|
+
|
|
126
|
+
Show the currently active app for this project.
|
|
127
|
+
|
|
128
|
+
> **Note**: Listing all apps via API key auth is not yet available. API keys are scoped to a single app. Use `select_app` to switch apps.
|
|
129
|
+
|
|
130
|
+
**Parameters**: none
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### `add_memory`
|
|
135
|
+
|
|
136
|
+
Add a new memory to the active smritea app.
|
|
137
|
+
|
|
138
|
+
**Parameters**
|
|
139
|
+
|
|
140
|
+
| Name | Type | Required | Description |
|
|
141
|
+
|------|------|----------|-------------|
|
|
142
|
+
| `content` | string | Yes | The memory content to store |
|
|
143
|
+
| `user_id` | string | No | User ID to associate with this memory |
|
|
144
|
+
| `metadata` | object | No | Optional key-value metadata |
|
|
145
|
+
|
|
146
|
+
**Example**
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Add a memory: "User prefers dark mode and uses vim keybindings" for user_id "alice"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### `search_memories`
|
|
155
|
+
|
|
156
|
+
Search for memories semantically. Returns results ranked by relevance score.
|
|
157
|
+
|
|
158
|
+
**Parameters**
|
|
159
|
+
|
|
160
|
+
| Name | Type | Required | Description |
|
|
161
|
+
|------|------|----------|-------------|
|
|
162
|
+
| `query` | string | Yes | Natural language search query |
|
|
163
|
+
| `user_id` | string | No | Filter results to a specific user |
|
|
164
|
+
| `limit` | number | No | Maximum number of results to return |
|
|
165
|
+
| `method` | string | No | Search method: `quick_search`, `deep_search`, `context_aware_search` |
|
|
166
|
+
| `threshold` | number | No | Minimum relevance score (0.0–1.0) |
|
|
167
|
+
| `graph_depth` | number | No | Graph traversal depth override |
|
|
168
|
+
| `conversation_id` | string | No | Filter to a specific conversation |
|
|
169
|
+
|
|
170
|
+
**Example**
|
|
171
|
+
|
|
172
|
+
```
|
|
173
|
+
Search memories for "editor preferences" for user_id "alice", limit 5
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### `get_memory`
|
|
179
|
+
|
|
180
|
+
Retrieve a single memory by its ID.
|
|
181
|
+
|
|
182
|
+
**Parameters**
|
|
183
|
+
|
|
184
|
+
| Name | Type | Required | Description |
|
|
185
|
+
|------|------|----------|-------------|
|
|
186
|
+
| `memory_id` | string | Yes | The memory ID (e.g. `mem_abc123`) |
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### `delete_memory`
|
|
191
|
+
|
|
192
|
+
Delete a memory by its ID. This action is irreversible.
|
|
193
|
+
|
|
194
|
+
**Parameters**
|
|
195
|
+
|
|
196
|
+
| Name | Type | Required | Description |
|
|
197
|
+
|------|------|----------|-------------|
|
|
198
|
+
| `memory_id` | string | Yes | The memory ID to delete |
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## How it works
|
|
203
|
+
|
|
204
|
+
smritea-mcp is a TypeScript MCP server using `stdio` transport. It wraps the [smritea TypeScript SDK](https://github.com/SmrutAI/smritea-sdk) and exposes memory operations as MCP tools.
|
|
205
|
+
|
|
206
|
+
All JSON-RPC communication flows over stdout. All logging goes to stderr so it never interferes with the MCP protocol stream.
|
|
207
|
+
|
|
208
|
+
```
|
|
209
|
+
AI assistant (Claude Code / Cursor)
|
|
210
|
+
↕ JSON-RPC over stdio
|
|
211
|
+
smritea-mcp (this server)
|
|
212
|
+
↕ HTTPS
|
|
213
|
+
smritea TypeScript SDK → smritea Cloud API
|
|
214
|
+
```
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface UserConfig {
|
|
2
|
+
api_key: string;
|
|
3
|
+
base_url: string;
|
|
4
|
+
first_person_user_id?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ProjectConfig {
|
|
7
|
+
app_id: string;
|
|
8
|
+
app_name?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ResolvedConfig {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
appId: string;
|
|
14
|
+
/** Used as user_id when the user refers to themselves ("I prefer…", "I like…"). */
|
|
15
|
+
firstPersonUserId?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function loadConfig(): ResolvedConfig;
|
|
18
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAaA,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,mFAAmF;IACnF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAWD,wBAAgB,UAAU,IAAI,cAAc,CAyB3C"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two-tier configuration for smritea-mcp.
|
|
3
|
+
*
|
|
4
|
+
* User-scoped (~/.smritea/mcp-config.json): api_key, base_url
|
|
5
|
+
* Project-scoped (.smritea/config.json): app_id, app_name
|
|
6
|
+
*
|
|
7
|
+
* Environment variable overrides:
|
|
8
|
+
* SMRITEA_API_KEY, SMRITEA_BASE_URL, SMRITEA_APP_ID
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
const USER_CONFIG_PATH = join(homedir(), '.smritea', 'mcp-config.json');
|
|
14
|
+
const PROJECT_CONFIG_PATH = join(process.cwd(), '.smritea', 'config.json');
|
|
15
|
+
function readJsonFile(path) {
|
|
16
|
+
if (!existsSync(path))
|
|
17
|
+
return null;
|
|
18
|
+
const raw = readFileSync(path, 'utf-8');
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
export function loadConfig() {
|
|
22
|
+
const user = readJsonFile(USER_CONFIG_PATH);
|
|
23
|
+
const project = readJsonFile(PROJECT_CONFIG_PATH);
|
|
24
|
+
const apiKey = process.env['SMRITEA_API_KEY'] ?? user?.api_key;
|
|
25
|
+
const baseUrl = process.env['SMRITEA_BASE_URL'] ?? user?.base_url ?? 'https://api.smritea.ai';
|
|
26
|
+
const appId = process.env['SMRITEA_APP_ID'] ?? project?.app_id;
|
|
27
|
+
const firstPersonUserId = process.env['SMRITEA_FIRST_PERSON_USER_ID'] ?? user?.first_person_user_id;
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
throw new Error('smritea API key not configured. Run: npx smritea-mcp init\n' +
|
|
30
|
+
`Or set SMRITEA_API_KEY env var.\n` +
|
|
31
|
+
`Config file expected at: ${USER_CONFIG_PATH}`);
|
|
32
|
+
}
|
|
33
|
+
if (!appId) {
|
|
34
|
+
throw new Error('smritea app ID not configured. Run: npx smritea-mcp select-app <app_id>\n' +
|
|
35
|
+
`Or set SMRITEA_APP_ID env var.\n` +
|
|
36
|
+
`Config file expected at: ${PROJECT_CONFIG_PATH}`);
|
|
37
|
+
}
|
|
38
|
+
return { apiKey, baseUrl, appId, firstPersonUserId };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=config.js.map
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class McpConfigError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class McpToolError extends Error {
|
|
5
|
+
readonly cause?: unknown | undefined;
|
|
6
|
+
constructor(message: string, cause?: unknown | undefined);
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAK5B;AAED,qBAAa,YAAa,SAAQ,KAAK;aACQ,KAAK,CAAC,EAAE,OAAO;gBAAhD,OAAO,EAAE,MAAM,EAAkB,KAAK,CAAC,EAAE,OAAO,YAAA;CAK7D"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class McpConfigError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'McpConfigError';
|
|
5
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export class McpToolError extends Error {
|
|
9
|
+
cause;
|
|
10
|
+
constructor(message, cause) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.cause = cause;
|
|
13
|
+
this.name = 'McpToolError';
|
|
14
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=errors.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startServer } from './server.js';
|
|
3
|
+
import { createInterface } from 'node:readline/promises';
|
|
4
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { homedir } from 'node:os';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
async function runInit() {
|
|
8
|
+
const configDir = join(homedir(), '.smritea');
|
|
9
|
+
const configPath = join(configDir, 'mcp-config.json');
|
|
10
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
11
|
+
try {
|
|
12
|
+
const baseUrlAnswer = await rl.question('API base URL [https://api.smritea.ai]: ');
|
|
13
|
+
const baseUrl = baseUrlAnswer.trim() || 'https://api.smritea.ai';
|
|
14
|
+
const apiKeyAnswer = await rl.question('API key: ');
|
|
15
|
+
const apiKey = apiKeyAnswer.trim();
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
console.error('Error: API key is required.');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
const firstPersonUserIdAnswer = await rl.question('Your name or user ID (used when you say "I prefer…", "I like…") [optional]: ');
|
|
21
|
+
const firstPersonUserId = firstPersonUserIdAnswer.trim() || undefined;
|
|
22
|
+
mkdirSync(configDir, { recursive: true });
|
|
23
|
+
const configData = { api_key: apiKey, base_url: baseUrl };
|
|
24
|
+
if (firstPersonUserId !== undefined) {
|
|
25
|
+
configData['first_person_user_id'] = firstPersonUserId;
|
|
26
|
+
}
|
|
27
|
+
writeFileSync(configPath, JSON.stringify(configData, null, 2) + '\n');
|
|
28
|
+
console.error('✓ Config saved to ' + configPath);
|
|
29
|
+
if (firstPersonUserId !== undefined) {
|
|
30
|
+
console.error(`✓ First-person user ID set to: ${firstPersonUserId}`);
|
|
31
|
+
}
|
|
32
|
+
console.error('Next: use the select_app tool in your AI assistant to set an app ID.');
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
rl.close();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const subcommand = process.argv[2];
|
|
39
|
+
if (subcommand === 'serve' || subcommand === undefined) {
|
|
40
|
+
startServer().catch((err) => {
|
|
41
|
+
console.error('Fatal:', err);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
else if (subcommand === 'init') {
|
|
46
|
+
runInit().catch((err) => {
|
|
47
|
+
console.error('Init failed:', err);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.error(`Unknown subcommand: ${subcommand}`);
|
|
53
|
+
console.error('Usage: smritea-mcp [serve|init]');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAcA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAwGjD"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { SmriteaClient } from 'smritea-sdk';
|
|
5
|
+
import { loadConfig } from './config.js';
|
|
6
|
+
import { AddMemoryInput, SearchMemoriesInput, GetMemoryInput, DeleteMemoryInput, SelectAppInput } from './types.js';
|
|
7
|
+
import { handleAddMemory, handleSearchMemories, handleGetMemory, handleDeleteMemory, } from './tools/memory.js';
|
|
8
|
+
import { handleSelectApp, handleListApps } from './tools/app.js';
|
|
9
|
+
export async function startServer() {
|
|
10
|
+
const config = loadConfig();
|
|
11
|
+
const client = new SmriteaClient({
|
|
12
|
+
apiKey: config.apiKey,
|
|
13
|
+
appId: config.appId,
|
|
14
|
+
baseUrl: config.baseUrl,
|
|
15
|
+
});
|
|
16
|
+
const server = new McpServer({ name: 'smritea', version: '0.1.0' }, {
|
|
17
|
+
instructions: 'smritea is a persistent AI memory system. Use it to remember facts, preferences, ' +
|
|
18
|
+
'decisions, and context across conversations. Proactively store anything the user tells ' +
|
|
19
|
+
'you that they would want recalled later. Before starting work on a task, search memories ' +
|
|
20
|
+
'to surface relevant context the user may not have re-stated. When the user says "remember" ' +
|
|
21
|
+
'or "don\'t forget", always call add_memory immediately.',
|
|
22
|
+
});
|
|
23
|
+
const firstPersonUserId = config.firstPersonUserId;
|
|
24
|
+
server.tool('add_memory', 'Store a memory in smritea. Call this whenever the user shares a preference, decision, fact ' +
|
|
25
|
+
'about themselves, or anything they would want recalled in a future conversation. Do not wait ' +
|
|
26
|
+
'to be asked — if the user says "I prefer X", "my X is Y", "remember that", or "don\'t forget", ' +
|
|
27
|
+
'call this immediately. If the user says "I" or refers to themselves, omit user_id — it is ' +
|
|
28
|
+
'automatically set to the configured default.', AddMemoryInput.shape, async (input) => handleAddMemory(client, AddMemoryInput.parse(input), firstPersonUserId));
|
|
29
|
+
server.tool('search_memories', 'Search smritea memories by natural language query. Call this at the start of a new task or ' +
|
|
30
|
+
'topic to surface relevant context — user preferences, past decisions, stated constraints — ' +
|
|
31
|
+
'without waiting for the user to re-explain them. Also call when the user asks "do you remember", ' +
|
|
32
|
+
'"what do you know about", or "remind me". Omit user_id when searching for the current user\'s ' +
|
|
33
|
+
'own memories — it defaults to the configured user automatically.', SearchMemoriesInput.shape, async (input) => handleSearchMemories(client, SearchMemoriesInput.parse(input), firstPersonUserId));
|
|
34
|
+
server.tool('get_memory', 'Retrieve a specific memory by its ID. Use when you already have a memory_id from a previous ' +
|
|
35
|
+
'search result and need the full memory object.', GetMemoryInput.shape, async (input) => handleGetMemory(client, GetMemoryInput.parse(input)));
|
|
36
|
+
server.tool('delete_memory', 'Permanently delete a memory by its ID. Use when the user explicitly asks to forget something ' +
|
|
37
|
+
'or says a stored fact is no longer true. This action is irreversible — confirm the memory_id ' +
|
|
38
|
+
'from a search result before deleting.', DeleteMemoryInput.shape, async (input) => handleDeleteMemory(client, DeleteMemoryInput.parse(input)));
|
|
39
|
+
server.tool('select_app', 'Set the active smritea app for this project. Writes a project-scoped config to ' +
|
|
40
|
+
'.smritea/config.json so all subsequent memory operations use this app. Call this once ' +
|
|
41
|
+
'when setting up smritea in a new project.', SelectAppInput.shape, (input) => handleSelectApp(SelectAppInput.parse(input)));
|
|
42
|
+
server.tool('list_apps', 'Show the currently configured smritea app for this project. Useful for confirming which ' +
|
|
43
|
+
'app memories are being stored under.', {}, () => handleListApps(config));
|
|
44
|
+
server.prompt('recall', 'Search your smritea memories and surface everything relevant to the current topic or task. ' +
|
|
45
|
+
'Use this at the start of a conversation or when switching context.', { topic: z.string().describe('The topic, task, or question to search memories for') }, ({ topic }) => ({
|
|
46
|
+
messages: [
|
|
47
|
+
{
|
|
48
|
+
role: 'user',
|
|
49
|
+
content: {
|
|
50
|
+
type: 'text',
|
|
51
|
+
text: `Search my smritea memories for everything relevant to: "${topic}"\n\n` +
|
|
52
|
+
'Retrieve the most relevant results and summarise what you find before we continue. ' +
|
|
53
|
+
'If you find preferences, constraints, or past decisions related to this topic, ' +
|
|
54
|
+
'apply them proactively without waiting for me to re-state them.',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
}));
|
|
59
|
+
const transport = new StdioServerTransport();
|
|
60
|
+
await server.connect(transport);
|
|
61
|
+
console.error('smritea MCP server running (stdio)');
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import type { SelectAppInput } from '../types.js';
|
|
3
|
+
import type { ResolvedConfig } from '../config.js';
|
|
4
|
+
export declare function handleSelectApp(input: SelectAppInput): CallToolResult;
|
|
5
|
+
/**
|
|
6
|
+
* list_apps — stub.
|
|
7
|
+
*
|
|
8
|
+
* Listing apps via API key is not yet supported by the smritea SDK API.
|
|
9
|
+
* API key auth is scoped to a single app; a dedicated /api/v1/sdk/apps
|
|
10
|
+
* endpoint is needed before this can enumerate all apps for an organisation.
|
|
11
|
+
*
|
|
12
|
+
* For now, returns the currently active app from config.
|
|
13
|
+
*/
|
|
14
|
+
export declare function handleListApps(config: ResolvedConfig): CallToolResult;
|
|
15
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/tools/app.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAMnD,wBAAgB,eAAe,CAAC,KAAK,EAAE,cAAc,GAAG,cAAc,CAuBrE;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAcrE"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const SMRITEA_DIR = join(process.cwd(), '.smritea');
|
|
4
|
+
const CONFIG_PATH = join(SMRITEA_DIR, 'config.json');
|
|
5
|
+
const GITIGNORE_PATH = join(SMRITEA_DIR, '.gitignore');
|
|
6
|
+
export function handleSelectApp(input) {
|
|
7
|
+
mkdirSync(SMRITEA_DIR, { recursive: true });
|
|
8
|
+
const config = { app_id: input.app_id };
|
|
9
|
+
if (input.app_name !== undefined) {
|
|
10
|
+
config.app_name = input.app_name;
|
|
11
|
+
}
|
|
12
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
13
|
+
// Prevent committing the project-scoped config — it is machine/project-local
|
|
14
|
+
if (!existsSync(GITIGNORE_PATH)) {
|
|
15
|
+
writeFileSync(GITIGNORE_PATH, 'config.json\n', 'utf-8');
|
|
16
|
+
}
|
|
17
|
+
const name = input.app_name !== undefined ? ` (${input.app_name})` : '';
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: `App selected: ${input.app_id}${name}\nConfig written to: ${CONFIG_PATH}\nAll memory operations in this project will now use this app.`,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* list_apps — stub.
|
|
29
|
+
*
|
|
30
|
+
* Listing apps via API key is not yet supported by the smritea SDK API.
|
|
31
|
+
* API key auth is scoped to a single app; a dedicated /api/v1/sdk/apps
|
|
32
|
+
* endpoint is needed before this can enumerate all apps for an organisation.
|
|
33
|
+
*
|
|
34
|
+
* For now, returns the currently active app from config.
|
|
35
|
+
*/
|
|
36
|
+
export function handleListApps(config) {
|
|
37
|
+
return {
|
|
38
|
+
content: [
|
|
39
|
+
{
|
|
40
|
+
type: 'text',
|
|
41
|
+
text: [
|
|
42
|
+
`Currently active app: ${config.appId}`,
|
|
43
|
+
'',
|
|
44
|
+
'Note: listing all apps is not yet available via API key auth.',
|
|
45
|
+
'To switch apps, use the select_app tool with the desired app ID.',
|
|
46
|
+
].join('\n'),
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SmriteaClient } from 'smritea-sdk';
|
|
2
|
+
import type { Memory, SearchResult } from 'smritea-sdk';
|
|
3
|
+
import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
4
|
+
import type { AddMemoryInput, SearchMemoriesInput, GetMemoryInput, DeleteMemoryInput } from '../types.js';
|
|
5
|
+
export declare function formatMemory(memory: Memory): string;
|
|
6
|
+
export declare function formatSearchResult(result: SearchResult): string;
|
|
7
|
+
export declare function formatError(err: unknown): string;
|
|
8
|
+
export declare function handleAddMemory(client: SmriteaClient, input: AddMemoryInput, firstPersonUserId?: string): Promise<CallToolResult>;
|
|
9
|
+
export declare function handleSearchMemories(client: SmriteaClient, input: SearchMemoriesInput, firstPersonUserId?: string): Promise<CallToolResult>;
|
|
10
|
+
export declare function handleGetMemory(client: SmriteaClient, input: GetMemoryInput): Promise<CallToolResult>;
|
|
11
|
+
export declare function handleDeleteMemory(client: SmriteaClient, input: DeleteMemoryInput): Promise<CallToolResult>;
|
|
12
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/tools/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAuC,MAAM,aAAa,CAAC;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,mBAAmB,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE1G,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAE/D;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAQhD;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,cAAc,EACrB,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,cAAc,CAAC,CAYzB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,mBAAmB,EAC1B,iBAAiB,CAAC,EAAE,MAAM,GACzB,OAAO,CAAC,cAAc,CAAC,CA4BzB;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,cAAc,CAAC,CASzB;AAED,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAAC,cAAc,CAAC,CASzB"}
|