planko-mcp-server 0.4.0 → 0.4.2

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/index.js CHANGED
@@ -20,6 +20,7 @@ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
20
20
  import { join } from 'node:path';
21
21
 
22
22
  import { createApiClient } from './src/api.js';
23
+ import { isBlankValue, applyDueDateFallback } from './src/sanitize.js';
23
24
  import {
24
25
  blockNoteToMarkdown,
25
26
  markdownToBlockNote,
@@ -633,9 +634,10 @@ const taskProps = {
633
634
  function buildTaskBody(params) {
634
635
  const body = {};
635
636
  for (const [key, value] of Object.entries(params)) {
636
- if (value === undefined) continue;
637
+ if (isBlankValue(value)) continue;
637
638
  body[key] = value;
638
639
  }
640
+ applyDueDateFallback(body);
639
641
  if ('description' in body) {
640
642
  body.description = markdownToDescription(body.description);
641
643
  }
@@ -680,7 +682,7 @@ async function handleCreate(type, params, kindLabel) {
680
682
 
681
683
  // Resolve projectName -> projectId via the API. When omitted, OMIT the key
682
684
  // entirely so the backend applies the user's default project.
683
- if (projectName) {
685
+ if (!isBlankValue(projectName)) {
684
686
  body.projectId = await resolveProjectId(projectName);
685
687
  }
686
688
 
@@ -885,10 +887,10 @@ async function buildListParams(type, params) {
885
887
  const { projectName, ...rest } = params;
886
888
  const out = { type };
887
889
  for (const [key, value] of Object.entries(rest)) {
888
- if (value === undefined) continue;
890
+ if (isBlankValue(value)) continue;
889
891
  out[key] = value;
890
892
  }
891
- if (projectName) {
893
+ if (!isBlankValue(projectName)) {
892
894
  out.projectId = await resolveProjectId(projectName);
893
895
  }
894
896
  return out;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "planko-mcp-server",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "MCP server for syncing Planko tasks with local Markdown files",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Argument sanitization for MCP tool inputs.
3
+ *
4
+ * LLM tool-callers (especially GPT-class models) tend to fill EVERY schema
5
+ * field with placeholder/default values instead of omitting optional ones:
6
+ * empty strings, the all-zero ObjectId ("000000000000000000000000"), and
7
+ * empty arrays. Left untouched, a placeholder `projectId` makes the backend
8
+ * throw "Project not found", a placeholder `parentId` filters to a nonexistent
9
+ * parent (→ 0 results), and empty `tags`/`search` add noise. Treat all of
10
+ * these as "field omitted".
11
+ */
12
+
13
+ export const ZERO_OBJECT_ID = '000000000000000000000000';
14
+
15
+ export function isBlankValue(value) {
16
+ if (value === undefined || value === null) return true;
17
+ if (typeof value === 'string') {
18
+ const trimmed = value.trim();
19
+ return trimmed === '' || trimmed === ZERO_OBJECT_ID;
20
+ }
21
+ if (Array.isArray(value)) return value.length === 0;
22
+ return false;
23
+ }
24
+
25
+ /**
26
+ * LLM callers often put the date in `datePlain` and leave `dueDate` empty.
27
+ * The backend derives datePlain FROM dueDate but not the reverse, so a
28
+ * datePlain-only task ends up with dueDate=null and never appears on day/agenda
29
+ * views (it looks "not created"). Mirror datePlain -> dueDate in that case.
30
+ * Mutates and returns `body`.
31
+ */
32
+ export function applyDueDateFallback(body) {
33
+ if (isBlankValue(body.dueDate) && !isBlankValue(body.datePlain)) {
34
+ body.dueDate = body.datePlain;
35
+ }
36
+ return body;
37
+ }