@snvshal/sndo 0.1.0 → 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 +133 -5
- package/build/index.js +277 -64
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,15 +1,143 @@
|
|
|
1
1
|
# sndo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`sndo` is a **Model Context Protocol (MCP) server** that securely exposes a user’s personal context (areas, tasks, notes, achievements) via **API key–based access**, designed for AI agents and MCP-compatible clients.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- **Node.js 18+** or **Bun** (recommended)
|
|
10
|
+
- An API key generated from
|
|
11
|
+
👉 [https://sndo.vercel.app](https://sndo.vercel.app)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
4
16
|
|
|
5
17
|
```bash
|
|
6
|
-
|
|
18
|
+
npm install -g @snvshal/sndo
|
|
19
|
+
# or
|
|
20
|
+
bun add -g @snvshal/sndo
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## MCP Client Configuration
|
|
26
|
+
|
|
27
|
+
`sndo` communicates over **stdio**, as required by MCP-compatible clients.
|
|
28
|
+
|
|
29
|
+
### Option 1: Global installation (recommended)
|
|
30
|
+
|
|
31
|
+
If installed globally, use the `sndo` binary directly:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"mcpServers": {
|
|
36
|
+
"sndo": {
|
|
37
|
+
"name": "sndo",
|
|
38
|
+
"command": "sndo",
|
|
39
|
+
"args": [],
|
|
40
|
+
"env": {
|
|
41
|
+
"SNDO_API_KEY": "your_sndo_api_key_here"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Option 2: No global installation (via npx / bunx)
|
|
49
|
+
|
|
50
|
+
If you don’t want a global install, run via `npx` (Node) or `bunx` (Bun):
|
|
51
|
+
|
|
52
|
+
```json
|
|
53
|
+
{
|
|
54
|
+
"mcpServers": {
|
|
55
|
+
"sndo": {
|
|
56
|
+
"name": "sndo",
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["-y", "@snvshal/sndo"],
|
|
59
|
+
"env": {
|
|
60
|
+
"SNDO_API_KEY": "your_sndo_api_key_here"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
7
65
|
```
|
|
8
66
|
|
|
9
|
-
|
|
67
|
+
> **Recommendation:** Use **Option 1** when possible.
|
|
68
|
+
> Use **Option 2** for quick setup or sandbox environments.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Authentication
|
|
73
|
+
|
|
74
|
+
`sndo` uses **Bearer API keys**.
|
|
75
|
+
|
|
76
|
+
Provide your API key via environment variable:
|
|
10
77
|
|
|
11
78
|
```bash
|
|
12
|
-
|
|
79
|
+
export SNDO_API_KEY=your_api_key_here
|
|
13
80
|
```
|
|
14
81
|
|
|
15
|
-
|
|
82
|
+
or directly inside your MCP client configuration.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## MCP Tools
|
|
87
|
+
|
|
88
|
+
### Context
|
|
89
|
+
|
|
90
|
+
- **`get_context`**
|
|
91
|
+
|
|
92
|
+
- Returns:
|
|
93
|
+
|
|
94
|
+
- Areas
|
|
95
|
+
- Tasks
|
|
96
|
+
- Notes
|
|
97
|
+
- Last updated timestamps
|
|
98
|
+
|
|
99
|
+
### Achievements
|
|
100
|
+
|
|
101
|
+
- **`get_achievements`**
|
|
102
|
+
|
|
103
|
+
- Fetch achievements
|
|
104
|
+
- Defaults to **today**
|
|
105
|
+
- Supports date filtering
|
|
106
|
+
|
|
107
|
+
- **`add_achievement`**
|
|
108
|
+
|
|
109
|
+
- Add a new achievement
|
|
110
|
+
- Optional note support (appends, not overwrites)
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## API Scopes
|
|
115
|
+
|
|
116
|
+
API keys use **fine-grained scopes** for access control.
|
|
117
|
+
|
|
118
|
+
### Supported Scopes
|
|
119
|
+
|
|
120
|
+
- `mcp:areas:read`
|
|
121
|
+
- `mcp:achievements:read`
|
|
122
|
+
- `mcp:achievements:write`
|
|
123
|
+
|
|
124
|
+
Scopes are validated on every request.
|
|
125
|
+
Missing scopes result in **`403 Forbidden`**.
|
|
126
|
+
|
|
127
|
+
You can manage scopes per API key from the dashboard:
|
|
128
|
+
👉 [https://sndo.vercel.app/api-keys](https://sndo.vercel.app/api-keys)
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Security Notes
|
|
133
|
+
|
|
134
|
+
- API keys are **hashed at rest**
|
|
135
|
+
- API usage is **tracked per key and per endpoint**
|
|
136
|
+
- Keys can be **revoked instantly**
|
|
137
|
+
- **Never** commit API keys to source control
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## License
|
|
142
|
+
|
|
143
|
+
MIT
|
package/build/index.js
CHANGED
|
@@ -1453,37 +1453,37 @@ var require_dataType = __commonJS((exports) => {
|
|
|
1453
1453
|
DataType2[DataType2["Wrong"] = 1] = "Wrong";
|
|
1454
1454
|
})(DataType || (exports.DataType = DataType = {}));
|
|
1455
1455
|
function getSchemaTypes(schema) {
|
|
1456
|
-
const
|
|
1457
|
-
const hasNull =
|
|
1456
|
+
const types2 = getJSONTypes(schema.type);
|
|
1457
|
+
const hasNull = types2.includes("null");
|
|
1458
1458
|
if (hasNull) {
|
|
1459
1459
|
if (schema.nullable === false)
|
|
1460
1460
|
throw new Error("type: null contradicts nullable: false");
|
|
1461
1461
|
} else {
|
|
1462
|
-
if (!
|
|
1462
|
+
if (!types2.length && schema.nullable !== undefined) {
|
|
1463
1463
|
throw new Error('"nullable" cannot be used without "type"');
|
|
1464
1464
|
}
|
|
1465
1465
|
if (schema.nullable === true)
|
|
1466
|
-
|
|
1466
|
+
types2.push("null");
|
|
1467
1467
|
}
|
|
1468
|
-
return
|
|
1468
|
+
return types2;
|
|
1469
1469
|
}
|
|
1470
1470
|
exports.getSchemaTypes = getSchemaTypes;
|
|
1471
1471
|
function getJSONTypes(ts) {
|
|
1472
|
-
const
|
|
1473
|
-
if (
|
|
1474
|
-
return
|
|
1475
|
-
throw new Error("type must be JSONType or JSONType[]: " +
|
|
1472
|
+
const types2 = Array.isArray(ts) ? ts : ts ? [ts] : [];
|
|
1473
|
+
if (types2.every(rules_1.isJSONType))
|
|
1474
|
+
return types2;
|
|
1475
|
+
throw new Error("type must be JSONType or JSONType[]: " + types2.join(","));
|
|
1476
1476
|
}
|
|
1477
1477
|
exports.getJSONTypes = getJSONTypes;
|
|
1478
|
-
function coerceAndCheckDataType(it,
|
|
1478
|
+
function coerceAndCheckDataType(it, types2) {
|
|
1479
1479
|
const { gen, data, opts } = it;
|
|
1480
|
-
const coerceTo = coerceToTypes(
|
|
1481
|
-
const checkTypes =
|
|
1480
|
+
const coerceTo = coerceToTypes(types2, opts.coerceTypes);
|
|
1481
|
+
const checkTypes = types2.length > 0 && !(coerceTo.length === 0 && types2.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types2[0]));
|
|
1482
1482
|
if (checkTypes) {
|
|
1483
|
-
const wrongType = checkDataTypes(
|
|
1483
|
+
const wrongType = checkDataTypes(types2, data, opts.strictNumbers, DataType.Wrong);
|
|
1484
1484
|
gen.if(wrongType, () => {
|
|
1485
1485
|
if (coerceTo.length)
|
|
1486
|
-
coerceData(it,
|
|
1486
|
+
coerceData(it, types2, coerceTo);
|
|
1487
1487
|
else
|
|
1488
1488
|
reportTypeError(it);
|
|
1489
1489
|
});
|
|
@@ -1492,15 +1492,15 @@ var require_dataType = __commonJS((exports) => {
|
|
|
1492
1492
|
}
|
|
1493
1493
|
exports.coerceAndCheckDataType = coerceAndCheckDataType;
|
|
1494
1494
|
var COERCIBLE = new Set(["string", "number", "integer", "boolean", "null"]);
|
|
1495
|
-
function coerceToTypes(
|
|
1496
|
-
return coerceTypes ?
|
|
1495
|
+
function coerceToTypes(types2, coerceTypes) {
|
|
1496
|
+
return coerceTypes ? types2.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
|
|
1497
1497
|
}
|
|
1498
|
-
function coerceData(it,
|
|
1498
|
+
function coerceData(it, types2, coerceTo) {
|
|
1499
1499
|
const { gen, data, opts } = it;
|
|
1500
1500
|
const dataType = gen.let("dataType", (0, codegen_1._)`typeof ${data}`);
|
|
1501
1501
|
const coerced = gen.let("coerced", (0, codegen_1._)`undefined`);
|
|
1502
1502
|
if (opts.coerceTypes === "array") {
|
|
1503
|
-
gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(
|
|
1503
|
+
gen.if((0, codegen_1._)`${dataType} == 'object' && Array.isArray(${data}) && ${data}.length == 1`, () => gen.assign(data, (0, codegen_1._)`${data}[0]`).assign(dataType, (0, codegen_1._)`typeof ${data}`).if(checkDataTypes(types2, data, opts.strictNumbers), () => gen.assign(coerced, data)));
|
|
1504
1504
|
}
|
|
1505
1505
|
gen.if((0, codegen_1._)`${coerced} !== undefined`);
|
|
1506
1506
|
for (const t of coerceTo) {
|
|
@@ -1576,19 +1576,19 @@ var require_dataType = __commonJS((exports) => {
|
|
|
1576
1576
|
return checkDataType(dataTypes[0], data, strictNums, correct);
|
|
1577
1577
|
}
|
|
1578
1578
|
let cond;
|
|
1579
|
-
const
|
|
1580
|
-
if (
|
|
1579
|
+
const types2 = (0, util_1.toHash)(dataTypes);
|
|
1580
|
+
if (types2.array && types2.object) {
|
|
1581
1581
|
const notObj = (0, codegen_1._)`typeof ${data} != "object"`;
|
|
1582
|
-
cond =
|
|
1583
|
-
delete
|
|
1584
|
-
delete
|
|
1585
|
-
delete
|
|
1582
|
+
cond = types2.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`;
|
|
1583
|
+
delete types2.null;
|
|
1584
|
+
delete types2.array;
|
|
1585
|
+
delete types2.object;
|
|
1586
1586
|
} else {
|
|
1587
1587
|
cond = codegen_1.nil;
|
|
1588
1588
|
}
|
|
1589
|
-
if (
|
|
1590
|
-
delete
|
|
1591
|
-
for (const t in
|
|
1589
|
+
if (types2.number)
|
|
1590
|
+
delete types2.integer;
|
|
1591
|
+
for (const t in types2)
|
|
1592
1592
|
cond = (0, codegen_1.and)(cond, checkDataType(t, data, strictNums, correct));
|
|
1593
1593
|
return cond;
|
|
1594
1594
|
}
|
|
@@ -1845,9 +1845,9 @@ var require_keyword = __commonJS((exports) => {
|
|
|
1845
1845
|
const passSchema = !(("compile" in def) && !$data || def.schema === false);
|
|
1846
1846
|
gen.assign(valid, (0, codegen_1._)`${_await}${(0, code_1.callValidateCode)(cxt, validateRef, passCxt, passSchema)}`, def.modifying);
|
|
1847
1847
|
}
|
|
1848
|
-
function reportErrs(
|
|
1848
|
+
function reportErrs(errors4) {
|
|
1849
1849
|
var _a3;
|
|
1850
|
-
gen.if((0, codegen_1.not)((_a3 = def.valid) !== null && _a3 !== undefined ? _a3 : valid),
|
|
1850
|
+
gen.if((0, codegen_1.not)((_a3 = def.valid) !== null && _a3 !== undefined ? _a3 : valid), errors4);
|
|
1851
1851
|
}
|
|
1852
1852
|
}
|
|
1853
1853
|
exports.funcKeywordCode = funcKeywordCode;
|
|
@@ -2376,9 +2376,9 @@ var require_validate = __commonJS((exports) => {
|
|
|
2376
2376
|
function typeAndKeywords(it, errsCount) {
|
|
2377
2377
|
if (it.opts.jtd)
|
|
2378
2378
|
return schemaKeywords(it, [], false, errsCount);
|
|
2379
|
-
const
|
|
2380
|
-
const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it,
|
|
2381
|
-
schemaKeywords(it,
|
|
2379
|
+
const types2 = (0, dataType_1.getSchemaTypes)(it.schema);
|
|
2380
|
+
const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it, types2);
|
|
2381
|
+
schemaKeywords(it, types2, !checkedTypes, errsCount);
|
|
2382
2382
|
}
|
|
2383
2383
|
function checkRefsAndKeywords(it) {
|
|
2384
2384
|
const { schema, errSchemaPath, opts, self } = it;
|
|
@@ -2428,7 +2428,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
2428
2428
|
if (items instanceof codegen_1.Name)
|
|
2429
2429
|
gen.assign((0, codegen_1._)`${evaluated}.items`, items);
|
|
2430
2430
|
}
|
|
2431
|
-
function schemaKeywords(it,
|
|
2431
|
+
function schemaKeywords(it, types2, typeErrors, errsCount) {
|
|
2432
2432
|
const { gen, schema, data, allErrors, opts, self } = it;
|
|
2433
2433
|
const { RULES } = self;
|
|
2434
2434
|
if (schema.$ref && (opts.ignoreKeywordsWithRef || !(0, util_1.schemaHasRulesButRef)(schema, RULES))) {
|
|
@@ -2436,7 +2436,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
2436
2436
|
return;
|
|
2437
2437
|
}
|
|
2438
2438
|
if (!opts.jtd)
|
|
2439
|
-
checkStrictTypes(it,
|
|
2439
|
+
checkStrictTypes(it, types2);
|
|
2440
2440
|
gen.block(() => {
|
|
2441
2441
|
for (const group of RULES.rules)
|
|
2442
2442
|
groupKeywords(group);
|
|
@@ -2448,7 +2448,7 @@ var require_validate = __commonJS((exports) => {
|
|
|
2448
2448
|
if (group.type) {
|
|
2449
2449
|
gen.if((0, dataType_2.checkDataType)(group.type, data, opts.strictNumbers));
|
|
2450
2450
|
iterateKeywords(it, group);
|
|
2451
|
-
if (
|
|
2451
|
+
if (types2.length === 1 && types2[0] === group.type && typeErrors) {
|
|
2452
2452
|
gen.else();
|
|
2453
2453
|
(0, dataType_2.reportTypeError)(it);
|
|
2454
2454
|
}
|
|
@@ -2472,27 +2472,27 @@ var require_validate = __commonJS((exports) => {
|
|
|
2472
2472
|
}
|
|
2473
2473
|
});
|
|
2474
2474
|
}
|
|
2475
|
-
function checkStrictTypes(it,
|
|
2475
|
+
function checkStrictTypes(it, types2) {
|
|
2476
2476
|
if (it.schemaEnv.meta || !it.opts.strictTypes)
|
|
2477
2477
|
return;
|
|
2478
|
-
checkContextTypes(it,
|
|
2478
|
+
checkContextTypes(it, types2);
|
|
2479
2479
|
if (!it.opts.allowUnionTypes)
|
|
2480
|
-
checkMultipleTypes(it,
|
|
2480
|
+
checkMultipleTypes(it, types2);
|
|
2481
2481
|
checkKeywordTypes(it, it.dataTypes);
|
|
2482
2482
|
}
|
|
2483
|
-
function checkContextTypes(it,
|
|
2484
|
-
if (!
|
|
2483
|
+
function checkContextTypes(it, types2) {
|
|
2484
|
+
if (!types2.length)
|
|
2485
2485
|
return;
|
|
2486
2486
|
if (!it.dataTypes.length) {
|
|
2487
|
-
it.dataTypes =
|
|
2487
|
+
it.dataTypes = types2;
|
|
2488
2488
|
return;
|
|
2489
2489
|
}
|
|
2490
|
-
|
|
2490
|
+
types2.forEach((t) => {
|
|
2491
2491
|
if (!includesType(it.dataTypes, t)) {
|
|
2492
2492
|
strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`);
|
|
2493
2493
|
}
|
|
2494
2494
|
});
|
|
2495
|
-
narrowSchemaTypes(it,
|
|
2495
|
+
narrowSchemaTypes(it, types2);
|
|
2496
2496
|
}
|
|
2497
2497
|
function checkMultipleTypes(it, ts) {
|
|
2498
2498
|
if (ts.length > 1 && !(ts.length === 2 && ts.includes("null"))) {
|
|
@@ -2765,9 +2765,9 @@ var require_validation_error = __commonJS((exports) => {
|
|
|
2765
2765
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2766
2766
|
|
|
2767
2767
|
class ValidationError extends Error {
|
|
2768
|
-
constructor(
|
|
2768
|
+
constructor(errors4) {
|
|
2769
2769
|
super("validation failed");
|
|
2770
|
-
this.errors =
|
|
2770
|
+
this.errors = errors4;
|
|
2771
2771
|
this.ajv = this.validation = true;
|
|
2772
2772
|
}
|
|
2773
2773
|
}
|
|
@@ -4090,10 +4090,10 @@ var require_core = __commonJS((exports) => {
|
|
|
4090
4090
|
this.formats[name] = format;
|
|
4091
4091
|
return this;
|
|
4092
4092
|
}
|
|
4093
|
-
errorsText(
|
|
4094
|
-
if (!
|
|
4093
|
+
errorsText(errors4 = this.errors, { separator = ", ", dataVar = "data" } = {}) {
|
|
4094
|
+
if (!errors4 || errors4.length === 0)
|
|
4095
4095
|
return "No errors";
|
|
4096
|
-
return
|
|
4096
|
+
return errors4.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
|
|
4097
4097
|
}
|
|
4098
4098
|
$dataMetaSchema(metaSchema, keywordsJsonPointers) {
|
|
4099
4099
|
const rules = this.RULES.all;
|
|
@@ -5342,13 +5342,13 @@ var require_additionalProperties = __commonJS((exports) => {
|
|
|
5342
5342
|
}
|
|
5343
5343
|
}
|
|
5344
5344
|
}
|
|
5345
|
-
function applyAdditionalSchema(key, valid,
|
|
5345
|
+
function applyAdditionalSchema(key, valid, errors4) {
|
|
5346
5346
|
const subschema = {
|
|
5347
5347
|
keyword: "additionalProperties",
|
|
5348
5348
|
dataProp: key,
|
|
5349
5349
|
dataPropType: util_1.Type.Str
|
|
5350
5350
|
};
|
|
5351
|
-
if (
|
|
5351
|
+
if (errors4 === false) {
|
|
5352
5352
|
Object.assign(subschema, {
|
|
5353
5353
|
compositeRule: true,
|
|
5354
5354
|
createErrors: false,
|
|
@@ -16422,6 +16422,118 @@ var getRelativePath = (pathA, pathB) => {
|
|
|
16422
16422
|
}
|
|
16423
16423
|
return [(pathA.length - i).toString(), ...pathB.slice(i)].join("/");
|
|
16424
16424
|
};
|
|
16425
|
+
// node_modules/zod/v3/external.js
|
|
16426
|
+
var exports_external = {};
|
|
16427
|
+
__export(exports_external, {
|
|
16428
|
+
void: () => voidType2,
|
|
16429
|
+
util: () => util2,
|
|
16430
|
+
unknown: () => unknownType2,
|
|
16431
|
+
union: () => unionType2,
|
|
16432
|
+
undefined: () => undefinedType2,
|
|
16433
|
+
tuple: () => tupleType2,
|
|
16434
|
+
transformer: () => effectsType2,
|
|
16435
|
+
symbol: () => symbolType2,
|
|
16436
|
+
string: () => stringType2,
|
|
16437
|
+
strictObject: () => strictObjectType2,
|
|
16438
|
+
setErrorMap: () => setErrorMap,
|
|
16439
|
+
set: () => setType2,
|
|
16440
|
+
record: () => recordType2,
|
|
16441
|
+
quotelessJson: () => quotelessJson,
|
|
16442
|
+
promise: () => promiseType2,
|
|
16443
|
+
preprocess: () => preprocessType2,
|
|
16444
|
+
pipeline: () => pipelineType2,
|
|
16445
|
+
ostring: () => ostring,
|
|
16446
|
+
optional: () => optionalType2,
|
|
16447
|
+
onumber: () => onumber,
|
|
16448
|
+
oboolean: () => oboolean,
|
|
16449
|
+
objectUtil: () => objectUtil2,
|
|
16450
|
+
object: () => objectType2,
|
|
16451
|
+
number: () => numberType2,
|
|
16452
|
+
nullable: () => nullableType2,
|
|
16453
|
+
null: () => nullType2,
|
|
16454
|
+
never: () => neverType2,
|
|
16455
|
+
nativeEnum: () => nativeEnumType2,
|
|
16456
|
+
nan: () => nanType2,
|
|
16457
|
+
map: () => mapType2,
|
|
16458
|
+
makeIssue: () => makeIssue2,
|
|
16459
|
+
literal: () => literalType2,
|
|
16460
|
+
lazy: () => lazyType2,
|
|
16461
|
+
late: () => late2,
|
|
16462
|
+
isValid: () => isValid2,
|
|
16463
|
+
isDirty: () => isDirty2,
|
|
16464
|
+
isAsync: () => isAsync2,
|
|
16465
|
+
isAborted: () => isAborted2,
|
|
16466
|
+
intersection: () => intersectionType2,
|
|
16467
|
+
instanceof: () => instanceOfType,
|
|
16468
|
+
getParsedType: () => getParsedType3,
|
|
16469
|
+
getErrorMap: () => getErrorMap2,
|
|
16470
|
+
function: () => functionType2,
|
|
16471
|
+
enum: () => enumType2,
|
|
16472
|
+
effect: () => effectsType2,
|
|
16473
|
+
discriminatedUnion: () => discriminatedUnionType2,
|
|
16474
|
+
defaultErrorMap: () => en_default3,
|
|
16475
|
+
datetimeRegex: () => datetimeRegex2,
|
|
16476
|
+
date: () => dateType2,
|
|
16477
|
+
custom: () => custom2,
|
|
16478
|
+
coerce: () => coerce,
|
|
16479
|
+
boolean: () => booleanType2,
|
|
16480
|
+
bigint: () => bigIntType2,
|
|
16481
|
+
array: () => arrayType2,
|
|
16482
|
+
any: () => anyType2,
|
|
16483
|
+
addIssueToContext: () => addIssueToContext2,
|
|
16484
|
+
ZodVoid: () => ZodVoid2,
|
|
16485
|
+
ZodUnknown: () => ZodUnknown3,
|
|
16486
|
+
ZodUnion: () => ZodUnion3,
|
|
16487
|
+
ZodUndefined: () => ZodUndefined2,
|
|
16488
|
+
ZodType: () => ZodType3,
|
|
16489
|
+
ZodTuple: () => ZodTuple2,
|
|
16490
|
+
ZodTransformer: () => ZodEffects2,
|
|
16491
|
+
ZodSymbol: () => ZodSymbol2,
|
|
16492
|
+
ZodString: () => ZodString3,
|
|
16493
|
+
ZodSet: () => ZodSet2,
|
|
16494
|
+
ZodSchema: () => ZodType3,
|
|
16495
|
+
ZodRecord: () => ZodRecord3,
|
|
16496
|
+
ZodReadonly: () => ZodReadonly3,
|
|
16497
|
+
ZodPromise: () => ZodPromise2,
|
|
16498
|
+
ZodPipeline: () => ZodPipeline2,
|
|
16499
|
+
ZodParsedType: () => ZodParsedType2,
|
|
16500
|
+
ZodOptional: () => ZodOptional3,
|
|
16501
|
+
ZodObject: () => ZodObject3,
|
|
16502
|
+
ZodNumber: () => ZodNumber3,
|
|
16503
|
+
ZodNullable: () => ZodNullable3,
|
|
16504
|
+
ZodNull: () => ZodNull3,
|
|
16505
|
+
ZodNever: () => ZodNever3,
|
|
16506
|
+
ZodNativeEnum: () => ZodNativeEnum2,
|
|
16507
|
+
ZodNaN: () => ZodNaN2,
|
|
16508
|
+
ZodMap: () => ZodMap2,
|
|
16509
|
+
ZodLiteral: () => ZodLiteral3,
|
|
16510
|
+
ZodLazy: () => ZodLazy2,
|
|
16511
|
+
ZodIssueCode: () => ZodIssueCode2,
|
|
16512
|
+
ZodIntersection: () => ZodIntersection3,
|
|
16513
|
+
ZodFunction: () => ZodFunction2,
|
|
16514
|
+
ZodFirstPartyTypeKind: () => ZodFirstPartyTypeKind2,
|
|
16515
|
+
ZodError: () => ZodError3,
|
|
16516
|
+
ZodEnum: () => ZodEnum3,
|
|
16517
|
+
ZodEffects: () => ZodEffects2,
|
|
16518
|
+
ZodDiscriminatedUnion: () => ZodDiscriminatedUnion3,
|
|
16519
|
+
ZodDefault: () => ZodDefault3,
|
|
16520
|
+
ZodDate: () => ZodDate2,
|
|
16521
|
+
ZodCatch: () => ZodCatch3,
|
|
16522
|
+
ZodBranded: () => ZodBranded2,
|
|
16523
|
+
ZodBoolean: () => ZodBoolean3,
|
|
16524
|
+
ZodBigInt: () => ZodBigInt2,
|
|
16525
|
+
ZodArray: () => ZodArray3,
|
|
16526
|
+
ZodAny: () => ZodAny2,
|
|
16527
|
+
Schema: () => ZodType3,
|
|
16528
|
+
ParseStatus: () => ParseStatus2,
|
|
16529
|
+
OK: () => OK2,
|
|
16530
|
+
NEVER: () => NEVER2,
|
|
16531
|
+
INVALID: () => INVALID2,
|
|
16532
|
+
EMPTY_PATH: () => EMPTY_PATH,
|
|
16533
|
+
DIRTY: () => DIRTY2,
|
|
16534
|
+
BRAND: () => BRAND2
|
|
16535
|
+
});
|
|
16536
|
+
|
|
16425
16537
|
// node_modules/zod/v3/helpers/util.js
|
|
16426
16538
|
var util2;
|
|
16427
16539
|
(function(util3) {
|
|
@@ -16572,6 +16684,11 @@ var ZodIssueCode2 = util2.arrayToEnum([
|
|
|
16572
16684
|
"not_multiple_of",
|
|
16573
16685
|
"not_finite"
|
|
16574
16686
|
]);
|
|
16687
|
+
var quotelessJson = (obj) => {
|
|
16688
|
+
const json = JSON.stringify(obj, null, 2);
|
|
16689
|
+
return json.replace(/"([^"]+)":/g, "$1:");
|
|
16690
|
+
};
|
|
16691
|
+
|
|
16575
16692
|
class ZodError3 extends Error {
|
|
16576
16693
|
get errors() {
|
|
16577
16694
|
return this.issues;
|
|
@@ -16645,7 +16762,7 @@ class ZodError3 extends Error {
|
|
|
16645
16762
|
return this.issues.length === 0;
|
|
16646
16763
|
}
|
|
16647
16764
|
flatten(mapper = (issue2) => issue2.message) {
|
|
16648
|
-
const fieldErrors =
|
|
16765
|
+
const fieldErrors = {};
|
|
16649
16766
|
const formErrors = [];
|
|
16650
16767
|
for (const sub of this.issues) {
|
|
16651
16768
|
if (sub.path.length > 0) {
|
|
@@ -16772,10 +16889,12 @@ var en_default3 = errorMap2;
|
|
|
16772
16889
|
|
|
16773
16890
|
// node_modules/zod/v3/errors.js
|
|
16774
16891
|
var overrideErrorMap2 = en_default3;
|
|
16892
|
+
function setErrorMap(map) {
|
|
16893
|
+
overrideErrorMap2 = map;
|
|
16894
|
+
}
|
|
16775
16895
|
function getErrorMap2() {
|
|
16776
16896
|
return overrideErrorMap2;
|
|
16777
16897
|
}
|
|
16778
|
-
|
|
16779
16898
|
// node_modules/zod/v3/helpers/parseUtil.js
|
|
16780
16899
|
var makeIssue2 = (params) => {
|
|
16781
16900
|
const { data, path, errorMaps, issueData } = params;
|
|
@@ -16802,6 +16921,7 @@ var makeIssue2 = (params) => {
|
|
|
16802
16921
|
message: errorMessage
|
|
16803
16922
|
};
|
|
16804
16923
|
};
|
|
16924
|
+
var EMPTY_PATH = [];
|
|
16805
16925
|
function addIssueToContext2(ctx, issueData) {
|
|
16806
16926
|
const overrideMap = getErrorMap2();
|
|
16807
16927
|
const issue2 = makeIssue2({
|
|
@@ -16881,7 +17001,6 @@ var isAborted2 = (x) => x.status === "aborted";
|
|
|
16881
17001
|
var isDirty2 = (x) => x.status === "dirty";
|
|
16882
17002
|
var isValid2 = (x) => x.status === "valid";
|
|
16883
17003
|
var isAsync2 = (x) => typeof Promise !== "undefined" && x instanceof Promise;
|
|
16884
|
-
|
|
16885
17004
|
// node_modules/zod/v3/helpers/errorUtil.js
|
|
16886
17005
|
var errorUtil2;
|
|
16887
17006
|
(function(errorUtil3) {
|
|
@@ -20156,6 +20275,33 @@ ZodReadonly3.create = (type, params) => {
|
|
|
20156
20275
|
...processCreateParams2(params)
|
|
20157
20276
|
});
|
|
20158
20277
|
};
|
|
20278
|
+
function cleanParams(params, data) {
|
|
20279
|
+
const p = typeof params === "function" ? params(data) : typeof params === "string" ? { message: params } : params;
|
|
20280
|
+
const p2 = typeof p === "string" ? { message: p } : p;
|
|
20281
|
+
return p2;
|
|
20282
|
+
}
|
|
20283
|
+
function custom2(check, _params = {}, fatal) {
|
|
20284
|
+
if (check)
|
|
20285
|
+
return ZodAny2.create().superRefine((data, ctx) => {
|
|
20286
|
+
const r = check(data);
|
|
20287
|
+
if (r instanceof Promise) {
|
|
20288
|
+
return r.then((r2) => {
|
|
20289
|
+
if (!r2) {
|
|
20290
|
+
const params = cleanParams(_params, data);
|
|
20291
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
20292
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
20293
|
+
}
|
|
20294
|
+
});
|
|
20295
|
+
}
|
|
20296
|
+
if (!r) {
|
|
20297
|
+
const params = cleanParams(_params, data);
|
|
20298
|
+
const _fatal = params.fatal ?? fatal ?? true;
|
|
20299
|
+
ctx.addIssue({ code: "custom", ...params, fatal: _fatal });
|
|
20300
|
+
}
|
|
20301
|
+
return;
|
|
20302
|
+
});
|
|
20303
|
+
return ZodAny2.create();
|
|
20304
|
+
}
|
|
20159
20305
|
var late2 = {
|
|
20160
20306
|
object: ZodObject3.lazycreate
|
|
20161
20307
|
};
|
|
@@ -20198,6 +20344,9 @@ var ZodFirstPartyTypeKind2;
|
|
|
20198
20344
|
ZodFirstPartyTypeKind3["ZodPipeline"] = "ZodPipeline";
|
|
20199
20345
|
ZodFirstPartyTypeKind3["ZodReadonly"] = "ZodReadonly";
|
|
20200
20346
|
})(ZodFirstPartyTypeKind2 || (ZodFirstPartyTypeKind2 = {}));
|
|
20347
|
+
var instanceOfType = (cls, params = {
|
|
20348
|
+
message: `Input not instance of ${cls.name}`
|
|
20349
|
+
}) => custom2((data) => data instanceof cls, params);
|
|
20201
20350
|
var stringType2 = ZodString3.create;
|
|
20202
20351
|
var numberType2 = ZodNumber3.create;
|
|
20203
20352
|
var nanType2 = ZodNaN2.create;
|
|
@@ -20232,7 +20381,20 @@ var optionalType2 = ZodOptional3.create;
|
|
|
20232
20381
|
var nullableType2 = ZodNullable3.create;
|
|
20233
20382
|
var preprocessType2 = ZodEffects2.createWithPreprocess;
|
|
20234
20383
|
var pipelineType2 = ZodPipeline2.create;
|
|
20235
|
-
|
|
20384
|
+
var ostring = () => stringType2().optional();
|
|
20385
|
+
var onumber = () => numberType2().optional();
|
|
20386
|
+
var oboolean = () => booleanType2().optional();
|
|
20387
|
+
var coerce = {
|
|
20388
|
+
string: (arg) => ZodString3.create({ ...arg, coerce: true }),
|
|
20389
|
+
number: (arg) => ZodNumber3.create({ ...arg, coerce: true }),
|
|
20390
|
+
boolean: (arg) => ZodBoolean3.create({
|
|
20391
|
+
...arg,
|
|
20392
|
+
coerce: true
|
|
20393
|
+
}),
|
|
20394
|
+
bigint: (arg) => ZodBigInt2.create({ ...arg, coerce: true }),
|
|
20395
|
+
date: (arg) => ZodDate2.create({ ...arg, coerce: true })
|
|
20396
|
+
};
|
|
20397
|
+
var NEVER2 = INVALID2;
|
|
20236
20398
|
// node_modules/zod-to-json-schema/dist/esm/parsers/any.js
|
|
20237
20399
|
function parseAnyDef(refs) {
|
|
20238
20400
|
if (refs.target !== "openAi") {
|
|
@@ -20885,15 +21047,15 @@ function parseUnionDef(def, refs) {
|
|
|
20885
21047
|
return asAnyOf(def, refs);
|
|
20886
21048
|
const options = def.options instanceof Map ? Array.from(def.options.values()) : def.options;
|
|
20887
21049
|
if (options.every((x) => (x._def.typeName in primitiveMappings) && (!x._def.checks || !x._def.checks.length))) {
|
|
20888
|
-
const
|
|
21050
|
+
const types2 = options.reduce((types3, x) => {
|
|
20889
21051
|
const type = primitiveMappings[x._def.typeName];
|
|
20890
|
-
return type && !
|
|
21052
|
+
return type && !types3.includes(type) ? [...types3, type] : types3;
|
|
20891
21053
|
}, []);
|
|
20892
21054
|
return {
|
|
20893
|
-
type:
|
|
21055
|
+
type: types2.length > 1 ? types2 : types2[0]
|
|
20894
21056
|
};
|
|
20895
21057
|
} else if (options.every((x) => x._def.typeName === "ZodLiteral" && !x.description)) {
|
|
20896
|
-
const
|
|
21058
|
+
const types2 = options.reduce((acc, x) => {
|
|
20897
21059
|
const type = typeof x._def.value;
|
|
20898
21060
|
switch (type) {
|
|
20899
21061
|
case "string":
|
|
@@ -20912,8 +21074,8 @@ function parseUnionDef(def, refs) {
|
|
|
20912
21074
|
return acc;
|
|
20913
21075
|
}
|
|
20914
21076
|
}, []);
|
|
20915
|
-
if (
|
|
20916
|
-
const uniqueTypes =
|
|
21077
|
+
if (types2.length === options.length) {
|
|
21078
|
+
const uniqueTypes = types2.filter((x, i, a) => a.indexOf(x) === i);
|
|
20917
21079
|
return {
|
|
20918
21080
|
type: uniqueTypes.length > 1 ? uniqueTypes : uniqueTypes[0],
|
|
20919
21081
|
enum: options.reduce((acc, x) => {
|
|
@@ -23585,10 +23747,9 @@ class StdioServerTransport {
|
|
|
23585
23747
|
});
|
|
23586
23748
|
}
|
|
23587
23749
|
}
|
|
23588
|
-
|
|
23589
23750
|
// src/index.ts
|
|
23590
|
-
var API_URL = process.env.
|
|
23591
|
-
var API_KEY = process.env.
|
|
23751
|
+
var API_URL = process.env.SNDO_API_URL || "https://sndo.vercel.app";
|
|
23752
|
+
var API_KEY = process.env.SNDO_API_KEY;
|
|
23592
23753
|
if (!API_URL || !API_KEY) {
|
|
23593
23754
|
throw new Error("MCP_API_URL and MCP_API_KEY are required");
|
|
23594
23755
|
}
|
|
@@ -23597,7 +23758,7 @@ var server = new McpServer({
|
|
|
23597
23758
|
version: "1.0.0"
|
|
23598
23759
|
});
|
|
23599
23760
|
server.registerTool("get_context", {
|
|
23600
|
-
description: "Get todos and notes"
|
|
23761
|
+
description: "Get user's todos and notes from sndo(stats-daily) app"
|
|
23601
23762
|
}, async () => {
|
|
23602
23763
|
const res = await fetch(`${API_URL}/api/mcp/context`, {
|
|
23603
23764
|
headers: {
|
|
@@ -23617,6 +23778,58 @@ server.registerTool("get_context", {
|
|
|
23617
23778
|
]
|
|
23618
23779
|
};
|
|
23619
23780
|
});
|
|
23781
|
+
server.registerTool("get_achievements", {
|
|
23782
|
+
description: "Get user's achievements and note from sndo(stats-daily) app on the specified date",
|
|
23783
|
+
inputSchema: {
|
|
23784
|
+
date: exports_external.string().optional()
|
|
23785
|
+
}
|
|
23786
|
+
}, async ({ date: date4 }) => {
|
|
23787
|
+
const res = await fetch(`${API_URL}/api/mcp/achievements?date=${date4}`, {
|
|
23788
|
+
headers: {
|
|
23789
|
+
Authorization: `Bearer ${API_KEY}`
|
|
23790
|
+
}
|
|
23791
|
+
});
|
|
23792
|
+
if (!res.ok) {
|
|
23793
|
+
throw new Error(`Failed to get achievements: ${res.status} ${res.statusText}`);
|
|
23794
|
+
}
|
|
23795
|
+
const json = await res.json();
|
|
23796
|
+
return {
|
|
23797
|
+
content: [
|
|
23798
|
+
{
|
|
23799
|
+
type: "text",
|
|
23800
|
+
text: JSON.stringify(json, null, 2)
|
|
23801
|
+
}
|
|
23802
|
+
]
|
|
23803
|
+
};
|
|
23804
|
+
});
|
|
23805
|
+
server.registerTool("add_achievement", {
|
|
23806
|
+
description: "Add user's new achievement to sndo(stats-daily) app",
|
|
23807
|
+
inputSchema: {
|
|
23808
|
+
text: exports_external.string().min(1, "Achievement text is required"),
|
|
23809
|
+
note: exports_external.string().optional()
|
|
23810
|
+
}
|
|
23811
|
+
}, async (input) => {
|
|
23812
|
+
const res = await fetch(`${API_URL}/api/mcp/achievements`, {
|
|
23813
|
+
method: "POST",
|
|
23814
|
+
headers: {
|
|
23815
|
+
Authorization: `Bearer ${API_KEY}`,
|
|
23816
|
+
"Content-Type": "application/json"
|
|
23817
|
+
},
|
|
23818
|
+
body: JSON.stringify(input)
|
|
23819
|
+
});
|
|
23820
|
+
if (!res.ok) {
|
|
23821
|
+
throw new Error(`Failed to add achievement: ${res.status} ${res.statusText}`);
|
|
23822
|
+
}
|
|
23823
|
+
const json = await res.json();
|
|
23824
|
+
return {
|
|
23825
|
+
content: [
|
|
23826
|
+
{
|
|
23827
|
+
type: "text",
|
|
23828
|
+
text: JSON.stringify(json, null, 2)
|
|
23829
|
+
}
|
|
23830
|
+
]
|
|
23831
|
+
};
|
|
23832
|
+
});
|
|
23620
23833
|
async function main() {
|
|
23621
23834
|
const transport = new StdioServerTransport;
|
|
23622
23835
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snvshal/sndo",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sndo": "build/index.js"
|
|
7
7
|
},
|
|
8
8
|
"scripts": {
|
|
9
9
|
"build": "bun build src/index.ts --outdir build --target node",
|
|
10
|
-
"prepublishOnly": "
|
|
10
|
+
"prepublishOnly": "bun run build"
|
|
11
11
|
},
|
|
12
|
-
"files": [
|
|
12
|
+
"files": [
|
|
13
|
+
"build"
|
|
14
|
+
],
|
|
13
15
|
"dependencies": {
|
|
14
|
-
"@modelcontextprotocol/sdk": "^1.25.2"
|
|
16
|
+
"@modelcontextprotocol/sdk": "^1.25.2",
|
|
17
|
+
"zod": "3"
|
|
15
18
|
}
|
|
16
19
|
}
|