@2kw/ai-mcp-server 6.3.0-dev.92 → 6.3.0-dev.96
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/overlay.d.ts +10 -0
- package/dist/lib/overlay.js +20 -0
- package/dist/tools/agents.js +15 -2
- package/dist/tools/datasets.js +10 -15
- package/dist/tools/experiments.js +22 -26
- package/dist/tools/prompts.js +10 -9
- package/dist/tools/schemas.js +14 -7
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The body for a PUT that replaces the whole row: the stored resource as its GET returned it, with
|
|
3
|
+
* every change the caller gave written over it. A change that is `undefined` was not given and keeps
|
|
4
|
+
* the stored value; the backend would store it as null otherwise (#1195). Server-managed fields in
|
|
5
|
+
* `current` (a dataset's or prompt's latestVersionId) go back as read, which is what keeps them.
|
|
6
|
+
*/
|
|
7
|
+
export declare function overlay<T extends object>(current: T, changes: Record<string, unknown>): T;
|
|
8
|
+
/** Refuses a name that is present but blank, which a full-replace PUT would otherwise store. */
|
|
9
|
+
export declare function assertNameNotBlank(name: string | undefined): void;
|
|
10
|
+
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The body for a PUT that replaces the whole row: the stored resource as its GET returned it, with
|
|
3
|
+
* every change the caller gave written over it. A change that is `undefined` was not given and keeps
|
|
4
|
+
* the stored value; the backend would store it as null otherwise (#1195). Server-managed fields in
|
|
5
|
+
* `current` (a dataset's or prompt's latestVersionId) go back as read, which is what keeps them.
|
|
6
|
+
*/
|
|
7
|
+
export function overlay(current, changes) {
|
|
8
|
+
const body = { ...current };
|
|
9
|
+
for (const [key, value] of Object.entries(changes)) {
|
|
10
|
+
if (value !== undefined)
|
|
11
|
+
body[key] = value;
|
|
12
|
+
}
|
|
13
|
+
return body;
|
|
14
|
+
}
|
|
15
|
+
/** Refuses a name that is present but blank, which a full-replace PUT would otherwise store. */
|
|
16
|
+
export function assertNameNotBlank(name) {
|
|
17
|
+
if (name !== undefined && !name.trim())
|
|
18
|
+
throw new Error("name must not be empty.");
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=overlay.js.map
|
package/dist/tools/agents.js
CHANGED
|
@@ -2,6 +2,7 @@ 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
4
|
import { buildRunEnvelope, chatUrlFor, MODE_RULE } from "../lib/agent-run.js";
|
|
5
|
+
import { assertNameNotBlank } from "../lib/overlay.js";
|
|
5
6
|
const modelSchema = z
|
|
6
7
|
.string()
|
|
7
8
|
.min(1)
|
|
@@ -159,9 +160,9 @@ export function register(server, client) {
|
|
|
159
160
|
}
|
|
160
161
|
});
|
|
161
162
|
// ── update_agent ────────────────────────────────────────────────────────
|
|
162
|
-
server.tool("2kw_update_agent", "Update an agent's metadata and configuration. name
|
|
163
|
+
server.tool("2kw_update_agent", "Update an agent's metadata and configuration. Omitted fields keep their current values (the tool reads the agent's name and description first when either is omitted). `model` or `models` replaces the stored model list (a shorter list leaves no tail); omit both to keep the current models. Does not create a version.", {
|
|
163
164
|
agentId: z.string().describe("The agent ID"),
|
|
164
|
-
name: z.string().
|
|
165
|
+
name: z.string().optional().describe("New agent name"),
|
|
165
166
|
model: modelSchema.optional(),
|
|
166
167
|
models: modelsSchema.optional(),
|
|
167
168
|
description: z.string().optional().describe("Agent description"),
|
|
@@ -172,6 +173,18 @@ export function register(server, client) {
|
|
|
172
173
|
hitlPolicy: z.unknown().optional().describe("Human-in-the-loop approval policy (JSON object)"),
|
|
173
174
|
}, async ({ agentId, name, model, models, description, instructions, options, tools, skills, hitlPolicy }) => {
|
|
174
175
|
try {
|
|
176
|
+
assertNameNotBlank(name);
|
|
177
|
+
// PUT /v1/agents/{id} keeps the configuration fields a body omits, but name is required and
|
|
178
|
+
// description is written as sent, so an omitted description would be cleared (#1195).
|
|
179
|
+
if (name === undefined || description === undefined) {
|
|
180
|
+
const { data: current } = await client.GET("/v1/agents/{id}", {
|
|
181
|
+
params: { path: { id: agentId } },
|
|
182
|
+
});
|
|
183
|
+
if (!current)
|
|
184
|
+
throw new Error(`Agent ${agentId} could not be read.`);
|
|
185
|
+
name ??= current.name;
|
|
186
|
+
description ??= current.description;
|
|
187
|
+
}
|
|
175
188
|
const body = { name, ...modelFields(model, models, false) };
|
|
176
189
|
if (description !== undefined)
|
|
177
190
|
body.description = description;
|
package/dist/tools/datasets.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatErrorForMcp } from "../errors.js";
|
|
3
|
+
import { assertNameNotBlank, overlay } from "../lib/overlay.js";
|
|
3
4
|
async function resolveLatestVersionId(client, datasetId) {
|
|
4
5
|
const { data } = await client.GET("/v1/datasets/{id}/versions/latest", { params: { path: { id: datasetId } } });
|
|
5
6
|
const versionId = data?.id;
|
|
@@ -104,7 +105,7 @@ export function register(server, client) {
|
|
|
104
105
|
}
|
|
105
106
|
});
|
|
106
107
|
// ── update_dataset ─────────────────────────────────────────────
|
|
107
|
-
server.tool("2kw_update_dataset", "Update an existing dataset's details.", {
|
|
108
|
+
server.tool("2kw_update_dataset", "Update an existing dataset's details. Omitted fields keep their current values (the tool reads the dataset first).", {
|
|
108
109
|
datasetId: z.string().describe("The dataset ID"),
|
|
109
110
|
name: z.string().optional().describe("New name"),
|
|
110
111
|
description: z.string().optional().describe("New description"),
|
|
@@ -114,22 +115,16 @@ export function register(server, client) {
|
|
|
114
115
|
metadata: z.unknown().optional().describe("Arbitrary metadata (JSON object)"),
|
|
115
116
|
}, async ({ datasetId, name, description, type, inputSchema, expectedOutputSchema, metadata }) => {
|
|
116
117
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
if (inputSchema !== undefined)
|
|
125
|
-
body.inputSchema = inputSchema;
|
|
126
|
-
if (expectedOutputSchema !== undefined)
|
|
127
|
-
body.expectedOutputSchema = expectedOutputSchema;
|
|
128
|
-
if (metadata !== undefined)
|
|
129
|
-
body.metadata = metadata;
|
|
118
|
+
assertNameNotBlank(name);
|
|
119
|
+
// PUT /v1/datasets/{id} replaces the whole dataset, so an omitted field would be stored as null.
|
|
120
|
+
const { data: current } = await client.GET("/v1/datasets/{id}", {
|
|
121
|
+
params: { path: { id: datasetId } },
|
|
122
|
+
});
|
|
123
|
+
if (!current)
|
|
124
|
+
throw new Error(`Dataset ${datasetId} could not be read.`);
|
|
130
125
|
const { data } = await client.PUT("/v1/datasets/{id}", {
|
|
131
126
|
params: { path: { id: datasetId } },
|
|
132
|
-
body:
|
|
127
|
+
body: overlay(current, { name, description, type, inputSchema, expectedOutputSchema, metadata }),
|
|
133
128
|
});
|
|
134
129
|
return {
|
|
135
130
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatErrorForMcp } from "../errors.js";
|
|
3
|
+
import { assertNameNotBlank, overlay } from "../lib/overlay.js";
|
|
3
4
|
export function register(server, client) {
|
|
4
5
|
// ── list_experiments ────────────────────────────────────────────
|
|
5
6
|
server.tool("2kw_list_experiments", "List experiments in the organization with optional search, status filter, and pagination.", {
|
|
@@ -99,7 +100,7 @@ export function register(server, client) {
|
|
|
99
100
|
}
|
|
100
101
|
});
|
|
101
102
|
// ── update_experiment ───────────────────────────────────────────
|
|
102
|
-
server.tool("2kw_update_experiment", "Update an existing experiment's details.", {
|
|
103
|
+
server.tool("2kw_update_experiment", "Update an existing experiment's details. Omitted fields keep their current values (the tool reads the experiment first).", {
|
|
103
104
|
experimentId: z.string().describe("The experiment ID"),
|
|
104
105
|
name: z.string().optional().describe("New name"),
|
|
105
106
|
description: z.string().optional().describe("New description"),
|
|
@@ -108,20 +109,16 @@ export function register(server, client) {
|
|
|
108
109
|
metadata: z.unknown().optional().describe("Arbitrary metadata (JSON object)"),
|
|
109
110
|
}, async ({ experimentId, name, description, type, datasetVersionId, metadata }) => {
|
|
110
111
|
try {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
if (datasetVersionId !== undefined)
|
|
119
|
-
body.datasetVersionId = datasetVersionId;
|
|
120
|
-
if (metadata !== undefined)
|
|
121
|
-
body.metadata = metadata;
|
|
112
|
+
assertNameNotBlank(name);
|
|
113
|
+
// PUT /v1/experiments/{id} replaces the whole experiment, so an omitted field would be stored as null.
|
|
114
|
+
const { data: current } = await client.GET("/v1/experiments/{id}", {
|
|
115
|
+
params: { path: { id: experimentId } },
|
|
116
|
+
});
|
|
117
|
+
if (!current)
|
|
118
|
+
throw new Error(`Experiment ${experimentId} could not be read.`);
|
|
122
119
|
const { data } = await client.PUT("/v1/experiments/{id}", {
|
|
123
120
|
params: { path: { id: experimentId } },
|
|
124
|
-
body:
|
|
121
|
+
body: overlay(current, { name, description, type, datasetVersionId, metadata }),
|
|
125
122
|
});
|
|
126
123
|
return {
|
|
127
124
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
@@ -210,7 +207,7 @@ export function register(server, client) {
|
|
|
210
207
|
}
|
|
211
208
|
});
|
|
212
209
|
// ── update_variant ──────────────────────────────────────────────
|
|
213
|
-
server.tool("2kw_update_variant", "Update an existing variant's details.", {
|
|
210
|
+
server.tool("2kw_update_variant", "Update an existing variant's details. Omitted fields keep their current values (the tool reads the variant first).", {
|
|
214
211
|
experimentId: z.string().describe("The experiment ID"),
|
|
215
212
|
variantId: z.string().describe("The variant ID"),
|
|
216
213
|
name: z.string().optional().describe("New variant name"),
|
|
@@ -220,20 +217,19 @@ export function register(server, client) {
|
|
|
220
217
|
sortOrder: z.number().optional().describe("New sort order for display"),
|
|
221
218
|
}, async ({ experimentId, variantId, name, taskType, configuration, description, sortOrder }) => {
|
|
222
219
|
try {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
body.sortOrder = sortOrder;
|
|
220
|
+
assertNameNotBlank(name);
|
|
221
|
+
// PUT .../variants/{variantId} writes name, description and configuration as sent, so an
|
|
222
|
+
// omitted one would be cleared. There is no single-variant GET: read it from the list. The
|
|
223
|
+
// read's version goes back with the body, so a change made since is refused with 409.
|
|
224
|
+
const { data: variants } = await client.GET("/v1/experiments/{id}/variants", {
|
|
225
|
+
params: { path: { id: experimentId } },
|
|
226
|
+
});
|
|
227
|
+
const current = variants?.find((v) => v.id === variantId);
|
|
228
|
+
if (!current)
|
|
229
|
+
throw new Error(`Variant ${variantId} is not a variant of experiment ${experimentId}.`);
|
|
234
230
|
const { data } = await client.PUT("/v1/experiments/{id}/variants/{variantId}", {
|
|
235
231
|
params: { path: { id: experimentId, variantId } },
|
|
236
|
-
body:
|
|
232
|
+
body: overlay(current, { name, taskType, configuration, description, sortOrder }),
|
|
237
233
|
});
|
|
238
234
|
return {
|
|
239
235
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
package/dist/tools/prompts.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { formatErrorForMcp } from "../errors.js";
|
|
3
|
+
import { assertNameNotBlank, overlay } from "../lib/overlay.js";
|
|
3
4
|
export function register(server, client) {
|
|
4
5
|
// ── prompts ─────────────────────────────────────────────────────────────
|
|
5
6
|
server.tool("2kw_list_prompts", "List org prompts with optional name/type filter and pagination.", {
|
|
@@ -74,23 +75,23 @@ export function register(server, client) {
|
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
});
|
|
77
|
-
server.tool("2kw_update_prompt", "Update prompt metadata (name, description, type).", {
|
|
78
|
+
server.tool("2kw_update_prompt", "Update prompt metadata (name, description, type). Omitted fields keep their current values (the tool reads the prompt first).", {
|
|
78
79
|
promptId: z.string(),
|
|
79
80
|
name: z.string().optional(),
|
|
80
81
|
description: z.string().optional(),
|
|
81
82
|
type: z.enum(["TEXT", "CHAT"]).optional(),
|
|
82
83
|
}, async (params) => {
|
|
83
84
|
try {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (
|
|
90
|
-
|
|
85
|
+
assertNameNotBlank(params.name);
|
|
86
|
+
// PUT /v1/prompts/{id} replaces the whole prompt, so an omitted field would be stored as null.
|
|
87
|
+
const { data: current } = await client.GET("/v1/prompts/{id}", {
|
|
88
|
+
params: { path: { id: params.promptId } },
|
|
89
|
+
});
|
|
90
|
+
if (!current)
|
|
91
|
+
throw new Error(`Prompt ${params.promptId} could not be read.`);
|
|
91
92
|
const { data } = await client.PUT("/v1/prompts/{id}", {
|
|
92
93
|
params: { path: { id: params.promptId } },
|
|
93
|
-
body:
|
|
94
|
+
body: overlay(current, { name: params.name, description: params.description, type: params.type }),
|
|
94
95
|
});
|
|
95
96
|
return {
|
|
96
97
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
package/dist/tools/schemas.js
CHANGED
|
@@ -85,20 +85,27 @@ export function register(server, client) {
|
|
|
85
85
|
}
|
|
86
86
|
});
|
|
87
87
|
// ── update_schema ───────────────────────────────────────────────────────
|
|
88
|
-
server.tool("2kw_update_schema", "Update an existing schema.", {
|
|
88
|
+
server.tool("2kw_update_schema", "Update an existing schema. Omitted fields keep their current values (the tool reads the schema first).", {
|
|
89
89
|
schemaId: z.string().describe("The schema ID"),
|
|
90
90
|
name: z.string().optional().describe("New schema name"),
|
|
91
91
|
description: z.string().optional().describe("New schema description"),
|
|
92
92
|
}, async ({ schemaId, name, description }) => {
|
|
93
93
|
try {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
if (description
|
|
98
|
-
|
|
94
|
+
if (name !== undefined && !name.trim())
|
|
95
|
+
throw new Error("name must not be empty.");
|
|
96
|
+
// PUT /v1/schemas/{id} replaces the whole schema, so an omitted field would be stored as null.
|
|
97
|
+
if (name === undefined || description === undefined) {
|
|
98
|
+
const { data: current } = await client.GET("/v1/schemas/{id}", {
|
|
99
|
+
params: { path: { id: schemaId } },
|
|
100
|
+
});
|
|
101
|
+
if (!current)
|
|
102
|
+
throw new Error(`Schema ${schemaId} could not be read.`);
|
|
103
|
+
name ??= current.name;
|
|
104
|
+
description ??= current.description;
|
|
105
|
+
}
|
|
99
106
|
const { data } = await client.PUT("/v1/schemas/{id}", {
|
|
100
107
|
params: { path: { id: schemaId } },
|
|
101
|
-
body:
|
|
108
|
+
body: { name, description },
|
|
102
109
|
});
|
|
103
110
|
return {
|
|
104
111
|
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
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.96",
|
|
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": [
|