@2kw/ai-mcp-server 6.3.0-dev.14 → 6.3.0-dev.153

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.
@@ -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
- const body = {};
85
- if (params.name !== undefined)
86
- body.name = params.name;
87
- if (params.description !== undefined)
88
- body.description = params.description;
89
- if (params.type !== undefined)
90
- body.type = params.type;
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: 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) }],
@@ -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
- const body = {};
95
- if (name !== undefined)
96
- body.name = name;
97
- if (description !== undefined)
98
- body.description = description;
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: body,
108
+ body: { name, description },
102
109
  });
103
110
  return {
104
111
  content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
@@ -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 (includePrompts / includeCompletions). These control whether raw prompt and completion content are persisted on ingested spans. Defaults are false for both.", {}, async () => {
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 external session id stamped by the exporter.", {
167
- sessionId: z.string().describe("Session id"),
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/{sessionId}", {
171
- params: { path: { sessionId: params.sessionId } },
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.14",
3
+ "version": "6.3.0-dev.153",
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
  },