@jini-ai/mcp 0.4.2 → 0.4.4
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/CHANGELOG.md +4 -0
- package/README.md +1 -0
- package/dist/agent-install/chrome-devtools.d.ts +4 -0
- package/dist/agent-install/chrome-devtools.js +7 -0
- package/dist/agent-install/index.d.ts +1 -0
- package/dist/agent-install/index.js +1 -0
- package/dist/agent-install/install.js +102 -194
- package/dist/bin/serve.d.ts +4 -1
- package/dist/bin/serve.js +120 -45
- package/dist/client/client.js +71 -58
- package/dist/core/config.js +113 -149
- package/dist/core/tokens.js +29 -53
- package/dist/federation/adapter.http.js +3 -2
- package/dist/federation/approvals/call-confirmation.js +5 -70
- package/dist/federation/approvals/remembered-approval.d.ts +39 -0
- package/dist/federation/approvals/remembered-approval.js +98 -0
- package/dist/federation/approvals/revocation-reason.d.ts +9 -0
- package/dist/federation/approvals/revocation-reason.js +11 -0
- package/dist/federation/approvals/revocation.js +19 -13
- package/dist/federation/approvals/scope-option.d.ts +6 -0
- package/dist/federation/approvals/scope-option.js +6 -0
- package/dist/federation/bootstrap.d.ts +18 -0
- package/dist/federation/bootstrap.js +4 -4
- package/dist/federation/presets.d.ts +16 -17
- package/dist/federation/presets.js +35 -28
- package/dist/federation/stdio/adapter.stdio.js +14 -6
- package/dist/federation/stdio/default-connect.js +24 -27
- package/dist/federation/stdio/stdio-launch-resolver.d.ts +1 -1
- package/dist/federation/stdio/stdio-launch-resolver.js +13 -9
- package/dist/federation/tool-approvals.js +3 -1
- package/dist/federation/trust.d.ts +23 -2
- package/dist/federation/trust.js +69 -35
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/server/daemon-client.d.ts +2 -0
- package/dist/server/daemon-client.js +86 -69
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/resources/site-guide-resource.d.ts +15 -0
- package/dist/server/resources/site-guide-resource.js +25 -0
- package/dist/server/tool-arguments.d.ts +46 -0
- package/dist/server/tool-arguments.js +111 -0
- package/dist/server/tool-protocol.d.ts +62 -7
- package/dist/server/tool-protocol.js +30 -40
- package/dist/server/tool-server.d.ts +19 -0
- package/dist/server/tool-server.js +91 -19
- package/dist/server/tools/delegated-tool.d.ts +47 -0
- package/dist/server/tools/delegated-tool.js +35 -2
- package/dist/server/tools/tool-catalog-tools.js +30 -8
- package/dist/tools/ask-choice/index.d.ts +213 -14
- package/dist/tools/ask-choice/index.js +90 -46
- package/dist/tools/ask-choice/question.d.ts +5 -1
- package/dist/tools/ask-choice/question.js +36 -2
- package/dist/tools/ask-choice/schema.d.ts +196 -10
- package/dist/tools/ask-choice/schema.js +33 -12
- package/dist/tools/ask-choice/types.d.ts +8 -0
- package/package.json +6 -5
- package/dist/core/oauth.d.ts +0 -274
- package/dist/core/oauth.js +0 -667
- package/dist/core/secure-write.d.ts +0 -17
- package/dist/core/secure-write.js +0 -72
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { parseQuestion, QuestionShapeError } from './question.js';
|
|
2
|
-
import { ASK_CHOICE_INPUT_SCHEMA } from './schema.js';
|
|
1
|
+
import { matchesOptions, parseQuestion, QuestionShapeError } from './question.js';
|
|
2
|
+
import { ASK_CHOICE_INPUT_SCHEMA, ASK_CHOICE_INVOCATION_SCHEMA } from './schema.js';
|
|
3
3
|
export { ASK_CHOICE_INPUT_SCHEMA } from './schema.js';
|
|
4
4
|
export { createAskChoiceAnswerTicketStore } from './pending.js';
|
|
5
5
|
/** Existing callback wire keys; the tool's identity is supplied by the host. */
|
|
@@ -14,7 +14,32 @@ function describeAnswer(params, messages) {
|
|
|
14
14
|
return { submitted: false, reason: 'no-answer', note: messages.noAnswer };
|
|
15
15
|
return { submitted: true, ...(choice === undefined ? {} : { choice }), ...(selections === undefined ? {} : { selections }), note: messages.submitted };
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
// Classifies a delivered form answer: cancel wins over typed text, typed text wins over picks,
|
|
18
|
+
// and picks must stay inside the offered options and actions.
|
|
19
|
+
function classifyReceived({ params, question, messages }) {
|
|
20
|
+
if (params[SURFACE_DISMISSED_PARAM] === true)
|
|
21
|
+
return { submitted: false, reason: 'cancelled', note: messages.cancelled };
|
|
22
|
+
const typed = params[SURFACE_TYPED_ANSWER_PARAM];
|
|
23
|
+
if (typeof typed === 'string' && typed.trim())
|
|
24
|
+
return { submitted: true, freeText: typed.trim(), note: messages.typed };
|
|
25
|
+
const described = describeAnswer(params, messages);
|
|
26
|
+
if (described.submitted !== true)
|
|
27
|
+
return described;
|
|
28
|
+
return withOfferedAction({ described, params, question, messages });
|
|
29
|
+
}
|
|
30
|
+
function withOfferedAction({ described, params, question, messages }) {
|
|
31
|
+
const invalid = { submitted: false, reason: 'invalid-answer', note: messages.noAnswer };
|
|
32
|
+
// Live exchanges need the same offered-option boundary as fallback tickets.
|
|
33
|
+
if (!matchesOptions({ question, params }))
|
|
34
|
+
return invalid;
|
|
35
|
+
if (!question.actions)
|
|
36
|
+
return described;
|
|
37
|
+
const action = params['__choiceAction'];
|
|
38
|
+
if (!question.actions.some(offered => offered.id === action))
|
|
39
|
+
return invalid;
|
|
40
|
+
return { ...described, action };
|
|
41
|
+
}
|
|
42
|
+
async function waitForAnswer({ exchange, ui, signal, messages, question }) {
|
|
18
43
|
const close = () => exchange.close({});
|
|
19
44
|
signal.addEventListener('abort', close, { once: true });
|
|
20
45
|
try {
|
|
@@ -26,18 +51,31 @@ async function waitForAnswer({ exchange, ui, signal, messages }) {
|
|
|
26
51
|
const answer = await exchange.receive({});
|
|
27
52
|
if (answer.status !== 'received')
|
|
28
53
|
return { submitted: false, reason: answer.status, note: messages[answer.status] };
|
|
29
|
-
|
|
30
|
-
return { submitted: false, reason: 'cancelled', note: messages.cancelled };
|
|
31
|
-
const typed = answer.params[SURFACE_TYPED_ANSWER_PARAM];
|
|
32
|
-
if (typeof typed === 'string' && typed.trim())
|
|
33
|
-
return { submitted: true, freeText: typed.trim(), note: messages.typed };
|
|
34
|
-
return describeAnswer(answer.params, messages);
|
|
54
|
+
return classifyReceived({ params: answer.params, question, messages });
|
|
35
55
|
}
|
|
36
56
|
finally {
|
|
37
57
|
signal.removeEventListener('abort', close);
|
|
38
58
|
close();
|
|
39
59
|
}
|
|
40
60
|
}
|
|
61
|
+
/** Run a trusted host question through the same abort, delivery, validation and cleanup lifecycle
|
|
62
|
+
* as the model-facing tool. The host owns presentation and action policy, never exchange mechanics.
|
|
63
|
+
* @returns Classified human answer, including an offered action when configured.
|
|
64
|
+
* @complexity O(n) offered options and selections.
|
|
65
|
+
*/
|
|
66
|
+
export async function askChoice({ exchange, question, toolId, principalId, presentation, signal, messages }, _options = {}) {
|
|
67
|
+
let ui;
|
|
68
|
+
try {
|
|
69
|
+
ui = presentation.render({ toolName: toolId, principalId, question,
|
|
70
|
+
baseParams: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id },
|
|
71
|
+
}, { cancelParams: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id, [SURFACE_DISMISSED_PARAM]: true } });
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
exchange.close({});
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
return waitForAnswer({ exchange, ui, signal, messages, question });
|
|
78
|
+
}
|
|
41
79
|
/** Creates a choice tool without a CMS, renderer, permission system, clock, or global store.
|
|
42
80
|
* The host composes this descriptor/handler into its registry and mounts callbacks against the
|
|
43
81
|
* same exchange and pending stores. Policy and presentation are required, with no product defaults.
|
|
@@ -49,50 +87,56 @@ export function createAskChoiceTool({ toolId, description, permission, policy, p
|
|
|
49
87
|
};
|
|
50
88
|
return {
|
|
51
89
|
descriptor,
|
|
90
|
+
invocationSchema: ASK_CHOICE_INVOCATION_SCHEMA,
|
|
52
91
|
async handler({ ctx }) {
|
|
53
92
|
await policy.authorize({ ctx, toolId });
|
|
54
93
|
const input = typeof ctx.input === 'object' && ctx.input !== null ? ctx.input : {};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const ticket = typeof rawTicket === 'string' ? rawTicket : undefined;
|
|
59
|
-
if (!pendingQuestions.redeem({ ticket, principalId: ctx.principalId, params: input })) {
|
|
60
|
-
throw policy.inputError({ message: messages.forgedAnswer });
|
|
61
|
-
}
|
|
62
|
-
return describeAnswer(input, messages);
|
|
63
|
-
}
|
|
64
|
-
let question;
|
|
65
|
-
try {
|
|
66
|
-
question = parseQuestion({ input, toolId });
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
if (!(error instanceof QuestionShapeError))
|
|
70
|
-
throw error;
|
|
71
|
-
throw policy.shapeError({ message: error.message, toolId, inputSchema: ASK_CHOICE_INPUT_SCHEMA });
|
|
72
|
-
}
|
|
94
|
+
if (isAnswerCall(input))
|
|
95
|
+
return redeemAnswer({ input, ctx, pendingQuestions, policy, messages });
|
|
96
|
+
const question = parseOrRefuse({ input, toolId, policy });
|
|
73
97
|
if (ctx.signal.aborted)
|
|
74
98
|
return { submitted: false, reason: 'abandoned', note: messages.abandoned };
|
|
75
99
|
const exchange = ctx.emitSurface ? surfaceExchanges.open({ toolId, principalId: ctx.principalId, emit: ctx.emitSurface }) : undefined;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
ui = presentation.render({
|
|
81
|
-
toolName: toolId, principalId: ctx.principalId, question, baseParams,
|
|
82
|
-
}, { ...(exchange ? { cancelParams: { [SURFACE_EXCHANGE_ID_PARAM]: exchange.id, [SURFACE_DISMISSED_PARAM]: true } } : {}),
|
|
83
|
-
});
|
|
84
|
-
if (!exchange)
|
|
85
|
-
return presentation.buildResult({ modelText: messages.pending, ui });
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
exchange?.close({});
|
|
89
|
-
// A form that was not delivered must not leave a redeemable fallback ticket behind.
|
|
90
|
-
if (ticket !== undefined)
|
|
91
|
-
pendingQuestions.redeem({ ticket, principalId: ctx.principalId, params: {} });
|
|
92
|
-
throw error;
|
|
93
|
-
}
|
|
94
|
-
return waitForAnswer({ exchange, ui, signal: ctx.signal, messages });
|
|
100
|
+
if (exchange)
|
|
101
|
+
return askChoice({ exchange, question, toolId, principalId: ctx.principalId, presentation, signal: ctx.signal, messages });
|
|
102
|
+
return presentFallback({ question, toolId, principalId: ctx.principalId, pendingQuestions, presentation, messages });
|
|
95
103
|
},
|
|
96
104
|
};
|
|
97
105
|
}
|
|
106
|
+
// A second call carrying a pick (and no question title) is an answer to a fallback form.
|
|
107
|
+
function isAnswerCall(input) {
|
|
108
|
+
return typeof input['title'] !== 'string' && typeof input['question'] !== 'string' && (typeof input['choice'] === 'string' || Array.isArray(input['selections']));
|
|
109
|
+
}
|
|
110
|
+
// The ticket is the only proof the answer came from a rendered form: redeem is fail-closed.
|
|
111
|
+
function redeemAnswer({ input, ctx, pendingQuestions, policy, messages }) {
|
|
112
|
+
const rawTicket = input[ASK_CHOICE_ANSWER_TICKET_PARAM];
|
|
113
|
+
const ticket = typeof rawTicket === 'string' ? rawTicket : undefined;
|
|
114
|
+
if (!pendingQuestions.redeem({ ticket, principalId: ctx.principalId, params: input })) {
|
|
115
|
+
throw policy.inputError({ message: messages.forgedAnswer });
|
|
116
|
+
}
|
|
117
|
+
return describeAnswer(input, messages);
|
|
118
|
+
}
|
|
119
|
+
function parseOrRefuse({ input, toolId, policy }) {
|
|
120
|
+
try {
|
|
121
|
+
return parseQuestion({ input, toolId });
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (!(error instanceof QuestionShapeError))
|
|
125
|
+
throw error;
|
|
126
|
+
throw policy.shapeError({ message: error.message, toolId, inputSchema: ASK_CHOICE_INPUT_SCHEMA });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// No emit seam: return the form itself, bound to a freshly minted single-use ticket.
|
|
130
|
+
function presentFallback({ question, toolId, principalId, pendingQuestions, presentation, messages }) {
|
|
131
|
+
const ticket = pendingQuestions.mint({ principalId, question });
|
|
132
|
+
try {
|
|
133
|
+
const ui = presentation.render({ toolName: toolId, principalId, question, baseParams: { [ASK_CHOICE_ANSWER_TICKET_PARAM]: ticket } });
|
|
134
|
+
return presentation.buildResult({ modelText: messages.pending, ui });
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
// A form that was not delivered must not leave a redeemable fallback ticket behind.
|
|
138
|
+
pendingQuestions.redeem({ ticket, principalId, params: {} });
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
98
142
|
//# sourceMappingURL=index.js.map
|
|
@@ -5,10 +5,14 @@ export declare class QuestionShapeError extends Error {
|
|
|
5
5
|
message: string;
|
|
6
6
|
}, options?: ErrorOptions);
|
|
7
7
|
}
|
|
8
|
+
/** Normalize the flat model question into the existing UI/answer contract.
|
|
9
|
+
* @throws QuestionShapeError for malformed questions or ambiguous mixed forms.
|
|
10
|
+
* @complexity O(n) in offered options; no effects.
|
|
11
|
+
*/
|
|
8
12
|
export declare function parseQuestion({ input, toolId }: {
|
|
9
13
|
input: Record<string, unknown>;
|
|
10
14
|
toolId: string;
|
|
11
|
-
}): AskChoiceQuestion;
|
|
15
|
+
}, _options?: {}): AskChoiceQuestion;
|
|
12
16
|
export declare function matchesOptions({ question, params }: {
|
|
13
17
|
question: AskChoiceQuestion;
|
|
14
18
|
params: Record<string, unknown>;
|
|
@@ -21,11 +21,45 @@ function readSelect(raw, field, toolId) {
|
|
|
21
21
|
if (typeof value !== 'string' || !value || typeof label !== 'string' || !label) {
|
|
22
22
|
throw new QuestionShapeError({ message: `${toolId}: every '${field}' option requires a string 'value' and 'label'.` });
|
|
23
23
|
}
|
|
24
|
-
return { value, label };
|
|
24
|
+
return { value, label, ...(typeof option['description'] === 'string' ? { description: option['description'] } : {}) };
|
|
25
25
|
});
|
|
26
26
|
return { label: spec['label'], options, ...(typeof spec['hint'] === 'string' ? { hint: spec['hint'] } : {}) };
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
function normalizeFlatQuestion({ input, toolId }, _options = {}) {
|
|
29
|
+
const question = input['question'];
|
|
30
|
+
if (typeof question !== 'string' || !question.trim())
|
|
31
|
+
throw new QuestionShapeError({ message: `${toolId}: 'question' is required.` });
|
|
32
|
+
if (input['multiple'] !== undefined && typeof input['multiple'] !== 'boolean') {
|
|
33
|
+
throw new QuestionShapeError({ message: `${toolId}: 'multiple' must be a boolean.` });
|
|
34
|
+
}
|
|
35
|
+
const options = normalizeFlatOptions({ rawOptions: input['options'] });
|
|
36
|
+
const select = { label: question, options };
|
|
37
|
+
return { title: question, ...(typeof input['description'] === 'string' ? { description: input['description'] } : {}),
|
|
38
|
+
...(typeof input['submitLabel'] === 'string' ? { submitLabel: input['submitLabel'] } : {}),
|
|
39
|
+
[input['multiple'] === true ? 'multiSelect' : 'singleSelect']: select };
|
|
40
|
+
}
|
|
41
|
+
function normalizeFlatOptions({ rawOptions }, _options = {}) {
|
|
42
|
+
return Array.isArray(rawOptions) ? rawOptions.map(entry => {
|
|
43
|
+
if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
|
|
44
|
+
return entry;
|
|
45
|
+
const option = entry;
|
|
46
|
+
return { ...option, value: option['value'] === undefined ? option['label'] : option['value'] };
|
|
47
|
+
}) : rawOptions;
|
|
48
|
+
}
|
|
49
|
+
function normalizeQuestion({ input, toolId }, _options = {}) {
|
|
50
|
+
if (input['question'] === undefined && input['options'] === undefined && input['multiple'] === undefined)
|
|
51
|
+
return input;
|
|
52
|
+
if (input['title'] !== undefined || input['singleSelect'] !== undefined || input['multiSelect'] !== undefined) {
|
|
53
|
+
throw new QuestionShapeError({ message: `${toolId}: use {question, options, multiple?} or the legacy nested selects; do not mix them.` });
|
|
54
|
+
}
|
|
55
|
+
return normalizeFlatQuestion({ input, toolId });
|
|
56
|
+
}
|
|
57
|
+
/** Normalize the flat model question into the existing UI/answer contract.
|
|
58
|
+
* @throws QuestionShapeError for malformed questions or ambiguous mixed forms.
|
|
59
|
+
* @complexity O(n) in offered options; no effects.
|
|
60
|
+
*/
|
|
61
|
+
export function parseQuestion({ input, toolId }, _options = {}) {
|
|
62
|
+
input = normalizeQuestion({ input, toolId });
|
|
29
63
|
const title = input['title'];
|
|
30
64
|
if (typeof title !== 'string' || !title)
|
|
31
65
|
throw new QuestionShapeError({ message: `${toolId}: 'title' is required.` });
|
|
@@ -1,16 +1,51 @@
|
|
|
1
|
-
/** Model
|
|
1
|
+
/** Model-authored question. Legacy nested selects remain aliases; answers are form-only. */
|
|
2
2
|
export declare const ASK_CHOICE_INPUT_SCHEMA: {
|
|
3
3
|
readonly type: "object";
|
|
4
4
|
readonly additionalProperties: false;
|
|
5
|
-
readonly required: readonly ["title"];
|
|
6
5
|
readonly anyOf: readonly [{
|
|
7
|
-
readonly required: readonly ["
|
|
6
|
+
readonly required: readonly ["question", "options"];
|
|
8
7
|
}, {
|
|
9
|
-
readonly required: readonly ["
|
|
8
|
+
readonly required: readonly ["title", "singleSelect"];
|
|
9
|
+
}, {
|
|
10
|
+
readonly required: readonly ["title", "multiSelect"];
|
|
10
11
|
}];
|
|
11
12
|
readonly properties: {
|
|
13
|
+
readonly question: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
readonly minLength: 1;
|
|
16
|
+
readonly description: "The question to ask the human.";
|
|
17
|
+
};
|
|
18
|
+
readonly options: {
|
|
19
|
+
readonly type: "array";
|
|
20
|
+
readonly minItems: 1;
|
|
21
|
+
readonly items: {
|
|
22
|
+
readonly type: "object";
|
|
23
|
+
readonly additionalProperties: false;
|
|
24
|
+
readonly required: readonly ["label"];
|
|
25
|
+
readonly properties: {
|
|
26
|
+
readonly label: {
|
|
27
|
+
readonly type: "string";
|
|
28
|
+
readonly minLength: 1;
|
|
29
|
+
};
|
|
30
|
+
readonly value: {
|
|
31
|
+
readonly type: "string";
|
|
32
|
+
readonly minLength: 1;
|
|
33
|
+
readonly description: "Defaults to label when omitted.";
|
|
34
|
+
};
|
|
35
|
+
readonly description: {
|
|
36
|
+
readonly type: "string";
|
|
37
|
+
};
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
readonly multiple: {
|
|
42
|
+
readonly type: "boolean";
|
|
43
|
+
readonly description: "Allow several selections. Defaults to false.";
|
|
44
|
+
};
|
|
12
45
|
readonly title: {
|
|
13
46
|
readonly type: "string";
|
|
47
|
+
readonly minLength: 1;
|
|
48
|
+
readonly description: "Legacy alias: title with singleSelect or multiSelect.";
|
|
14
49
|
};
|
|
15
50
|
readonly description: {
|
|
16
51
|
readonly type: "string";
|
|
@@ -19,25 +54,33 @@ export declare const ASK_CHOICE_INPUT_SCHEMA: {
|
|
|
19
54
|
readonly type: "string";
|
|
20
55
|
};
|
|
21
56
|
readonly singleSelect: {
|
|
57
|
+
readonly description: "Legacy nested single-choice alias.";
|
|
22
58
|
readonly type: "object";
|
|
23
59
|
readonly additionalProperties: false;
|
|
24
60
|
readonly required: readonly ["label", "options"];
|
|
25
61
|
readonly properties: {
|
|
26
62
|
readonly label: {
|
|
27
63
|
readonly type: "string";
|
|
64
|
+
readonly minLength: 1;
|
|
28
65
|
};
|
|
29
66
|
readonly options: {
|
|
30
67
|
readonly type: "array";
|
|
31
68
|
readonly minItems: 1;
|
|
32
69
|
readonly items: {
|
|
70
|
+
readonly required: readonly ["value", "label"];
|
|
33
71
|
readonly type: "object";
|
|
34
72
|
readonly additionalProperties: false;
|
|
35
|
-
readonly required: readonly ["value", "label"];
|
|
36
73
|
readonly properties: {
|
|
74
|
+
readonly label: {
|
|
75
|
+
readonly type: "string";
|
|
76
|
+
readonly minLength: 1;
|
|
77
|
+
};
|
|
37
78
|
readonly value: {
|
|
38
79
|
readonly type: "string";
|
|
80
|
+
readonly minLength: 1;
|
|
81
|
+
readonly description: "Defaults to label when omitted.";
|
|
39
82
|
};
|
|
40
|
-
readonly
|
|
83
|
+
readonly description: {
|
|
41
84
|
readonly type: "string";
|
|
42
85
|
};
|
|
43
86
|
};
|
|
@@ -46,25 +89,33 @@ export declare const ASK_CHOICE_INPUT_SCHEMA: {
|
|
|
46
89
|
};
|
|
47
90
|
};
|
|
48
91
|
readonly multiSelect: {
|
|
92
|
+
readonly description: "Legacy nested multiple-choice alias.";
|
|
49
93
|
readonly properties: {
|
|
50
94
|
readonly hint: {
|
|
51
95
|
readonly type: "string";
|
|
52
96
|
};
|
|
53
97
|
readonly label: {
|
|
54
98
|
readonly type: "string";
|
|
99
|
+
readonly minLength: 1;
|
|
55
100
|
};
|
|
56
101
|
readonly options: {
|
|
57
102
|
readonly type: "array";
|
|
58
103
|
readonly minItems: 1;
|
|
59
104
|
readonly items: {
|
|
105
|
+
readonly required: readonly ["value", "label"];
|
|
60
106
|
readonly type: "object";
|
|
61
107
|
readonly additionalProperties: false;
|
|
62
|
-
readonly required: readonly ["value", "label"];
|
|
63
108
|
readonly properties: {
|
|
109
|
+
readonly label: {
|
|
110
|
+
readonly type: "string";
|
|
111
|
+
readonly minLength: 1;
|
|
112
|
+
};
|
|
64
113
|
readonly value: {
|
|
65
114
|
readonly type: "string";
|
|
115
|
+
readonly minLength: 1;
|
|
116
|
+
readonly description: "Defaults to label when omitted.";
|
|
66
117
|
};
|
|
67
|
-
readonly
|
|
118
|
+
readonly description: {
|
|
68
119
|
readonly type: "string";
|
|
69
120
|
};
|
|
70
121
|
};
|
|
@@ -75,17 +126,152 @@ export declare const ASK_CHOICE_INPUT_SCHEMA: {
|
|
|
75
126
|
readonly additionalProperties: false;
|
|
76
127
|
readonly required: readonly ["label", "options"];
|
|
77
128
|
};
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
/** Execution-only callback fields. The same handler still verifies the principal-bound ticket. */
|
|
132
|
+
export declare const ASK_CHOICE_INVOCATION_SCHEMA: {
|
|
133
|
+
readonly anyOf: readonly [{
|
|
134
|
+
readonly required: readonly ["question", "options"];
|
|
135
|
+
}, {
|
|
136
|
+
readonly required: readonly ["title", "singleSelect"];
|
|
137
|
+
}, {
|
|
138
|
+
readonly required: readonly ["title", "multiSelect"];
|
|
139
|
+
}, {
|
|
140
|
+
readonly required: readonly ["choice", "__askChoiceAnswerTicket"];
|
|
141
|
+
}, {
|
|
142
|
+
readonly required: readonly ["selections", "__askChoiceAnswerTicket"];
|
|
143
|
+
}];
|
|
144
|
+
readonly properties: {
|
|
78
145
|
readonly choice: {
|
|
79
146
|
readonly type: "string";
|
|
80
|
-
readonly description: "Set by the rendered form; never by the model.";
|
|
81
147
|
};
|
|
82
148
|
readonly selections: {
|
|
83
149
|
readonly type: "array";
|
|
84
150
|
readonly items: {
|
|
85
151
|
readonly type: "string";
|
|
86
152
|
};
|
|
87
|
-
|
|
153
|
+
};
|
|
154
|
+
readonly __askChoiceAnswerTicket: {
|
|
155
|
+
readonly type: "string";
|
|
156
|
+
};
|
|
157
|
+
readonly question: {
|
|
158
|
+
readonly type: "string";
|
|
159
|
+
readonly minLength: 1;
|
|
160
|
+
readonly description: "The question to ask the human.";
|
|
161
|
+
};
|
|
162
|
+
readonly options: {
|
|
163
|
+
readonly type: "array";
|
|
164
|
+
readonly minItems: 1;
|
|
165
|
+
readonly items: {
|
|
166
|
+
readonly type: "object";
|
|
167
|
+
readonly additionalProperties: false;
|
|
168
|
+
readonly required: readonly ["label"];
|
|
169
|
+
readonly properties: {
|
|
170
|
+
readonly label: {
|
|
171
|
+
readonly type: "string";
|
|
172
|
+
readonly minLength: 1;
|
|
173
|
+
};
|
|
174
|
+
readonly value: {
|
|
175
|
+
readonly type: "string";
|
|
176
|
+
readonly minLength: 1;
|
|
177
|
+
readonly description: "Defaults to label when omitted.";
|
|
178
|
+
};
|
|
179
|
+
readonly description: {
|
|
180
|
+
readonly type: "string";
|
|
181
|
+
};
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
readonly multiple: {
|
|
186
|
+
readonly type: "boolean";
|
|
187
|
+
readonly description: "Allow several selections. Defaults to false.";
|
|
188
|
+
};
|
|
189
|
+
readonly title: {
|
|
190
|
+
readonly type: "string";
|
|
191
|
+
readonly minLength: 1;
|
|
192
|
+
readonly description: "Legacy alias: title with singleSelect or multiSelect.";
|
|
193
|
+
};
|
|
194
|
+
readonly description: {
|
|
195
|
+
readonly type: "string";
|
|
196
|
+
};
|
|
197
|
+
readonly submitLabel: {
|
|
198
|
+
readonly type: "string";
|
|
199
|
+
};
|
|
200
|
+
readonly singleSelect: {
|
|
201
|
+
readonly description: "Legacy nested single-choice alias.";
|
|
202
|
+
readonly type: "object";
|
|
203
|
+
readonly additionalProperties: false;
|
|
204
|
+
readonly required: readonly ["label", "options"];
|
|
205
|
+
readonly properties: {
|
|
206
|
+
readonly label: {
|
|
207
|
+
readonly type: "string";
|
|
208
|
+
readonly minLength: 1;
|
|
209
|
+
};
|
|
210
|
+
readonly options: {
|
|
211
|
+
readonly type: "array";
|
|
212
|
+
readonly minItems: 1;
|
|
213
|
+
readonly items: {
|
|
214
|
+
readonly required: readonly ["value", "label"];
|
|
215
|
+
readonly type: "object";
|
|
216
|
+
readonly additionalProperties: false;
|
|
217
|
+
readonly properties: {
|
|
218
|
+
readonly label: {
|
|
219
|
+
readonly type: "string";
|
|
220
|
+
readonly minLength: 1;
|
|
221
|
+
};
|
|
222
|
+
readonly value: {
|
|
223
|
+
readonly type: "string";
|
|
224
|
+
readonly minLength: 1;
|
|
225
|
+
readonly description: "Defaults to label when omitted.";
|
|
226
|
+
};
|
|
227
|
+
readonly description: {
|
|
228
|
+
readonly type: "string";
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
readonly multiSelect: {
|
|
236
|
+
readonly description: "Legacy nested multiple-choice alias.";
|
|
237
|
+
readonly properties: {
|
|
238
|
+
readonly hint: {
|
|
239
|
+
readonly type: "string";
|
|
240
|
+
};
|
|
241
|
+
readonly label: {
|
|
242
|
+
readonly type: "string";
|
|
243
|
+
readonly minLength: 1;
|
|
244
|
+
};
|
|
245
|
+
readonly options: {
|
|
246
|
+
readonly type: "array";
|
|
247
|
+
readonly minItems: 1;
|
|
248
|
+
readonly items: {
|
|
249
|
+
readonly required: readonly ["value", "label"];
|
|
250
|
+
readonly type: "object";
|
|
251
|
+
readonly additionalProperties: false;
|
|
252
|
+
readonly properties: {
|
|
253
|
+
readonly label: {
|
|
254
|
+
readonly type: "string";
|
|
255
|
+
readonly minLength: 1;
|
|
256
|
+
};
|
|
257
|
+
readonly value: {
|
|
258
|
+
readonly type: "string";
|
|
259
|
+
readonly minLength: 1;
|
|
260
|
+
readonly description: "Defaults to label when omitted.";
|
|
261
|
+
};
|
|
262
|
+
readonly description: {
|
|
263
|
+
readonly type: "string";
|
|
264
|
+
};
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
readonly type: "object";
|
|
270
|
+
readonly additionalProperties: false;
|
|
271
|
+
readonly required: readonly ["label", "options"];
|
|
88
272
|
};
|
|
89
273
|
};
|
|
274
|
+
readonly type: "object";
|
|
275
|
+
readonly additionalProperties: false;
|
|
90
276
|
};
|
|
91
277
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1,27 +1,48 @@
|
|
|
1
1
|
const optionSchema = {
|
|
2
|
-
type: 'object', additionalProperties: false, required: ['
|
|
3
|
-
properties: {
|
|
2
|
+
type: 'object', additionalProperties: false, required: ['label'],
|
|
3
|
+
properties: {
|
|
4
|
+
label: { type: 'string', minLength: 1 },
|
|
5
|
+
value: { type: 'string', minLength: 1, description: 'Defaults to label when omitted.' },
|
|
6
|
+
description: { type: 'string' },
|
|
7
|
+
},
|
|
4
8
|
};
|
|
5
9
|
const selectSchema = {
|
|
6
10
|
type: 'object', additionalProperties: false, required: ['label', 'options'],
|
|
7
11
|
properties: {
|
|
8
|
-
label: { type: 'string' },
|
|
9
|
-
options: { type: 'array', minItems: 1, items: optionSchema },
|
|
12
|
+
label: { type: 'string', minLength: 1 },
|
|
13
|
+
options: { type: 'array', minItems: 1, items: { ...optionSchema, required: ['value', 'label'] } },
|
|
10
14
|
},
|
|
11
15
|
};
|
|
12
|
-
/** Model
|
|
16
|
+
/** Model-authored question. Legacy nested selects remain aliases; answers are form-only. */
|
|
13
17
|
export const ASK_CHOICE_INPUT_SCHEMA = {
|
|
14
|
-
type: 'object', additionalProperties: false,
|
|
15
|
-
anyOf: [
|
|
18
|
+
type: 'object', additionalProperties: false,
|
|
19
|
+
anyOf: [
|
|
20
|
+
{ required: ['question', 'options'] },
|
|
21
|
+
{ required: ['title', 'singleSelect'] },
|
|
22
|
+
{ required: ['title', 'multiSelect'] },
|
|
23
|
+
],
|
|
16
24
|
properties: {
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
question: { type: 'string', minLength: 1, description: 'The question to ask the human.' },
|
|
26
|
+
options: { type: 'array', minItems: 1, items: optionSchema },
|
|
27
|
+
multiple: { type: 'boolean', description: 'Allow several selections. Defaults to false.' },
|
|
28
|
+
title: { type: 'string', minLength: 1, description: 'Legacy alias: title with singleSelect or multiSelect.' },
|
|
29
|
+
description: { type: 'string' }, submitLabel: { type: 'string' },
|
|
30
|
+
singleSelect: { ...selectSchema, description: 'Legacy nested single-choice alias.' },
|
|
19
31
|
multiSelect: {
|
|
20
|
-
...selectSchema,
|
|
32
|
+
...selectSchema, description: 'Legacy nested multiple-choice alias.',
|
|
21
33
|
properties: { ...selectSchema.properties, hint: { type: 'string' } },
|
|
22
34
|
},
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/** Execution-only callback fields. The same handler still verifies the principal-bound ticket. */
|
|
38
|
+
export const ASK_CHOICE_INVOCATION_SCHEMA = {
|
|
39
|
+
...ASK_CHOICE_INPUT_SCHEMA,
|
|
40
|
+
anyOf: [...ASK_CHOICE_INPUT_SCHEMA.anyOf, { required: ['choice', '__askChoiceAnswerTicket'] }, { required: ['selections', '__askChoiceAnswerTicket'] }],
|
|
41
|
+
properties: {
|
|
42
|
+
...ASK_CHOICE_INPUT_SCHEMA.properties,
|
|
43
|
+
choice: { type: 'string' },
|
|
44
|
+
selections: { type: 'array', items: { type: 'string' } },
|
|
45
|
+
__askChoiceAnswerTicket: { type: 'string' },
|
|
25
46
|
},
|
|
26
47
|
};
|
|
27
48
|
//# sourceMappingURL=schema.js.map
|
|
@@ -2,9 +2,12 @@
|
|
|
2
2
|
export interface AskChoiceSelect {
|
|
3
3
|
readonly label: string;
|
|
4
4
|
readonly hint?: string;
|
|
5
|
+
/** Host-supplied checked defaults; answers are still validated against offered options. */
|
|
6
|
+
readonly value?: readonly string[];
|
|
5
7
|
readonly options: ReadonlyArray<{
|
|
6
8
|
value: string;
|
|
7
9
|
label: string;
|
|
10
|
+
description?: string;
|
|
8
11
|
}>;
|
|
9
12
|
}
|
|
10
13
|
export interface AskChoiceQuestion {
|
|
@@ -13,6 +16,11 @@ export interface AskChoiceQuestion {
|
|
|
13
16
|
readonly submitLabel?: string;
|
|
14
17
|
readonly singleSelect?: AskChoiceSelect;
|
|
15
18
|
readonly multiSelect?: AskChoiceSelect;
|
|
19
|
+
/** Trusted host actions, carried by the form under __choiceAction. */
|
|
20
|
+
readonly actions?: readonly {
|
|
21
|
+
id: string;
|
|
22
|
+
label: string;
|
|
23
|
+
}[];
|
|
16
24
|
}
|
|
17
25
|
export interface AskChoiceMessages {
|
|
18
26
|
readonly pending: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"description": "Model Context Protocol adapters: server config schema and IO, an MCP client, and agent-side MCP server installation.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -83,11 +83,11 @@
|
|
|
83
83
|
"registry": "https://registry.npmjs.org"
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
|
-
"@jini-ai/core": "^0.4.
|
|
86
|
+
"@jini-ai/core": "^0.4.9"
|
|
87
87
|
},
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"@cfworker/json-schema": "^4.1.1",
|
|
90
|
-
"@jini-ai/platform": "^0.5.
|
|
90
|
+
"@jini-ai/platform": "^0.5.3",
|
|
91
91
|
"@modelcontextprotocol/sdk": "^1.29.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependenciesMeta": {
|
|
@@ -104,8 +104,9 @@
|
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@cfworker/json-schema": "^4.1.1",
|
|
106
106
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
107
|
-
"@jini-ai/core": "^0.4.
|
|
108
|
-
"@jini-ai/platform": "^0.5.
|
|
107
|
+
"@jini-ai/core": "^0.4.9",
|
|
108
|
+
"@jini-ai/platform": "^0.5.10",
|
|
109
|
+
"@jini-ai/daemon": "^0.6.9"
|
|
109
110
|
},
|
|
110
111
|
"scripts": {
|
|
111
112
|
"build": "tsc -p tsconfig.json && chmod +x dist/bin/serve.js",
|