@clien-ai/mcp 0.2.1 → 0.4.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 +124 -14
- package/dist/auth/oauth.js +69 -39
- package/dist/auth/oauth.js.map +1 -1
- package/dist/server.js +47 -9
- package/dist/server.js.map +1 -1
- package/dist/tools/collections.js +440 -0
- package/dist/tools/collections.js.map +1 -0
- package/dist/tools/interview.js +249 -25
- package/dist/tools/interview.js.map +1 -1
- package/dist/tools/output-schemas.js +130 -0
- package/dist/tools/output-schemas.js.map +1 -0
- package/dist/tools/param-aliases.js +144 -0
- package/dist/tools/param-aliases.js.map +1 -0
- package/dist/tools/personas.js +556 -44
- package/dist/tools/personas.js.map +1 -1
- package/dist/tools/projects.js +554 -6
- package/dist/tools/projects.js.map +1 -1
- package/dist/tools/registry.js +327 -37
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/report-digest.js +531 -0
- package/dist/tools/report-digest.js.map +1 -0
- package/dist/tools/reports.js +11 -1
- package/dist/tools/reports.js.map +1 -1
- package/dist/tools/research.js +12 -1
- package/dist/tools/research.js.map +1 -1
- package/dist/tools/scoped-research.js +19 -3
- package/dist/tools/scoped-research.js.map +1 -1
- package/dist/tools/status.js +12 -1
- package/dist/tools/status.js.map +1 -1
- package/dist/types/report.js +180 -2
- package/dist/types/report.js.map +1 -1
- package/dist/util/single-flight.js +63 -0
- package/dist/util/single-flight.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backward-compatible camelCase → snake_case parameter aliasing (FUL-233).
|
|
3
|
+
*
|
|
4
|
+
* The tool surface settled on snake_case params (`project_id`, `persona_id`,
|
|
5
|
+
* `job_id`, …) as every read tool landed, but five older write tools still
|
|
6
|
+
* advertised camelCase (`projectId`, `companySize`, `groundInEvidence`, …).
|
|
7
|
+
* A public API cannot simply rename those: `@clien-ai/mcp` is published to npm
|
|
8
|
+
* and existing callers pass the old spellings. So each legacy name survives as
|
|
9
|
+
* a DEPRECATED ALIAS while snake_case becomes the documented form.
|
|
10
|
+
*
|
|
11
|
+
* How it fits together — three pieces, one source of truth per tool:
|
|
12
|
+
*
|
|
13
|
+
* 1. The tool's Zod schema declares the canonical snake_case field normally,
|
|
14
|
+
* plus the camelCase alias via `deprecatedAlias()`. Declaring the alias is
|
|
15
|
+
* load-bearing, not cosmetic: `zodToInputSchema` stamps
|
|
16
|
+
* `additionalProperties: false` on every advert, so an UNDECLARED alias
|
|
17
|
+
* would be a field the schema publicly disowns while the server quietly
|
|
18
|
+
* honours it — and strict MCP clients would drop it before it ever reached
|
|
19
|
+
* us. Declared, the advert and the runtime tell the same story.
|
|
20
|
+
* 2. The registry entry carries the matching `paramAliases` map, and
|
|
21
|
+
* `defineTool` wraps the handler with `normalizeParamAliases` — so a tool
|
|
22
|
+
* cannot forget to call it, and handlers only ever see canonical keys.
|
|
23
|
+
* 3. `__tests__/param-aliases.test.ts` binds (1) to (2): every camelCase
|
|
24
|
+
* property advertised by any registry tool must appear in that tool's
|
|
25
|
+
* alias map, and vice versa. Adding one without the other fails there
|
|
26
|
+
* rather than silently advertising a param nothing honours.
|
|
27
|
+
*
|
|
28
|
+
* Normalization runs BEFORE `safeParse`, which is why the alias's declared Zod
|
|
29
|
+
* type never actually validates anything — by parse time the key is gone,
|
|
30
|
+
* rewritten onto its canonical name. It exists purely so the advert is
|
|
31
|
+
* truthful. It still mirrors the canonical validator so a client reading the
|
|
32
|
+
* advert is not misled about what the alias accepts.
|
|
33
|
+
*
|
|
34
|
+
* Deliberately NOT a Zod `.refine()`/`.transform()`: those produce a
|
|
35
|
+
* `ZodEffects`, which has no `.shape`, and both `zodToInputSchema` and
|
|
36
|
+
* `__tests__/schema-contract.test.ts` depend on `.shape` (see the same note in
|
|
37
|
+
* `interview.ts` and `personas.ts`). Cross-field rules live in plain code.
|
|
38
|
+
*
|
|
39
|
+
* ── SCOPE OF THE COMPATIBILITY GUARANTEE ────────────────────────────────────
|
|
40
|
+
* An alias is honoured at `tools/call`, AND an alias-only payload is valid
|
|
41
|
+
* against the advertised schema. `required` no longer excludes alias-only calls.
|
|
42
|
+
*
|
|
43
|
+
* That second half was not free (FUL-238). An alias is a SEPARATE optional
|
|
44
|
+
* property, so it can never satisfy a `required` entry naming the canonical
|
|
45
|
+
* field — the two are different keys. The only way to make the advert accept
|
|
46
|
+
* what the runtime accepts was to drop the canonical id from `required`
|
|
47
|
+
* entirely: the three aliased ids (`create_persona` / `generate_persona`
|
|
48
|
+
* `project_id`, `enrich_persona` `persona_id`) are declared `.optional()` and
|
|
49
|
+
* their presence is enforced in the handler by `requireId` in `personas.ts` —
|
|
50
|
+
* the convention `interview_persona` (per-action ids) and `update_persona`
|
|
51
|
+
* (at-least-one-field) already use. `interview_persona`'s three ids were always
|
|
52
|
+
* this way, so they never had the gap.
|
|
53
|
+
*
|
|
54
|
+
* The trade, taken deliberately: every caller loses the advert's machine-readable
|
|
55
|
+
* "this field is mandatory" signal for those ids, so each one says REQUIRED in
|
|
56
|
+
* its `.describe()` prose and a missing id fails with a clear handler error
|
|
57
|
+
* naming the canonical param — before any HTTP call. In exchange, no client can
|
|
58
|
+
* refuse a legacy caller's payload before sending it.
|
|
59
|
+
*
|
|
60
|
+
* `__tests__/param-aliases.test.ts` pins BOTH halves — the advert no longer
|
|
61
|
+
* listing those ids in `required`, and an alias-only call still reaching the
|
|
62
|
+
* real request — so neither can drift silently.
|
|
63
|
+
*/
|
|
64
|
+
import { ToolError } from './errors.js';
|
|
65
|
+
/**
|
|
66
|
+
* Declare a deprecated camelCase alias property for a tool's Zod schema.
|
|
67
|
+
*
|
|
68
|
+
* Pass the canonical field's BASE validator (before `.optional()`): the alias
|
|
69
|
+
* is always optional regardless of whether the canonical field is required, so
|
|
70
|
+
* a caller may supply either one.
|
|
71
|
+
*/
|
|
72
|
+
export function deprecatedAlias(canonical, schema) {
|
|
73
|
+
return schema
|
|
74
|
+
.optional()
|
|
75
|
+
.describe(`DEPRECATED alias for \`${canonical}\` — pass \`${canonical}\` instead. ` +
|
|
76
|
+
'Still accepted for backward compatibility. Passing both this and ' +
|
|
77
|
+
`\`${canonical}\` with DIFFERENT values is rejected.`);
|
|
78
|
+
}
|
|
79
|
+
/** A key counts as supplied when present and not `undefined` (explicit `null` counts). */
|
|
80
|
+
function isSupplied(source, key) {
|
|
81
|
+
return key in source && source[key] !== undefined;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Whether two supplied values are interchangeable. Aliased params are strings
|
|
85
|
+
* and booleans today; the JSON fallback keeps the check correct if one ever
|
|
86
|
+
* becomes structured, and never throws (both values came off a JSON payload).
|
|
87
|
+
*/
|
|
88
|
+
function sameValue(a, b) {
|
|
89
|
+
if (Object.is(a, b))
|
|
90
|
+
return true;
|
|
91
|
+
if (typeof a !== typeof b)
|
|
92
|
+
return false;
|
|
93
|
+
if (typeof a !== 'object')
|
|
94
|
+
return false;
|
|
95
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Rewrite any supplied camelCase aliases onto their canonical snake_case names.
|
|
99
|
+
*
|
|
100
|
+
* Returns the input untouched (same reference) when no alias is present, so the
|
|
101
|
+
* overwhelmingly common modern call path allocates nothing. Non-object input is
|
|
102
|
+
* passed straight through — the tool's own `safeParse` owns that error message.
|
|
103
|
+
*
|
|
104
|
+
* Supplying BOTH spellings of one field is only an error when the values
|
|
105
|
+
* DIFFER: a caller that sends the same value twice is unambiguous, and failing
|
|
106
|
+
* it would break a belt-and-braces caller for no safety gain. Differing values
|
|
107
|
+
* are genuinely ambiguous, so we refuse rather than pick a winner — silently
|
|
108
|
+
* preferring one would send a persona edit to whichever project the caller
|
|
109
|
+
* did NOT mean.
|
|
110
|
+
*
|
|
111
|
+
* @throws {ToolError} when one field is supplied under both names with
|
|
112
|
+
* different values.
|
|
113
|
+
*/
|
|
114
|
+
export function normalizeParamAliases(input, aliases) {
|
|
115
|
+
if (input === null || typeof input !== 'object' || Array.isArray(input))
|
|
116
|
+
return input;
|
|
117
|
+
const source = input;
|
|
118
|
+
const supplied = Object.keys(aliases).filter((alias) => isSupplied(source, alias));
|
|
119
|
+
if (supplied.length === 0)
|
|
120
|
+
return input;
|
|
121
|
+
const out = { ...source };
|
|
122
|
+
for (const alias of supplied) {
|
|
123
|
+
const canonical = aliases[alias];
|
|
124
|
+
const aliasValue = source[alias];
|
|
125
|
+
const canonicalSupplied = isSupplied(source, canonical);
|
|
126
|
+
// A `null` alias alongside a real canonical value is a JSON-shaped "unset",
|
|
127
|
+
// not a competing answer — dropping it is unambiguous, so refusing the call
|
|
128
|
+
// would fail a caller who is not actually contradicting themselves. Left
|
|
129
|
+
// alone when the alias is the ONLY spelling supplied, so the canonical
|
|
130
|
+
// field's own Zod error ("expected string, received null") still surfaces.
|
|
131
|
+
if (aliasValue === null && canonicalSupplied && source[canonical] !== null) {
|
|
132
|
+
delete out[alias];
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (canonicalSupplied && !sameValue(source[canonical], aliasValue)) {
|
|
136
|
+
throw new ToolError(`Invalid input — \`${canonical}\` and its deprecated alias \`${alias}\` were both ` +
|
|
137
|
+
`provided with different values. Pass only \`${canonical}\`.`);
|
|
138
|
+
}
|
|
139
|
+
out[canonical] = aliasValue;
|
|
140
|
+
delete out[alias];
|
|
141
|
+
}
|
|
142
|
+
return out;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=param-aliases.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"param-aliases.js","sourceRoot":"","sources":["../../src/tools/param-aliases.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAKvC;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAC7B,SAAiB,EACjB,MAAoB;IAEpB,OAAO,MAAM;SACV,QAAQ,EAAE;SACV,QAAQ,CACP,0BAA0B,SAAS,eAAe,SAAS,cAAc;QACvE,mEAAmE;QACnE,KAAK,SAAS,uCAAuC,CACxD,CAAA;AACL,CAAC;AAED,0FAA0F;AAC1F,SAAS,UAAU,CAAC,MAA+B,EAAE,GAAW;IAC9D,OAAO,GAAG,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,SAAS,CAAA;AACnD,CAAC;AAED;;;;GAIG;AACH,SAAS,SAAS,CAAC,CAAU,EAAE,CAAU;IACvC,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAAE,OAAO,IAAI,CAAA;IAChC,IAAI,OAAO,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACvC,IAAI,OAAO,CAAC,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAA;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAc,EAAE,OAAiB;IACrE,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAErF,MAAM,MAAM,GAAG,KAAgC,CAAA;IAC/C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IAClF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEvC,MAAM,GAAG,GAAG,EAAE,GAAG,MAAM,EAAE,CAAA;IACzB,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAE,CAAA;QACjC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;QAChC,MAAM,iBAAiB,GAAG,UAAU,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;QAEvD,4EAA4E;QAC5E,4EAA4E;QAC5E,yEAAyE;QACzE,uEAAuE;QACvE,2EAA2E;QAC3E,IAAI,UAAU,KAAK,IAAI,IAAI,iBAAiB,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,IAAI,EAAE,CAAC;YAC3E,OAAO,GAAG,CAAC,KAAK,CAAC,CAAA;YACjB,SAAQ;QACV,CAAC;QAED,IAAI,iBAAiB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;YACnE,MAAM,IAAI,SAAS,CACjB,qBAAqB,SAAS,iCAAiC,KAAK,eAAe;gBACjF,+CAA+C,SAAS,KAAK,CAChE,CAAA;QACH,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,GAAG,UAAU,CAAA;QAC3B,OAAO,GAAG,CAAC,KAAK,CAAC,CAAA;IACnB,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
|