@2kw/ai-mcp-server 6.3.0-dev.13 → 6.3.0-dev.131
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/README.md +3 -1
- package/dist/client.d.ts +9 -2
- package/dist/client.js +15 -1
- package/dist/index.js +5 -53
- package/dist/lib/agent-decide.d.ts +94 -0
- package/dist/lib/agent-decide.js +216 -0
- package/dist/lib/agent-run.d.ts +125 -0
- package/dist/lib/agent-run.js +259 -0
- package/dist/lib/connect-pause.d.ts +71 -0
- package/dist/lib/connect-pause.js +149 -0
- package/dist/lib/overlay.d.ts +10 -0
- package/dist/lib/overlay.js +20 -0
- package/dist/tools/agents.js +118 -4
- package/dist/tools/ai-gateway.js +25 -11
- package/dist/tools/conversations.js +26 -0
- package/dist/tools/datasets.js +10 -15
- package/dist/tools/experiments.js +24 -28
- package/dist/tools/index.d.ts +21 -0
- package/dist/tools/index.js +190 -0
- package/dist/tools/knowledge.js +41 -23
- package/dist/tools/memory.d.ts +9 -0
- package/dist/tools/memory.js +43 -0
- package/dist/tools/prompts.js +10 -9
- package/dist/tools/schemas.js +14 -7
- package/dist/tools/tracing.js +21 -8
- package/package.json +5 -1
package/dist/tools/knowledge.js
CHANGED
|
@@ -1,10 +1,42 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { basename } from "node:path";
|
|
4
|
-
import {
|
|
4
|
+
import { formatErrorForMcp } from "../errors.js";
|
|
5
5
|
import { getMimeType } from "../mime.js";
|
|
6
6
|
const ChunkingStrategy = z.enum(["AUTO", "FIXED", "HIERARCHICAL", "CUSTOM"]);
|
|
7
7
|
const DocumentStatus = z.enum(["PENDING", "PARSING", "CHUNKING", "EMBEDDING", "READY", "ERROR"]);
|
|
8
|
+
/** The 29 Postgres text-search configurations the backend's TextSearchLanguage enum accepts (#1249). */
|
|
9
|
+
const TEXT_SEARCH_LANGUAGES = [
|
|
10
|
+
"arabic",
|
|
11
|
+
"armenian",
|
|
12
|
+
"basque",
|
|
13
|
+
"catalan",
|
|
14
|
+
"danish",
|
|
15
|
+
"dutch",
|
|
16
|
+
"english",
|
|
17
|
+
"finnish",
|
|
18
|
+
"french",
|
|
19
|
+
"german",
|
|
20
|
+
"greek",
|
|
21
|
+
"hindi",
|
|
22
|
+
"hungarian",
|
|
23
|
+
"indonesian",
|
|
24
|
+
"irish",
|
|
25
|
+
"italian",
|
|
26
|
+
"lithuanian",
|
|
27
|
+
"nepali",
|
|
28
|
+
"norwegian",
|
|
29
|
+
"portuguese",
|
|
30
|
+
"romanian",
|
|
31
|
+
"russian",
|
|
32
|
+
"serbian",
|
|
33
|
+
"simple",
|
|
34
|
+
"spanish",
|
|
35
|
+
"swedish",
|
|
36
|
+
"tamil",
|
|
37
|
+
"turkish",
|
|
38
|
+
"yiddish",
|
|
39
|
+
];
|
|
8
40
|
/**
|
|
9
41
|
* Fields shared by knowledge-base create and update — the backend treats
|
|
10
42
|
* every one of these as mandatory on both requests (name, slug,
|
|
@@ -24,6 +56,7 @@ const knowledgeBaseFields = {
|
|
|
24
56
|
parentChunkSize: z.number().int().min(100).max(8000).optional().describe("Parent chunk size for hierarchical chunking (100-8000), must exceed chunkSize"),
|
|
25
57
|
hybridSearchEnabled: z.boolean().optional().describe("Enable hybrid dense + lexical search"),
|
|
26
58
|
rerankerProviderId: z.string().optional().describe("Provider id used for reranking"),
|
|
59
|
+
textSearchLanguage: z.enum(TEXT_SEARCH_LANGUAGES).optional().describe("Text-search language for the lexical leg (default simple on create). On update, omit to leave the active and any pending language untouched — resending the current value would cancel a reindex in progress; changing it starts one in the background."),
|
|
27
60
|
};
|
|
28
61
|
function buildKnowledgeBaseBody(params) {
|
|
29
62
|
const body = {
|
|
@@ -41,6 +74,7 @@ function buildKnowledgeBaseBody(params) {
|
|
|
41
74
|
"parentChunkSize",
|
|
42
75
|
"hybridSearchEnabled",
|
|
43
76
|
"rerankerProviderId",
|
|
77
|
+
"textSearchLanguage",
|
|
44
78
|
]) {
|
|
45
79
|
if (params[key] !== undefined)
|
|
46
80
|
body[key] = params[key];
|
|
@@ -203,29 +237,13 @@ export function register(server, client) {
|
|
|
203
237
|
const filename = basename(filePath);
|
|
204
238
|
formData.append("files", new Blob([buffer], { type: getMimeType(filename) }), filename);
|
|
205
239
|
}
|
|
206
|
-
const {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
240
|
+
const { data } = await client.POST("/v1/knowledge-bases/{knowledgeBaseId}/documents", {
|
|
241
|
+
params: { path: { knowledgeBaseId } },
|
|
242
|
+
// Provide placeholder body for type checking; actual payload comes from bodySerializer
|
|
243
|
+
body: { files: [] },
|
|
244
|
+
bodySerializer: () => formData,
|
|
211
245
|
});
|
|
212
|
-
|
|
213
|
-
let body;
|
|
214
|
-
try {
|
|
215
|
-
body = await res.json();
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
body = {
|
|
219
|
-
error: res.statusText,
|
|
220
|
-
message: `HTTP ${res.status}: ${res.statusText}`,
|
|
221
|
-
status: res.status,
|
|
222
|
-
timestamp: new Date().toISOString(),
|
|
223
|
-
};
|
|
224
|
-
}
|
|
225
|
-
throw new BackboneApiError(body);
|
|
226
|
-
}
|
|
227
|
-
const data = (await res.json());
|
|
228
|
-
const lines = data.map((r) => r.error
|
|
246
|
+
const lines = (data ?? []).map((r) => r.error
|
|
229
247
|
? `- ${r.filename}: rejected — ${r.error}`
|
|
230
248
|
: `- ${r.filename}: accepted, documentId=${r.upload?.document?.id}, versionId=${r.upload?.acceptedVersion?.id}, status=${r.upload?.acceptedVersion?.status}`);
|
|
231
249
|
return {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import type { ApiClient } from "../client.js";
|
|
3
|
+
/**
|
|
4
|
+
* Agent memory of organization members (agent memory spec §7.1, §7.4). The "my" tools act on
|
|
5
|
+
* the API key owner's own memory; the member tools are for ADMIN and OWNER keys and never
|
|
6
|
+
* show paths or content.
|
|
7
|
+
*/
|
|
8
|
+
export declare function register(server: McpServer, client: ApiClient): void;
|
|
9
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { formatErrorForMcp } from "../errors.js";
|
|
3
|
+
async function reply(action) {
|
|
4
|
+
try {
|
|
5
|
+
const data = await action();
|
|
6
|
+
return {
|
|
7
|
+
content: [{ type: "text", text: data === undefined ? "Done." : JSON.stringify(data, null, 2) }],
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
return { content: [{ type: "text", text: formatErrorForMcp(error) }], isError: true };
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const path = z.string().min(1).describe("Memory path, /memories or below, e.g. /memories/preferences.md");
|
|
15
|
+
/**
|
|
16
|
+
* Agent memory of organization members (agent memory spec §7.1, §7.4). The "my" tools act on
|
|
17
|
+
* the API key owner's own memory; the member tools are for ADMIN and OWNER keys and never
|
|
18
|
+
* show paths or content.
|
|
19
|
+
*/
|
|
20
|
+
export function register(server, client) {
|
|
21
|
+
server.tool("2kw_list_my_memory_files", "List the files in your agent memory (the API key owner's), ordered by path, without content.", {}, async () => reply(async () => (await client.GET("/v1/memories/me/files")).data));
|
|
22
|
+
server.tool("2kw_read_my_memory_file", "Read one of your agent memory files: the raw content, its version (for ifMatchVersion) and which agent wrote it last.", { path }, async ({ path }) => reply(async () => (await client.GET("/v1/memories/me/file", { params: { query: { path } } })).data));
|
|
23
|
+
server.tool("2kw_write_my_memory_file", "Create or replace one of your agent memory files with the complete content. Pass ifMatchVersion (the version from 2kw_read_my_memory_file) to refuse the write if the file changed since. Credentials are refused; files are limited to 32 KB.", {
|
|
24
|
+
path,
|
|
25
|
+
content: z.string().describe("The complete UTF-8 text content of the file"),
|
|
26
|
+
ifMatchVersion: z.number().int().min(0).optional().describe("Only replace the file if it still has this version"),
|
|
27
|
+
}, async ({ path, content, ifMatchVersion }) => reply(async () => (await client.PUT("/v1/memories/me/file", {
|
|
28
|
+
params: { query: { path } },
|
|
29
|
+
body: { content },
|
|
30
|
+
headers: ifMatchVersion === undefined ? undefined : { "If-Match": `"${ifMatchVersion}"` },
|
|
31
|
+
})).data));
|
|
32
|
+
server.tool("2kw_delete_my_memory_file", "Delete one of your agent memory files, or a directory with everything below it. Irreversible.", { path }, async ({ path }) => reply(async () => {
|
|
33
|
+
await client.DELETE("/v1/memories/me/file", { params: { query: { path } } });
|
|
34
|
+
}));
|
|
35
|
+
server.tool("2kw_forget_my_memory", "Delete your whole agent memory. Irreversible: confirm with the user before calling.", {}, async () => reply(async () => {
|
|
36
|
+
await client.DELETE("/v1/memories/me");
|
|
37
|
+
}));
|
|
38
|
+
server.tool("2kw_list_member_memory_usage", "Agent memory totals per organization member: file count, total bytes and last write. Never paths or content. Needs an ADMIN or OWNER key.", {}, async () => reply(async () => (await client.GET("/v1/memories/users")).data));
|
|
39
|
+
server.tool("2kw_erase_member_memory", "Erase one member's whole agent memory in this organization. Irreversible: confirm with the user before calling. Needs an ADMIN or OWNER key.", { userId: z.string().min(1).describe("The member's user id, from 2kw_list_member_memory_usage") }, async ({ userId }) => reply(async () => {
|
|
40
|
+
await client.DELETE("/v1/memories/users/{userId}", { params: { path: { userId } } });
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=memory.js.map
|
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/dist/tools/tracing.js
CHANGED
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { formatErrorForMcp } from "../errors.js";
|
|
3
3
|
export function register(server, client) {
|
|
4
4
|
// ── settings ────────────────────────────────────────────────────────────
|
|
5
|
-
server.tool("2kw_get_tracing_settings", "Return the org's tracing settings
|
|
5
|
+
server.tool("2kw_get_tracing_settings", "Return the org's tracing settings: includePrompts / includeCompletions (whether raw prompt and completion content are persisted on ingested spans; false by default) and trace retention: retentionDays (the org's choice, null = follow the plan), planRetentionDays and effectiveRetentionDays.", {}, async () => {
|
|
6
6
|
try {
|
|
7
7
|
const { data } = await client.GET("/v1/tracing/settings");
|
|
8
8
|
return {
|
|
@@ -16,7 +16,7 @@ export function register(server, client) {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
|
-
server.tool("2kw_update_tracing_settings", "Update the org's tracing settings. Admin-only. Omit a field to leave it at its current value — the tool reads the current setting first and merges.", {
|
|
19
|
+
server.tool("2kw_update_tracing_settings", "Update the org's tracing settings. Admin-only. Omit a field to leave it at its current value — the tool reads the current setting first and merges. Shortening retentionDays deletes older traces at the next daily sweep.", {
|
|
20
20
|
includePrompts: z
|
|
21
21
|
.boolean()
|
|
22
22
|
.optional()
|
|
@@ -25,12 +25,20 @@ export function register(server, client) {
|
|
|
25
25
|
.boolean()
|
|
26
26
|
.optional()
|
|
27
27
|
.describe("Retain completion content on ingested spans (default false)"),
|
|
28
|
+
retentionDays: z
|
|
29
|
+
.number()
|
|
30
|
+
.int()
|
|
31
|
+
.min(1)
|
|
32
|
+
.nullable()
|
|
33
|
+
.optional()
|
|
34
|
+
.describe("Trace retention in days, at most the plan's limit (planRetentionDays). null follows the plan. Shortening deletes older traces at the next daily sweep."),
|
|
28
35
|
}, async (params) => {
|
|
29
36
|
try {
|
|
30
37
|
const { data: current } = await client.GET("/v1/tracing/settings");
|
|
31
38
|
const merged = {
|
|
32
39
|
includePrompts: params.includePrompts ?? current?.includePrompts ?? false,
|
|
33
40
|
includeCompletions: params.includeCompletions ?? current?.includeCompletions ?? false,
|
|
41
|
+
retentionDays: params.retentionDays !== undefined ? params.retentionDays : (current?.retentionDays ?? null),
|
|
34
42
|
};
|
|
35
43
|
const { data } = await client.PUT("/v1/tracing/settings", {
|
|
36
44
|
body: merged,
|
|
@@ -121,8 +129,12 @@ export function register(server, client) {
|
|
|
121
129
|
}
|
|
122
130
|
});
|
|
123
131
|
// ── sessions ────────────────────────────────────────────────────────────
|
|
124
|
-
server.tool("2kw_list_trace_sessions", "List sessions (spans grouped by an exporter-stamped session id) in the org, with turn/error counts, duration, tokens and cost. Optional free-text search across session id and name, and a start-time range.", {
|
|
132
|
+
server.tool("2kw_list_trace_sessions", "List sessions (spans grouped by an exporter-stamped session id) in the org, with turn/error counts, duration, tokens and cost. Optional free-text search across session id and name, an exact session id match, and a start-time range. Each line starts with the session's own id, which 2kw_get_trace_session takes.", {
|
|
125
133
|
search: z.string().optional().describe("Free-text search across session id and name"),
|
|
134
|
+
sessionId: z
|
|
135
|
+
.string()
|
|
136
|
+
.optional()
|
|
137
|
+
.describe("Exact match on the session.id the exporter stamped (slashes allowed)"),
|
|
126
138
|
from: z.string().optional().describe("Start of time range (ISO-8601)"),
|
|
127
139
|
to: z.string().optional().describe("End of time range (ISO-8601)"),
|
|
128
140
|
page: z.number().int().min(0).optional().describe("Page index (0-based)"),
|
|
@@ -133,6 +145,7 @@ export function register(server, client) {
|
|
|
133
145
|
params: {
|
|
134
146
|
query: {
|
|
135
147
|
search: params.search,
|
|
148
|
+
sessionId: params.sessionId,
|
|
136
149
|
from: params.from,
|
|
137
150
|
to: params.to,
|
|
138
151
|
page: params.page ?? 0,
|
|
@@ -145,7 +158,7 @@ export function register(server, client) {
|
|
|
145
158
|
const services = Array.isArray(s.sourceServices)
|
|
146
159
|
? s.sourceServices.join(", ")
|
|
147
160
|
: "";
|
|
148
|
-
return `- ${s.sessionId} ${s.name ?? "(unnamed)"} · ${s.durationMs}ms · ${s.turnCount} turns · ${s.errorCount} errors · ${services}`;
|
|
161
|
+
return `- ${s.id} ${s.sessionId} ${s.name ?? "(unnamed)"} · ${s.durationMs}ms · ${s.turnCount} turns · ${s.errorCount} errors · ${services}`;
|
|
149
162
|
});
|
|
150
163
|
return {
|
|
151
164
|
content: [
|
|
@@ -163,12 +176,12 @@ export function register(server, client) {
|
|
|
163
176
|
};
|
|
164
177
|
}
|
|
165
178
|
});
|
|
166
|
-
server.tool("2kw_get_trace_session", "Get every span recorded under a session, flat and sorted by start time — reconstruct the conversation chronologically. The id is the
|
|
167
|
-
|
|
179
|
+
server.tool("2kw_get_trace_session", "Get every span recorded under a session, flat and sorted by start time — reconstruct the conversation chronologically. The id is the session's own id from 2kw_list_trace_sessions; to find a session by the session id the exporter stamped, use that tool's sessionId filter.", {
|
|
180
|
+
id: z.string().describe("Session id, as 2kw_list_trace_sessions lists it"),
|
|
168
181
|
}, async (params) => {
|
|
169
182
|
try {
|
|
170
|
-
const { data } = await client.GET("/v1/traces/sessions/{
|
|
171
|
-
params: { path: {
|
|
183
|
+
const { data } = await client.GET("/v1/traces/sessions/{id}", {
|
|
184
|
+
params: { path: { id: params.id } },
|
|
172
185
|
});
|
|
173
186
|
return {
|
|
174
187
|
content: [
|
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.131",
|
|
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": [
|
|
@@ -41,6 +41,10 @@
|
|
|
41
41
|
"check:generated": "tsx openapi/scripts/check-generated.ts",
|
|
42
42
|
"fix:generated": "tsx openapi/scripts/check-generated.ts --write",
|
|
43
43
|
"check:coverage": "tsx ../cli/openapi/scripts/check-coverage.ts",
|
|
44
|
+
"docs:tools": "tsx scripts/tool-docs.ts",
|
|
45
|
+
"check:tool-docs": "tsx scripts/tool-docs.ts --check",
|
|
46
|
+
"sync:connect-pause": "tsx scripts/sync-connect-pause.ts",
|
|
47
|
+
"check:connect-pause": "tsx scripts/sync-connect-pause.ts --check",
|
|
44
48
|
"test": "tsx --test tests/*.test.ts",
|
|
45
49
|
"typecheck": "tsc --noEmit"
|
|
46
50
|
},
|