clef-mcp 0.1.1 → 0.1.3

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 CHANGED
@@ -63,6 +63,18 @@ args = []
63
63
  }
64
64
  ```
65
65
 
66
+ **ZCode** — `~/.zcode/cli/config.json` (user scope; servers auto-connect in new sessions):
67
+
68
+ ```json
69
+ {
70
+ "mcp": {
71
+ "servers": {
72
+ "clef-mcp": { "command": "clef-mcp", "args": [], "type": "stdio" }
73
+ }
74
+ }
75
+ }
76
+ ```
77
+
66
78
  **Generic MCP client** — any client that speaks MCP over stdio: run `clef-mcp` as the server command.
67
79
 
68
80
  Ready-made snippets live in [`examples/`](examples/).
@@ -104,7 +116,7 @@ Ready-made snippets live in [`examples/`](examples/).
104
116
 
105
117
  | Type | Criteria | Meaning |
106
118
  |------|----------|---------|
107
- | `choice` | map of `option id → description` | Pick among named options |
119
+ | `choice` | map of `option id → description`, or a plain list of options (ids = the strings) | Pick among named options |
108
120
  | `score` | ordered list of descriptions | Ordered scale (index = score) |
109
121
  | `noul` | optional `{"true": "...", "false": "..."}` | Yes/no question |
110
122
 
@@ -131,6 +143,22 @@ Strictly structured — the server never turns the result into prose:
131
143
 
132
144
  `state` is treated strictly as **data**: it is never interpreted as instructions for the MCP server itself (see [Security](#security--data-handling)).
133
145
 
146
+ ## Teach your agent (skill)
147
+
148
+ The tool schema tells the client *what* `clef_decide` accepts; agents also need to know *when* to reach for it and *how* to frame decisions. The bundled skill covers that: decision patterns (next action / routing / classification / noul / score), batching up to 64 questions into one forward pass, writing mutually exclusive criteria, interpreting distributions (argmax vs flat), thresholds for safety-adjacent calls, and error recovery.
149
+
150
+ Install it for your agent(s):
151
+
152
+ ```bash
153
+ # from this repo
154
+ cp -r skills/clef-decisions ~/.agents/skills/
155
+
156
+ # or from the npm package
157
+ cp -r "$(npm root -g)/clef-mcp/skills/clef-decisions" ~/.agents/skills/
158
+ ```
159
+
160
+ `~/.agents/skills/` is the shared location for ZCode / Claude Code / Codex-style agents.
161
+
134
162
  ## CLI
135
163
 
136
164
  ```bash
package/dist/cli.js CHANGED
@@ -74,7 +74,9 @@ function toSystemOneRequest(input, defaultModel) {
74
74
  const questions = {};
75
75
  for (const [qid, q] of Object.entries(input.questions)) {
76
76
  const question = { type: q.type, instructions: q.instructions };
77
- if (q.criteria !== void 0) question.criteria = q.criteria;
77
+ if (q.criteria !== void 0) {
78
+ question.criteria = q.type === "choice" && Array.isArray(q.criteria) ? Object.fromEntries(q.criteria.map((c) => [c, c])) : q.criteria;
79
+ }
78
80
  questions[qid] = question;
79
81
  }
80
82
  return {
@@ -804,14 +806,23 @@ import { z } from "zod";
804
806
  function criteriaDescription(limits) {
805
807
  return z.string().min(1).max(limits.maxCriteriaDescChars);
806
808
  }
809
+ function countIssues(count, limits) {
810
+ if (count === 0) return "criteria must not be empty";
811
+ if (count > limits.maxCriteriaPerQuestion) return `too many criteria (max ${limits.maxCriteriaPerQuestion})`;
812
+ return void 0;
813
+ }
807
814
  function choiceCriteria(limits) {
808
- return z.record(z.string().min(1).max(limits.maxCriteriaDescChars), criteriaDescription(limits)).check((ctx) => {
809
- const count = Object.keys(ctx.value).length;
810
- if (count === 0) ctx.issues.push({ code: "custom", message: "criteria must not be empty", input: ctx.value });
811
- if (count > limits.maxCriteriaPerQuestion) {
812
- ctx.issues.push({ code: "custom", message: `too many criteria (max ${limits.maxCriteriaPerQuestion})`, input: ctx.value });
813
- }
814
- });
815
+ return z.union([
816
+ // Plain list: option ids equal to the strings themselves.
817
+ z.array(criteriaDescription(limits)).check((ctx) => {
818
+ const problem = countIssues(ctx.value.length, limits);
819
+ if (problem) ctx.issues.push({ code: "custom", message: problem, input: ctx.value });
820
+ }),
821
+ z.record(z.string().min(1).max(limits.maxCriteriaDescChars), criteriaDescription(limits)).check((ctx) => {
822
+ const problem = countIssues(Object.keys(ctx.value).length, limits);
823
+ if (problem) ctx.issues.push({ code: "custom", message: problem, input: ctx.value });
824
+ })
825
+ ]);
815
826
  }
816
827
  function scoreCriteria(limits) {
817
828
  return z.array(criteriaDescription(limits)).check((ctx) => {
@@ -832,7 +843,7 @@ function questionSchema(limits) {
832
843
  if (!parsed.success) {
833
844
  ctx.addIssue({
834
845
  code: "custom",
835
- message: `"choice" questions require non-empty criteria as a map of option id -> description (${parsed.error.issues[0]?.message ?? "invalid criteria"})`
846
+ message: `"choice" questions require non-empty criteria as a map of option id -> description, or a plain list of options (${parsed.error.issues[0]?.message ?? "invalid criteria"})`
836
847
  });
837
848
  }
838
849
  } else if (q.type === "score") {
@@ -1736,7 +1747,7 @@ async function confirm(question, yes) {
1736
1747
  }
1737
1748
 
1738
1749
  // src/version.ts
1739
- var VERSION = "0.1.1";
1750
+ var VERSION = "0.1.3";
1740
1751
 
1741
1752
  // src/cli/install.ts
1742
1753
  var OS_NAMES = {