idiolect-mcp 0.5.2 → 0.6.1
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 +17 -25
- package/package.json +1 -1
- package/progressive-catalog.json +311 -5964
- package/public-contract.js +12 -1
- package/server.mjs +11 -276
package/public-contract.js
CHANGED
|
@@ -81,5 +81,16 @@ export function publicMcpState(state, message, next, details = {}) {
|
|
|
81
81
|
delete safeDetails.state;
|
|
82
82
|
delete safeDetails.message;
|
|
83
83
|
delete safeDetails.next;
|
|
84
|
-
return {
|
|
84
|
+
return {
|
|
85
|
+
state,
|
|
86
|
+
message,
|
|
87
|
+
...safeDetails,
|
|
88
|
+
next: {
|
|
89
|
+
action: next.action,
|
|
90
|
+
tool: next.tool,
|
|
91
|
+
...(next.action === "retry" && typeof next.retryAfterSeconds === "number"
|
|
92
|
+
? { retryAfterSeconds: next.retryAfterSeconds }
|
|
93
|
+
: {}),
|
|
94
|
+
},
|
|
95
|
+
};
|
|
85
96
|
}
|
package/server.mjs
CHANGED
|
@@ -47,7 +47,16 @@ export const accountRuntimeCompatibilityToolNames = Object.freeze([
|
|
|
47
47
|
"feedback",
|
|
48
48
|
"refine",
|
|
49
49
|
"profile",
|
|
50
|
-
"
|
|
50
|
+
"list_writing_samples",
|
|
51
|
+
"create_writing_profile",
|
|
52
|
+
"add_writing_samples",
|
|
53
|
+
"rename_writing_profile",
|
|
54
|
+
"set_current_writing_profile",
|
|
55
|
+
"set_default_writing_template",
|
|
56
|
+
"add_writing_rule",
|
|
57
|
+
"remove_writing_rule",
|
|
58
|
+
"remove_writing_sample",
|
|
59
|
+
"delete_writing_profile",
|
|
51
60
|
"learn_writing_style",
|
|
52
61
|
"get_my_voice",
|
|
53
62
|
"create_my_voice",
|
|
@@ -142,195 +151,14 @@ export function formatLegacyScoreVoiceResult(result) {
|
|
|
142
151
|
return `${lines}\n\n(${provenance} — higher = reads more like you; not a forensic identity claim.)`;
|
|
143
152
|
}
|
|
144
153
|
|
|
145
|
-
const CREDIT_STATES = new Set([
|
|
146
|
-
"free_beta",
|
|
147
|
-
"balance_available",
|
|
148
|
-
"low_balance",
|
|
149
|
-
"zero_balance",
|
|
150
|
-
"legacy_unlimited",
|
|
151
|
-
"accounting_unavailable",
|
|
152
|
-
]);
|
|
153
|
-
const CREDIT_MODES = new Set(["free", "credits"]);
|
|
154
|
-
|
|
155
154
|
function plainObject(value) {
|
|
156
155
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
157
156
|
}
|
|
158
157
|
|
|
159
|
-
function safeManageUrl(value) {
|
|
160
|
-
if (value === null) return null;
|
|
161
|
-
if (typeof value !== "string") return undefined;
|
|
162
|
-
try {
|
|
163
|
-
const parsed = new URL(value);
|
|
164
|
-
const accountOrigin = new URL(ACCOUNT_BASE).origin;
|
|
165
|
-
const firstParty = parsed.hostname === "idiolect.app" || parsed.hostname.endsWith(".idiolect.app");
|
|
166
|
-
if (
|
|
167
|
-
!["https:", "http:"].includes(parsed.protocol)
|
|
168
|
-
|| (parsed.origin !== accountOrigin && !firstParty)
|
|
169
|
-
|| parsed.pathname !== "/settings"
|
|
170
|
-
|| parsed.username
|
|
171
|
-
|| parsed.password
|
|
172
|
-
|| parsed.search !== "?section=credits"
|
|
173
|
-
|| parsed.hash
|
|
174
|
-
) return undefined;
|
|
175
|
-
return `${parsed.origin}/settings?section=credits`;
|
|
176
|
-
} catch {
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function creditMessage(credits) {
|
|
182
|
-
const { state, balance, charged, manageUrl, mode } = credits;
|
|
183
|
-
if (state === "free_beta") {
|
|
184
|
-
return charged === 0
|
|
185
|
-
? "Free during beta. No credits are being used."
|
|
186
|
-
: "Completed free during beta. No credit was used.";
|
|
187
|
-
}
|
|
188
|
-
if (state === "legacy_unlimited") return "Unlimited generation access is active. Credits are not used.";
|
|
189
|
-
if (state === "accounting_unavailable") {
|
|
190
|
-
return mode === "free"
|
|
191
|
-
? "Free during beta. The credit balance is temporarily unavailable; generation remains available."
|
|
192
|
-
: "The credit balance is temporarily unavailable. Generation is paused; no credit was used.";
|
|
193
|
-
}
|
|
194
|
-
if (state === "zero_balance") {
|
|
195
|
-
return charged === 1
|
|
196
|
-
? "This request completed with the last credit."
|
|
197
|
-
: `The credit balance is 0. A draft or rewrite needs 1 credit. Credit management: ${manageUrl}.`;
|
|
198
|
-
}
|
|
199
|
-
if (charged === 1) return `1 credit used. ${balance} ${balance === 1 ? "remains" : "remain"}.`;
|
|
200
|
-
if (state === "low_balance") return `${balance} credit${balance === 1 ? "" : "s"} remain. Credit management: ${manageUrl}.`;
|
|
201
|
-
return `${balance} credits available. Each draft or rewrite uses 1 credit.`;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function validCreditInvariant(credits) {
|
|
205
|
-
const {
|
|
206
|
-
state,
|
|
207
|
-
mode,
|
|
208
|
-
balance,
|
|
209
|
-
balancePreview,
|
|
210
|
-
unitCost,
|
|
211
|
-
charged,
|
|
212
|
-
unlimited,
|
|
213
|
-
accountingAvailable,
|
|
214
|
-
generationAvailable,
|
|
215
|
-
purchaseRequired,
|
|
216
|
-
manageUrl,
|
|
217
|
-
} = credits;
|
|
218
|
-
if (state === "free_beta") {
|
|
219
|
-
return mode === "free" && balance !== null && balancePreview && unitCost === 0 && charged === 0
|
|
220
|
-
&& !unlimited && accountingAvailable && generationAvailable && !purchaseRequired && manageUrl === null;
|
|
221
|
-
}
|
|
222
|
-
if (state === "legacy_unlimited") {
|
|
223
|
-
return mode === "credits" && balance === null && !balancePreview && unitCost === 0 && charged === 0
|
|
224
|
-
&& unlimited && accountingAvailable && generationAvailable && !purchaseRequired && manageUrl === null;
|
|
225
|
-
}
|
|
226
|
-
if (state === "accounting_unavailable") {
|
|
227
|
-
return balance === null && !balancePreview && charged === 0 && !unlimited && !accountingAvailable
|
|
228
|
-
&& !purchaseRequired && manageUrl === null && unitCost === (mode === "free" ? 0 : 1)
|
|
229
|
-
&& generationAvailable === (mode === "free");
|
|
230
|
-
}
|
|
231
|
-
if (mode !== "credits" || balance === null || balancePreview || unitCost !== 1 || unlimited || !accountingAvailable) {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
if (state === "balance_available") {
|
|
235
|
-
return balance > 5 && generationAvailable && !purchaseRequired && manageUrl === null;
|
|
236
|
-
}
|
|
237
|
-
if (state === "low_balance") {
|
|
238
|
-
return balance >= 1 && balance <= 5 && generationAvailable && !purchaseRequired && manageUrl !== null;
|
|
239
|
-
}
|
|
240
|
-
return state === "zero_balance" && balance === 0 && !generationAvailable && purchaseRequired && manageUrl !== null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Rebuild the public credit envelope from known primitives only. Never copy an
|
|
245
|
-
* upstream message or unknown field into an MCP response.
|
|
246
|
-
*/
|
|
247
|
-
export function safeCreditPresentation(value) {
|
|
248
|
-
if (!plainObject(value)) return null;
|
|
249
|
-
const balance = value.balance;
|
|
250
|
-
const manageUrl = safeManageUrl(value.manageUrl);
|
|
251
|
-
if (
|
|
252
|
-
!CREDIT_STATES.has(value.state)
|
|
253
|
-
|| !CREDIT_MODES.has(value.mode)
|
|
254
|
-
|| !(balance === null || (Number.isSafeInteger(balance) && balance >= 0))
|
|
255
|
-
|| typeof value.balancePreview !== "boolean"
|
|
256
|
-
|| ![0, 1].includes(value.unitCost)
|
|
257
|
-
|| ![0, 1].includes(value.charged)
|
|
258
|
-
|| typeof value.unlimited !== "boolean"
|
|
259
|
-
|| typeof value.accountingAvailable !== "boolean"
|
|
260
|
-
|| typeof value.generationAvailable !== "boolean"
|
|
261
|
-
|| typeof value.purchaseRequired !== "boolean"
|
|
262
|
-
|| manageUrl === undefined
|
|
263
|
-
|| typeof value.replayed !== "boolean"
|
|
264
|
-
) return null;
|
|
265
|
-
|
|
266
|
-
const credits = {
|
|
267
|
-
state: value.state,
|
|
268
|
-
mode: value.mode,
|
|
269
|
-
balance,
|
|
270
|
-
balancePreview: value.balancePreview,
|
|
271
|
-
unitCost: value.unitCost,
|
|
272
|
-
charged: value.charged,
|
|
273
|
-
unlimited: value.unlimited,
|
|
274
|
-
accountingAvailable: value.accountingAvailable,
|
|
275
|
-
generationAvailable: value.generationAvailable,
|
|
276
|
-
purchaseRequired: value.purchaseRequired,
|
|
277
|
-
manageUrl,
|
|
278
|
-
replayed: value.replayed,
|
|
279
|
-
};
|
|
280
|
-
if (!validCreditInvariant(credits)) return null;
|
|
281
|
-
return { ...credits, message: creditMessage(credits) };
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
function safeCreditToolErrorPayload(value) {
|
|
285
|
-
if (!plainObject(value) || !plainObject(value.error) || !plainObject(value.retry)) return null;
|
|
286
|
-
const credits = safeCreditPresentation(value.credits);
|
|
287
|
-
if (!credits) return null;
|
|
288
|
-
const purchaseRequired = value.status === "purchase_required"
|
|
289
|
-
&& value.error.code === "insufficient_credits"
|
|
290
|
-
&& credits.state === "zero_balance"
|
|
291
|
-
&& credits.mode === "credits"
|
|
292
|
-
&& credits.balance === 0
|
|
293
|
-
&& credits.charged === 0
|
|
294
|
-
&& value.retry.after === "balance_updated";
|
|
295
|
-
const accountingUnavailable = value.status === "accounting_unavailable"
|
|
296
|
-
&& value.error.code === "credit_accounting_unavailable"
|
|
297
|
-
&& credits.state === "accounting_unavailable"
|
|
298
|
-
&& credits.mode === "credits"
|
|
299
|
-
&& credits.charged === 0
|
|
300
|
-
&& value.retry.after === "accounting_available";
|
|
301
|
-
if (
|
|
302
|
-
(!purchaseRequired && !accountingUnavailable)
|
|
303
|
-
|| value.error.retryable !== true
|
|
304
|
-
|| value.retry.safe !== true
|
|
305
|
-
|| value.retry.idempotent !== true
|
|
306
|
-
|| value.retry.scope !== "pre_generation"
|
|
307
|
-
) return null;
|
|
308
|
-
|
|
309
|
-
const message = purchaseRequired
|
|
310
|
-
? `This request needs 1 credit; the balance is 0. Nothing was charged. Credit management: ${credits.manageUrl}. The same request remains safe to retry after the balance changes.`
|
|
311
|
-
: "The credit balance is temporarily unavailable. No generation started and no credit was used. The same request remains safe to retry after accounting recovers.";
|
|
312
|
-
return {
|
|
313
|
-
status: value.status,
|
|
314
|
-
message,
|
|
315
|
-
error: { code: value.error.code, retryable: true },
|
|
316
|
-
credits: { ...credits, message },
|
|
317
|
-
retry: {
|
|
318
|
-
safe: true,
|
|
319
|
-
idempotent: true,
|
|
320
|
-
scope: "pre_generation",
|
|
321
|
-
after: value.retry.after,
|
|
322
|
-
},
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
158
|
async function accountRequestError(res, toolName) {
|
|
327
159
|
if ((res.headers.get("content-type") || "").includes("application/json")) {
|
|
328
160
|
try {
|
|
329
161
|
const payload = await res.json();
|
|
330
|
-
const creditContent = safeCreditToolErrorPayload(payload);
|
|
331
|
-
if (creditContent) {
|
|
332
|
-
return new AccountRequestError(creditContent.message, res.status, creditContent);
|
|
333
|
-
}
|
|
334
162
|
const creationContent = toolName === "create_my_voice" ? safeCreationFailurePayload(payload) : null;
|
|
335
163
|
if (creationContent) {
|
|
336
164
|
return new AccountRequestError(creationContent.message, res.status, creationContent);
|
|
@@ -608,72 +436,6 @@ const PROVENANCE_OUTPUT_PROPERTIES = {
|
|
|
608
436
|
voice: SELECTED_VOICE_SCHEMA,
|
|
609
437
|
evidence: EVIDENCE_OUTPUT_SCHEMA,
|
|
610
438
|
};
|
|
611
|
-
const CREDIT_OUTPUT_SCHEMA = {
|
|
612
|
-
type: "object",
|
|
613
|
-
additionalProperties: false,
|
|
614
|
-
properties: {
|
|
615
|
-
state: {
|
|
616
|
-
type: "string",
|
|
617
|
-
enum: ["free_beta", "balance_available", "low_balance", "zero_balance", "legacy_unlimited", "accounting_unavailable"],
|
|
618
|
-
},
|
|
619
|
-
mode: { type: "string", enum: ["free", "credits"] },
|
|
620
|
-
balance: { type: ["integer", "null"], minimum: 0 },
|
|
621
|
-
balancePreview: { type: "boolean" },
|
|
622
|
-
unitCost: { type: "number", enum: [0, 1] },
|
|
623
|
-
charged: { type: "number", enum: [0, 1] },
|
|
624
|
-
unlimited: { type: "boolean" },
|
|
625
|
-
accountingAvailable: { type: "boolean" },
|
|
626
|
-
generationAvailable: { type: "boolean" },
|
|
627
|
-
purchaseRequired: { type: "boolean" },
|
|
628
|
-
manageUrl: { type: ["string", "null"] },
|
|
629
|
-
replayed: { type: "boolean" },
|
|
630
|
-
message: { type: "string" },
|
|
631
|
-
},
|
|
632
|
-
required: [
|
|
633
|
-
"state",
|
|
634
|
-
"mode",
|
|
635
|
-
"balance",
|
|
636
|
-
"balancePreview",
|
|
637
|
-
"unitCost",
|
|
638
|
-
"charged",
|
|
639
|
-
"unlimited",
|
|
640
|
-
"accountingAvailable",
|
|
641
|
-
"generationAvailable",
|
|
642
|
-
"purchaseRequired",
|
|
643
|
-
"manageUrl",
|
|
644
|
-
"replayed",
|
|
645
|
-
"message",
|
|
646
|
-
],
|
|
647
|
-
};
|
|
648
|
-
const CREDIT_ERROR_SCHEMA = {
|
|
649
|
-
type: "object",
|
|
650
|
-
additionalProperties: false,
|
|
651
|
-
properties: {
|
|
652
|
-
code: { type: "string", enum: ["insufficient_credits", "credit_accounting_unavailable"] },
|
|
653
|
-
retryable: { type: "boolean", enum: [true] },
|
|
654
|
-
},
|
|
655
|
-
required: ["code", "retryable"],
|
|
656
|
-
};
|
|
657
|
-
const CREDIT_RETRY_SCHEMA = {
|
|
658
|
-
type: "object",
|
|
659
|
-
additionalProperties: false,
|
|
660
|
-
properties: {
|
|
661
|
-
safe: { type: "boolean", enum: [true] },
|
|
662
|
-
idempotent: { type: "boolean", enum: [true] },
|
|
663
|
-
scope: { type: "string", enum: ["pre_generation"] },
|
|
664
|
-
after: { type: "string", enum: ["balance_updated", "accounting_available"] },
|
|
665
|
-
},
|
|
666
|
-
required: ["safe", "idempotent", "scope", "after"],
|
|
667
|
-
};
|
|
668
|
-
// Carried by a credit-gate refusal only; a completed draft or rewrite no longer reports
|
|
669
|
-
// credits (13 constants in free mode) or a status beside `state` (IDI-381).
|
|
670
|
-
const CREDIT_GENERATION_OUTPUT_PROPERTIES = {
|
|
671
|
-
status: { type: "string", enum: ["purchase_required", "accounting_unavailable"] },
|
|
672
|
-
message: { type: "string" },
|
|
673
|
-
credits: CREDIT_OUTPUT_SCHEMA,
|
|
674
|
-
error: CREDIT_ERROR_SCHEMA,
|
|
675
|
-
retry: CREDIT_RETRY_SCHEMA,
|
|
676
|
-
};
|
|
677
439
|
const NEXT_ACTION_SCHEMA = {
|
|
678
440
|
type: "object",
|
|
679
441
|
additionalProperties: false,
|
|
@@ -735,7 +497,6 @@ const REWRITE_OUTPUT_SCHEMA = {
|
|
|
735
497
|
reason: { type: ["string", "null"], description: "Present only when scoring is enabled and the engine did not measure; says why." },
|
|
736
498
|
voice: SELECTED_VOICE_SCHEMA,
|
|
737
499
|
evidence: EVIDENCE_OUTPUT_SCHEMA,
|
|
738
|
-
...CREDIT_GENERATION_OUTPUT_PROPERTIES,
|
|
739
500
|
...CAVEATS_OUTPUT_PROPERTY,
|
|
740
501
|
},
|
|
741
502
|
};
|
|
@@ -747,7 +508,6 @@ const DRAFT_OUTPUT_SCHEMA = {
|
|
|
747
508
|
reason: { type: ["string", "null"], description: "Present only when scoring is enabled and the engine did not measure; says why." },
|
|
748
509
|
voice: SELECTED_VOICE_SCHEMA,
|
|
749
510
|
evidence: EVIDENCE_OUTPUT_SCHEMA,
|
|
750
|
-
...CREDIT_GENERATION_OUTPUT_PROPERTIES,
|
|
751
511
|
...CAVEATS_OUTPUT_PROPERTY,
|
|
752
512
|
},
|
|
753
513
|
};
|
|
@@ -1012,20 +772,6 @@ const accountToolDefinitions = [
|
|
|
1012
772
|
]),
|
|
1013
773
|
annotations: { readOnlyHint: false, openWorldHint: false, destructiveHint: true, title: "Delete Writing Profile" },
|
|
1014
774
|
},
|
|
1015
|
-
{
|
|
1016
|
-
name: "get_credit_status",
|
|
1017
|
-
description:
|
|
1018
|
-
"Check the account's current generation-credit state. During the free beta, this confirms that no credits are being used.",
|
|
1019
|
-
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
1020
|
-
outputSchema: CREDIT_OUTPUT_SCHEMA,
|
|
1021
|
-
annotations: {
|
|
1022
|
-
readOnlyHint: true,
|
|
1023
|
-
idempotentHint: true,
|
|
1024
|
-
openWorldHint: false,
|
|
1025
|
-
destructiveHint: false,
|
|
1026
|
-
title: "Check Credit Status",
|
|
1027
|
-
},
|
|
1028
|
-
},
|
|
1029
775
|
{
|
|
1030
776
|
name: "score_voice",
|
|
1031
777
|
description:
|
|
@@ -1480,18 +1226,7 @@ export async function handleAccountTool(name, args = {}) {
|
|
|
1480
1226
|
}
|
|
1481
1227
|
}
|
|
1482
1228
|
if (name === "get_credit_status") {
|
|
1483
|
-
|
|
1484
|
-
const credits = safeCreditPresentation(await accountRequest("/api/v1/credits", {
|
|
1485
|
-
toolName: "get_credit_status",
|
|
1486
|
-
}));
|
|
1487
|
-
if (!credits) return toolError("get_credit_status: Idiolect AI returned an invalid credit status.");
|
|
1488
|
-
return {
|
|
1489
|
-
content: [{ type: "text", text: credits.message }],
|
|
1490
|
-
structuredContent: credits,
|
|
1491
|
-
};
|
|
1492
|
-
} catch (error) {
|
|
1493
|
-
return accountToolError(error);
|
|
1494
|
-
}
|
|
1229
|
+
return toolError("This tool is no longer available. Refresh the Idiolect AI tool catalogue.");
|
|
1495
1230
|
}
|
|
1496
1231
|
|
|
1497
1232
|
if (name === "get_my_voice") {
|