@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.
Files changed (3) hide show
  1. package/README.md +133 -5
  2. package/build/index.js +277 -64
  3. package/package.json +7 -4
package/README.md CHANGED
@@ -1,15 +1,143 @@
1
1
  # sndo
2
2
 
3
- To install dependencies:
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
- bun install
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
- To run:
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
- bun run index.ts
79
+ export SNDO_API_KEY=your_api_key_here
13
80
  ```
14
81
 
15
- This project was created using `bun init` in bun v1.3.5. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
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 types = getJSONTypes(schema.type);
1457
- const hasNull = types.includes("null");
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 (!types.length && schema.nullable !== undefined) {
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
- types.push("null");
1466
+ types2.push("null");
1467
1467
  }
1468
- return types;
1468
+ return types2;
1469
1469
  }
1470
1470
  exports.getSchemaTypes = getSchemaTypes;
1471
1471
  function getJSONTypes(ts) {
1472
- const types = Array.isArray(ts) ? ts : ts ? [ts] : [];
1473
- if (types.every(rules_1.isJSONType))
1474
- return types;
1475
- throw new Error("type must be JSONType or JSONType[]: " + types.join(","));
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, types) {
1478
+ function coerceAndCheckDataType(it, types2) {
1479
1479
  const { gen, data, opts } = it;
1480
- const coerceTo = coerceToTypes(types, opts.coerceTypes);
1481
- const checkTypes = types.length > 0 && !(coerceTo.length === 0 && types.length === 1 && (0, applicability_1.schemaHasRulesForType)(it, types[0]));
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(types, data, opts.strictNumbers, DataType.Wrong);
1483
+ const wrongType = checkDataTypes(types2, data, opts.strictNumbers, DataType.Wrong);
1484
1484
  gen.if(wrongType, () => {
1485
1485
  if (coerceTo.length)
1486
- coerceData(it, types, coerceTo);
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(types, coerceTypes) {
1496
- return coerceTypes ? types.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
1495
+ function coerceToTypes(types2, coerceTypes) {
1496
+ return coerceTypes ? types2.filter((t) => COERCIBLE.has(t) || coerceTypes === "array" && t === "array") : [];
1497
1497
  }
1498
- function coerceData(it, types, coerceTo) {
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(types, data, opts.strictNumbers), () => gen.assign(coerced, data)));
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 types = (0, util_1.toHash)(dataTypes);
1580
- if (types.array && types.object) {
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 = types.null ? notObj : (0, codegen_1._)`!${data} || ${notObj}`;
1583
- delete types.null;
1584
- delete types.array;
1585
- delete types.object;
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 (types.number)
1590
- delete types.integer;
1591
- for (const t in types)
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(errors3) {
1848
+ function reportErrs(errors4) {
1849
1849
  var _a3;
1850
- gen.if((0, codegen_1.not)((_a3 = def.valid) !== null && _a3 !== undefined ? _a3 : valid), errors3);
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 types = (0, dataType_1.getSchemaTypes)(it.schema);
2380
- const checkedTypes = (0, dataType_1.coerceAndCheckDataType)(it, types);
2381
- schemaKeywords(it, types, !checkedTypes, errsCount);
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, types, typeErrors, errsCount) {
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, types);
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 (types.length === 1 && types[0] === group.type && typeErrors) {
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, types) {
2475
+ function checkStrictTypes(it, types2) {
2476
2476
  if (it.schemaEnv.meta || !it.opts.strictTypes)
2477
2477
  return;
2478
- checkContextTypes(it, types);
2478
+ checkContextTypes(it, types2);
2479
2479
  if (!it.opts.allowUnionTypes)
2480
- checkMultipleTypes(it, types);
2480
+ checkMultipleTypes(it, types2);
2481
2481
  checkKeywordTypes(it, it.dataTypes);
2482
2482
  }
2483
- function checkContextTypes(it, types) {
2484
- if (!types.length)
2483
+ function checkContextTypes(it, types2) {
2484
+ if (!types2.length)
2485
2485
  return;
2486
2486
  if (!it.dataTypes.length) {
2487
- it.dataTypes = types;
2487
+ it.dataTypes = types2;
2488
2488
  return;
2489
2489
  }
2490
- types.forEach((t) => {
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, types);
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(errors3) {
2768
+ constructor(errors4) {
2769
2769
  super("validation failed");
2770
- this.errors = errors3;
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(errors3 = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4094
- if (!errors3 || errors3.length === 0)
4093
+ errorsText(errors4 = this.errors, { separator = ", ", dataVar = "data" } = {}) {
4094
+ if (!errors4 || errors4.length === 0)
4095
4095
  return "No errors";
4096
- return errors3.map((e) => `${dataVar}${e.instancePath} ${e.message}`).reduce((text, msg) => text + separator + msg);
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, errors3) {
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 (errors3 === false) {
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 = Object.create(null);
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 types = options.reduce((types2, x) => {
21050
+ const types2 = options.reduce((types3, x) => {
20889
21051
  const type = primitiveMappings[x._def.typeName];
20890
- return type && !types2.includes(type) ? [...types2, type] : types2;
21052
+ return type && !types3.includes(type) ? [...types3, type] : types3;
20891
21053
  }, []);
20892
21054
  return {
20893
- type: types.length > 1 ? types : types[0]
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 types = options.reduce((acc, x) => {
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 (types.length === options.length) {
20916
- const uniqueTypes = types.filter((x, i, a) => a.indexOf(x) === i);
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.MCP_API_URL || "https://sndo.vercel.app";
23591
- var API_KEY = process.env.MCP_API_KEY;
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": "0.1.0",
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": "npm run build"
10
+ "prepublishOnly": "bun run build"
11
11
  },
12
- "files": ["build"],
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
  }