@2kw/ai-mcp-server 7.1.0-dev.2 → 7.1.0-dev.7
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-run.js +7 -9
- package/dist/tools/ai-gateway.js +25 -4
- package/package.json +1 -1
package/dist/lib/agent-run.js
CHANGED
|
@@ -78,7 +78,8 @@ function parseArguments(raw) {
|
|
|
78
78
|
/**
|
|
79
79
|
* A connector tool's open approval, decoded from the standard `mcp_approval_request` item (#949). The item
|
|
80
80
|
* is the MCP twin of `backbone:approval_request` (#805): it names the server and the tool, carries no call
|
|
81
|
-
* id
|
|
81
|
+
* id or status (its policy class is an optional extension, #1084; "unknown" without it), and exists only
|
|
82
|
+
* while the decision is open, so its id doubles as the call id
|
|
82
83
|
* and the tool is named as the approval row stores it, `mcp__<server_label>__<tool>`.
|
|
83
84
|
*/
|
|
84
85
|
function connectorApproval(i) {
|
|
@@ -87,7 +88,7 @@ function connectorApproval(i) {
|
|
|
87
88
|
callId: String(i.id),
|
|
88
89
|
tool: `mcp__${String(i.server_label)}__${String(i.name)}`,
|
|
89
90
|
arguments: parseArguments(i.arguments),
|
|
90
|
-
policyClass: "unknown",
|
|
91
|
+
policyClass: typeof i.policy_class === "string" ? i.policy_class : "unknown",
|
|
91
92
|
reason: null,
|
|
92
93
|
preview: null,
|
|
93
94
|
};
|
|
@@ -274,15 +275,12 @@ export function buildRunEnvelope(result, agentRef, chatUrl = DEFAULT_CHAT_URL) {
|
|
|
274
275
|
}
|
|
275
276
|
else if (status === "requires_tool_output" && pendingConnections.length > 0 && pendingToolCalls.length === 0 && responseId) {
|
|
276
277
|
// A connector-only pause (no relayed call, so 2kw_decide_agent_approvals has nothing to decide,
|
|
277
|
-
// spec M11)
|
|
278
|
-
// conversation (the gateway answers 400)
|
|
279
|
-
// that continues by response id (#1254 review: continueWithDecisions does send previous_response_id
|
|
280
|
-
// for every other pause this server can decide).
|
|
278
|
+
// spec M11) is continued by id with 2kw_create_response `previousResponseId` (#1065); a connect
|
|
279
|
+
// pause cannot be continued by conversation (the gateway answers 400).
|
|
281
280
|
next =
|
|
282
281
|
`Tell the user to ${connectionsPhrase(pendingConnections)} in ${chatUrl}/connectors. ` +
|
|
283
|
-
`
|
|
284
|
-
|
|
285
|
-
"or continue the response by its id from n8n (Previous Response ID) or any client that sends previous_response_id.";
|
|
282
|
+
`Once they have connected, call 2kw_create_response with agent ${JSON.stringify(ref)} and previousResponseId ` +
|
|
283
|
+
`${JSON.stringify(responseId)} and an empty input (not \`conversation\`: a connect pause cannot be continued by conversation).`;
|
|
286
284
|
}
|
|
287
285
|
else if (status === "requires_tool_output" && pendingConnections.length === 0 && pendingToolCalls.length === 0 && responseId) {
|
|
288
286
|
// Paused on nothing the envelope decodes: say so, rather than hand back a pause with no way on.
|
package/dist/tools/ai-gateway.js
CHANGED
|
@@ -92,9 +92,23 @@ export function register(server, client) {
|
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
|
+
// An empty input resumes a paused response (#1065): the wire takes `[]`, not an empty message.
|
|
96
|
+
const requestInput = (input, mode) => {
|
|
97
|
+
const message = input === "" ? [] : [{ type: "message", role: "user", content: input }];
|
|
98
|
+
if (mode)
|
|
99
|
+
return [...message, modeItem(mode)];
|
|
100
|
+
return input === "" ? [] : input;
|
|
101
|
+
};
|
|
95
102
|
// ── 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 an approval pause with 2kw_decide_agent_approvals. A run paused on a relayed (client-side) tool call comes back with status requires_tool_output and `pendingToolCalls`; answer it with 2kw_decide_agent_approvals `outputs`. A run that needs the user's own sign-in to a connector pauses with status requires_tool_output and `pendingConnections` (label, host, reason): no tool here can answer that; tell the user to connect it in chat.2kw.ai → Connectors, as the envelope's `next` says. A run where a connector asks the user a question pauses with status requires_tool_output and `pendingInputs` (label, tool, mode, answerUrl): the user answers it in chat at `answerUrl`, not here. Always non-streaming. " + MODE_RULE, {
|
|
97
|
-
input: z
|
|
103
|
+
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 an approval pause with 2kw_decide_agent_approvals. A run paused on a relayed (client-side) tool call comes back with status requires_tool_output and `pendingToolCalls`; answer it with 2kw_decide_agent_approvals `outputs`. A run that needs the user's own sign-in to a connector pauses with status requires_tool_output and `pendingConnections` (label, host, reason): no tool here can answer that; tell the user to connect it in chat.2kw.ai → Connectors, as the envelope's `next` says; once they have, resume it with 2kw_create_response `previousResponseId` (and an empty `input`). A run where a connector asks the user a question pauses with status requires_tool_output and `pendingInputs` (label, tool, mode, answerUrl): the user answers it in chat at `answerUrl`, not here. Always non-streaming. " + MODE_RULE, {
|
|
104
|
+
input: z
|
|
105
|
+
.string()
|
|
106
|
+
.describe("The input text (sent as a single user message). May be empty only with `previousResponseId`, to resume a paused response without a new message."),
|
|
107
|
+
previousResponseId: z
|
|
108
|
+
.string()
|
|
109
|
+
.min(1)
|
|
110
|
+
.optional()
|
|
111
|
+
.describe("Resume this paused response (sent as previous_response_id), e.g. after the user connected a connector in chat.2kw.ai → Connectors. Use the `responseId` from the pause envelope."),
|
|
98
112
|
agent: z
|
|
99
113
|
.string()
|
|
100
114
|
.optional()
|
|
@@ -113,8 +127,14 @@ export function register(server, client) {
|
|
|
113
127
|
.enum(["plan", "ask", "auto"])
|
|
114
128
|
.optional()
|
|
115
129
|
.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 }) => {
|
|
130
|
+
}, async ({ input, previousResponseId, agent, agentModel, model, conversation, mode }) => {
|
|
117
131
|
try {
|
|
132
|
+
if (input === "" && previousResponseId === undefined) {
|
|
133
|
+
throw new Error("`input` is empty; it may only be empty with `previousResponseId`.");
|
|
134
|
+
}
|
|
135
|
+
if (input === "" && mode !== undefined) {
|
|
136
|
+
throw new Error("`mode` needs a non-empty `input`: an empty input only resumes a paused response.");
|
|
137
|
+
}
|
|
118
138
|
if (agent && model) {
|
|
119
139
|
throw new Error("Use either `agent` or `model`, not both.");
|
|
120
140
|
}
|
|
@@ -136,8 +156,9 @@ export function register(server, client) {
|
|
|
136
156
|
// Without `mode` the wire `input` is the bare string (shorthand for one user message; see
|
|
137
157
|
// ResponseItemInputDeserializer). With it, the backbone:mode item follows the message (#656);
|
|
138
158
|
// that literal item array is what the cast below is for.
|
|
139
|
-
input:
|
|
159
|
+
input: requestInput(input, mode),
|
|
140
160
|
stream: false,
|
|
161
|
+
...(previousResponseId !== undefined && { previous_response_id: previousResponseId }),
|
|
141
162
|
...(conversation !== undefined && { conversation }),
|
|
142
163
|
};
|
|
143
164
|
const { data } = await client.POST("/v1/responses", {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@2kw/ai-mcp-server",
|
|
3
|
-
"version": "7.1.0-dev.
|
|
3
|
+
"version": "7.1.0-dev.7",
|
|
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. 199 tools for Claude Code, Cursor, and Windsurf.",
|
|
5
5
|
"mcpName": "ai.2kw/mcp-server",
|
|
6
6
|
"keywords": [
|