@2kw/ai-mcp-server 6.3.0-dev.36 → 6.3.0-dev.42
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/dist/lib/agent-decide.d.ts +3 -2
- package/dist/lib/agent-decide.js +5 -2
- package/dist/lib/agent-run.d.ts +21 -2
- package/dist/lib/agent-run.js +25 -1
- package/dist/tools/agents.js +9 -4
- package/dist/tools/ai-gateway.js +14 -7
- package/dist/tools/conversations.js +26 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ApiClient } from "../client.js";
|
|
2
2
|
import type { components } from "../generated/openapi.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type ConversationMode, type ResponsesResult } from "./agent-run.js";
|
|
4
4
|
/**
|
|
5
5
|
* Decide the approvals a paused agent run is waiting for (#667; spec 2026-09-14-cli-agents-design.md
|
|
6
6
|
* §4.2, D10, D11). Mirrors `cli/src/lib/agent-decide.ts` and `agent-lookup.ts`, with a per-approval
|
|
@@ -48,6 +48,7 @@ export declare function planDecisions(pending: ApprovalRow[], input: DecideInput
|
|
|
48
48
|
* Continues on `agent/<agentId>[@<label>][#<model>]`: the server resolves the continuation's tools,
|
|
49
49
|
* instructions and model from this reference, so label and model must be the run's (D11). Never the
|
|
50
50
|
* echoed `name@<versionNumber>` — it would 404 as a label.
|
|
51
|
+
* With `mode`, the `backbone:mode` item follows the decisions (#656).
|
|
51
52
|
*/
|
|
52
|
-
export declare function continueWithDecisions(client: ApiClient, agentId: string, responseId: string, items: ApprovalItem[], label?: string, model?: string): Promise<ResponsesResult>;
|
|
53
|
+
export declare function continueWithDecisions(client: ApiClient, agentId: string, responseId: string, items: ApprovalItem[], label?: string, model?: string, mode?: ConversationMode): Promise<ResponsesResult>;
|
|
53
54
|
//# sourceMappingURL=agent-decide.d.ts.map
|
package/dist/lib/agent-decide.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { modeItem } from "./agent-run.js";
|
|
1
2
|
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2
3
|
const PAGE_SIZE = 100;
|
|
3
4
|
const MAX_PAGES = 50;
|
|
@@ -105,13 +106,15 @@ export function planDecisions(pending, input, responseId) {
|
|
|
105
106
|
* Continues on `agent/<agentId>[@<label>][#<model>]`: the server resolves the continuation's tools,
|
|
106
107
|
* instructions and model from this reference, so label and model must be the run's (D11). Never the
|
|
107
108
|
* echoed `name@<versionNumber>` — it would 404 as a label.
|
|
109
|
+
* With `mode`, the `backbone:mode` item follows the decisions (#656).
|
|
108
110
|
*/
|
|
109
|
-
export async function continueWithDecisions(client, agentId, responseId, items, label, model) {
|
|
111
|
+
export async function continueWithDecisions(client, agentId, responseId, items, label, model, mode) {
|
|
110
112
|
const body = {
|
|
111
113
|
model: `agent/${agentId}${label ? `@${label}` : ""}${model ? `#${model}` : ""}`,
|
|
112
114
|
previous_response_id: responseId,
|
|
113
115
|
stream: false,
|
|
114
|
-
|
|
116
|
+
// With `mode`, the backbone:mode item follows the decisions (#656 D7); without it, nothing is added (D2).
|
|
117
|
+
input: mode ? [...items, modeItem(mode)] : items,
|
|
115
118
|
};
|
|
116
119
|
// The generated body type does not model backbone: extension items, hence the cast.
|
|
117
120
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
package/dist/lib/agent-run.d.ts
CHANGED
|
@@ -25,10 +25,28 @@ export interface PendingToolCall {
|
|
|
25
25
|
callId: string;
|
|
26
26
|
arguments: unknown;
|
|
27
27
|
}
|
|
28
|
+
/** The end user's conversation mode (epic &59); the server matches these three values exactly. */
|
|
29
|
+
export type ConversationMode = "plan" | "ask" | "auto";
|
|
30
|
+
export declare const CONVERSATION_MODES: readonly ConversationMode[];
|
|
31
|
+
/**
|
|
32
|
+
* Who may change the mode (#656 D8), stated in both tool descriptions: the mode outlives the request
|
|
33
|
+
* that sets it, so a model that switched on its own would lift the user's choice for every later turn.
|
|
34
|
+
*/
|
|
35
|
+
export declare const MODE_RULE: string;
|
|
36
|
+
/** The `backbone:mode` input item (S1 D7); always appended last. */
|
|
37
|
+
export declare function modeItem(mode: ConversationMode): {
|
|
38
|
+
type: "backbone:mode";
|
|
39
|
+
mode: ConversationMode;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The mode the response ran under (`conversation_mode`, #656). An absent key (a server before #656)
|
|
43
|
+
* and any value other than the three read as null, which also means "none set".
|
|
44
|
+
*/
|
|
45
|
+
export declare function responseMode(result: ResponsesResult | undefined): ConversationMode | null;
|
|
28
46
|
export interface RunEnvelope {
|
|
29
47
|
status: RunStatus;
|
|
30
|
-
/**
|
|
31
|
-
mode: null;
|
|
48
|
+
/** The conversation mode the request ran under (`conversation_mode`); null when none is set. */
|
|
49
|
+
mode: ConversationMode | null;
|
|
32
50
|
agent: string | null;
|
|
33
51
|
version: number | null;
|
|
34
52
|
responseId: string | null;
|
|
@@ -61,6 +79,7 @@ export interface ResponsesResult {
|
|
|
61
79
|
incomplete_details?: {
|
|
62
80
|
reason?: string;
|
|
63
81
|
} | null;
|
|
82
|
+
conversation_mode?: string | null;
|
|
64
83
|
}
|
|
65
84
|
/** The assistant's text: every `output_text` part of every `message` item, in order. */
|
|
66
85
|
export declare function extractResponseText(result: ResponsesResult | undefined): string;
|
package/dist/lib/agent-run.js
CHANGED
|
@@ -3,6 +3,30 @@
|
|
|
3
3
|
* from `cli/src/lib/agent-run.ts` because cli/ and mcp/ share no package (#667). Keep the two in step;
|
|
4
4
|
* only the text of `next` differs, because an MCP caller decides through a tool, not a shell command.
|
|
5
5
|
*/
|
|
6
|
+
export const CONVERSATION_MODES = ["plan", "ask", "auto"];
|
|
7
|
+
/**
|
|
8
|
+
* Who may change the mode (#656 D8), stated in both tool descriptions: the mode outlives the request
|
|
9
|
+
* that sets it, so a model that switched on its own would lift the user's choice for every later turn.
|
|
10
|
+
*/
|
|
11
|
+
export const MODE_RULE = "Set `mode` only when the user explicitly asks for that mode. Never set it to retry a refused approval " +
|
|
12
|
+
"or to leave plan mode on your own: ask the user first. plan = read-only; ask = no automatic approver, " +
|
|
13
|
+
"calls that need approval wait for a person; auto = the operator's policy as written. Leave it out to " +
|
|
14
|
+
"keep the conversation's mode.";
|
|
15
|
+
function isConversationMode(value) {
|
|
16
|
+
return typeof value === "string" && CONVERSATION_MODES.includes(value);
|
|
17
|
+
}
|
|
18
|
+
/** The `backbone:mode` input item (S1 D7); always appended last. */
|
|
19
|
+
export function modeItem(mode) {
|
|
20
|
+
return { type: "backbone:mode", mode };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The mode the response ran under (`conversation_mode`, #656). An absent key (a server before #656)
|
|
24
|
+
* and any value other than the three read as null, which also means "none set".
|
|
25
|
+
*/
|
|
26
|
+
export function responseMode(result) {
|
|
27
|
+
const raw = result?.conversation_mode;
|
|
28
|
+
return isConversationMode(raw) ? raw : null;
|
|
29
|
+
}
|
|
6
30
|
/** The assistant's text: every `output_text` part of every `message` item, in order. */
|
|
7
31
|
export function extractResponseText(result) {
|
|
8
32
|
const parts = [];
|
|
@@ -105,7 +129,7 @@ export function buildRunEnvelope(result, agentRef) {
|
|
|
105
129
|
: null;
|
|
106
130
|
return {
|
|
107
131
|
status,
|
|
108
|
-
mode:
|
|
132
|
+
mode: responseMode(result),
|
|
109
133
|
agent,
|
|
110
134
|
version,
|
|
111
135
|
responseId,
|
package/dist/tools/agents.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatErrorForMcp } from "../errors.js";
|
|
3
3
|
import { continueWithDecisions, fetchPendingApprovals, planDecisions, resolveAgentId, splitAgentRef } from "../lib/agent-decide.js";
|
|
4
|
-
import { buildRunEnvelope } from "../lib/agent-run.js";
|
|
4
|
+
import { buildRunEnvelope, MODE_RULE } from "../lib/agent-run.js";
|
|
5
5
|
const modelSchema = z
|
|
6
6
|
.string()
|
|
7
7
|
.min(1)
|
|
@@ -533,7 +533,8 @@ export function register(server, client) {
|
|
|
533
533
|
server.tool("2kw_decide_agent_approvals", "Answer the tool approvals a paused agent run is waiting for (a 2kw_create_response envelope with status requires_approval), then continue the run. "
|
|
534
534
|
+ "Decide only what the user decided: show them each pending approval (tool, arguments, policy class) first and never approve on your own. "
|
|
535
535
|
+ "Every pending approval of the response must be decided in this one call. Pass `agent` exactly as the run was started (keep '@label' and '#model'). "
|
|
536
|
-
+ "Returns the continuation's run envelope, which can pause again."
|
|
536
|
+
+ "Returns the continuation's run envelope, which can pause again."
|
|
537
|
+
+ " " + MODE_RULE, {
|
|
537
538
|
agent: z.string().min(1).describe("Agent id or name as the run used it: 'ref[@label][#model]'"),
|
|
538
539
|
responseId: z.string().min(1).describe("The paused response's id (envelope `responseId`)"),
|
|
539
540
|
decisions: z
|
|
@@ -548,7 +549,11 @@ export function register(server, client) {
|
|
|
548
549
|
decideAll: z.enum(["approve", "reject"]).optional().describe("Decide every pending approval the same way"),
|
|
549
550
|
reason: z.string().optional().describe("Only with `decideAll`: reason stored on every decision"),
|
|
550
551
|
remember: z.boolean().optional().describe("Only with `decideAll`: remember every approval for the conversation"),
|
|
551
|
-
|
|
552
|
+
mode: z
|
|
553
|
+
.enum(["plan", "ask", "auto"])
|
|
554
|
+
.optional()
|
|
555
|
+
.describe("The conversation mode this continuation and later turns run under. Only on the user's explicit request."),
|
|
556
|
+
}, async ({ agent, responseId, decisions, decideAll, reason, remember, mode }) => {
|
|
552
557
|
try {
|
|
553
558
|
if (decisions !== undefined && (reason !== undefined || remember !== undefined)) {
|
|
554
559
|
throw new Error("With `decisions`, give `reason` and `remember` per entry.");
|
|
@@ -557,7 +562,7 @@ export function register(server, client) {
|
|
|
557
562
|
const agentId = await resolveAgentId(client, name);
|
|
558
563
|
const pending = await fetchPendingApprovals(client, agentId, responseId);
|
|
559
564
|
const items = planDecisions(pending, { decisions, decideAll, reason, remember }, responseId);
|
|
560
|
-
const result = await continueWithDecisions(client, agentId, responseId, items, label, model);
|
|
565
|
+
const result = await continueWithDecisions(client, agentId, responseId, items, label, model, mode);
|
|
561
566
|
const envelope = buildRunEnvelope(result, agent);
|
|
562
567
|
return { content: [{ type: "text", text: JSON.stringify(envelope, null, 2) }] };
|
|
563
568
|
}
|
package/dist/tools/ai-gateway.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatErrorForMcp } from "../errors.js";
|
|
3
|
-
import { buildRunEnvelope } from "../lib/agent-run.js";
|
|
3
|
+
import { buildRunEnvelope, MODE_RULE, modeItem } from "../lib/agent-run.js";
|
|
4
4
|
/**
|
|
5
5
|
* The `model` value for an agent run: `agent/<ref>[@<label>]`, plus `#<model>` when the
|
|
6
6
|
* request switches to another entry of the version's `models` list (#626, spec #591 §4.1).
|
|
@@ -93,7 +93,7 @@ export function register(server, client) {
|
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
95
|
// ── create_response ─────────────────────────────────────────────────────
|
|
96
|
-
server.tool("2kw_create_response", "Send a request through Backbone's OpenAI-compatible OpenResponses endpoint (POST /v1/responses). Model format: 'provider/model' for a direct gateway call via `model`, or use `agent` to invoke a stored agent by id or name (optionally 'ref@label', e.g. 'support-bot@latest'). With `agent`, `agentModel` runs this one request on another model from the agent version's `models` list. With `agent`, the reply is the run envelope as JSON (status, text, tool calls, pending approvals, response id, next step) followed by the model that answered; answer a pause with 2kw_decide_agent_approvals. Always non-streaming.", {
|
|
96
|
+
server.tool("2kw_create_response", "Send a request through Backbone's OpenAI-compatible OpenResponses endpoint (POST /v1/responses). Model format: 'provider/model' for a direct gateway call via `model`, or use `agent` to invoke a stored agent by id or name (optionally 'ref@label', e.g. 'support-bot@latest'). With `agent`, `agentModel` runs this one request on another model from the agent version's `models` list. With `agent`, the reply is the run envelope as JSON (status, text, tool calls, pending approvals, response id, next step) followed by the model that answered; answer a pause with 2kw_decide_agent_approvals. Always non-streaming. " + MODE_RULE, {
|
|
97
97
|
input: z.string().min(1).describe("The input text (sent as a single user message)"),
|
|
98
98
|
agent: z
|
|
99
99
|
.string()
|
|
@@ -109,7 +109,11 @@ export function register(server, client) {
|
|
|
109
109
|
.optional()
|
|
110
110
|
.describe("Model identifier in 'provider/model' format. Mutually exclusive with `agent`."),
|
|
111
111
|
conversation: z.string().optional().describe("Conversation ID this response belongs to"),
|
|
112
|
-
|
|
112
|
+
mode: z
|
|
113
|
+
.enum(["plan", "ask", "auto"])
|
|
114
|
+
.optional()
|
|
115
|
+
.describe("Only with `agent`: the conversation mode for this and later turns (plan, ask or auto). Only on the user's explicit request."),
|
|
116
|
+
}, async ({ input, agent, agentModel, model, conversation, mode }) => {
|
|
113
117
|
try {
|
|
114
118
|
if (agent && model) {
|
|
115
119
|
throw new Error("Use either `agent` or `model`, not both.");
|
|
@@ -123,13 +127,16 @@ export function register(server, client) {
|
|
|
123
127
|
if (agentModel !== undefined && agent?.includes("#")) {
|
|
124
128
|
throw new Error("Give the model switch once: either 'ref#model' in `agent` or `agentModel`.");
|
|
125
129
|
}
|
|
130
|
+
if (mode !== undefined && !agent) {
|
|
131
|
+
throw new Error("`mode` applies to agent runs; it needs `agent`.");
|
|
132
|
+
}
|
|
126
133
|
const resolvedModel = agentModelReference(agent, agentModel) ?? model;
|
|
127
134
|
const body = {
|
|
128
135
|
model: resolvedModel,
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
input,
|
|
136
|
+
// Without `mode` the wire `input` is the bare string (shorthand for one user message; see
|
|
137
|
+
// ResponseItemInputDeserializer), which the generated type does not model. With it, the
|
|
138
|
+
// backbone:mode item follows the message (#656).
|
|
139
|
+
input: mode ? [{ type: "message", role: "user", content: input }, modeItem(mode)] : input,
|
|
133
140
|
stream: false,
|
|
134
141
|
...(conversation !== undefined && { conversation }),
|
|
135
142
|
};
|
|
@@ -134,6 +134,32 @@ export function register(server, client) {
|
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
});
|
|
137
|
+
// ── cancel_conversation_turn ─────────────────────────────────────────────
|
|
138
|
+
server.tool("2kw_cancel_conversation_turn", "Ask a running turn of a conversation to stop. turnId is the value the client sent as the Backbone-Turn-Id header on the responses call. The stop is accepted, not confirmed: the turn's own response reports status cancelled, or completed when the stop arrived too late.", {
|
|
139
|
+
conversationId: z.string().describe("The conversation ID"),
|
|
140
|
+
turnId: z
|
|
141
|
+
.string()
|
|
142
|
+
.regex(/^[A-Za-z0-9_-]{1,64}$/)
|
|
143
|
+
.describe("The turn id from the Backbone-Turn-Id header: 1-64 characters of A-Z, a-z, 0-9, '_' or '-'"),
|
|
144
|
+
}, async ({ conversationId, turnId }) => {
|
|
145
|
+
try {
|
|
146
|
+
await client.POST("/v1/conversations/{conversationId}/cancel", {
|
|
147
|
+
params: { path: { conversationId } },
|
|
148
|
+
body: { turn_id: turnId },
|
|
149
|
+
});
|
|
150
|
+
return {
|
|
151
|
+
content: [
|
|
152
|
+
{ type: "text", text: `Stop requested for turn ${turnId} of conversation ${conversationId}.` },
|
|
153
|
+
],
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return {
|
|
158
|
+
content: [{ type: "text", text: formatErrorForMcp(error) }],
|
|
159
|
+
isError: true,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
});
|
|
137
163
|
// ── list_conversation_items ──────────────────────────────────────────────
|
|
138
164
|
server.tool("2kw_list_conversation_items", "Fetch a conversation's items in replay order, oldest first.", { conversationId: z.string().describe("The conversation ID") }, async ({ conversationId }) => {
|
|
139
165
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2kw/ai-mcp-server",
|
|
3
|
-
"version": "6.3.0-dev.
|
|
3
|
+
"version": "6.3.0-dev.42",
|
|
4
4
|
"description": "MCP server for 2kw.ai — EU-hosted AI platform: OpenAI-compatible LLM gateway, schema-driven document extraction, transcription, agents with a knowledge base, and cost observability. 158 tools for Claude Code, Cursor, and Windsurf.",
|
|
5
5
|
"mcpName": "ai.2kw/mcp-server",
|
|
6
6
|
"keywords": [
|