@aistoragedepot/mcp 0.5.2 → 0.5.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/index.mjs +32 -2
- package/package.json +1 -1
- package/pull.mjs +17 -9
package/index.mjs
CHANGED
|
@@ -145,6 +145,34 @@ function fillBody(body, placeholders, args) {
|
|
|
145
145
|
return out;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
+
// Which MCP arg-names are required (their [placeholder] was marked required by the author).
|
|
149
|
+
function requiredArgNames(placeholders, requiredFields = []) {
|
|
150
|
+
const req = new Set(requiredFields);
|
|
151
|
+
const out = new Set();
|
|
152
|
+
for (const [argName, original] of argMap(placeholders)) if (req.has(original)) out.add(argName);
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// A "stop and ask first" instruction for any required field missing from the args — prepended to the
|
|
157
|
+
// prompt so even clients that ignore `required` don't run half-blank. "" when nothing's missing.
|
|
158
|
+
function missingRequiredPreamble(placeholders, requiredFields = [], args = {}) {
|
|
159
|
+
const req = new Set(requiredFields);
|
|
160
|
+
const missing = [];
|
|
161
|
+
for (const [argName, original] of argMap(placeholders)) {
|
|
162
|
+
if (!req.has(original)) continue;
|
|
163
|
+
const v = args?.[argName];
|
|
164
|
+
if (v == null || v === "") missing.push(original);
|
|
165
|
+
}
|
|
166
|
+
if (!missing.length) return "";
|
|
167
|
+
const list = missing.map((f) => `[${f}]`).join(", ");
|
|
168
|
+
const one = missing.length === 1;
|
|
169
|
+
return (
|
|
170
|
+
`⚠ Before doing anything else, ask me for a value for ${one ? "this field" : "these fields"}: ${list}. ` +
|
|
171
|
+
`Do not explore, run, generate, or take any action until I answer. Then substitute my ${one ? "answer" : "answers"} ` +
|
|
172
|
+
`for ${one ? "that field" : "those fields"} and carry out the prompt below.\n\n`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
148
176
|
const server = new Server(
|
|
149
177
|
{ name: "aistoragedepot", version: "0.5.0" },
|
|
150
178
|
{ capabilities: { resources: {}, prompts: {}, tools: {} } },
|
|
@@ -177,13 +205,14 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
177
205
|
const prompts = [];
|
|
178
206
|
for (const [name, it] of scopedPromptIndex(await snapshot())) {
|
|
179
207
|
const am = argMap(it.placeholders);
|
|
208
|
+
const req = requiredArgNames(it.placeholders, it.requiredFields);
|
|
180
209
|
prompts.push({
|
|
181
210
|
name,
|
|
182
211
|
description: `${it.type?.name || "Prompt"}: “${it.title}” (AIStorageDepot)`,
|
|
183
212
|
arguments: [...am.keys()].map((a) => ({
|
|
184
213
|
name: a,
|
|
185
214
|
description: `Value for [${am.get(a)}]`,
|
|
186
|
-
required:
|
|
215
|
+
required: req.has(a),
|
|
187
216
|
})),
|
|
188
217
|
});
|
|
189
218
|
}
|
|
@@ -193,7 +222,8 @@ server.setRequestHandler(ListPromptsRequestSchema, async () => {
|
|
|
193
222
|
server.setRequestHandler(GetPromptRequestSchema, async (req) => {
|
|
194
223
|
const it = scopedPromptIndex(await snapshot()).get(req.params.name);
|
|
195
224
|
if (!it) throw new Error(`Unknown prompt: ${req.params.name}`);
|
|
196
|
-
const
|
|
225
|
+
const args = req.params.arguments || {};
|
|
226
|
+
const text = missingRequiredPreamble(it.placeholders, it.requiredFields, args) + fillBody(it.body, it.placeholders, args);
|
|
197
227
|
return {
|
|
198
228
|
description: it.title,
|
|
199
229
|
messages: [{ role: "user", content: { type: "text", text } }],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aistoragedepot/mcp",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "Model Context Protocol server for your AIStorageDepot library — exposes your prompts, rules, docs, and skills to MCP-aware AI clients (Claude, Cursor, Cline, …).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/pull.mjs
CHANGED
|
@@ -52,14 +52,22 @@ function fmFile(fm, body) {
|
|
|
52
52
|
return ["---", ...Object.entries(fm).map(([k, v]) => `${k}: "${yq(v)}"`), "---", "", String(body).trim(), ""].join("\n");
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
// Required fields the author marked (intersected with the fields actually in this body).
|
|
56
|
+
const reqOf = (it, fields) => (it.requiredFields || []).filter((f) => fields.includes(f));
|
|
57
|
+
// A forceful "these are required — ask first" clause, or the gentle version when none are required.
|
|
58
|
+
const askClause = (required) =>
|
|
59
|
+
required.length
|
|
60
|
+
? ` ${required.length === 1 ? "This field is" : "These fields are"} REQUIRED: ${required.map((f) => `[${f}]`).join(", ")} — if a value wasn't provided, STOP and ask me first; don't explore, run, or generate anything until I answer.`
|
|
61
|
+
: ` Ask me for any value that's missing.`;
|
|
62
|
+
|
|
55
63
|
// Tail for tools that substitute $ARGUMENTS (claude, codex).
|
|
56
|
-
const argsTail = (fields) =>
|
|
64
|
+
const argsTail = (fields, required = []) =>
|
|
57
65
|
fields.length
|
|
58
|
-
? `\n\nInput: $ARGUMENTS\n\nFill the bracketed ${fields.length === 1 ? "field" : "fields"} in the prompt above from the input
|
|
66
|
+
? `\n\nInput: $ARGUMENTS\n\nFill the bracketed ${fields.length === 1 ? "field" : "fields"} in the prompt above from the input.${askClause(required)} Then carry out the prompt.`
|
|
59
67
|
: "\n\n$ARGUMENTS";
|
|
60
68
|
// Tail for tools that just pass the typed text along with the inserted prompt.
|
|
61
|
-
const fillTail = (fields) =>
|
|
62
|
-
`\n\nFill the bracketed ${fields.length === 1 ? "field" : "fields"} (${fields.map((f) => `[${f}]`).join(", ")}) from anything I typed along with this command
|
|
69
|
+
const fillTail = (fields, required = []) =>
|
|
70
|
+
`\n\nFill the bracketed ${fields.length === 1 ? "field" : "fields"} (${fields.map((f) => `[${f}]`).join(", ")}) from anything I typed along with this command.${askClause(required)} Then carry out the prompt.`;
|
|
63
71
|
|
|
64
72
|
// ── per-target renderers ─────────────────────────────────────────────────────
|
|
65
73
|
|
|
@@ -72,7 +80,7 @@ function renderClaude(it) {
|
|
|
72
80
|
const fields = fieldsOf(content);
|
|
73
81
|
const fm = { description: `Prompt — ${it.title} (AIStorageDepot)` };
|
|
74
82
|
if (fields.length) fm["argument-hint"] = fields.map((f) => `[${f}]`).join(" ");
|
|
75
|
-
return fmFile(fm, content.trim() + argsTail(fields));
|
|
83
|
+
return fmFile(fm, content.trim() + argsTail(fields, reqOf(it, fields)));
|
|
76
84
|
}
|
|
77
85
|
if (/^---\r?\n/.test(body)) return body.endsWith("\n") ? body : `${body}\n`;
|
|
78
86
|
return fmFile({ description: `${it.title} (AIStorageDepot)` }, body);
|
|
@@ -85,7 +93,7 @@ function renderCodex(it) {
|
|
|
85
93
|
const fields = isPromptItem(it) ? fieldsOf(content) : [];
|
|
86
94
|
const fm = { description: descFor(it, meta) };
|
|
87
95
|
if (isPromptItem(it) && fields.length) fm["argument-hint"] = fields.map((f) => `[${f}]`).join(" ");
|
|
88
|
-
return fmFile(fm, content.trim() + (isPromptItem(it) ? argsTail(fields) : ""));
|
|
96
|
+
return fmFile(fm, content.trim() + (isPromptItem(it) ? argsTail(fields, reqOf(it, fields)) : ""));
|
|
89
97
|
}
|
|
90
98
|
|
|
91
99
|
// VS Code prompt files: description + argument-hint frontmatter; typed extras arrive as
|
|
@@ -95,21 +103,21 @@ function renderVscode(it) {
|
|
|
95
103
|
const fields = isPromptItem(it) ? fieldsOf(content) : [];
|
|
96
104
|
const fm = { description: descFor(it, meta) };
|
|
97
105
|
if (fields.length) fm["argument-hint"] = fields.map((f) => `[${f}]`).join(" ");
|
|
98
|
-
return fmFile(fm, content.trim() + (fields.length ? fillTail(fields) : ""));
|
|
106
|
+
return fmFile(fm, content.trim() + (fields.length ? fillTail(fields, reqOf(it, fields)) : ""));
|
|
99
107
|
}
|
|
100
108
|
|
|
101
109
|
// Windsurf workflows: description frontmatter; 12k char cap per file (enforced by caller).
|
|
102
110
|
function renderWindsurf(it) {
|
|
103
111
|
const { meta, content } = splitFrontmatter(it.body || "");
|
|
104
112
|
const fields = isPromptItem(it) ? fieldsOf(content) : [];
|
|
105
|
-
return fmFile({ description: descFor(it, meta) }, content.trim() + (fields.length ? fillTail(fields) : ""));
|
|
113
|
+
return fmFile({ description: descFor(it, meta) }, content.trim() + (fields.length ? fillTail(fields, reqOf(it, fields)) : ""));
|
|
106
114
|
}
|
|
107
115
|
|
|
108
116
|
// Cursor commands: plain markdown — no frontmatter dialect; the body IS the prompt.
|
|
109
117
|
function renderCursor(it) {
|
|
110
118
|
const { content } = splitFrontmatter(it.body || "");
|
|
111
119
|
const fields = isPromptItem(it) ? fieldsOf(content) : [];
|
|
112
|
-
return content.trim() + (fields.length ? fillTail(fields) : "") + "\n";
|
|
120
|
+
return content.trim() + (fields.length ? fillTail(fields, reqOf(it, fields)) : "") + "\n";
|
|
113
121
|
}
|
|
114
122
|
|
|
115
123
|
// ── targets ──────────────────────────────────────────────────────────────────
|